为啥基类捕获块捕获派生类对象?
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 子句的列出顺序。如果以下任何一项为真,则例外是匹配项:E
和T
是同一类型(忽略T
上的*** cv 限定符)T
是对(可能是 cv 限定的)E
的左值引用T
是E
的明确公共基类 […]
Base
是Derived
的明确基数,因此选择了第一个 catch 块。由于 catch-all 处理程序 (catch(...)
) 只能出现在 catch 处理程序列表的最后,因此它是最不可行的 catch 处理程序候选者。
【讨论】:
【参考方案3】:这样想。
Derived* d = new Derived;
Base* b = d;
这是有效的,因为父类引用变量可以指向子类对象。并且从Derived
到Base
的类型是自动转换的(隐式转换)。
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 子句可能会导致意外行为。
【讨论】:
以上是关于为啥基类捕获块捕获派生类对象?的主要内容,如果未能解决你的问题,请参考以下文章
为啥可以从指向实例化基类对象的强制转换指针调用非静态派生类方法?