I'm currently learning C and would like to understand how constructors work in the context of inheritance. Here's my parent and child classes:
#include <iostream>
#include <string>
enum COLOR { Green, Blue, White, Black, Brown, };
class Animal
{
protected:
std::string _name;
COLOR _color;
public:
Animal(): _name("unknown"){
std::cout << "constructing Animal object " << _name << std::endl;
}
Animal(std::string animalName, COLOR animalColor){
_name = animalName;
_color = animalColor;
std::cout << "constructing ";
std::cout<< _color;
std::cout << " Animal object " << _name << std::endl;
}
~Animal(){
std::cout << "destructing Animal object " << _name << std::endl;
}
void speak(){
std::cout << "Animal speaks" << std::endl;
}
void move() const {}
private:
};
class Mammal: public Animal{
public:
Mammal():Animal(){}
Mammal(std::string mammalName, COLOR animalColor)
:Animal(mammalName, animalColor){}
~Mammal(){
std::cout << "Destructing Mammal object " << _name << std::endl;
}
void eat() const{
std::cout << "Mammal eat" << std::endl;
}
private:
};
class Dog: public Mammal{
public:
Dog():Mammal(){}
Dog(std::string dogName, COLOR dogColor)
:Mammal(dogName, dogColor){}
~Dog(){
std::cout << "Destructing Dog object " << _name << std::endl;
}
private:
};
And here is my main:
int main(){
Animal a;
a.speak();
Animal b("Boar", Blue);
b.speak();
Mammal m("Monkey", Black);
m.speak();
Dog d("Panda", Brown);
std::cout << "Program exiting..." << std::endl;
return 0;
}
This is how the output looks like:
constructing Animal object unknown
Animal speaks
constructing 1 Animal object Boar
Animal speaks
constructing 3 Animal object Monkey
Animal speaks
constructing 4 Animal object Panda
Program exiting...
Destructing Dog object Panda
Destructing Mammal object Panda
destructing Animal object Panda
Destructing Mammal object Monkey
destructing Animal object Monkey
destructing Animal object Boar
destructing Animal object unknown
Process returned 0 (0x0) execution time : 1.102 s
What I am curious about is the destructor statements. It seems like if I add a destructor to a subclass (the same seems to go for constructors as well), you see that the destructor for the subclass runs, and then the destructor for the parent class also runs. Hence you have destructors in sequence Dog -> Mammal -> Animal. What does this mean? Does this mean that C has initialized 3 instanced objects just to make 1 Dog object? How does the flow of destructors (and constructors) work here?
CodePudding user response:
There's a specific sequence used for constructing objects, and the inverse sequence is used for destroying them.
For construction:
Initialization order
The order of member initializers in the list is irrelevant: the actual order of initialization is as follows:
- If the constructor is for the most-derived class, virtual bases are initialized in the order in which they appear in depth-first left-to-right traversal of the base class declarations (left-to-right refers to the appearance in base-specifier lists)
- Then, direct bases are initialized in left-to-right order as they appear in this class's base-specifier list
- Then, non-static data member are initialized in order of declaration in the class definition.
- Finally, the body of the constructor is executed
And for destruction:
Destruction sequence
For both user-defined or implicitly-defined destructors, after the body of the destructor is executed, the compiler calls the destructors for all non-static non-variant members of the class, in reverse order of declaration, then it calls the destructors of all direct non-virtual base classes in reverse order of construction (which in turn call the destructors of their members and their base classes, etc), and then, if this object is of most-derived class, it calls the destructors of all virtual bases.
Even when the destructor is called directly (e.g.
obj.~Foo();
), the return statement in~Foo()
does not return control to the caller immediately: it calls all those member and base destructors first.
Essentially construction builds the base class part first and then the derived class, and then destruction destroys it in reverse order.
In your code, first the body of the Dog
destructor executes, then the Mammal
destructor executes. First its body executes, and then the destructor of the Animal
base executes.
CodePudding user response:
Does this mean that C has initialized 3 instanced objects just to make 1 Dog object?
No. That means, that derived class invokes parent classes destructors implicitly, so they still have a chance to free private resources. All three destructors in this case are called because of only one instance of the Dog class is being destructed.
the same seems to go for constructors as well
Constructors have similar concept, but unlike destructors you often call them explicitly, so that is not that surprising.