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 destructorTo 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
EasyLet'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 aCreatureclass with:- A protected
std::string namemember - A constructor that takes a
const std::string¶meter, stores it inname, and prints:Creature [<name>] awakens - A destructor that prints:
Creature [<name>] fades away
- A protected
Monster.h: Define aMonsterclass that publicly inherits fromCreature:- A protected
int powermember - A constructor that takes a name and power, passes the name to the
Creatureconstructor using the initialization list, stores the power, and prints:Monster [<name>] emerges with power <power> - A destructor that prints:
Monster [<name>] is defeated
- A protected
main.cpp: Read two inputs (each on a separate line):- Dragon name (string)
- Power level (integer)
Define a
Dragonclass directly in main.cpp that publicly inherits fromMonster:- A constructor that takes a name and power, passes both to the
Monsterconstructor, and prints:Dragon [<name>] takes flight! - A destructor that prints:
Dragon [<name>] returns to slumber
Create a
Dragonobject 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 destructorTo 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;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container