Order of Construction of Derived Classes

In the Basic Inheritance in C++ lesson, you learned that classes can inherit members and functions from other classes. In this lesson, we'll examine more closely the order of construction that occurs when a derived class is instantiated.

First, let's introduce some new classes that will help illustrate some key concepts.

class Storage
{
public:
    int m_capacity {};

    Storage(int capacity=0)
        : m_capacity { capacity }
    {
    }

    int getCapacity() const { return m_capacity; }
};

class Container: public Storage
{
public:
    double m_weight {};

    Container(double weight=0.0)
        : m_weight { weight }
    {
    }

    double getWeight() const { return m_weight; }
};

In this example, class Container is derived from class Storage.

Because Container inherits functions and variables from Storage, you might assume that the members of Storage are copied into Container. However, this is not accurate. Instead, we can think of Container as a two-part class: one part Container, and one part Storage.

You've already seen many examples of what happens when we instantiate a regular (non-derived) class:

int main()
{
    Storage storage;

    return 0;
}

Storage is a non-derived class because it doesn't inherit from any other classes. C++ allocates memory for Storage, then calls Storage's default constructor to perform initialization.

Now let's examine what happens when we instantiate a derived class:

int main()
{
    Container container;

    return 0;
}

If you were to try this yourself, you wouldn't notice any difference from the previous example where we instantiate non-derived class Storage. But behind the scenes, the process is slightly different. As mentioned above, Container is really two parts: a Storage part, and a Container part. When C++ constructs derived objects, it does so in phases. First, the most-base class (at the top of the inheritance tree) is constructed. Then each child class is constructed in order, until the most-child class (at the bottom of the inheritance tree) is constructed last.

So when we instantiate an instance of Container, first the Storage portion of Container is constructed (using the Storage default constructor). Once the Storage portion is complete, the Container portion is constructed (using the Container default constructor). At this point, there are no more derived classes, so we're done.

This process is straightforward to demonstrate.

#include <iostream>

class Storage
{
public:
    int m_capacity {};

    Storage(int capacity=0)
        : m_capacity { capacity }
    {
        std::cout << "Storage\n";
    }

    int getCapacity() const { return m_capacity; }
};

class Container: public Storage
{
public:
    double m_weight {};

    Container(double weight=0.0)
        : m_weight { weight }
    {
        std::cout << "Container\n";
    }

    double getWeight() const { return m_weight; }
};

int main()
{
    std::cout << "Instantiating Storage\n";
    Storage storage;

    std::cout << "Instantiating Container\n";
    Container container;

    return 0;
}

This program produces the following output:

Instantiating Storage Storage Instantiating Container Storage Container

As you can see, when we constructed Container, the Storage portion of Container was constructed first. This makes sense: logically, a child cannot exist without a parent. It's also the safe approach: the child class often uses variables and functions from the parent, but the parent class knows nothing about the child. Instantiating the parent class first ensures those variables are already initialized by the time the derived class is created and ready to use them.

Order of construction for inheritance chains

Sometimes classes are derived from other classes, which are themselves derived from other classes. For example:

#include <iostream>

class Component
{
public:
    Component()
    {
        std::cout << "Component\n";
    }
};

class Module: public Component
{
public:
    Module()
    {
        std::cout << "Module\n";
    }
};

class System: public Module
{
public:
    System()
    {
        std::cout << "System\n";
    }
};

class Enterprise: public System
{
public:
    Enterprise()
    {
        std::cout << "Enterprise\n";
    }
};

Remember that C++ always constructs the "first" or "most base" class first. It then walks through the inheritance tree in order and constructs each successive derived class.

Here's a short program that illustrates the construction order along the entire inheritance chain.

int main()
{
    std::cout << "Constructing Component: \n";
    Component component;

    std::cout << "Constructing Module: \n";
    Module module;

    std::cout << "Constructing System: \n";
    System system;

    std::cout << "Constructing Enterprise: \n";
    Enterprise enterprise;
}

This code prints the following:

Constructing Component: Component Constructing Module: Component Module Constructing System: Component Module System Constructing Enterprise: Component Module System Enterprise

Conclusion

C++ constructs derived classes in phases, starting with the most-base class (at the top of the inheritance tree) and finishing with the most-child class (at the bottom of the inheritance tree). As each class is constructed, the appropriate constructor from that class is called to initialize that part of the class.

You'll notice that our example classes in this section have all used base class default constructors (for simplicity). In the next lesson, we'll examine the role of constructors more closely in the process of constructing derived classes (including how to explicitly choose which base class constructor you want your derived class to use).

Summary

Derived class structure: A derived class conceptually contains two parts: one part for each inherited base class and one part for itself. The members of the base class are not copied into the derived class; instead, the derived class contains both the base portion and the derived portion as separate components.

Construction order: When a derived class object is instantiated, C++ constructs it in phases, starting with the most-base class (at the top of the inheritance tree) and finishing with the most-child class (at the bottom). This ensures that base class members are initialized before derived class constructors run, allowing the derived class to safely use base class members.

Memory allocation and initialization: For a derived class object, C++ first allocates memory for the entire object (including both base and derived portions), then calls constructors in order from most-base to most-derived. If no specific base class constructor is specified, the default constructor is used for each base class.

Inheritance chains: The construction order applies recursively through the entire inheritance hierarchy. For example, when constructing Enterprise (derived from System, derived from Module, derived from Component), C++ constructs Component first, then Module, then System, and finally Enterprise. Each constructor completes before the next one begins.

This phased construction approach ensures the child class doesn't try to use parent class members before they've been properly initialized, preventing undefined behavior and making inheritance both safe and predictable.