Programming Pandit

c/c++/c#/Javav/Python


Latest Update

Friday, October 23, 2026

Constructors, Destructors and Inheritance


Constructors, Destructors and Inheritance


Learning Objectives

After completing this module, students should be able to:

•      State the order in which constructors and destructors run in single, multilevel and multiple inheritance.

•      Pass arguments to a base-class constructor with a member initializer list.

•      Explain the complete initialization sequence of an object (bases, members, body).

•      Write correct copy constructors for derived classes, and use inherited constructors (C++11).

•      Explain why a base class used polymorphically needs a virtual destructor.

 

1. Introduction

A derived object contains a base-class subobject, and that part must be initialised before the derived part can safely use it. C++ therefore runs constructors from the base class towards the derived class and destructors in the opposite order. Constructors and destructors are not inherited, but a derived constructor always makes sure that a base constructor is called.

Title: Constructors run from base to derived; destructors run from derived to base (class C : B, class B : A). - Description: Constructors run from base to derived; destructors run from derived to base (class C : B, class B : A).

Figure 3.1: Constructors run from base to derived; destructors run from derived to base (class C : B, class B : A).

2. Order of Constructors and Destructors

2.1 Single inheritance

Program 3.1: The base constructor runs automatically before the derived constructor.

#include <iostream>

using namespace std;

 

class Person {

public:

    Person() { cout << "Person constructor called\n"; }

};

 

class Student : public Person {

public:

    Student() { cout << "Student constructor called\n"; }

};

 

int main()

{

    Student s;

    return 0;

}

 

Output:

Person constructor called

Student constructor called

 

Creating a Student first builds the Person part by calling its default constructor, then runs the body of the Student constructor.

Program 3.2: Constructors and destructors together.

#include <iostream>

using namespace std;

 

class Base {

public:

    Base() { cout << "Base constructor\n"; }

    ~Base() { cout << "Base destructor\n"; }

};

 

class Derived : public Base {

public:

    Derived() { cout << "Derived constructor\n"; }

    ~Derived() { cout << "Derived destructor\n"; }

};

 

int main()

{

    Derived obj;

    cout << "Inside main\n";

    return 0;

}

 

Output:

Base constructor

Derived constructor

Inside main

Derived destructor

Base destructor

 

1.      Constructors: Base first, then Derived.

2.      “Inside main” is printed after the object is complete.

3.      At the end of main() the object is destroyed in the reverse order: the Derived destructor first, then Base. The derived part may depend on the base part, so the base part must be the last to disappear.

 

2.2 Multilevel inheritance

Program 3.3: A chain A → B → C.

#include <iostream>

using namespace std;

 

class A {

public:

    A() { cout << "A constructor\n"; }

    ~A() { cout << "A destructor\n"; }

};

 

class B : public A {

public:

    B() { cout << "B constructor\n"; }

    ~B() { cout << "B destructor\n"; }

};

 

class C : public B {

public:

    C() { cout << "C constructor\n"; }

    ~C() { cout << "C destructor\n"; }

};

 

int main()

{

    C obj;

    return 0;

}

 

Output:

A constructor

B constructor

C constructor

C destructor

B destructor

A destructor

 

The constructors run A, B, C – from the most basic class to the most derived – and the destructors run C, B, A.

3. Passing Arguments to Base-Class Constructors

If the base class has a constructor with parameters, the derived constructor must supply the arguments in its member initializer list, written between the parameter list and the body:

Derived(parameters) : Base(arguments), member1(value), member2(value)

{

    // body

}

 

Program 3.4: A Student passes the name to the Person constructor.

#include <iostream>

#include <string>

using namespace std;

 

class Person {

protected:

    string name;

 

public:

    Person(string n) : name(n) { cout << "Person constructor\n"; }

};

 

class Student : public Person {

private:

    int rollNumber;

 

public:

    Student(string n, int r) : Person(n), rollNumber(r)

    {

        cout << "Student constructor\n";

    }

 

    void display() const

    {

        cout << "Name: " << name << "\n";

        cout << "Roll number: " << rollNumber << "\n";

    }

};

 

int main()

{

    Student s("Rahul", 101);

    s.display();

    return 0;

}

 

Output:

Person constructor

Student constructor

Name: Rahul

Roll number: 101

 

•      Student(string n, int r) : Person(n), rollNumber(r) calls Person(n) to initialise the inherited part and initialises rollNumber.

•      The base constructor runs first, so “Person constructor” is printed before “Student constructor”.

 

3.1 A base class without a default constructor

If the base class has no default constructor (no constructor callable without arguments), every derived constructor must call a base constructor explicitly.

Program 3.5: The derived constructor supplies a fixed argument.

#include <iostream>

using namespace std;

 

class Base {

public:

    Base(int x) { cout << "Base value: " << x << "\n"; }

};

 

class Derived : public Base {

public:

    Derived() : Base(100) { cout << "Derived constructor\n"; }

};

 

int main()

{

    Derived d;

    return 0;

}

 

Output:

Base value: 100

Derived constructor

 

If the initializer Base(100) were removed, the compiler would try to call Base(), which does not exist:

error: no matching function for call to ‘Base::Base()’

 

Note: Without an explicit call, the compiler inserts a call to the default constructor of the base class. If you write no constructor at all for the derived class, the compiler-generated default constructor does the same.

 

4. Multiple Inheritance: Constructors and Destructors

With several base classes, the constructors run in the order in which the base classes are declared in the class header, not in the order written in the initializer list. The destructors run in the reverse order.

Program 3.6: The initializer list is written in a different order from the declaration.

#include <iostream>

using namespace std;

 

class A {

public:

    A() { cout << "A constructor\n"; }

    ~A() { cout << "A destructor\n"; }

};

 

class B {

public:

    B() { cout << "B constructor\n"; }

    ~B() { cout << "B destructor\n"; }

};

 

class C : public A, public B {          // declaration order: A first, then B

public:

    C() : B(), A()                      // written order is different

    {

        cout << "C constructor\n";

    }

    ~C() { cout << "C destructor\n"; }

};

 

int main()

{

    C obj;

    return 0;

}

 

Output:

A constructor

B constructor

C constructor

C destructor

B destructor

A destructor

 

class C : public A, public B declares A first, so A is constructed first although the initializer list says B(), A(). A compiler warns about the misleading order:

warning: base ‘B’ will be initialized after [-Wreorder]

 

Important: Always write the initializer list in the same order as the declarations. The order in which the list is written never changes the order of initialization.

 

5. Complete Initialization Sequence

Title: The order in which the parts of a derived object are initialised. - Description: The order in which the parts of a derived object are initialised.

Figure 3.2: The order in which the parts of a derived object are initialised.

1.      Virtual base classes (Module 4), in the order in which they appear in a depth-first, left-to-right traversal of the base-class graph.

2.      Direct non-virtual base classes, in the order of declaration in the class header.

3.      Non-static data members, in the order of their declaration in the class.

4.      The body of the constructor.

 

Program 3.7: A class with a base class and two member objects.

#include <iostream>

using namespace std;

 

class Engine {

public:

    Engine() { cout << "  Engine constructed\n"; }

    ~Engine() { cout << "  Engine destroyed\n"; }

};

 

class Body {

public:

    Body() { cout << "  Body constructed\n"; }

    ~Body() { cout << "  Body destroyed\n"; }

};

 

class Vehicle {

public:

    Vehicle() { cout << "  Vehicle (base) constructed\n"; }

    ~Vehicle() { cout << "  Vehicle (base) destroyed\n"; }

};

 

class Car : public Vehicle {

    Engine engine;                       // declared first

    Body body;                           // declared second

 

public:

    Car() : body(), engine()             // written in the opposite order

    {

        cout << "  Car constructor body\n";

    }

    ~Car() { cout << "  Car destructor body\n"; }

};

 

int main()

{

    cout << "Creating a Car:\n";

    {

        Car c;

    }

    cout << "Car has gone out of scope.\n";

    return 0;

}

 

Output:

Creating a Car:

  Vehicle (base) constructed

  Engine constructed

  Body constructed

  Car constructor body

  Car destructor body

  Body destroyed

  Engine destroyed

  Vehicle (base) destroyed

Car has gone out of scope.

 

The base part (Vehicle) comes first, then the members engine and body in the order in which they are declared (not in the order of the initializer list body(), engine()), then the body of the Car constructor. The destruction runs backwards: the body of the destructor, then the members in reverse order, then the base.

warning: ‘Car::b’ will be initialized after [-Wreorder]

 

5.1 Members that must use the initializer list

•      Base classes without a default constructor (Section 3).

•      const members and reference members, because they cannot be assigned in the constructor body.

•      Member objects whose classes have no default constructor.

 

Assigning a const member in the body is an error:

error: uninitialized const member in ‘const int’ [-fpermissive]

 

6. Copy Constructors in Derived Classes

If you write a copy constructor for a derived class, you are responsible for copying the base part too. If you do not call the base copy constructor in the initializer list, the compiler calls the base default constructor instead and the base data is not copied.

Program 3.8: A derived copy constructor that forgets the base part, and one that does it correctly.

#include <iostream>

using namespace std;

 

class Base {

public:

    Base() { cout << "Base default constructor\n"; }

    Base(const Base &) { cout << "Base copy constructor\n"; }

};

 

class Wrong : public Base {

public:

    Wrong() {}

    // Base() is used!

    Wrong(const Wrong &) { cout << "Wrong copy constructor\n"; }

};

 

class Right : public Base {

public:

    Right() {}

    // copies the base part

    Right(const Right &other) : Base(other)

    {

        cout << "Right copy constructor\n";

    }

};

 

int main()

{

    Wrong w1;

    cout << "--- copying Wrong\n";

    Wrong w2 = w1;

 

    Right r1;

    cout << "--- copying Right\n";

    Right r2 = r1;

    return 0;

}

 

Output:

Base default constructor

--- copying Wrong

Base default constructor

Wrong copy constructor

Base default constructor

--- copying Right

Base copy constructor

Right copy constructor

 

Copying Wrong calls Base() (the default constructor), so the base data would be lost. Copying Right calls Base(other), the base copy constructor. GCC with -Wextra reports the mistake:

warning: base class ‘class Base’ should be explicitly initialized in the copy constructor [-Wextra]

 

The same applies to a user-written copy assignment operator: it must call Base::operator=(other). If you write no copy operations at all, the compiler-generated ones copy the base part correctly.

7. Inheriting Constructors (C++11)

Constructors are not inherited, so a derived class normally needs to repeat constructors that only forward their arguments. In C++11 a using-declaration can pass the base constructors on to the derived class.

Program 3.9: using Person::Person; makes Person’s constructor available for Student.

#include <iostream>

#include <string>

using namespace std;

 

class Person {

protected:

    string name;

    int age;

 

public:

    Person(string n, int a) : name(n), age(a)

    {

        cout << "Person(" << name << ", " << age << ")\n";

    }

};

 

class Student : public Person {

public:

    using Person::Person;                // C++11: inherit the constructors

    void show() const { cout << name << " is " << age << " years old\n"; }

};

 

int main()

{

    Student s("Asha", 19);               // Person(string, int) is used for Student

    s.show();

    return 0;

}

 

Output:

Person(Asha, 19)

Asha is 19 years old

 

Version Note: Inheriting constructors is a C++11 feature. In C++98 and C++03 each derived class must declare its own constructors and call the base constructor.

Data members of the derived class that are not initialised by the base constructor get their default initialization, so give them default member initializers (also C++11).

 

8. Destructors and Virtual Destructors

A destructor never calls the base destructor explicitly; the compiler calls it automatically after the derived destructor finishes. There is, however, a serious problem when an object of a derived class is deleted through a pointer to its base class:

Base *ptr = new Derived;

delete ptr;      // undefined behaviour if ~Base() is not virtual!

 

If the base destructor is not virtual, only the base destructor may run, and the derived part is never destroyed (resources leak). If it is virtual, the correct destructor is selected at run time.

Program 3.10: A virtual destructor.

#include <iostream>

using namespace std;

 

class Base {

public:

    virtual ~Base() { cout << "Base destructor\n"; }

};

 

class Derived : public Base {

public:

    ~Derived() override { cout << "Derived destructor\n"; }

};

 

int main()

{

    Base *ptr = new Derived;

    delete ptr;                          // calls Derived's destructor, then Base's

    return 0;

}

 

Output:

Derived destructor

Base destructor

 

Important: Rule: if a class has any virtual function, or if objects will be deleted through base-class pointers, declare the destructor virtual. Virtual functions are studied in Module 5.

 

Version Note: The keyword override on the destructor of Derived (and = default on destructors) are C++11 features. In C++98 simply write virtual ~Base() {} and ~Derived() {}.

 

9. Summary of Constructor and Destructor Rules

Rule

Explanation

Constructors are not inherited

Each class has its own constructors; C++11 allows using Base::Base;

Base before derived

Constructors run from the base class to the derived class

Declaration order decides

For bases and members, the order of the declaration, not of the initializer list

Default base constructor is implicit

Called automatically unless another base constructor is named in the initializer list

Destructors run in reverse

Derived body, members (reverse), then base (reverse of base order)

Copy operations

A user-written copy constructor or assignment must also copy the base part

Virtual destructor

Needed to delete a derived object through a base pointer

 

10. Quick Revision

•      Construction: bases (declaration order) → members (declaration order) → constructor body. Destruction: exact reverse.

•      Pass arguments to the base with Derived(...) : Base(args) { }; required if the base has no default constructor.

•      Use the initializer list in the declaration order; const and reference members must use it.

•      User-written copy constructors must call the base copy constructor.

•      Make the base destructor virtual when deleting through a base pointer.

 

11. Important Examination Questions

Short-answer questions

1.      Are constructors inherited? Explain.

2.      In which order are constructors and destructors called in inheritance?

3.      How are arguments passed to a base-class constructor?

4.      What happens if the base class has no default constructor?

5.      What decides the order of initialization with multiple base classes?

6.      Why should a base-class destructor be virtual?

 

Long-answer and programming questions

1.      Explain the order of execution of constructors and destructors in multilevel inheritance, with a program and its output.

2.      Write a program to pass parameters from a derived-class constructor to a base-class constructor.

3.      Explain the complete initialization sequence of an object with base classes and member objects.

4.      Write a derived class with a user-defined copy constructor and explain what goes wrong if the base part is not copied.

5.      Explain inheriting constructors and state the C++ version in which they were introduced.

 

End of Module 3

Next: Module 4 – Granting Access, Friends and Virtual Base Classes

No comments:

Post a Comment