为啥基类捕获块捕获派生类对象?

Posted

技术标签:

【中文标题】为啥基类捕获块捕获派生类对象?【英文标题】:Why Base Class catch block catch derived class object?为什么基类捕获块捕获派生类对象? 【发布时间】:2015-04-19 14:15:30 【问题描述】:

为什么Base catch 处理程序会捕获Derived 对象,如:

#include <iostream>
using namespace std;
class Base ;
class Derived: public Base ;

int main()

   Derived d;
   try 
        throw d;
       
   catch(Base b)
   
        cout << "Caught Base Exception";
       
   catch(...)
   
       cout << "Default\n";
   
   return 0;

我得到的输出是“Caught Base Exception”。我期待“默认”。

【问题讨论】:

请注意,您在捕获对象的同时对对象进行切片。为避免这种情况,请按引用捕获。 【参考方案1】:

因为Derived 可以隐式转换为Base,所以当尝试第一个catch 处理程序时,它会成功。这就是为什么我们可以将所有std 异常称为:

catch (std::exception const& e) 
   ..

否则,我们将不得不枚举所有这些 - 往好里说是乏味的,往坏里说是不可能的。

【讨论】:

【参考方案2】:

Catch 子句按照它们列出的顺序进行评估。当 catch 子句参数满足与 throw 表达式相对应的某些先决条件时,停止对可行的 catch 子句的搜索:

复合语句中的任何语句抛出E类型的异常时,它会与每个catch-clauseT的类型相匹配/em> 在 handler-seq 中,按照 catch 子句的列出顺序。如果以下任何一项为真,则例外是匹配项:

ET 是同一类型(忽略 T 上的*** cv 限定符) T 是对(可能是 cv 限定的)E 的左值引用 TE 的明确公共基类 […]

BaseDerived 的明确基数,因此选择了第一个 catch 块。由于 catch-all 处理程序 (catch(...)) 只能出现在 catch 处理程序列表的最后,因此它是最不可行的 catch 处理程序候选者。

【讨论】:

【参考方案3】:

这样想。

Derived* d = new Derived;
Base* b = d;

这是有效的,因为父类引用变量可以指向子类对象。并且从DerivedBase 的类型是自动转换的(隐式转换)。

try-catch 块中的场景相同。

你抛出一个Derived 对象(referebce)。它可以从Derived 参考或前任参考中捕获。

理想情况下,try-catch 应如下所示。

   Derived d;
   try 
        throw d;
       
   catch(Derived d)
   
        cout << "Caught Derived Exception";
       
   catch(Base b)
   
       cout << "Caught Base Exception";
       // This will catch any other references which extends Base as well.
   
   catch(...)
   
       cout << "Default\n";
   
   return 0;

交换第一个和第二个 catch 子句可能会导致意外行为。

【讨论】:

以上是关于为啥基类捕获块捕获派生类对象?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我可以通过指向派生对象的基类指针访问派生私有成员函数?

为啥可以从指向实例化基类对象的强制转换指针调用非静态派生类方法?

请问含有多个对象成员的派生类的构造函数执行时不是先执行基类么?为啥这个先输出的是“正式生是”这个

为啥指向基类的派生类指针可以调用派生类成员函数? [复制]

为啥对派生类使用基类指针

定义派生类时,为啥基类标记为“public”? [复制]