Lesson 4: Foundations of Inheritance & Constructor Lifecycle
Lesson Overview
This lesson covers the fundamentals of Inheritance—how C++ allows you to reuse, extend, and specialize existing code, how to control access using visibility modes, and how the compiler coordinates the initialization and destruction of parent and child subobjects in memory.
🚀 1. The “Why” & Concept Breakdown
Inheritance is the mechanism by which a derived class {specialized child} acquires the attributes and behaviors of an existing base class {generalized parent}. It directly implements the DRY (Don’t Repeat Yourself) principle and models the “Is-A” relationship (e.g., a Studentis aPerson).
graph BT
subgraph Specialization ["Derived / Specialized Classes"]
D1["Student<br>(Adds GPA, Roll)"]
D2["Teacher<br>(Adds Subject, Salary)"]
end
subgraph Generalization ["Base / Generalized Class"]
B["Person<br>(name, id, age)"]
end
D1 -->|Inherits : public| B
D2 -->|Inherits : public| B
⚡ Inheritance: Python vs. C++
Syntax & Modes:class Child : public Parent (must specify : public mode; defaults to private).
Constructor Delegation: Invokes parent constructor via Initializer List: Child(...) : Parent(args), childVar(val) {} (equivalent to Python’s super().__init__()).
Access Levels: Base private members are inaccessible to children. Use protected for members that child classes need direct access to.
Key Architectural Principles
Generalization{grouping shared fields into a parent} vs. Specialization{adding unique fields in a child}.
The protected Access Specifier:
private members cannot be accessed directly by derived classes (though they occupy memory in the derived object’s subobject{the nested parent memory chunk}).
public members are accessible everywhere.
protected members behave like private variables to outside main() code, but are directly accessible inside derived classes.
Inheritance Visibility Modes
When deriving a class (class Child : visibility_mode Parent), the visibility mode sets the upper ceiling for inherited access:
Base Class Member
Public Derivation (: public)
Protected Derivation (: protected)
Private Derivation (: private / default)
Private
Inaccessible (Not Inherited)
Inaccessible (Not Inherited)
Inaccessible (Not Inherited)
Protected
Becomes Protected
Becomes Protected
Becomes Private
Public
Becomes Public
Becomes Protected
Becomes Private
🔑 2. Keyword & Syntax Dictionary
Keyword / Symbol
Meaning
Practical Syntax Example
:
Separator denoting inheritance.
class Student : public Person { ... };
protected
Access modifier granting access to derived classes only.
protected: int roll_number;
: Base(...)
Initializer list syntax calling a parameterized parent constructor from the derived class.
Student(string n, int r) : Person(n), roll(r) {}
💻 3. Step-by-Step Code Evolution
Step 4.1: Public vs. Private Derivation
#include <iostream>#include <string>using namespace std;class Person {protected: string name; // Accessible in derived classesprivate: int id; // Inaccessible to derived classespublic: void setDetails(string n, int i) { name = n; id = i; } void displayBase() const { cout << "ID: " << id << " | Name: " << name << endl; }};// 1. Public Derivation: Public members stay publicclass Student : public Person {private: float gpa;public: void setStudent(string n, int i, float g) { setDetails(n, i); // Using public base setter name = n; // Direct access to protected member gpa = g; } void displayStudent() const { cout << "Student: " << name << " | GPA: " << gpa << endl; }};// 2. Private Derivation: Public base members become privateclass Employee : private Person {private: float salary;public: void setEmployee(string n, int i, float s) { setDetails(n, i); salary = s; } void displayEmployee() const { displayBase(); // Permitted internally cout << "Salary: $" << salary << endl; }};int main() { Student s; s.setStudent("Alice", 101, 3.9); s.displayBase(); // ALLOWED: Public in Student Employee e; e.setEmployee("Bob", 202, 75000); // e.displayBase(); // COMPILE ERROR: displayBase is private inside Employee e.displayEmployee(); // ALLOWED return 0;}
Step 4.2: Constructor & Destructor Execution Order in Hierarchies
When a derived class object is created and destroyed:
Constructors execute top-down (Base → Derived).
Destructors execute bottom-up (Derived → Base).
sequenceDiagram
autonumber
participant main as main() Scope
participant Grandfather
participant Father
participant Child
main->>Grandfather: 1. Construct Grandfather Subobject
Grandfather->>Father: 2. Construct Father Subobject
Father->>Child: 3. Construct Child Subobject
Note over Child: Object active in memory
main->>Child: 4. Destroy Child (Exit Scope)
Child->>Father: 5. Destroy Father Subobject
Father->>Grandfather: 6. Destroy Grandfather Subobject
--- Instantiating Child ---[+] Grandfather Constructor[+] Father Constructor[+] Child Constructor[-] Child Destructor[-] Father Destructor[-] Grandfather Destructor--- Child Exited Scope ---
🧠 4. Under-the-Hood Memory Layout
Object of Class 'Student' in Memory:
+-----------------------------------------------------------+
| [Base Class Subobject: Person] |
| +-----------------------------------------------------+ |
| | string name (inherited protected field) | |
| | int id (inherited private field) | |
| +-----------------------------------------------------+ |
+-----------------------------------------------------------+
| [Derived Class Native Data] |
| +-----------------------------------------------------+ |
| | float gpa (Student's own private field) | |
| +-----------------------------------------------------+ |
+-----------------------------------------------------------+
⚠️ 5. The Debugger’s Guide (Common Traps)
Trap 1: Base Class Lacks Default Constructor
class Parent {public: Parent(int x) {} // No default constructor!};class Child : public Parent {public: Child() {} // ❌ ERROR: no matching function for call to 'Parent::Parent()'};
The Fix: Explicitly pass parameters to the base constructor in the child’s initializer list: Child(int val) : Parent(val) {}.
Trap 2: Direct Private Access Attempt
Attempting to read/write a private base variable in a child function triggers a compiler error. Use protected in the base class or call public base getters/setters.
📝 6. Practice Quiz
Q1. If class B : A {} is declared with no visibility mode, what is the default?
A. public
B. protected
C. private
D. virtual
Q2. Which base members can be directly accessed in derived classes but remain hidden from main()?
A. private
B. protected
C. public
D. Static private
Q3. What is the execution order of destructors in multi-level inheritance?
A. Base first, then derived.
B. Derived first, then base.
C. Random based on memory layout.
D. Destructors execute simultaneously.
Quiz Answers
C (class defaults to private inheritance).
B (protected access specifier).
B (Destructors execute in reverse order of construction: Derived → Base).
💬 7. Viva Quick-Prep
Q1: Can constructors and destructors be inherited in C++?
Answer:No. Constructors and destructors are never inherited. Each class must define or rely on compiler generation for its own constructor and destructor.
Q2: Why is a base constructor invoked before a derived constructor?
Answer: Because a derived object is built on top of the base subobject. The base portion must be fully constructed and initialized in memory before the derived constructor runs to ensure safe access to parent properties.
Q3: How do we resolve a naming collision if base and derived classes define identical function names?
Answer: The derived function overrides/hides the base function. To call the base version from an object of the derived class, qualify it with the scope resolution operator: object.BaseClass::function().