C++:通过引用引发派生类在捕获基类时不起作用
C++: Throwing a derived class by reference does not work when catching base class
我想用基类Exception
抛出我自己的异常。有一个虚拟方法print
,它将被子类覆盖。我只捕获类型Exception&
,并使用print
来获得特定的错误。问题是,一旦我抛出一个子类的引用,它就会被当作基类来处理。
这里有一个例子:
#include <iostream>
using namespace std;
class Exception
{
public:
virtual void print()
{
cout << "Exception" << endl;
}
};
class IllegalArgumentException : public Exception
{
public:
virtual void print()
{
cout << "IllegalArgumentException" << endl;
}
};
int main(int argc, char **argv)
{
try
{
IllegalArgumentException i;
Exception& ref = i;
cout << "ref.print: ";
ref.print();
throw ref;
}
catch(Exception& e)
{
cout << "catched: ";
e.print();
}
}
这个例子的输出是:
ref.print: IllegalArgumentException
catched: Exception
使用引用应该会导致使用派生类的print
方法。在try块中,引用确实使用了它。为什么捕获的Exception&
的行为不像IllegalArgumentException
,我如何获得这种行为?
下面的代码似乎做了它应该做的事情:
try
{
IllegalArgumentException i;
Exception* pointer = &i;
throw pointer;
}
catch(Exception* e)
{
cout << "pointer catched: ";
e->print();
}
但是指针在try块的范围之外是否可能变得无效?这样做是有风险的,如果我在堆上分配内存来解决这个问题,我就有责任删除catch块中的内容,这也不太好。那么你将如何解决这个问题呢?
throw
隐式复制,从而进行切片。引用C++11,§15.1/3:
throw表达式初始化一个临时对象,称为异常对象,其类型是通过从throw操作数的静态类型中删除任何顶级cv限定符并将类型从"array of
T
"或"function returningT
"调整为"pointer toT
"或"pointer for function returnedT
"来确定的,分别地临时是一个左值,用于初始化匹配的处理程序中命名的变量。如果异常对象的类型是一个不完整的类型或指向除(可能是cv限定的)void
之外的不完整类型的指针,则程序是格式错误的。除了这些限制和15.3中提到的对类型匹配的限制外,throw
的操作数被完全视为调用中的函数参数或返回语句的操作数。
我见过一些代码库通过向异常而不是直接向对象抛出指针来解决这个问题,但就我个人而言,我首先会重新考虑您是否"需要"这样做。
当抛出一个对象时,会将该对象的副本复制到其他位置,以便可以继续堆栈展开,并且可以将副本传递给异常处理程序(通过引用不进行进一步的复制,或者通过值创建第二个副本)。
如果您使异常不可复制,则可以验证是否已创建副本。您将无法再投掷该类型的对象。
当实现复制您抛出的对象时,它会查看表达式的静态类型,而不是动态类型。这意味着在您的代码中,它看到您抛出了一个Exception
,因此生成的副本是切片的一个示例(即,不复制完整的对象,只复制基类子对象)。
如果确保throw表达式的静态类型与完整对象的类型匹配,则可以避免切片,只需不将类型强制为Exception
即可。
这应该打印"catchd:IllegalArgumentException"。
try
{
IllegalArgumentException i;
throw i;
}
catch(Exception& e)
{
cout << "caught: ";
e.print();
}
您看到的称为切片。
您显然已经习惯了多态性,在多态性中,您可以将指向子类的指针分配给指向基类的指针,并保留(子)类型和所有数据。毕竟,它只是一个被复制的指针,而不是对象本身。然而,当赋值直接在对象本身之间时,不会发生类似的情况;基类通常具有较短的实例,并且基类类型的变量后面可能没有可用空间(在您的情况下,在堆栈上;在其他人的情况下在堆上)。
因此,C++被定义为对对象执行切片。只有基类部分被复制,并且类型也被"降级"为基类。
您可以使用指针而不是引用来避免切片:
int main(int argc, char **argv)
{
try
{
IllegalArgumentException* pIAE = new IllegalArgumentException();
throw pIAE;
}
catch(IllegalArgumentException* i)
{
i->print();
}
}
捕获的指针指向同一对象,因为复制构造函数不是隐式调用的。
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- 打开 gl 精灵类不起作用
- 将子类添加到基类向量C++不起作用
- C++ 运算符删除重载对派生类不起作用
- 基类矢量填充了子类不起作用
- Fl_Window子类不起作用
- 为什么空基类优化(EBO)在MSVC中不起作用
- 识别标识符为的派生类不起作用
- 使用默认构造函数调用的网格类不起作用 OpenGL C++
- 朋友类不起作用
- 使用虚拟重写基类方法不起作用
- 前向声明枚举类不起作用
- C++:通过引用引发派生类在捕获基类时不起作用
- 带有私有实现的dllexport纯虚拟类不起作用
- 类不起作用的折旧函数
- Swig csclassmodifiers对C函数的Module类不起作用
- 当基类具有两个或多个模板参数时,C++11 别名基模板类变量在模板派生类中不起作用
- 用不同值进行比较的c++ int包装器类不起作用
- 为什么这个特性类不起作用,来测试一个类是否有某个typedef
- 为什么我自己的输出流类不起作用