
Protected inheritance
Finally, we have the protected access modifier. It specifies the access level of the class members if they're used in the class body. Protected members are private to the class users, but are public to derived classes. If the modifier is used to specify the type of inheritance, it behaves similarly to the private inheritance for derived class users. While private inheritance hides the public interface of the base class from all the derived class users, protected inheritance makes it accessible to descendants of the derived class.
It's hard to imagine a scenario where you would need protected inheritance, but you should consider it as a tool that might be useful in unexpectedly obvious designs. Let's suppose we need to design a stack data structure adapter. The stack is usually implemented based on a vector (one-dimensional array), a linked list, or a dequeue.
The stack itself doesn't represent a data structure; it sits on top of a data structure and adapts its usage by limiting, modifying, or extending its functions. The following is a simple declaration of the Vector class representing a one-dimensional array of integers:
class Vector {
public:
Vector();
Vector(const Vector&);
Vector(Vector&&);
Vector& operator=(const Vector&);
Vector& operator=(Vector&&);
~Vector();
public:
void push_back(int value);
void insert(int index, int value);
void remove(int index);
int operator[](int index);
int size() const;
int capacity() const;
private:
int size_;
int capacity_;
int* array_;
};
The preceding Vector is not an STL-compatible container with random access iterator support; it contains the bare minimum for a dynamically increasing array. It can be declared and used in the following way:
Vector v;
v.push_back(4);
v.push_back(5);
v[1] = 2;
While the Vector class provides operator[], which allows us to access any of its items randomly, the Stack prohibits random accesses. The Stack provides push and pop operations so that we can insert a value into its underlying data structure and fetch the value, respectively:
class Stack : private Vector {
public:
// constructors, assignment operators and the destructor are omitted for brevity
void push(int value) {
push_back(value);
}
int pop() {
int value{this[size() - 1]};
remove(size() - 1);
return value;
}
};
The Stack can be used in the following way:
Stack s;
s.push(5);
s.push(6);
s.push(3);
std::cout << s.pop(); // outputs 3
std::cout << s.pop(); // outputs 6
s[2] = 42; // compile error, the Stack has no publicly available operator[] defined
The stack adapts the Vector and provides two member functions so that we can access it. Private inheritance allows us to use the full capabilities of the Vector and hide the inheritance information from the Stack users. What if we want to inherit the Stack to create an advanced version of it? Let's say the AdvancedStack class provides the min() function, which returns the minimum value contained in the stack in constant time.
The private inheritance prohibits the AdvancedStack so that it uses the public interface of the Vector, so we need a way to allow the Stack subclasses to use its base class, but hide the base class' existence from class users. Protected inheritance serves that goal, as shown in the following coe:
class Stack : protected Vector {
// code omitted for brevity
};
class AdvancedStack : public Stack {
// can use the Vector
};
By inheriting the Stack from the Vector, we allow the subclass of the Stack to use the Vector public interface. But the users of both the Stack and AdvancedStack won't be able to access them as a Vector.