请参阅以下代码及其输出 - 请解释我的代码
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;
}
输出:
Constructor Called
Constructor Called
-1
任何人都可以解释为什么在正常堆栈展开的情况下不会调用析构函数
答案 0 :(得分:10)
这个指针:
A * Aptr = new A[2];
是一个原始指针。当它超出范围时,只有指针本身被销毁 - 它指向的数组没有任何操作。因此,数组不是delete[]
,并且不会调用析构函数。这是内存泄漏的典型示例。
这个问题有三种典型的解决方案:
std::vector
std::auto_ptr
- 它不适合与数组一起使用。答案 1 :(得分:7)
不会调用析构函数,因为您分配的对象永远不会被删除。如果删除了throw
,则会获得相同的输出。
另一方面,如果您将功能更改为:
void abc(int p)
{
A A_array[2];
if (p<0)
throw p;
}
你会看到析构函数被调用。
答案 2 :(得分:6)
正如其他地方所提到的,C ++指针不会删除超出范围时指向的内存。您有几个选择:
艰难的方法 - 尝试自己做内存管理。这种方式存在内存泄漏和缓冲区溢出。尽量不要这样做。
void abc(int p)
{
A * Aptr = new A[2];
if(p<0)
{
delete [] Aptr;
throw p;
}
delete [] Aptr;
}
将数组放在堆栈上,让正常的堆栈展开处理它:
void abc(int p)
{
A Aptr[2];
if (p<0)
throw p;
}
不使用原始指针指向新分配的数组,而是使用智能指针类(如scoped_array
或shared_array
或其他一些RAII类来保持它:
void abc(int p)
{
boost::scoped_array<A> Aptr (new A[2]);
if(p<0)
throw p;
}
}
2和3实际上是C ++代码中唯一使用异常的安全选项 - 如果使用原始指针和手动内存管理,迟早会出现内存泄漏。无论你多么小心,异常安全代码几乎都需要RAII。
答案 3 :(得分:3)
除了有关boost::shared_array
和boost::scoped_array
的建议外,您还可以使用std :: vector:
std::vector<A> array( 2 );
if( p < 0 )
throw p;
请注意,如果你走这条路线,你的类型应该是可复制的,因为std :: vector会在插入期间或内部调整大小时复制它们等等。
答案 4 :(得分:0)
可能是因为您没有在delete
课程上致电A
吗?如果不删除动态分配的类,则不会调用析构函数。
答案 5 :(得分:0)
因为你在堆上分配了。释放的是包含指向对象数组的指针的变量。不是对象本身。
答案 6 :(得分:0)
Aptr是堆分配的变量,而不是堆栈变量。您需要显式删除它以便调用析构函数(并释放内存)。
答案 7 :(得分:0)
如果你愿意这样做:
A a[2];
而不是
A *Aptr = new A[2];
你得到了析构函数。 你动态分配的任何东西都必须自行解除分配。
答案 8 :(得分:0)
您在堆上创建了对象。删除对象时,析构函数将为callen。
如果不在堆上创建对象,则将按预期调用析构函数。