在下面的代码中,基于堆栈的变量“ex”被抛出并被捕获到超出声明ex范围的函数中。这对我来说有点奇怪,因为(AFAIK)基于堆栈的变量不能在它们被声明的范围之外使用(堆栈被解开)。
void f() {
SomeKindOfException ex(...);
throw ex;
}
void g() {
try {
f();
} catch (SomeKindOfException& ex) {
//Handling code...
}
}
我在SomeKindOfException的析构函数中添加了一个print语句,它显示ex在f()中超出范围后被破坏但是它被g()捕获并且一旦它超出范围就再次被破坏。
任何帮助?
答案 0 :(得分:19)
异常对象被复制到一个特殊位置,以便在堆栈展开后继续存在。您看到两个析构的原因是因为当您退出f()时,原始异常将被销毁,当您退出g()时,副本将被销毁。
答案 1 :(得分:9)
将对象复制到异常对象中,该对象在堆栈展开后仍然存在。该对象的内存来自何处未指定。对于大对象,它可能是malloc
'ed,对于较小的对象,实现可能有一个预分配的缓冲区(我可以想象这可以用于bad_alloc
异常)。
然后将引用ex
绑定到异常对象,这是一个临时对象(它没有名称)。
答案 2 :(得分:9)
C ++ Standard 15.1 / 4:
要抛出的异常的临时副本的内存以未指定的方式分配, 除非在3.7.3.1中注明。只要存在为此执行的处理程序,临时就会持续存在 例外。特别是,如果处理程序通过执行throw而退出;声明,将控制传递给另一个人 处理程序为同一个异常,所以临时保留。当最后一个处理程序被执行时 异常以抛出以外的方式退出;临时对象被销毁并执行 可以释放临时对象的内存;任何这种解除分配都是以未指明的方式完成的。 在销毁异常声明中声明的对象后立即发生破坏 在处理程序中。
没有什么可说的。
答案 3 :(得分:6)
当你抛出ex时,它会被复制到用于抛出异常对象的特殊内存位置。此类副本由普通拷贝构造函数执行。
您可以通过此示例轻松查看此内容:
#include <iostream>
void ThrowIt();
class TestException
{
public:
TestException()
{
std::cerr<<this<<" - inside default constructor"<<std::endl;
}
TestException(const TestException & Right)
{
(void)Right;
std::cerr<<this<<" - inside copy constructor"<<std::endl;
}
~TestException()
{
std::cerr<<this<<" - inside destructor"<<std::endl;
}
};
int main()
{
try
{
ThrowIt();
}
catch(TestException & ex)
{
std::cout<<"Caught exception ("<<&ex<<")"<<std::endl;
}
return 0;
}
void ThrowIt()
{
TestException ex;
throw ex;
}
示例输出:
matteo@teolapubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic ExceptionStack.cpp -o ExceptionStack.x
matteo@teolapubuntu:~/cpp/test$ ./ExceptionStack.x
0xbf8e202f - inside default constructor
0x9ec0068 - inside copy constructor
0xbf8e202f - inside destructor
Caught exception (0x9ec0068)
0x9ec0068 - inside destructor
顺便说一下,你可以在这里看到用于抛出对象的内存位置(0x09ec0068)肯定远离原始对象(0xbf8e202f):堆栈像往常一样具有高地址,而用于抛出对象的内存在虚拟地址空间中非常小。尽管如此,这是一个实现细节,因为正如其他答案所指出的那样,标准没有说明抛出对象的内存应该在何处以及应该如何分配。
答案 4 :(得分:3)
除了标准在15.1 / 4中所说的内容(“异常处理/抛出异常”) - 抛出的异常临时副本的内存以未指定的方式分配 - 其他一些琐事关于如何分配异常对象的是:
标准的3.7.3.1/4(“分配函数”)表示异常对象不能通过new
表达式或对“全局分配函数”的调用来分配(即。,operator new()
替代品。请注意,malloc()
不是标准定义的“全局分配函数”,因此malloc()
绝对是分配异常对象的选项。
“当抛出异常时,异常对象被创建并且通常放在某种异常数据堆栈上”(Stanley Lippman,“在C ++对象模型中” - 7.2异常处理)
来自Stroustrup的“The C ++ Programming Language,3rd Edition”:“要求C ++实现有足够的备用内存,以便在内存耗尽时抛出bad_alloc
。但是,有可能抛出一些其他异常将导致内存耗尽。“(14.4.5资源耗尽);并且,“实现可能会应用各种各样的策略来存储和传输异常。但是,保证有足够的内存允许new
抛出标准的内存不足异常,{{1 “(14.3捕捉异常)。
请注意,Stroustrup的引用是预标准的。我觉得有趣的是,该标准似乎并没有保证Stroustrup认为重要,足以提及两次。
答案 5 :(得分:1)
因为规范明确指出,创建了一个临时对象来代替throw
操作数。