我曾经studying谈论过C ++中的RAII机制,该机制取代了Java的finally
。
我编写了以下代码对其进行测试:
void foo(int* arr) {
cout << "A" << endl;
throw 20;
cout << "B" << endl;
}
class Finally {
private:
int* p;
public:
Finally(int* arr) {
cout << "constructor" << endl;
p = arr;
}
~Finally() {
cout << "destructor" << endl;
delete(p);
}
};
int main()
{
int * arr = new int[10];
new Finally(arr);
try {
foo(arr);
} catch (int e) {
cout << "Oh No!" << endl;
}
cout << "Done" << endl;
return 0;
}
我想释放用于arr
的内存,所以我设置了一个新类Finally
,它将指针保存到数组,当它退出范围时,应调用析构函数并释放它。但是输出是:
constructor
A
Oh No!
Done
不调用析构函数。当我将main
的正文移到其他void方法(如void foo()
)时,它也不起作用。我该怎么做才能达到预期的效果?
答案 0 :(得分:2)
那是因为用new Finally(arr)
创建的对象并没有在程序中被破坏。
您对对象的分配只会立即将其丢弃,从而导致内存泄漏,但更重要的是,它是在try and catch范围之外创建的。
要使其正常工作,您必须做类似的事情
try {
Finally f(arr);
foo(arr);
} catch (int e) {
cout << "Oh No!" << endl;
}
这将在尝试中创建一个新对象f
,然后在引发异常时(以及在没有引发异常的情况下超出try
的范围时,该对象将被破坏) )。
答案 1 :(得分:1)
您假设C ++可以像Java一样工作。事实并非如此,因此您的代码实际上存在一些错误
声明
new Finally(arr);
动态分配Finally
,但是永远不会在您的代码中发布。因此,永远不会调用它的析构函数。
代替
Finally some_name(arr);
这将在Finally
的末尾调用main()
的析构函数,这将给您期望的输出结果。
但是,第二件事是错误的,Finally
的析构函数执行delete (p)
会给出不确定的行为,因为p
是{{的结果(在main()
中) 1}}。要使代码具有良好定义的行为,请将new int [10]
更改为delete (p)
。
第三,使用上述两个修复程序,您没有使用RAII。 RAII的意思是“资源获取就是初始化”,这实际上不是您的代码所做的。更好的形式是在delete [] p
构造函数中使用新表达式初始化p
,然后在析构函数中使用正确的Finally
表达式释放它。
delete
然后用一行替换class Finally
{
private:
int* p;
public:
Finally() : p(new int [10])
{
cout << "constructor" << endl;
};
~Finally()
{
cout << "destructor" << endl;
delete [] p;
};
int *data() {return p;};
};
的前两行
main()
和Finally some_name;
的调用
foo()
更一般地,停止假设C ++像Java一样工作。两种语言的工作方式不同。如果您坚持要像在Java中那样使用C ++构造函数,那么您将编写出令人毛骨悚然的C ++代码。