Question
Public vs Private vs Protected Inheritance in C++
Question
In C++, what is the difference between public, private, and protected inheritance, and how do they affect access to members of a base class from a derived class and from outside the class hierarchy?
Short Answer
By the end of this page, you will understand how inheritance access modes work in C++, how they change the visibility of inherited members, when each form is appropriate, and why public inheritance is the most common choice for modeling an “is-a” relationship.
Concept
Inheritance lets one class reuse and extend another class.
In C++, when you inherit from a base class, you must choose an inheritance mode:
class Derived : public Base { };
class Derived : protected Base { };
class Derived : private Base { };
This choice does not change the base class itself. Instead, it changes how the base class's members are viewed through the derived class.
The key idea
C++ member access already has three levels inside a class:
public: accessible from anywhereprotected: accessible inside the class and its derived classesprivate: accessible only inside the class itself
Inheritance adds another layer:
- public inheritance keeps the base class interface public/protected in the derived class
- protected inheritance makes the base class's public and protected members protected in the derived class
- private inheritance makes the base class's public and protected members private in the derived class
Important rule:
- Base class
privatemembers are never directly accessible in the derived class, no matter which inheritance mode you choose.
Why this matters
Inheritance is not just about code reuse. It also expresses a relationship between types.
Mental Model
Think of a base class as a toolbox and the derived class as a worker who receives that toolbox.
- With public inheritance, the worker keeps the same tools visible to everyone. Other people can still ask the worker to use the tools in the same public way.
- With protected inheritance, the worker keeps the tools available for themselves and for future apprentices, but not for the public.
- With private inheritance, the worker takes the tools into a private workshop. They can use them internally, but outsiders cannot access them through the worker.
Another way to remember it:
- public inheritance = open relationship
- protected inheritance = family-only relationship
- private inheritance = internal implementation detail
Syntax and Examples
Basic syntax
class Base {
public:
void publicMethod() {}
protected:
void protectedMethod() {}
private:
void privateMethod() {}
};
class PublicDerived : public Base {};
class ProtectedDerived : protected Base {};
class PrivateDerived : private Base {};
Example: how access changes
#include <iostream>
using namespace std;
class Base {
public:
void showPublic() {
cout << "Base public" << endl;
}
protected:
void {
cout << << endl;
}
};
: Base {
:
{
();
();
}
};
: Base {
:
{
();
();
}
};
: Base {
:
{
();
();
}
};
{
PublicDerived a;
a.();
ProtectedDerived b;
PrivateDerived c;
}
Step by Step Execution
Consider this example:
#include <iostream>
using namespace std;
class Base {
public:
void greet() {
cout << "Hello from Base" << endl;
}
};
class Derived : protected Base {
public:
void callGreet() {
greet();
}
};
int main() {
Derived d;
d.callGreet();
// d.greet(); // Error
}
Step by step
Basedefines a public method namedgreet().Derived : protected BasemeansBase's public members becomeprotectedinsideDerived.- Inside
Derived, the method can call because protected members are accessible from within the class.
Real World Use Cases
Public inheritance
Used when the derived type should truly behave like the base type.
Examples:
Doginherits fromAnimalTextBoxinherits fromWidgetCircleinherits fromShape- custom exception types inheriting from
std::exception
Private inheritance
Used when a class wants to reuse implementation from another class without exposing that class as part of its public API.
Examples:
- using a helper base class for low-level behavior
- inheriting from an empty policy class for optimization
- internal framework classes where the base should not be visible to API users
In many cases, composition is preferred instead:
class Engine {
public:
void start() {}
};
class Car {
private:
Engine engine;
public:
{
engine.();
}
};
Real Codebase Usage
In real projects, developers usually follow this rule:
- use public inheritance for polymorphism and clear type hierarchies
- prefer composition over private inheritance for implementation reuse
- use protected inheritance only when there is a strong design reason
Common patterns
1. Public inheritance for polymorphic interfaces
class Logger {
public:
virtual void log(const std::string& msg) = 0;
virtual ~Logger() = default;
};
class FileLogger : public Logger {
public:
void log(const std::string& msg) override {
// write to file
}
};
This allows code to depend on Logger while using different derived implementations.
2. Private inheritance for implementation details
class Helper {
:
{}
};
: Helper {
:
{
();
}
};
Common Mistakes
1. Thinking inheritance mode affects access inside the base class
It does not. It only affects how base members are seen through the derived class.
2. Expecting base private members to be accessible in derived classes
Broken example:
class Base {
private:
int x;
};
class Derived : public Base {
public:
void test() {
// x = 10; // Error
}
};
Fix
Use protected members carefully, or better, provide public/protected getter and setter functions when needed.
class Base {
private:
int x = 0;
protected:
void setX(int value) {
x = value;
}
};
3. Using private inheritance when composition is clearer
Broken design idea:
Comparisons
| Feature | Public inheritance | Protected inheritance | Private inheritance |
|---|---|---|---|
| Base public members become | public | protected | private |
| Base protected members become | protected | protected | private |
| Base private members become | not directly accessible | not directly accessible | not directly accessible |
| Can derived use base public/protected members internally? | Yes | Yes | Yes |
| Can outside code use base public members through derived object? |
Cheat Sheet
class D1 : public Base {};
class D2 : protected Base {};
class D3 : private Base {};
Access transformation
publicinheritance:- base
public->public - base
protected->protected
- base
protectedinheritance:- base
public->protected - base
protected->protected
- base
privateinheritance:- base
public->private - base
protected->private
- base
FAQ
When should I use public inheritance in C++?
Use public inheritance when the derived class truly is a specialized version of the base class and should be usable anywhere the base class is expected.
Can a derived class access private members of the base class?
No. Base class private members are never directly accessible in derived classes, regardless of inheritance mode.
Is private inheritance the same as composition?
No. Private inheritance still creates an inheritance relationship internally. Composition means storing another object as a member. Composition is often clearer.
Why is protected inheritance rarely used?
Because it solves a narrow design problem: exposing base functionality to subclasses but not to users. Most code is better served by public inheritance or composition.
Can I convert a privately derived object to a base pointer?
Not from normal outside code. Private inheritance hides that conversion.
Does inheritance mode affect virtual functions?
The inheritance mode affects accessibility and conversion rules, not whether virtual dispatch works inside the hierarchy.
Can I make a hidden inherited member public again?
Yes. You can re-expose selected base members using a using declaration in the derived class.
Mini Project
Description
Build a small class hierarchy that demonstrates all three inheritance modes. The project helps you see which functions remain publicly accessible, which become hidden, and how a derived class can still use inherited functionality internally.
Goal
Create a C++ program that compares public, protected, and private inheritance using one base class and three derived classes.
Requirements
- Create a base class with one public method and one protected method.
- Create three derived classes using public, protected, and private inheritance.
- In each derived class, add a public method that calls the inherited methods internally.
- In
main(), test which methods are callable from outside the class. - Include comments showing which lines would cause compilation errors if uncommented.
Keep learning
Related questions
Basic Rules and Idioms for Operator Overloading in C++
Learn the core rules, syntax, and common idioms for operator overloading in C++, including member vs non-member operators.
C++ Casts Explained: C-Style Cast vs static_cast vs dynamic_cast
Learn the difference between C-style casts, static_cast, and dynamic_cast in C++ with clear examples, safety rules, and real usage tips.
C++ Lambda Expressions Explained: What They Are and When to Use Them
Learn what C++ lambda expressions are, why they exist, when to use them, and how they simplify callbacks, algorithms, and local logic.