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.

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

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