我试图理解c ++中异常的行为。 我写了以下代码:
class A{
public:
A(){
};
~A(){
cout<<"hello";
};
};
int exceptionTest(){
throw "blablabla";
};
int main(){
A sd;
int test = exceptionTest();
return 0;
}
我注意到在这种情况下,即使没有人捕获异常,也会调用析构函数。 如果我将“主要”代码更改为:
int main(){
A* sd = new A();
int test = exceptionTest();
return 0;
}
不会调用析构函数。 谁能告诉我不同行为的原因是什么?
谢谢, 李
答案 0 :(得分:8)
您抛出异常的事实与此无关。在第一个示例中,sd
是堆栈中存在的对象。当执行退出其范围时,无论出于何种原因,它都会被销毁。在第二个示例中,sd
是指向使用new
显式分配的对象的指针。在将指针传递给delete
之前,不会销毁此对象;因为你从未这样做,你的程序目前正在泄露它。
答案 1 :(得分:4)
该标准在此事上有以下说法:
-9-如果在程序中找不到匹配的处理程序,则调用函数
terminate()
; 在调用terminate()
之前是否已展开堆栈是实现定义的。
所以你的编译器执行堆栈展开(调用本地的析构函数),其他人可能不会。例如,使用G ++或codepad.org,此程序将不输出“hello”。
动态分配的对象在您明确销毁它们之前不会被销毁(使用删除等)。特别是,如果在此期间发生异常,代码可能永远不会到达释放语句。
答案 2 :(得分:2)
一旦变量超出范围,就会自动调用局部变量析构函数。 绝不会在指针上调用析构函数,因此您必须自己调用它。
答案 3 :(得分:2)
我注意到在这种情况下,即使没有人捕获异常,也会调用析构函数。
这正是我所期待的。
这种机制是RAII的结果,使您“确定”即使存在异常也会释放资源。例如:
class File
{
public:
File( const std::string filename ) : file_handler(file_open( filename )) { } // whatever the implementation
~File() { file_close(file_handler); }
private:
FileHandler file_handler;
};
void test(){ throw "This is a test"; }
int main()
{
File file("test.txt");
test();
return false;
}
您确信即使使用throw也会关闭该文件。因此,如果您使用RAII来管理资源。
那是因为当抛出异常时,它会被捕获,它会返回到调用堆栈中,如果没有catch,则当我们超出范围时,本地对象将被破坏。
答案 4 :(得分:0)
这不是一个真正的答案,但在RAII机制的情况下,我可能会澄清我从其他答案和Mike的评论中理解的行为。
#include <iostream>
class Bar
{
public:
Bar() { std::cout << "Bar constructor" << std::endl; }
~Bar() { std::cout << "Bar destructor" << std::endl; }
};
void foo()
{
throw("Exception");
}
int main()
{
// Variation, add { to create a new scope
Bar bar;
foo();
// Variation : }
return 0;
}
使用g++
,此代码(未捕获异常)将输出以下内容:
Bar constructor
terminate called after throwing an instance of 'char const*'
Aborted
意味着g++
不会展开堆栈(或者让变量超出范围,如果我正确理解“变量”),那么析构函数不会被调用。
但是,如果您发现异常:
#include <iostream>
class Bar
{
public:
Bar() { std::cout << "Bar constructor" << std::endl; }
~Bar() { std::cout << "Bar destructor" << std::endl; }
};
void foo()
{
throw("Exception");
}
int main()
{
try
{
Bar bar;
foo();
}
catch (...)
{
// Nothing here
}
return 0;
}
然后输出
Bar constructor
Bar destructor
并恢复正确的行为。