Inheritance in C++
Inheritance in C++ Interview with follow-up questions
1. What is inheritance in C++ and why is it important?
Inheritance is a core OOP mechanism that lets one class (the derived or subclass) acquire the data members and member functions of another class (the base or superclass). The derived class extends or specialises the base class, and you can override base-class behaviour to suit the subtype.
Why it matters in interviews (and in practice)
- Code reuse — shared logic lives once in the base class; derived classes get it for free without copying.
- Extensibility — you can add new behaviour by deriving a new class rather than modifying proven code (Open/Closed Principle).
- Polymorphism —
virtualfunctions + a base-class pointer or reference let you write algorithms that work on any derived type discovered at runtime. - Modelling "is-a" relationships — a
Dogis-aAnimal; inheritance captures that relationship directly.
Basic syntax
class Animal {
public:
void breathe() { /* ... */ }
virtual void speak() = 0; // pure virtual — must override
virtual ~Animal() = default; // always virtual destructor in a base class
};
class Dog : public Animal {
public:
void speak() override { std::cout << "Woof\n"; }
};
Common follow-up gotchas
- Always declare the base-class destructor
virtual; otherwise deleting aDogthrough anAnimal*causes undefined behaviour. - Prefer
override(C++11 and later) on every overriding function — it turns silent typo bugs into compile errors. - Inheritance models is-a; favour composition for has-a relationships to avoid fragile base-class coupling.
- C++20 concepts let you express type constraints more cleanly than deep inheritance hierarchies when compile-time polymorphism suffices.
Follow-up 1
Can you explain the different types of inheritance supported in C++?
C++ supports several types of inheritance:
Single inheritance: A derived class inherits from a single base class.
Multiple inheritance: A derived class can inherit from multiple base classes.
Multilevel inheritance: A derived class inherits from another derived class, creating a hierarchy of classes.
Hierarchical inheritance: Multiple derived classes inherit from a single base class.
Hybrid inheritance: A combination of multiple inheritance and multilevel inheritance.
Follow-up 2
How does inheritance promote code reusability?
Inheritance promotes code reusability by allowing the derived class to inherit properties and behaviors from the base class. This means that the derived class does not need to redefine or reimplement those properties and behaviors, saving development time and reducing code duplication. By reusing existing code, developers can build upon and extend the functionality of the base class without having to start from scratch.
Follow-up 3
What is the syntax to implement inheritance in C++?
The syntax to implement inheritance in C++ is as follows:
// Base class
class BaseClass {
// Base class members
};
// Derived class
class DerivedClass : public BaseClass {
// Derived class members
};
In this example, the derived class DerivedClass is inheriting from the base class BaseClass using the public access specifier. The public access specifier indicates that the public members of the base class will be accessible in the derived class.
Follow-up 4
Can you provide a real-world example where inheritance can be effectively used?
One real-world example where inheritance can be effectively used is in a banking system. The base class could be Account, which contains common properties and methods for all types of accounts, such as accountNumber, balance, and deposit(). The derived classes could be SavingsAccount, CheckingAccount, and LoanAccount, which inherit from the Account class and add specific properties and methods unique to each type of account. This allows for code reusability and the ability to treat different types of accounts as instances of the base class, enabling polymorphism.
2. What is the difference between public, protected, and private inheritance in C++?
The access specifier used at the point of inheritance controls how base-class members are re-exposed in the derived class and its further descendants.
| Base member access | public inheritance |
protected inheritance |
private inheritance |
|---|---|---|---|
public |
public |
protected |
private |
protected |
protected |
protected |
private |
private |
inaccessible | inaccessible | inaccessible |
Public inheritance — models an is-a relationship. The public interface of the base class remains public in the derived class, so outside code can use derived objects through base-class pointers/references. This is the form used for runtime polymorphism.
class Animal { public: void breathe(); };
class Dog : public Animal { }; // Dog "is-a" Animal; breathe() is public on Dog
Protected inheritance — rarely used in practice. Inherited public members become protected, so they are accessible inside the derived class and its own subclasses, but not from outside code. Suitable when you want subclasses to share functionality without exposing it to users.
Private inheritance — models implemented-in-terms-of (a composition alternative). All inherited members become private in the derived class. Outside code cannot use the base interface through a derived pointer; the relationship is purely an implementation detail. structs default to public inheritance; classes default to private.
class Stack : private std::vector {
public:
void push(int v) { push_back(v); }
void pop() { pop_back(); }
};
// std::vector's public API is NOT exposed to Stack users
Key interview gotchas
- Base-class
privatemembers are never accessible in the derived class regardless of the inheritance specifier — they are simply inaccessible (not nonexistent). - Only
publicinheritance preserves the Liskov Substitution Principle. Usingprotectedorprivateinheritance means you cannot substitute the derived object for a base-class reference. class Derived : Basedefaults toprivateinheritance;struct Derived : Basedefaults topublicinheritance.
Follow-up 1
Can you provide an example of each?
Sure, here are examples of each type of inheritance:
- Public inheritance: ```cpp class Base { public: void publicMethod() {} protected: void protectedMethod() {} private: void privateMethod() {} };
class Derived : public Base { // publicMethod() is accessible // protectedMethod() is accessible // privateMethod() is not accessible };
- Protected inheritance:
```cpp
class Base {
public:
void publicMethod() {}
protected:
void protectedMethod() {}
private:
void privateMethod() {}
};
class Derived : protected Base {
// publicMethod() is not accessible
// protectedMethod() is accessible
// privateMethod() is not accessible
};
- Private inheritance: ```cpp class Base { public: void publicMethod() {} protected: void protectedMethod() {} private: void privateMethod() {} };
class Derived : private Base { // publicMethod() is not accessible // protectedMethod() is not accessible // privateMethod() is not accessible };
Follow-up 2
How does the access specifier of the base class affect the members of the derived class in each case?
The access specifier of the base class affects the accessibility of its members in the derived class as follows:
Public inheritance: Public members of the base class remain public in the derived class, protected members of the base class remain protected in the derived class, and private members of the base class remain inaccessible to the derived class.
Protected inheritance: Public and protected members of the base class become protected members of the derived class, and private members of the base class remain inaccessible to the derived class.
Private inheritance: Public and protected members of the base class become private members of the derived class, and private members of the base class remain inaccessible to the derived class.
Follow-up 3
In what scenarios would you use each type of inheritance?
The choice of inheritance type depends on the desired relationship between the base class and the derived class:
Public inheritance: Use public inheritance when the derived class is a specialized version of the base class and should inherit all its public and protected members.
Protected inheritance: Use protected inheritance when the derived class is a specialized version of the base class and should inherit all its protected members, but not its public members.
Private inheritance: Use private inheritance when the derived class is a specialized version of the base class and should inherit all its members, but they should be private and not accessible from outside the derived class.
3. What is multiple inheritance in C++ and how is it different from multilevel inheritance?
Multiple inheritance means a single class directly inherits from more than one base class at the same time.
class Flyable { public: virtual void fly() = 0; };
class Swimmable { public: virtual void swim() = 0; };
class Duck : public Flyable, public Swimmable {
public:
void fly() override { /* ... */ }
void swim() override { /* ... */ }
};
Multilevel inheritance means a class inherits from a derived class, forming a chain.
class Animal { /* ... */ };
class Mammal : public Animal { /* ... */ };
class Dog : public Mammal { /* ... */ }; // three levels
Key differences
| Multiple inheritance | Multilevel inheritance | |
|---|---|---|
| Structure | One class, two or more direct bases | Chain: A → B → C |
| Risk | Diamond problem, ambiguous member access | Deep hierarchies become fragile |
| Typical use | Mixin / interface combination | Specialisation ladder |
The diamond problem (multiple inheritance only)
When two base classes share a common grandparent, the derived class ends up with two copies of the grandparent's sub-object, causing ambiguity:
class A { public: int x; };
class B : public A {};
class C : public A {};
class D : public B, public C {}; // D::x is ambiguous — two copies of A
Resolved with virtual inheritance (class B : virtual public A), which guarantees a single shared sub-object regardless of how many paths lead to A.
Interview follow-ups
- Name resolution with multiple inheritance uses the C++ lookup rules; explicit scope (
B::foo()) resolves ambiguity. - Prefer interface-style classes (pure virtual only, no data) as secondary bases to minimise diamond problems.
- Multilevel chains deeper than three levels are usually a design smell — consider composition instead.
Follow-up 1
Can you provide an example of multiple and multilevel inheritance?
Sure! Here's an example of multiple inheritance:
#include
class Base1 {
public:
void display1() {
std::cout << "Base1 Display" << std::endl;
}
};
class Base2 {
public:
void display2() {
std::cout << "Base2 Display" << std::endl;
}
};
class Derived : public Base1, public Base2 {
public:
void displayDerived() {
std::cout << "Derived Display" << std::endl;
}
};
int main() {
Derived d;
d.display1();
d.display2();
d.displayDerived();
return 0;
}
And here's an example of multilevel inheritance:
#include
class Base {
public:
void displayBase() {
std::cout << "Base Display" << std::endl;
}
};
class Derived : public Base {
public:
void displayDerived() {
std::cout << "Derived Display" << std::endl;
}
};
class Derived2 : public Derived {
public:
void displayDerived2() {
std::cout << "Derived2 Display" << std::endl;
}
};
int main() {
Derived2 d;
d.displayBase();
d.displayDerived();
d.displayDerived2();
return 0;
}
Follow-up 2
What are the potential problems with multiple inheritance and how can they be avoided?
Multiple inheritance can introduce a few potential problems:
Ambiguity: If two or more base classes have a member with the same name, it can lead to ambiguity in the derived class. This can be avoided by using scope resolution operator (::) to specify which base class member to use.
Diamond Problem: If two base classes of a derived class have a common base class, it can lead to the diamond problem. This occurs when the common base class is inherited twice, causing ambiguity. This can be avoided by using virtual inheritance.
Complexity: Multiple inheritance can make the code more complex and harder to understand. It is important to carefully design the class hierarchy and consider alternative approaches if the complexity becomes too high.
Follow-up 3
How does C++ resolve the ambiguity in case of multiple inheritance?
C++ resolves the ambiguity in case of multiple inheritance by using the scope resolution operator (::) to specify which base class member to use. For example, if two base classes have a member with the same name, you can use the scope resolution operator to access the member of a specific base class.
Here's an example:
#include
class Base1 {
public:
void display() {
std::cout << "Base1 Display" << std::endl;
}
};
class Base2 {
public:
void display() {
std::cout << "Base2 Display" << std::endl;
}
};
class Derived : public Base1, public Base2 {
public:
void displayDerived() {
Base1::display(); // Accessing Base1's display()
Base2::display(); // Accessing Base2's display()
}
};
int main() {
Derived d;
d.displayDerived();
return 0;
}
In this example, the displayDerived() function in the Derived class uses the scope resolution operator to access the display() functions of both Base1 and Base2 classes.
4. What is the concept of virtual base class in C++?
A virtual base class is a base class declared with the virtual keyword in the inheritance list. It solves the diamond problem: when multiple inheritance paths all lead to the same base class, a virtual base ensures only one shared sub-object exists in the most-derived class rather than one copy per path.
The problem without virtual inheritance
class A { public: int x = 0; };
class B : public A {};
class C : public A {};
class D : public B, public C {};
D d;
d.x = 1; // error: ambiguous — D has two copies of A
The solution: virtual inheritance
class A { public: int x = 0; };
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
D d;
d.x = 1; // OK — exactly one A sub-object shared by B and C paths
How construction works
With virtual bases, the most-derived class (D) is responsible for constructing the virtual base directly, regardless of what B and C do. B and C constructors still list A in their member-initialiser lists (for when they are used as the most-derived type), but those calls are skipped when constructing through D.
class A { public: A(int v) : x(v) {} int x; };
class B : virtual public A { public: B() : A(1) {} };
class C : virtual public A { public: C() : A(2) {} };
class D : public B, public C { public: D() : A(42) {} };
// D's A::x == 42; B's and C's A(1)/A(2) initialisers are ignored
Key interview points
- Virtual inheritance adds a hidden pointer (vptr for the virtual base) per inheritance path, so objects are slightly larger.
- The most-derived class must always initialise the virtual base, even if it is several levels removed.
- Prefer designing interfaces (pure-virtual-only classes with no data members) as shared bases to avoid needing virtual inheritance at all.
Follow-up 1
Why is it used in the context of multiple inheritance?
Virtual base classes are used in the context of multiple inheritance to avoid the 'diamond problem'. The 'diamond problem' occurs when a class is derived from two or more classes that have a common base class. Without using virtual base classes, the derived class would have multiple instances of the common base class, leading to ambiguity and potential conflicts. By using virtual base classes, only one instance of the common base class is inherited, resolving the ambiguity.
Follow-up 2
How does it help in resolving ambiguity in multiple inheritance?
Virtual base classes help in resolving ambiguity in multiple inheritance by ensuring that only one instance of the base class is inherited, even if it is inherited through different paths in the inheritance hierarchy. This avoids the ambiguity and conflicts that can arise when a derived class has multiple instances of the same base class. By using virtual base classes, the derived class can access the common base class through any of its paths in the inheritance hierarchy without duplication.
Follow-up 3
Can you provide an example of the use of virtual base class?
Sure! Here's an example:
#include
class Animal {
public:
void eat() {
std::cout << "Animal is eating" << std::endl;
}
};
class Mammal : public virtual Animal {
public:
void walk() {
std::cout << "Mammal is walking" << std::endl;
}
};
class Bird : public virtual Animal {
public:
void fly() {
std::cout << "Bird is flying" << std::endl;
}
};
class Bat : public Mammal, public Bird {
public:
void sleep() {
std::cout << "Bat is sleeping" << std::endl;
}
};
int main() {
Bat bat;
bat.eat();
bat.walk();
bat.fly();
bat.sleep();
return 0;
}
In this example, the classes Mammal and Bird both inherit virtually from the Animal class. This ensures that the Bat class, which inherits from both Mammal and Bird, only has one instance of the Animal class. Without using virtual inheritance, Bat would have two instances of Animal, leading to ambiguity and potential conflicts.
5. What is the role of constructors and destructors in inheritance?
Construction order
When a derived-class object is created, constructors fire in this fixed order:
- Virtual base classes (depth-first, left-to-right declaration order).
- Non-virtual base classes (depth-first, left-to-right declaration order).
- Non-static data members, in declaration order.
- The derived class's own constructor body.
class Base {
public:
Base() { std::cout << "Base constructed\n"; }
};
class Derived : public Base {
public:
Derived() { std::cout << "Derived constructed\n"; }
};
// Output: Base constructed, then Derived constructed
Pass arguments to a base-class constructor via the member-initialiser list, not the constructor body:
class Base {
public:
explicit Base(int v) : value_(v) {}
private:
int value_;
};
class Derived : public Base {
public:
explicit Derived(int v) : Base(v) {}
};
Destruction order
Destruction is strictly the reverse of construction:
- Derived class destructor body.
- Non-static data members, in reverse declaration order.
- Non-virtual base classes, in reverse declaration order.
- Virtual base classes, in reverse order.
Critical rule: virtual destructors
If you ever delete a derived-class object through a base-class pointer, the base-class destructor must be virtual. Without it, only the base destructor runs — the derived part is never cleaned up (undefined behaviour).
class Base {
public:
virtual ~Base() = default; // always declare virtual in a polymorphic base
};
class Derived : public Base {
std::vector data_;
};
Base* p = new Derived();
delete p; // calls ~Derived then ~Base — correct
Modern note
With std::unique_ptr and std::shared_ptr you rarely call delete manually, but the virtual-destructor rule still applies because the smart pointer's deleter ultimately calls delete on the stored pointer. A non-virtual destructor in a polymorphic base is a well-known source of resource leaks.
Follow-up 1
What is the order of execution of constructors and destructors in inheritance?
In inheritance, the order of execution of constructors and destructors is as follows:
- When a derived class object is created, the constructor of the base class is called first, followed by the constructor of the derived class.
- When a derived class object is destroyed, the destructor of the derived class is called first, followed by the destructor of the base class.
This order ensures that the base class is properly initialized before the derived class, and that the resources allocated by the derived class are properly released before the base class.
Follow-up 2
How can base class constructors be called from derived class?
Base class constructors can be called from the derived class using the constructor initialization list. In the constructor initialization list, the base class constructor is called with the required arguments. Here's an example:
#include
using namespace std;
class Base {
public:
Base(int x) {
cout << "Base constructor called with " << x << endl;
}
};
class Derived : public Base {
public:
Derived(int x, int y) : Base(x) {
cout << "Derived constructor called with " << y << endl;
}
};
int main() {
Derived d(10, 20);
return 0;
}
Output:
Base constructor called with 10
Derived constructor called with 20
Follow-up 3
What happens if a base class destructor is not declared virtual?
If a base class destructor is not declared virtual and a derived class object is deleted through a pointer to the base class, the behavior is undefined. This is because the derived class destructor will not be called, leading to potential resource leaks or other issues. To ensure proper destruction of derived class objects, it is recommended to declare the base class destructor as virtual. This allows the destructor of the derived class to be called when deleting a derived class object through a pointer to the base class.
Live mock interview
Mock interview: Inheritance in C++
- Read your scene and goals
- Talk it out; goals tick off live
- Get a score and stronger lines
Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.