Menu
Coddy logo textTech

Ctor & Dtor Call Order

Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 50 of 104.

When you create a derived class object, C++ follows a specific order for calling constructors and destructors. Understanding this order is essential for proper initialization and cleanup in inheritance hierarchies.

The rule is simple: constructors are called from base to derived, while destructors are called from derived to base. This ensures the base class is fully initialized before the derived class tries to use it.

class Base {
public:
    Base() { std::cout << "Base constructor" << std::endl; }
    ~Base() { std::cout << "Base destructor" << std::endl; }
};

class Derived : public Base {
public:
    Derived() { std::cout << "Derived constructor" << std::endl; }
    ~Derived() { std::cout << "Derived destructor" << std::endl; }
};

int main() {
    Derived d;
    return 0;
}

Output:

Base constructor
Derived constructor
Derived destructor
Base destructor

To pass arguments to a base class constructor, use the initialization list:

class Animal {
    std::string name;
public:
    Animal(const std::string& n) : name(n) {
        std::cout << "Animal: " << name << std::endl;
    }
};

class Dog : public Animal {
public:
    Dog(const std::string& n) : Animal(n) {
        std::cout << "Dog created" << std::endl;
    }
};

The base class constructor must be called in the initialization list, not in the constructor body. This guarantees proper construction order and is required when the base class has no default constructor.

challenge icon

Challenge

Easy

Let's build a three-level class hierarchy to observe how constructors and destructors are called in a chain of inheritance. You'll create a Creature base class, a Monster class that inherits from it, and a Dragon class that inherits from Monster.

You'll organize your code across three files:

  • Creature.h: Define a Creature class with:
    • A protected std::string name member
    • A constructor that takes a const std::string& parameter, stores it in name, and prints: Creature [<name>] awakens
    • A destructor that prints: Creature [<name>] fades away
  • Monster.h: Define a Monster class that publicly inherits from Creature:
    • A protected int power member
    • A constructor that takes a name and power, passes the name to the Creature constructor using the initialization list, stores the power, and prints: Monster [<name>] emerges with power <power>
    • A destructor that prints: Monster [<name>] is defeated
  • main.cpp: Read two inputs (each on a separate line):
    1. Dragon name (string)
    2. Power level (integer)

    Define a Dragon class directly in main.cpp that publicly inherits from Monster:

    • A constructor that takes a name and power, passes both to the Monster constructor, and prints: Dragon [<name>] takes flight!
    • A destructor that prints: Dragon [<name>] returns to slumber

    Create a Dragon object inside a block scope { } so you can observe the full construction and destruction sequence before the program ends. After the block, print: Adventure complete!

For example, with inputs Smaug and 100:

Creature [Smaug] awakens
Monster [Smaug] emerges with power 100
Dragon [Smaug] takes flight!
Dragon [Smaug] returns to slumber
Monster [Smaug] is defeated
Creature [Smaug] fades away
Adventure complete!

Notice how constructors run from base to derived (Creature, then Monster, then Dragon), while destructors run in reverse order (Dragon, then Monster, then Creature). Don't forget header guards and to include the appropriate headers in each file.

Cheat sheet

In C++ inheritance, constructors are called from base to derived, while destructors are called from derived to base. This ensures proper initialization and cleanup.

class Base {
public:
    Base() { std::cout << "Base constructor" << std::endl; }
    ~Base() { std::cout << "Base destructor" << std::endl; }
};

class Derived : public Base {
public:
    Derived() { std::cout << "Derived constructor" << std::endl; }
    ~Derived() { std::cout << "Derived destructor" << std::endl; }
};

Output when creating a Derived object:

Base constructor
Derived constructor
Derived destructor
Base destructor

To pass arguments to a base class constructor, use the initialization list:

class Animal {
    std::string name;
public:
    Animal(const std::string& n) : name(n) {
        std::cout << "Animal: " << name << std::endl;
    }
};

class Dog : public Animal {
public:
    Dog(const std::string& n) : Animal(n) {
        std::cout << "Dog created" << std::endl;
    }
};

The base class constructor must be called in the initialization list, not in the constructor body. This is required when the base class has no default constructor.

Try it yourself

#include <iostream>
#include <string>
#include "Monster.h"

using namespace std;

// TODO: Define Dragon class that publicly inherits from Monster
// - Constructor takes name and power, passes both to Monster constructor
// - Constructor prints: "Dragon [<name>] takes flight!"
// - Destructor prints: "Dragon [<name>] returns to slumber"
class Dragon : public Monster {
public:
    Dragon(const string& n, int p) : Monster(n, p) {
        // TODO: Your code here
    }

    ~Dragon() {
        // TODO: Your code here
    }
};

int main() {
    string dragonName;
    int powerLevel;
    
    getline(cin, dragonName);
    cin >> powerLevel;

    // TODO: Create a Dragon object inside a block scope { }
    // so you can observe the full construction and destruction sequence
    {
        // TODO: Create Dragon object here
    }

    // TODO: Print "Adventure complete!" after the block

    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming