Reputation: 51
I am trying to understand the order in which constructors and destructors are called by writing some sample code and trying to follow the flow of the program. In most cases, I was able to understand (with the help of Google where needed). However, in one particular case, I have hit a bit of road-block.
This is the program I am using:
#include <iostream>
class baseC
{
public:
baseC() { std::cout << "Calling constructor of base class: " << std::endl; }
virtual char const * getName(){ return "Base Class";}
~baseC(){ std::cout << "Calling destructor of base class: " << std::endl;}
};
class childC : public baseC
{
public:
childC() { std::cout << "Calling constructor of child class: " << std::endl; }
char const * getName(){ return "Child Class";}
~childC(){ std::cout << "Calling destructor of child class: " << std::endl; }
};
int main()
{
baseC c3 = childC();
std::cout << c3.getName() << std::endl;
}
This is the output I get:
$ g++ test_vd_se.cpp -o test; ./test
Calling constructor of base class:
Calling constructor of child class:
Calling destructor of child class:
Calling destructor of base class:
Base Class
Calling destructor of base class:
The compiler seems to first create a base class and the child class (this is expected), however it goes on to destroy both classes, and yet it can call a member function from the base class and goes on to destroy the base class once again.
I would be grateful if someone could explain why the functions are called in that order.
Upvotes: 3
Views: 500
Reputation: 29362
The "anomaly" comes from the following assignment:
baseC c3 = childC();
where you first create a temporary childC
, invoking in order the constructors from the top down:
Calling constructor of base class:
Calling constructor of child class:
Then the assignment takes place, so a baseC
object is created. But this time, it is not your constructor that is invoked, but the default copy constructor. That is why, we did not observe again Calling constructor of base class:
(for the construction of the object c3
). To prove it, try to add a copy-constructor to your baseC class:
baseC(const baseC& other) { std::cout << "Calling Copy-constructor of base class: " << std::endl; }
And with the same main function, you will observe the sentence twice in the output:
Calling constructor of base class:
Calling constructor of child class:
**Calling copy-constructor of base class:**
Calling destructor of child class:
Calling destructor of base class:
Base Class
Calling destructor of base class:
Finally, the temporary child object is destroyed, so the destructors are invoked from the bottom up.
Calling destructor of child class:
Calling destructor of base class:
Now the baseC
object c3
is still there, invoked the getName() method, which outputs:
Child Class
And then when the variable c3
goes out of scope (end of main()
), c3
is destroyed:
Calling destructor of base class:
Finally, things would be different with baseC& c3 = ChildC();
(compiles with VS2015, I am not sure if it complies to the C++14 standard) which does not create two objects but only one. The sequence would be then:
contruction of baseC
contruction of childC
destruction of childC
destruction of baseC
Finally, it is always safer and good practice to declare your destructors as virtual.
Upvotes: 0
Reputation: 181068
The issue here is you are slicing the object.
baseC c3 = childC();
Is going to create a temporary childC
and then copy that objects into c3
. This is why you see
Calling constructor of base class: // create base part of temporary
Calling constructor of child class: // create temporary
// the copy happens here but you do not output when copying
Calling destructor of child class: // destroy base part of temporary
Calling destructor of base class: // destroy temporary
The corret way to do this is to use a smart pointer. If you change main()
to
int main()
{
auto c3 = std::make_unique<childC>();
std::cout << c3->getName() << std::endl;
}
Or if you do not have access to smart pointers:
int main()
{
baseC* c3 = new childC();
std::cout << c3->getName() << std::endl;
delete c3;
}
You get:
Calling constructor of base class:
Calling constructor of child class:
Child Class
Calling destructor of child class:
Calling destructor of base class:
We also need to make ~baseC()
virtual
so the the correct destructor is called.
virtual ~baseC(){ std::cout << "Calling destructor of base class: " << std::endl;}
You will also note that now Child Class
is printed instead of Base Class
since now that we have a pointer dynamic dispatch kicks in and it calls the correct virtual function.
Upvotes: 3