Understanding Inheritance Visibility in C++: Public, Protected, and Private
Inheritance is a cornerstone of object-oriented programming, enabling code reuse and the creation of hierarchical relationships between classes. C++ provides powerful mechanisms for controlling how inherited members are accessed, using the keywords public
, protected
, and private
. This tutorial will explain these visibility specifiers and how they impact the relationship between base and derived classes.
Access Specifiers: A Quick Review
Before diving into inheritance visibility, let’s quickly recap access specifiers within a class:
public
: Members are accessible from anywhere. This is the most open level of access.protected
: Members are accessible from within the class itself, from derived classes, and from friends of the class.private
: Members are only accessible from within the class itself.
These access levels apply to member variables and functions within a class. However, they also play a crucial role in determining how inherited members are visible to other parts of your program when you use inheritance.
Inheritance Visibility: Controlling Access to Inherited Members
When a class inherits from another class (the base class), it inherits all the members (variables and functions) of the base class. However, the visibility of these inherited members in the derived class—and to code that uses the derived class—is determined by the inheritance specifier used in the derived class’s inheritance declaration.
Let’s explore each inheritance specifier:
1. public
Inheritance
public
inheritance is the most common type. It preserves the access levels of the base class members in the derived class.
public
members of the base class remainpublic
in the derived class.protected
members of the base class remainprotected
in the derived class.private
members of the base class are not accessible in the derived class.
This means that any code that can access the derived class can also access the public
and protected
members inherited from the base class, just as if they were defined directly in the derived class.
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
class DerivedPublic : public Base {
public:
void accessBaseMembers() {
publicMember = 10; // Accessible
protectedMember = 20; // Accessible
// privateMember = 30; // Not accessible
}
};
2. protected
Inheritance
protected
inheritance limits access to inherited members.
public
members of the base class becomeprotected
in the derived class.protected
members of the base class remainprotected
in the derived class.private
members of the base class are still not accessible in the derived class.
This means that public
and protected
members inherited from the base class are only accessible within the derived class itself and from other derived classes. Code that uses the derived class directly cannot access these inherited members.
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
class DerivedProtected : protected Base {
public:
void accessBaseMembers() {
publicMember = 10; // Accessible
protectedMember = 20; // Accessible
// privateMember = 30; // Not accessible
}
};
3. private
Inheritance
private
inheritance is the most restrictive. It effectively hides the base class from the outside world through the derived class.
public
members of the base class becomeprivate
in the derived class.protected
members of the base class becomeprivate
in the derived class.private
members of the base class remain inaccessible.
This means that all inherited members (public and protected) are only accessible within the derived class itself. Any code that uses the derived class cannot access the inherited members.
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
class DerivedPrivate : private Base {
public:
void accessBaseMembers() {
publicMember = 10; // Accessible
protectedMember = 20; // Accessible
// privateMember = 30; // Not accessible
}
};
When to Use Different Types of Inheritance
public
inheritance: Use this in most cases when you want to extend the functionality of a base class and make it available to other parts of your program. It represents an "is-a" relationship (e.g., aButton
is aWindow
).protected
inheritance: Use this less frequently. It’s useful when you want to use the implementation details of a base class within a closed hierarchy of classes, but you don’t want to expose them to the outside world.private
inheritance: Use this when you want to reuse the implementation details of a base class without exposing its interface. It’s often used for creating helper classes or for implementing specific algorithms without making them part of the public interface.
Important Considerations
- C++ allows casting a derived class to a protected or private base class. However, this should be avoided, because it can lead to brittle code dependent on implementation details.
- Consider the design implications of each inheritance type carefully. Choose the one that best reflects the relationship between the base and derived classes and that provides the appropriate level of access control.