0

As we all know, constructor and destructor go in pairs.

But the following piece of code doesn't behave that way, the contructor is called twice but the destructor is called only once!

{
    Animal ahe;
    ahe = CreateAnimal();
}

Please read on for more detailed explanation.

Suppose there is a class named Animal, and we have the following code:

int main(int argc, char* argv[])
{
    Animal ahe;

    return 0;
}

It is expected that Animal's contructor and destructor will both be called once and only once. And when I run the code, it behaves exactly as what I have expected.

But it looks quite weird when it comes to the following code, when a reference of Animal is returned by a function. When I run the code, the contructor is called twice but the destructor is called only once!

#include "stdafx.h"
#include "stdio.h"
#include "iostream.h"

class Animal
{
public:
    Animal();
    Animal(long age);

    ~Animal();
private:
    long m_age;
};

Animal::~Animal()
{
    cout<<"Animal::~Animal()"<<"age="<<m_age<<endl;
}
Animal::Animal()
{
    m_age = 1;
    cout<<"Animal::Animal()"<<"age="<<m_age<<endl;
}

Animal::Animal(long age)
{
    m_age = age;
    cout<<"Animal::Animal()"<<"age="<<m_age<<endl;
}

Animal& CreateAnimal()
{
    Animal *pAnimal = new Animal(5);

    return *pAnimal;
}

int main(int argc, char* argv[])
{
    Animal ahe;

    ahe = CreateAnimal();

    return 0;
}

//output

Animal::Animal()age=1
Animal::Animal()age=5
Animal::~Animal()age=5

Obviously, destructor of the first object is not called, why? The problem can be serious, if Animal has a lot of resource which needs to be released in its destructor.

Jack
  • 125
  • 1
  • 1
  • 8

1 Answers1

6

You are creating the object in CreateAnimal via new which will call the constructor but you need to explicitly call the destructor for an object created via new. The code is actually working exactly as expected. The constructor is called for both objects but when the first object goes out of scope as it's on the stack it's destructor gets called but the one created using new never gets deleted. You need to call delete explicitly on it.

it's a copy of that object who's destructor is called. When you assign ahe=CreateAnimal(); a copy is created on the stack as ahe is not a reference

   Animal ahe; // -> constructor called for object on stack
     ahe = CreateAnimal();  // -> constructor called due to new call and ahe created in previous //line replaced with the object created via new
     } // main method ends, ahe goes out of scope and it's destructor is called but since  //it now contains a copy of the object created via new, it prints 5

If you don't want to remember to call delete for every object created using new then just use auto_ptrs.

Sid
  • 7,511
  • 2
  • 28
  • 41
  • +1 for spotting the operator new call in CreateAnimal so quickly. – stinky472 Mar 02 '12 at 03:43
  • But output of the code indicates destructor of the object that is created via new IS called. It is the first object (Animal ahe;) whose destructor is not called. – Jack Mar 02 '12 at 03:44
  • 2
    Might be worth mentioning smart pointers or move semantics and how we should never, ever return a reference to an object allocated on the free store inside a function. That's even worse than returning a pointer with the expectation for the client to delete as it would be an even more hidden memory leak that can't be fixed without changing the whole interface. – stinky472 Mar 02 '12 at 03:45
  • 1
    @Jack, no, you are copying the object created via new using the assignment operator. So the original object gets the value of 5, then it is destroyed, which is why you see `"Animal::~Animal()age=5"` rather than `"Animal::~Animal()age=1"` – Benjamin Lindley Mar 02 '12 at 03:46
  • No, it's a copy of that object who's destructor is called. When you assign ahe=CreateAnimal(); a copy is created on the stack as ahe is not a reference. – Sid Mar 02 '12 at 03:46
  • @Jack it is created when operator new is called. But any operator new requires a matching operator delete to free the memory and invoke the destructor. Same with operator new[]/delete[]. These things always need to come in matching pairs. – stinky472 Mar 02 '12 at 03:46
  • @Jack, the default assignment operator does a member-wise copy of each variable; it doesn't create a new Animal class object. The destructor that you are seeing is actually for the Animal class that was not created with `new`. – Justin Peel Mar 02 '12 at 03:50
  • The decision should not be made based on whether you want to remember to call delete. It should be made based on whether you want to take the responsibility of manually ensuring `delete` gets called at the right time, *always*, even in the presence of `return`s, `break`s, `continue`s, exceptions. – R. Martinho Fernandes Mar 02 '12 at 04:44
  • Many thanks to Sid, stinky472, Benjamin, Justin and R. Martinho. – Jack Mar 02 '12 at 06:22