简体   繁体   中英

Using RAII to replace the finally block to free memory

I was studying about the RAII mechanism in C++ which replaces the finally of Java. I wrote the following code to test it:

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;
}

I want to free the memory which I used for arr so I set a new class Finally which saves the pointer to the array and when it exits the scope it should call the destructor and free it. But the output is:

constructor
A
Oh No!
Done

No call for the destructor. It also does not work when I move the body of main to some other void method (like void foo() ). What fix should I do to achieve the desired action?

That's because the object you create with new Finally(arr) doesn't really gets destructed in your program.

Your allocation of the object just throws the object away immediately, leading to a memory leak, but more importantly it's created outside the scope of the try and catch.

For it to work you have to do something like

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

That would create a new object f in the try, which will then get destructed when the exception is thrown (as well as when it goes out of scope of the try if no exception is thrown).

You're assuming C++ works liked Java. It doesn't, so there are actually a few things wrong in your code

The statement

 new Finally(arr);

dynamically allocates a Finally , but it is NEVER released in your code. Hence its destructor is never called.

Instead do

 Finally some_name(arr);

This will invoke the destructor of Finally - at the end of main() - which will give the output your expect.

However, the second thing wrong is that the destructor of Finally does delete (p) which gives undefined behaviour, since p is the result (in main() ) of new int [10] . To give the code well-defined behaviour, change delete (p) to delete [] p .

Third, with the two fixes above, you are not using RAII. RAII means "Resource Acquisition Is Initialisation", which is not actually what your code does. A better form would be to initialise p using a new expression in Finally s constructor and release it with a correct delete expression in the destructor.

class Finally
{
     private:
        int* p;
     public:
        Finally() : p(new int [10])
        {
            cout << "constructor" << endl;
        };

       ~Finally()
       {
           cout << "destructor" << endl;
           delete [] p;
       };

       int *data() {return p;};
};

AND replace the first two lines of your main() with a single line

Finally some_name;

and the call of foo() with

foo(some_name.data());

More generally, stop assuming that C++ works like Java. Both languages work differently. If you insist on using C++ constructors like you would in Java, you will write terribly buggy C++ code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM