views:

190

answers:

9

Please see the following code and its output - please explain me the code

void abc(int);

class A
{
public:
    A()
    {
     cout<<"Constructor Called";
    }
    ~A()
    {
     cout<<"Destructor called";
    }
};

int main()
{
    try
    {
     abc(-1);
    }
    catch(int p)
    {
     cout<<p<<endl;
    }
    return 0;
}

void abc(int p)
{
    A * Aptr = new A[2];
    if(p<0)
        throw p;
}

Output:

Constructor Called
Constructor Called
-1

can anyone explain why is the destructor not being called as in the case of normal stack unwinding

+10  A: 

This pointer:

 A * Aptr = new A[2];

is a raw pointer. When it goes out of scope only the pointer itself is destroyed - nothing is done to the array it points to. So the array is not delete[]'ed and the destructors are not called. It's a typical example of a memory leak.

There're three typical solutions to the problem:

  • allocate the array itself on stack
  • use std::vector
  • use a smart pointer (not std::auto_ptr - it is not suitable for using with arrays.
sharptooth
And the typical solution is?
Martin York
A: 

Could it be because you are not calling delete on your A class? If you don't delete a dynamically allocated class the desnstructor is not called.

Miky Dinescu
A: 

because you allocated on the heap. What gets freed are the variables holding the pointer to the array of objects. Not the objects themselves.

Stefano Borini
A: 

Aptr is a heap-allocated variable, not a stack variable. You need to explicitly delete it for the destructor to be called (and to free the memory).

jlew
+7  A: 

The destructor is not called because the objects you allocate are never deleted. You would get the same output if you removed the throw.

If, on the other hand, you changed your function into this:

void abc(int p)
{
    A A_array[2];
    if (p<0)
        throw p;
}

You would see that the destructor was called.

Magnus Hoff
A: 

If you would instead do this:

A a[2];

and not

A *Aptr = new A[2];

you'd get the destructor called. Anything you allocate dynamically you will have to deallocate yourself.

Mattias Nilsson
A: 

You created the object on the heap. The destructor will be callen when you delete the object.

If you don't create the object on the heap, the destructor will be called as you expected.

fmuecke
+6  A: 

As mentioned elsewhere, C++ pointers do NOT delete the memory they point to when going out of scope. You have a few options:

  1. The hard way - try and do the memory management yourself. This way lies memory leaks and buffer overflows. Try not to do this.

    void abc(int p)
    {
        A * Aptr = new A[2];
        if(p<0)
        {
            delete [] Aptr;
            throw p;
        }
        delete [] Aptr;
    }
    
  2. Put the array on the stack and let the normal stack unwinding handle it:

    void abc(int p)
    {
       A Aptr[2];
       if (p<0)
           throw p;
    }
    
  3. Instead of using a raw pointer to point to the newly allocated array, hold onto it using a smart pointer class like scoped_array or shared_array, or some other RAII class:

    void abc(int p)
    {
        boost::scoped_array<A> Aptr (new A[2]);
        if(p<0)
            throw p;
        }
    }
    

2 and 3 are really the only safe options in C++ code that uses exceptions - if you use raw pointers and manual memory management, you WILL end up with memory leaks sooner or later. No matter how careful you are, exception safe code pretty much requires RAII.

Eclipse
+4  A: 

In addition to the suggestions concerning boost::shared_array and boost::scoped_array you could also just use std::vector:

std::vector<A> array( 2 );
if( p < 0 ) 
  throw p;

Note that your types should be copyable if you go this route, however, as std::vector will copy them around during insert or when internally resizing, et cetera.

Josh Petrie