C++ OOP — Level 2 Complete Notes¶
Inheritance, Constructor/Destructor Order, Function Inheritance, Method Hiding, Access Modes, Diamond Problem & Virtual Inheritance¶
1. Inheritance¶
1.1 What is Inheritance?¶
Inheritance is an OOP mechanism where a derived class acquires properties and behavior from a base class.
Terminology:
- Base class = Parent class / Superclass
- Derived class = Child class / Subclass
Basic syntax:
class Derived : public Base {
};
Example:
class Animal {
public:
void eat() {
cout << "Eating\n";
}
};
class Dog : public Animal {
public:
void bark() {
cout << "Barking\n";
}
};
Now:
Dog d;
d.eat(); // inherited
d.bark(); // own function
Output:
Eating
Barking
Conceptually:
Animal
└── eat()
Dog
├── eat() ← inherited
└── bark() ← own
1.2 Why Use Inheritance?¶
Main purposes:
- Code reuse
- Extending existing functionality
- Establishing an
is-arelationship - Foundation for runtime polymorphism
Example:
Dog is an Animal
Car is a Vehicle
Circle is a Shape
2. Types of Inheritance¶
There are five commonly discussed structural types.
2.1 Single Inheritance¶
One base → one derived.
A
↓
B
class B : public A {
};
2.2 Multilevel Inheritance¶
Inheritance occurs across multiple levels.
A
↓
B
↓
C
Example:
class B : public A {
};
class C : public B {
};
C indirectly inherits from A through B.
2.3 Hierarchical Inheritance¶
One base has multiple derived classes.
A
/ \
B C
Example:
class B : public A {
};
class C : public A {
};
2.4 Multiple Inheritance¶
One derived class inherits from multiple base classes.
A B
\ /
C
Example:
class C : public A, public B {
};
Potential problem:
class A {
public:
void fun() {
cout << "A";
}
};
class B {
public:
void fun() {
cout << "B";
}
};
class C : public A, public B {
};
C obj;
obj.fun(); // ❌ ambiguous
C++ doesn't know whether you mean:
obj.A::fun();
or:
obj.B::fun();
2.5 Hybrid Inheritance¶
Combination of multiple types of inheritance.
A common example is the diamond:
A
/ \
B C
\ /
D
This combines hierarchical and multiple inheritance.
3. Inheritance Type vs Inheritance Access Mode¶
These are different concepts.
Type of inheritance¶
Describes the structure:
Single
Multilevel
Multiple
Hierarchical
Hybrid
Access mode¶
Describes how inherited public and protected members are exposed:
class B : public A
class B : protected A
class B : private A
For example:
class B : private A
can still be single inheritance.
private here describes the access mode, not the structural type.
4. is-a vs has-a¶
Inheritance usually represents:
is-a
Example:
Dog is an Animal
Composition represents:
has-a
Example:
Car has an Engine
So:
class Dog : public Animal
represents an is-a relationship.
Whereas:
class Car {
Engine engine;
};
represents a has-a relationship.
5. Constructor Order in Inheritance¶
This is one of the most important C++ placement topics.
For:
A → B → C
where:
class B : public A
class C : public B
and:
C obj;
construction happens:
A constructor
↓
B constructor
↓
C constructor
Golden Rule¶
Construction: Base → Derived
5.1 Why?¶
A derived object contains a base-class subobject.
The base portion must be initialized before the derived portion can be initialized.
Example:
class Animal {
public:
Animal() {
cout << "Animal\n";
}
};
class Dog : public Animal {
public:
Dog() {
cout << "Dog\n";
}
};
Dog d;
Output:
Animal
Dog
6. Destructor Order in Inheritance¶
Destruction happens in the exact reverse order.
For:
A → B → C
construction:
A → B → C
destruction:
~C → ~B → ~A
Golden Rule¶
Destruction: Derived → Base
Example:
class A {
public:
A() {
cout << "A ";
}
~A() {
cout << "~A ";
}
};
class B : public A {
public:
B() {
cout << "B ";
}
~B() {
cout << "~B ";
}
};
class C : public B {
public:
C() {
cout << "C ";
}
~C() {
cout << "~C ";
}
};
C obj;
Output:
A B C ~C ~B ~A
7. Parameterized Base Constructor¶
A derived constructor must ensure that its base class can be constructed.
Consider:
class Base {
public:
Base(int x) {
cout << x;
}
};
class Derived : public Base {
public:
Derived() {
cout << "Derived";
}
};
This:
Derived obj;
causes a compilation error.
Why?
The compiler needs to construct the Base portion first.
It effectively needs:
Base();
But Base only has:
Base(int x);
and no default constructor exists.
8. Calling Base Constructor from Derived¶
Use the constructor initializer list:
class Base {
public:
Base(int x) {
cout << "Base " << x << "\n";
}
};
class Derived : public Base {
public:
Derived() : Base(10) {
cout << "Derived\n";
}
};
Now:
Derived obj;
works.
Execution:
Base(10)
Derived()
Output:
Base 10
Derived
Important:
Derived() : Base(10)
means:
Construct the
Basepart usingBase(10)before executing the body ofDerived().
9. General Construction Order¶
For a normal inheritance situation:
1. Base classes
2. Data members
3. Constructor body
Example:
class A {
public:
A() {
cout << "A ";
}
};
class B {
public:
B() {
cout << "B ";
}
};
class C : public A {
B b;
public:
C() {
cout << "C ";
}
};
C obj;
Output:
A B C
Because:
A → B → C
10. Member Initialization Order¶
A major C++ trap:
Data members are initialized in the order they are declared, not the order written in the initializer list.
Example:
class A {
int x;
int y;
public:
A() : y(20), x(10) {
cout << x << " " << y;
}
};
Even though the initializer list says:
y first
x second
the declaration is:
int x;
int y;
Therefore:
x → 10
y → 20
Output:
10 20
Placement Rule¶
Declaration order beats initializer-list order.
11. Multiple Inheritance Constructor Order¶
Consider:
class A {
public:
A() {
cout << "A ";
}
};
class B {
public:
B() {
cout << "B ";
}
};
class C : public A, public B {
public:
C() {
cout << "C ";
}
};
Creation:
C obj;
Output:
A B C
because the inheritance declaration is:
class C : public A, public B
Therefore:
A → B → C
12. Initializer List Does NOT Change Base Order¶
Consider:
class C : public A, public B {
public:
C() : B(), A() {
cout << "C ";
}
};
It may look like:
B → A → C
but that's wrong.
Base classes are initialized according to their order in the inheritance declaration:
class C : public A, public B
Therefore:
A → B → C
Important¶
Base-class initialization order = order in inheritance declaration.
13. Complete Destruction Order¶
Construction:
Base classes
↓
Data members
↓
Derived constructor body
Destruction:
Derived destructor body
↓
Data members in reverse declaration order
↓
Base classes in reverse construction order
For:
class C : public A {
B b;
};
construction:
A → B → C
destruction:
~C → ~B → ~A
14. Function Inheritance¶
A derived class can use accessible functions from its base class.
Example:
class Animal {
public:
void eat() {
cout << "Eating\n";
}
};
class Dog : public Animal {
public:
void bark() {
cout << "Barking\n";
}
};
Now:
Dog d;
d.eat();
d.bark();
Both are valid.
15. Private, Protected and Public Functions in Inheritance¶
Suppose:
class Animal {
private:
void eat();
protected:
void sleep();
public:
void run();
};
Inside the derived class:
eat() → ❌
sleep() → ✅
run() → ✅
So:
| Base member | Directly accessible in Derived? |
|---|---|
| private | ❌ |
| protected | ✅ |
| public | ✅ |
16. protected¶
protected is accessible:
- inside the class itself
- inside derived classes
but not through ordinary outside access.
Example:
class Animal {
protected:
void eat() {
cout << "Eating";
}
};
class Dog : public Animal {
public:
void test() {
eat(); // ✅
}
};
But:
Dog d;
d.eat(); // ❌
17. Inheritance Access Modes¶
There are three:
public
protected
private
Example:
class Derived : public Base
or:
class Derived : protected Base
or:
class Derived : private Base
These determine how inherited public and protected members are exposed through the derived class.
18. The Most Important Access Table¶
| Base member | Public inheritance | Protected inheritance | Private inheritance |
|---|---|---|---|
public |
public | protected | private |
protected |
protected | protected | private |
private |
inaccessible | inaccessible | inaccessible |
Critical rule¶
Base private members never become directly accessible in the derived class, regardless of inheritance mode.
19. Example of Private Inheritance¶
class A {
public:
int x = 10;
protected:
int y = 20;
private:
int z = 30;
};
class B : private A {
public:
void test() {
cout << x; // ✅
cout << y; // ✅
// cout << z; // ❌
}
};
Inside B:
x → accessible
y → accessible
z → inaccessible
But outside:
B obj;
cout << obj.x;
is an error because:
class B : private A
causes:
A public → B private
A protected → B private
20. Public vs Protected vs Private Inheritance¶
Public inheritance¶
Usually represents:
is-a
Example:
class Dog : public Animal
A Dog is an Animal.
Protected inheritance¶
Base's public/protected interface becomes protected in the derived class.
Private inheritance¶
Base's public/protected interface becomes private in the derived class.
Conceptually:
Public inheritance:
public → public
protected → protected
Protected inheritance:
public → protected
protected → protected
Private inheritance:
public → private
protected → private
21. Method Hiding¶
Suppose:
class Base {
public:
void fun() {
cout << "Base";
}
};
class Derived : public Base {
public:
void fun() {
cout << "Derived";
}
};
Then:
Derived d;
d.fun();
Output:
Derived
The derived declaration hides the base declaration with the same name.
This is called:
Method hiding / Name hiding
22. Hiding Is Not Overriding¶
The previous example does not automatically mean overriding.
class Base {
public:
void fun() {
cout << "Base";
}
};
class Derived : public Base {
public:
void fun() {
cout << "Derived";
}
};
Since Base::fun() is not virtual, this is simply name hiding.
Runtime overriding will be covered with virtual functions.
23. Entire Overload Set Can Be Hidden¶
This is a major placement trap.
class Base {
public:
void fun() {
cout << "Base";
}
void fun(int x) {
cout << "Base Int";
}
};
class Derived : public Base {
public:
void fun() {
cout << "Derived";
}
};
Now:
Derived d;
d.fun();
works:
Derived
But:
d.fun(10);
produces a compilation error.
Why?
Because declaring Derived::fun() hides the entire fun name from the Base class during normal name lookup.
It doesn't matter that the parameter list is different.
24. Bringing Base Overloads Back¶
Use:
using Base::fun;
Example:
class Derived : public Base {
public:
using Base::fun;
void fun() {
cout << "Derived";
}
};
Now:
Derived d;
d.fun();
d.fun(10);
works.
Output:
Derived
Base Int
So:
using Base::fun;
brings the base overloads back into scope.
25. Method Hiding Placement Rules¶
Remember:
Derived declares function with same name
↓
Base functions with that name become hidden
Even:
Base::fun()
Base::fun(int)
Base::fun(string)
can all be hidden by:
Derived::fun()
To restore them:
using Base::fun;
26. Diamond Problem¶
The diamond structure:
A
/ \
B C
\ /
D
Code:
class A {
public:
int x = 10;
};
class B : public A {
};
class C : public A {
};
class D : public B, public C {
};
27. Why Does the Diamond Cause a Problem?¶
Without virtual inheritance, D contains two copies of A.
Conceptually:
D
├── B
│ └── A
│ └── x
│
└── C
└── A
└── x
Therefore there are two separate A::x members.
28. Ambiguity in the Diamond¶
Now:
D obj;
cout << obj.x;
Which x?
There are two:
B → A → x
C → A → x
Therefore:
❌ Ambiguous
29. Resolving Ambiguity Explicitly¶
You can specify the path:
cout << obj.B::x;
or:
cout << obj.C::x;
These refer to different A subobjects.
You can even have:
obj.B::x = 10;
obj.C::x = 20;
because they are separate objects.
30. Virtual Inheritance¶
To solve the diamond problem:
class B : virtual public A {
};
class C : virtual public A {
};
Complete example:
class A {
public:
int x = 10;
};
class B : virtual public A {
};
class C : virtual public A {
};
class D : public B, public C {
};
Now D has only one shared A base subobject.
Conceptually:
A
/ \
B C
\ /
D
but internally:
D
├── B
├── C
└── shared A
Therefore:
D obj;
cout << obj.x;
is unambiguous.
31. What Does Virtual Inheritance Mean?¶
This:
class B : virtual public A
does not mean A itself is a "virtual class".
It means:
When a common most-derived object is formed, the virtual base
Ais shared rather than duplicated along each inheritance path.
32. Normal vs Virtual Diamond¶
Without virtual inheritance¶
D
├── B → A₁
└── C → A₂
There are two A subobjects.
With virtual inheritance¶
D
├── B
├── C
└── shared A
There is one A subobject.
33. Virtual Inheritance Constructor Order¶
Consider:
class A {
public:
A() {
cout << "A ";
}
};
class B : virtual public A {
public:
B() {
cout << "B ";
}
};
class C : virtual public A {
public:
C() {
cout << "C ";
}
};
class D : public B, public C {
public:
D() {
cout << "D ";
}
};
Creating:
D obj;
produces:
A B C D
Why?
Because:
- Virtual base
Ais constructed first. Bis constructed.Cis constructed.Dis constructed.
And A is constructed only once.
34. Most-Derived Class Controls Virtual Base¶
This is a very important interview rule.
Consider:
class A {
public:
A(int x) {
cout << "A" << x << " ";
}
};
class B : virtual public A {
public:
B() : A(10) {
cout << "B ";
}
};
class C : virtual public A {
public:
C() : A(20) {
cout << "C ";
}
};
class D : public B, public C {
public:
D() : A(30) {
cout << "D ";
}
};
When:
D obj;
is created, which A constructor runs?
Answer:
A(30)
NOT:
A(10)
and NOT:
A(20)
Why?
Because:
The most-derived class is responsible for constructing the virtual base.
Here, D is the most-derived class.
Therefore:
D → A(30)
controls the virtual base construction.
35. Level 2 Golden Rules¶
Inheritance¶
Derived inherits from Base
Inheritance Types¶
Single
Multilevel
Hierarchical
Multiple
Hybrid
Construction¶
Base → Members → Derived
Destruction¶
Derived → Members → Base
Member Initialization¶
Declaration order
NOT initializer-list order.
Multiple Base Initialization¶
Inheritance declaration order
NOT initializer-list order.
protected¶
Class + Derived
but not ordinary outside access.
Inheritance Access¶
public protected private
Base public public protected private
Base protected protected protected private
Base private ❌ ❌ ❌
Method Hiding¶
Derived function with same name
↓
hides Base functions with that name
Restore with:
using Base::fun;
Diamond Problem¶
Normal inheritance:
two copies of common base
Virtual inheritance:
one shared common base
Virtual Base Construction¶
Most-derived class controls virtual base
36. Placement Traps Checklist¶
Before answering an inheritance output question, check these in order:
Trap 1 — Constructor order¶
Ask:
What is the inheritance hierarchy?
Then construct from base toward derived.
Trap 2 — Destructor order¶
Reverse the construction order.
Trap 3 — Member order¶
Look at declaration order, not initializer-list order.
Trap 4 — Multiple inheritance¶
Look at the order in:
class D : public A, public B
So:
A → B
Trap 5 — Method hiding¶
If Derived defines:
fun()
then Base's:
fun()
fun(int)
fun(string)
may all be hidden from normal lookup.
Use:
using Base::fun;
to bring them back.
Trap 6 — Private inheritance¶
Don't confuse:
class B : private A
with:
"A's private members become accessible."
They do not.
Instead:
A public → B private
A protected → B private
A private → inaccessible
Trap 7 — Diamond¶
If you see:
A
/ \
B C
\ /
D
ask:
Is inheritance virtual?
If no:
two A subobjects
If yes:
one shared A subobject
Trap 8 — Virtual base constructor¶
If virtual inheritance exists:
The most-derived class constructs the virtual base.
37. Output Questions We Solved¶
Question 1 — Multilevel inheritance¶
class A {
public:
A() { cout << "A "; }
~A() { cout << "~A "; }
};
class B : public A {
public:
B() { cout << "B "; }
~B() { cout << "~B "; }
};
class C : public B {
public:
C() { cout << "C "; }
~C() { cout << "~C "; }
};
C obj;
Output:
A B C ~C ~B ~A
Question 2 — Base + Member + Derived¶
class A {
public:
A() { cout << "A "; }
~A() { cout << "~A "; }
};
class B {
public:
B() { cout << "B "; }
~B() { cout << "~B "; }
};
class C : public A {
B b;
public:
C() { cout << "C "; }
~C() { cout << "~C "; }
};
Output:
A B C ~C ~B ~A
Question 3 — Method hiding¶
class Base {
public:
void show() {
cout << "Base";
}
void show(int x) {
cout << "Base Int";
}
};
class Derived : public Base {
public:
void show() {
cout << "Derived";
}
};
Derived d;
d.show();
d.show(10);
Result:
d.show(); → Derived
d.show(10); → Compilation error
because Derived::show() hides the entire show name from Base.
Question 4 — Private inheritance¶
class A {
public:
int x = 10;
protected:
int y = 20;
};
class B : private A {
public:
void fun() {
cout << x << " ";
cout << y << " ";
}
};
Inside B:
10 20
But:
B obj;
cout << obj.x;
is an error.
Question 5 — Virtual inheritance¶
class A {
public:
A() { cout << "A "; }
};
class B : virtual public A {
public:
B() { cout << "B "; }
};
class C : virtual public A {
public:
C() { cout << "C "; }
};
class D : public B, public C {
public:
D() { cout << "D "; }
};
D obj;
Output:
A B C D
and there is only one A subobject.
38. Level 2 Placement Revision Sheet¶
INHERITANCE
↓
Base → Derived
Types:
Single
Multilevel
Hierarchical
Multiple
Hybrid
CONSTRUCTION
↓
Base classes
↓
Members
↓
Derived constructor body
DESTRUCTION
↓
Derived destructor body
↓
Members (reverse declaration order)
↓
Base destructors
MEMBER INITIALIZATION
↓
Declaration order
NOT initializer-list order
MULTIPLE INHERITANCE
↓
Base order = inheritance declaration order
ACCESS MODES
↓
public:
public → public
protected → protected
protected:
public → protected
protected → protected
private:
public → private
protected → private
Base private → inaccessible directly
METHOD HIDING
↓
Derived same-name function
↓
hides Base overloads
Fix:
using Base::fun;
DIAMOND
↓
A
/ \
B C
\ /
D
Normal:
2 copies of A
Virtual inheritance:
1 shared A
VIRTUAL BASE
↓
Most-derived class constructs it
39. Level 2 Final Mental Model¶
When you see an inheritance problem, mentally perform these steps:
Step 1¶
Draw the inheritance hierarchy.
Step 2¶
Identify whether inheritance is:
single / multilevel / multiple / hierarchical / hybrid
Step 3¶
For construction:
virtual bases
→ base classes
→ members
→ constructor body
For the basic cases covered in this level, remember:
Base → Members → Derived
Step 4¶
For destruction:
reverse everything
Step 5¶
Check access:
private / protected / public
and the inheritance mode.
Step 6¶
If a derived function has the same name as a base function:
think METHOD HIDING
Check whether:
using Base::fun;
is present.
Step 7¶
If you see:
A
/ \
B C
\ /
D
immediately ask:
Virtual inheritance?
If no → two copies of A.
If yes → one shared A.
Step 8¶
If the virtual base has constructors:
most-derived class controls the virtual base
Level 2 — Core Takeaways¶
The most important things to know for placements are:
- Inheritance allows a derived class to acquire accessible behavior/data from a base class.
- Five structural types: single, multilevel, hierarchical, multiple, hybrid.
- Construction is Base → Derived.
- Destruction is Derived → Base.
- Data members initialize in declaration order.
- Base classes initialize in inheritance-list order.
- A parameterized base constructor must be explicitly called if no default constructor exists.
protectedis accessible inside the class and derived classes, but not ordinary outside code.- Inheritance access mode changes the visibility of inherited public/protected members.
- Base private members remain inaccessible directly to Derived.
- A derived function with the same name hides the entire base overload set.
using Base::fun;can bring hidden base overloads back into scope.- Method hiding is different from overriding.
- The diamond problem creates two copies of a common base under normal multiple inheritance.
- Virtual inheritance creates one shared virtual-base subobject.
- Virtual bases are constructed before ordinary bases.
- The most-derived class controls construction of a virtual base.