Skip to main content

Inheritance Samples

1. Virtual Base Classes / Diamond Problem

Virtual Inheritance: Solves the diamond problem by ensuring a single instance of the base class.

class B {
public:
int i;
B() { i = 10; }
};

class D1: virtual public B { };
class D2: virtual public B { };
class C: public D1, public D2 { };

void main() {
C obj;
cout << obj.i; // Without virtual, this would be ambiguous
}

Diamond Problem Diagram:

Explanation:

  • Without virtual inheritance, class C would inherit two copies of B (one through D1 and one through D2)
  • This would make obj.i ambiguous - which copy of B's i should it refer to?
  • Using virtual inheritance ensures only one copy of B exists in C
  • This resolves the ambiguity, allowing direct access to i through C objects

Output: 10

2. Virtual Destructors

Virtual Destructors: Ensures proper calling order for destructors in class hierarchies. Essential when deleting derived objects through base class pointers to ensure proper cleanup.

class B {
public:
virtual ~B() { cout << "destructor B" << endl; }
};

class D: public B {
public:
int *p;
D() {
p = new int;
*p = 10;
}
~D() {
cout << "destructor D" << endl;
delete p;
}
};

void main() {
int z;
B *pobjB;
D *pobjD = new D();
pobjB = pobjD;
delete pobjB;
cin >> z;
}

Virtual Destructor Diagram:

Explanation:

  • When a derived class object is deleted through a base class pointer, the base class destructor must be virtual
  • Without virtual, only the base class destructor would be called, causing a memory leak (p would not be deleted)
  • With virtual, the correct derived class destructor is called first, then the base class destructor
  • This ensures proper cleanup of resources allocated by the derived class

Output:

destructor D
destructor B

3. Base-class Access Control

Access Control: Public, private, and protected members have different visibility in derived classes.

class Base {
public:
int m_i;
Base() {
m_i = 0;
m_j = 1;
m_k = 2;
}
private:
int m_j;
protected:
int m_k;
};

class D1: public Base { };

void main() {
D1 obj;
cout << obj.m_i; // Accessible
//cout << obj.m_j; // Not accessible - private in Base
//cout << obj.m_k; // Not accessible - protected in Base
}

Access Control Diagram:

Explanation:

  • public members (m_i) are accessible from anywhere
  • private members (m_j) are only accessible within the Base class
  • protected members (m_k) are accessible within Base and derived classes, but not from outside

Output: 0

4. Constructors and Destructors Execution Order

  • Constructors execute from base to derived
  • Destructors execute from derived to base
class B {
public:
B() { cout << "constructor B" << endl; }
~B() { cout << "Destructor B" << endl; }
};

class D1: public B {
public:
D1() { cout << "constructor D1" << endl; }
~D1() { cout << "Destructor D1" << endl; }
};

class D2: public D1 {
public:
D2() { cout << "constructor D2" << endl; }
~D2() { cout << "Destructor D2" << endl; }
};

void main() {
D2 obj;
}

Constructor/Destructor Order Diagram:

Explanation:

Constructors are called from base to derived (B → D1 → D2) Destructors are called in reverse order, from derived to base (D2 → D1 → B)

Output:

constructor B
constructor D1
constructor D2
Destructor D2
Destructor D1
Destructor B

5. Granting Access

We can change the access level of inherited members.

class B {
public:
int i; // public in base
B() { i = 10; }
};

// Inherit base as private
class D: private B {
public:
// here is access declaration
B::i; // make i public again
};

void main() {
D obj;
cout << obj.i;
int z;
cin >> z;
}

Access Declaration Diagram:

Explanation:

  • When inheriting privately, all public members of the base class become private in the derived class
  • The access declaration B::i; in the public section of D makes i public again
  • This allows direct access to i through D objects

Output: 10

6. Passing Parameters to Base-class Constructors

Use initializer lists to pass parameters to base class constructors.

class B {
int m;
protected:
int i;
public:
B(int x) {
i = x;
cout << "Constructing base : B\n";
}
~B() {
cout << "Destructing base : B\n";
}
};

class D: public B {
int j;
public:
// derived uses x; y is passed along to base
D(int x, int y): B(y) {
j = x;
cout << "Constructing derived : D\n";
}
~D() {
cout << "Destructing derived : D\n";
}
void show() {
cout << i << " " << j << "\n";
}
};

int main() {
D obj(1, 2);
obj.show(); // displays 2 1
return 0;
}

Constructor Parameter Passing Diagram:

Explanation:

The derived class constructor uses an initializer list (D(int x, int y): B(y)) to pass parameters to the base class constructor The parameter y is passed to the base class constructor, while x is used in the derived class In the show() method, i (from base) is 2 and j (from derived) is 1

Output:

Constructing base : B
Constructing derived : D
2 1
Destructing derived : D
Destructing base : B