使用 RAII 替换最终块以释放内存

Using RAII to replace the finally block to free memory

本文关键字:释放 内存 RAII 替换 使用      更新时间:2023-10-16

我正在研究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()(时,它也不起作用。我应该执行哪些修复才能实现所需的操作?

这是因为您使用new Finally(arr)创建的对象并没有真正在程序中被破坏。

你对对象的分配只会立即丢弃对象,导致内存泄漏,但更重要的是,它是在尝试和捕获的范围之外创建的。

要让它工作,你必须做一些类似的事情

try {
Finally f(arr);
foo(arr);
} catch (int e) {
cout << "Oh No!" << endl;
}

这将在 try 中f创建一个新对象,然后在抛出异常时(以及如果没有抛出异常,则超出try范围时(该对象将被销毁。

你假设C++作品喜欢Java。 它没有,所以你的代码中实际上有一些错误

声明

new Finally(arr);

动态分配一个Finally,但它永远不会在你的代码中发布。 因此,永远不会调用其析构函数。

而是做

Finally some_name(arr);

这将在main()结束时调用Finally的析构函数,这将提供您期望的输出。

然而,第二件错误的事情是Finally的析构函数delete (p)给出未定义的行为,因为pnew int [10]的结果(在main()年(。 要为代码提供明确定义的行为,请将delete (p)更改为delete [] p

第三,通过上述两个修复程序,您没有使用 RAII。 RAII 的意思是"资源获取是初始化",这实际上不是您的代码所做的。 更好的形式是在构造函数中使用新表达式初始化pFinally并在析构函数中使用正确的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()的呼唤

foo(some_name.data());

更一般地说,不要再假设C++像Java一样工作。 两种语言的工作方式不同。 如果你坚持像在 Java 中那样使用C++构造函数,你将编写出非常错误的C++代码。