Understanding Inheritance Visibility in C++: Public, Protected, and Private

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 remain public in the derived class.
  • protected members of the base class remain protected 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 become protected in the derived class.
  • protected members of the base class remain protected 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 become private in the derived class.
  • protected members of the base class become private 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., a Button is a Window).
  • 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.

Leave a Reply

Your email address will not be published. Required fields are marked *