Search code examples
c++inheritanceconstructorpolymorphism

C++ understanding constructors in inheritance


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?


Solution

  • 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:

    1. 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)
    2. Then, direct bases are initialized in left-to-right order as they appear in this class's base-specifier list
    3. Then, non-static data member are initialized in order of declaration in the class definition.
    4. 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.