12
try
{
    throw Derived();
}
catch (Base&)
{
    std::cout << "subtyping\n";
}

try
{
    throw "lol";
}
catch (std::string)
{
    std::cout << "coercion\n";
}

输出:

subtyping
terminate called after throwing an instance of 'char const*'

为什么异常处理对子类型很好,但对强制却不行?

4

2 回答 2

18

捕获抛出的异常与将参数传递给函数完全不同。有相似之处,但也有细微的差别。

3个主要区别是:

  • 异常总是至少被复制一次(根本不可能避免)
  • catch子句按照声明的顺序进行检查(不是最合适的)
  • 它们受到较少形式的类型转换
    • 基于继承的覆盖,
    • 从类型化指针到非类型化指针的转换(const void*捕获任何指针)

不允许任何其他类型的转换(例如inttodouble或隐式const char*to string- 您的示例)。

关于评论中的问题 假设存在层次结构:

class Base {}; 
class Derived: public Base {};
class Base2 {};
class Leaf: public Derived, public Base2 {};

现在根据catch子句的顺序,将执行适当的块。

try {
    cout << "Trying ..." << endl;
    throw Leaf();

} catch (Base& b) {
    cout << "In Base&";

} catch (Base2& m) {
    cout << "In Base2&"; //unreachable due to Base&

} catch (Derived& d) {
    cout << "In Derived&";  // unreachable due to Base& and Base2&
}

如果您切换BaseBase2捕获订单,您会注意到不同的行为。如果Leaf从 私下继承Base2,那么catch Base2&无论放在哪里都无法访问(假设我们抛出 a Leaf

通常很简单:顺序很重要。

于 2013-02-26T20:41:58.187 回答
8

C++11 标准的第 15.3/3 段定义了处理程序与某个异常对象匹配的确切条件,这些条件不允许用户定义的转换

处理程序是匹配异常对象的类型E,如果

— 处理程序的类型是cv Torcv T&并且ET是相同的类型(忽略顶级 -cv限定符),或者

— 处理程序的类型是cv Torcv T&并且是, orT的明确公共基类E

— 处理程序是类型cv1 T* cv2,并且E是一个指针类型,可以通过其中一个或两个转换为处理程序的类型

  • 标准指针转换 (4.10) 不涉及到指向私有或受保护或不明确类的指针的转换

  • 资格转换

— 处理程序是一个指针或指向成员类型的指针,并且Estd::nullptr_t.

[ ... ]

于 2013-02-26T20:21:21.350 回答