Introduction: Why Parent Constructors Are a Minefield for Beginners
Anyone who has started learning inheritance in C++ has sooner or later encountered a situation: you create a derived class, the compiler complains, the program doesn't build, and you don't understand why. Most often, the root of the evil lies in an incorrect call to the base class constructor. This question on Stack Overflow has thousands of views, and for good reason: even experienced developers sometimes forget the basic rules.
In this article, we will break down the rules for calling the base class constructor in C++, go through the top 5 most common mistakes, and show how to avoid them. If you want to write reliable code without surprises, read to the end.
Quick Primer: How Constructors Work with Inheritance
In C++, constructors are not inherited. When you create an object of a derived class, the base class constructor is called first (implicitly or explicitly), followed by the derived class constructor. If you do not specify which parent constructor to call, the compiler will attempt to call the default constructor of the base class. If it doesn't exist, you will get a compilation error.
An explicit call is made through the initialization list:
class Base {public: Base(int x) { /* ... */ }};
class Derived : public Base {public: // Correct: explicit call to Base(int) constructor Derived() : Base(42) { /* ... */ }};Seems simple? But this is where beginners make 90% of their mistakes. Let's break them down in order.
Top 5 Mistakes When Calling the Base Class Constructor
Mistake #1: Forgot to Call the Parent Constructor with Parameters
The most common problem. If the base class does not have a default constructor and you didn't call it explicitly, the compiler will throw an error.
class Base {public: Base(int x) { /* initialization */ }};
class Derived : public Base {public: // ERROR: compiler tries to call Base(), which doesn't exist Derived() { }};How to fix:
class Derived : public Base {public: // CORRECT: explicitly call Base(int) Derived() : Base(0) { } // Or pass a parameter from outside: Derived(int x) : Base(x) { }};Mistake #2: Incorrect Initialization Order
Many think that the order of constructor calls in the initialization list determines the execution order. This is not true. The base class is always initialized first, regardless of where you write its call.
class Derived : public Base {private: int value;public: // It seems like value is initialized first? No! Derived() : value(10), Base(5) { } // In reality: first Base(5), then value(10)};This can lead to logical errors if the initialization of value depends on the state of the base class.
Mistake #3: Calling the Parent Constructor in the Constructor Body
Some beginners try to call the base class constructor inside the curly braces, like a regular method. This won't work — a constructor can only be called in the initialization list.
class Derived : public Base {public: Derived() { // ERROR: this does not call the constructor Base(10); // Just creates a temporary object and destroys it }};Mistake #4: Confusion with Copy Constructors
When implementing a copy constructor in a derived class, you need to explicitly call the base class copy constructor. Otherwise, the default constructor will be called, and the base class data will not be copied.
class Derived : public Base {public: // ERROR: Base will be copied incorrectly Derived(const Derived& other) { /* ... */ }
// CORRECT: Derived(const Derived&