man*_*pt1 2 c++ exception-handling
请参阅以下代码及其输出 - 请解释我的代码
void abc(int);
class A
{
public:
A()
{
cout<<"Constructor Called";
}
~A()
{
cout<<"Destructor called";
}
};
int main()
{
try
{
abc(-1);
}
catch(int p)
{
cout<<p<<endl;
}
return 0;
}
void abc(int p)
{
A * Aptr = new A[2];
if(p<0)
throw p;
}
Run Code Online (Sandbox Code Playgroud)
输出:
Constructor Called
Constructor Called
-1
Run Code Online (Sandbox Code Playgroud)
任何人都可以解释为什么在正常堆栈展开的情况下不会调用析构函数
sha*_*oth 10
这个指针:
A * Aptr = new A[2];
Run Code Online (Sandbox Code Playgroud)
是一个原始指针.当它超出范围时,只有指针本身被销毁 - 它指向的数组没有任何操作.因此,数组不会delete[]被编辑,并且不会调用析构函数.这是内存泄漏的典型示例.
这个问题有三种典型的解决方案:
std::vectorstd::auto_ptr- 它不适合与数组一起使用.不会调用析构函数,因为您分配的对象永远不会被删除.如果你删除了,你会得到相同的输出throw.
另一方面,如果您将功能更改为:
void abc(int p)
{
A A_array[2];
if (p<0)
throw p;
}
Run Code Online (Sandbox Code Playgroud)
您会看到析构函数被调用.
正如其他地方所提到的,C++指针不会删除超出范围时指向的内存.你有几个选择:
艰难的方法 - 尝试自己做内存管理.这种方式存在内存泄漏和缓冲区溢出.尽量不要这样做.
void abc(int p)
{
A * Aptr = new A[2];
if(p<0)
{
delete [] Aptr;
throw p;
}
delete [] Aptr;
}
Run Code Online (Sandbox Code Playgroud)将数组放在堆栈上,让正常的堆栈展开处理它:
void abc(int p)
{
A Aptr[2];
if (p<0)
throw p;
}
Run Code Online (Sandbox Code Playgroud)不使用原始指针指向新分配的数组,而是使用类似scoped_arrayor shared_array或其他RAII类的智能指针类来保持它:
void abc(int p)
{
boost::scoped_array<A> Aptr (new A[2]);
if(p<0)
throw p;
}
}
Run Code Online (Sandbox Code Playgroud)2和3实际上是C++代码中唯一使用异常的安全选项 - 如果使用原始指针和手动内存管理,迟早会出现内存泄漏.无论你多么小心,异常安全代码几乎都需要RAII.