How Protected Inheritance Affects Member Visibility in C++
In the C++ programming language, inheritance models not only
relationships between classes but also dictates how encapsulation
boundaries are preserved. When a class derives from a base class using
protected inheritance (class Derived : protected Base), it
fundamentally shifts the visibility of inherited members to both outside
consumers and subsequent subclasses. Specifically, protected inheritance
promotes encapsulation by downgrading public base members to protected
status within the derived class, leaving protected members protected,
and keeping private members completely inaccessible.
The Core Visibility Transformation Rules
The access specifier used during inheritance acts as a filter that restricts member visibility; it can only reduce accessibility, never increase it. Under protected inheritance, the access levels of the base class members map to the derived class as follows:
- Public members become protected: Any public member of the base class is treated as protected within the derived class. It remains accessible to member functions and friends of the derived class, as well as to further derived classes, but it is completely inaccessible to external callers using an instance of the derived class.
- Protected members remain protected: Base class members that were originally protected maintain their protected status. They continue to be accessible within the derived class and any downstream classes inheriting from it, but remain hidden from the global scope.
- Private members remain inaccessible: Private members of the base class are inherited, but they cannot be accessed directly by the derived class under any inheritance model. They can only be interacted with via inherited public or protected accessors (which themselves become protected).
Impact on External Code and Type Conversion
The primary consequence of protected inheritance occurs outside the class hierarchy. When public members are converted into protected members, the derived class breaks the standard "is-a" relationship commonly associated with public inheritance.
External code interacting with an instance of the derived class
cannot invoke any methods that were public in the base class unless the
derived class explicitly exposes them (such as via a using
declaration). Furthermore, implicit upcasting is restricted. Outside the
class hierarchy, a pointer or reference to the derived type cannot be
implicitly converted to a pointer or reference to the base type. The
compiler enforces this restriction because treating the derived object
as a base object from the outside would circumvent the protected access
boundary.
Impact on Further Derived Classes
Unlike private inheritance—which converts all accessible base members into private members and prevents subsequent generations from accessing them—protected inheritance maintains the chain of inheritance for subclasses.
If a third class inherits from the derived class (e.g.,
class GrandChild : public Derived), it can still access all
the members that were originally public or protected in the root base
class. Within GrandChild, these members remain protected.
Additionally, member functions and friends of both Derived
and GrandChild retain the ability to implicitly cast
pointers or references of their type to the root base class type,
preserving implementation reuse across a restricted inheritance
tree.