使用RAII替换finally块以释放内存

时间:2019-09-14 13:04:18

标签: c++ raii

我曾经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())时,它也不起作用。我该怎么做才能达到预期的效果?

2 个答案:

答案 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 ++代码。