tags:

views:

118

answers:

2

in the example below

class X
{
    int *r;
public: 
    X() {
        cout << "X is created";
        r = new int[10];
    };
    ~X() {
        cout<< "X is destroyed";
        delete [] r;
    };
};
class Y
{
public: 
    Y() {
        X x;
        throw 44;
    }; 
    ~Y() {
        cout << "Y is destroyed";
    };
};

I got this example of RAII from one site and ave some doubts. please help.

  1. in the contructor of x we are not considering the scenation "if the memory allocation fails" .
  2. Here for the destructor of Y is safe as in y construcotr is not allocating any memory. what if we need to do some memory allocation also in y constructor?
+5  A: 

in the contructor of x we are not considering the scenation "if the memory allocation fails" .

You don't have to. If it fails, the constructor will throw std::bad_alloc. I.e.:

  1. Y constructor is called.
  2. X constructor is called.
  3. The new int[] allocation fails, throwing std::bad_alloc. The memory is never allocated.
  4. Because X never finished constructing, the Y constructor fails and Y never finishes constructing either.

Therefore there are no leaks.

Here for the destructor of Y is safe as in y construcotr is not allocating any memory. what if we need to do some memory allocation also in y constructor?

You still have no problem. Allocation failures will throw std::bad_alloc. That failure is the responsibility of those using your class.

  1. Y constructor is called.
  2. X constructor is called.
  3. The new int[] allocation succeeds.
  4. The Y constructor now fails somehow and needs to throw an exception (for example an allocation failure).
  5. The exception throwing mechanism unwinds the call stack and calls destructors on any local variables, in this case including X.
  6. X's destructor delete[]s the new int[].

Again, no resources are leaked here.

Note that you do need to be wary of multiple allocations. I.e.:

class Foo
{
    int * r;
public:
    Foo() {
        r = new int;
        throw myException;
    };
    ~Foo() {
        delete r;
    };
};

NOW we have a resource leak. When the exception is thrown from the constructor, the object is never fully constructed. Since it's never fully constructed, it's never going to have it's destructor called. Therefore we leak r.

Billy ONeal
+8  A: 

In the constructor of X, if new fails it throws an exception (std::bad_alloc). This means that the constructor never completes so the object's lifetime never starts so its destructor is never called (there is no object) and there is no mismatch between new[] and delete[]. (X should have a user-declared copy constructor and a user-declared copy assignment operator as the implementation provided ones would break this guarantee if construction succeeds and the object is copied or assigned.)

In Y, if it allocates memory in it's constructor and this allocation is successful then it needs to ensure that this memory is freed if the rest of the construction throws an exception at any point and, if the constructor completes that the memory is freed in the destructor (assuming that the memory is designed to last the length of the lifetime of the object).

To make this easier any allocated memory should be immediately handed to an object whose single responsibility is to free the memory. Having one class manage raw pointers to multiple blocks of allocated memory is a recipe for complicated and error-prone management code.

Charles Bailey