在this C++ tutorial中,标题为“标准异常”的部分中,有一个示例代码,它使用从STL中的标准异常类派生的类:
// standard exceptions
#include <iostream>
#include <exception>
using namespace std;
class myexception: public exception
{
virtual const char* what() const throw()
{
return "My exception happened";
}
} myex; //Declares an instance of myexception outside of the main function
int main () {
try
{
throw myex;
}
catch (exception& e) //My question is regarding this line of code
{
cout << e.what() << endl;
}
return 0;
}
该代码打印出My exception happened
。但是,如果我删除&符号,它会打印出std::exception
,这是当您使用标准异常类而不是派生类调用what()
时会发生的情况。
网站给出了这样的解释:
我们已经放置了一个通过引用捕获异常对象的处理程序 (请注意&符号后的&符号),因此也可以捕获 派生自异常的类,就像我们的类的myex对象一样 myexception。
抛出myex
类似于“调用catch
函数,并将myex
作为参数传递”?因为就是这种情况,我会想象你是按值还是通过引用抛出异常并不重要(这是&符号的权利?),因为你仍然在抛出myexception
而不是{exception
1}}。由于动态绑定和多态性等原因,e.what()
仍应打印My exception happened
而不是std::exception
。
答案 0 :(得分:10)
由于对象切片而发生这种情况。
如果将派生类的对象分配给基类的实例(如在通过值时使用副本c'tor的情况下),派生类的所有信息都将丢失(切片)。
例如,
class Base {
int baseInfo;
};
class Derived : public Base
{
int someInfo;
};
然后,如果你写这个:
Derived myDerivedInstance;
Base baseInstance = myDerivedInstance;
然后myDerivedInstance
中的“someInfo”被切除baseInstance
。
答案 1 :(得分:3)
你可以这样想,但是(就像函数调用一样),异常通过值传递给catch
处理程序。由于它是按值传递的,因此将其复制到std::exception
处理程序的catch
本地。它使用std::exception
的复制构造函数执行此操作,在这种情况下,副本不再具有myexception
所拥有的任何私有数据,也不具有派生函数。功能完全相同。这称为“对象切片”,这就是您永远不会按值存储虚拟基类型的原因,只能通过指针或引用来存储。您会看到与std::vector<std::exception>
相同的行为。包含的元素将丢失所有派生数据。
答案 2 :(得分:2)
在C ++中,您可以在传递和捕获对象时使用复制语义或引用语义,复制是从C继承的默认值。当您使用复制语义时,程序只构造异常来自 myexception 的对象。由于异常对其后代一无所知,因此您将丢失 myexception 对象的特定部分。
这就是为什么在需要多态时必须始终使用引用或指针。