22.4.  public, protected, and private derivation

[ fromfile: accessderivation.xml id: accessderivation ]

Most of the time, you are likely to see classes using public derivation, for example

  class Square : public Shape {
  // ...  };

public derivation describes an interface relationship between two classes. This means that the interface (public part) of the base class merges with the interface of the derived class. When it is correct to say that a derived object is a base class object, public derivation is appropriate. For example, a Square is a Shape (with certain additional attributes).

Much less commonly you might see protected or private derivation. This is considered an implementation relationship, rather than an is a relationship. The base class interface (public part) gets merged with the implementation (private or protected, depending on the kind of derivation) of the derived class.

In effect, private derivation is like adding an extra object as a private data member to the derived class.

Similarly, protected derivation is like adding an object as a protected data member to the derived class, but it shares the this pointer.

Example 22.10 is a concrete example of a situation in which private derivation might be appropriate. The template class Stack is privately derived from QList. The rationale for doing this is that a stack is, by definition, a datastructure which limits access to the top item. The class QStack, which is publicly derived from QVector, has the expected public interface for a stack but it also gives client code unlimited access to the items in the stack because it contains the entire public interface of QVector. Our Stack class is privately derived from QList so its public interface limits client code access to the handful of stack operations that are consistent with the definition of that data structure.

Example 22.10. src/privatederiv/stack.h

#ifndef _STACK_H_
#define _STACK_H_

#include <QList>

template<class T>
class Stack : private QList<T> { 
public:
    bool isEmpty() const {
        return QList<T>::isEmpty();
    }
    T pop() {
        return takeFirst();
    }
    void push(const T& value) {
        prepend(value);
    }
    const T& top() const {
        return first();
    }
    int size() const {
        return QList<T>::size();
    }
    void clear() {
        QList<T>::clear();
    }
};
#endif

<include src="src/privatederiv/stack.h" href="src/privatederiv/stack.h" id="privderivstackh" mode="cpp"/>


Example 22.11 shows that an attempt by client code to make use of the Stack's base class (QList) interface is not allowed.

Example 22.11. src/privatederiv/stack-test.cpp

#include "stack.h"
#include <QString>
#include <qstd.h>
using namespace qstd;

int main() {
    Stack<QString> strs;
    strs.push("hic");
    strs.push("haec");
    strs.push("hoc");
//  strs.removeAt(2);  1
    int n = strs.size();
    cout << n << " items in stack" << endl;
    for (int i = 0; i < n; ++i)
        cout << strs.pop() << endl;
}


1

Error, inherited QList methods are private.

<include src="src/privatederiv/stack-test.cpp" href="src/privatederiv/stack-test.cpp" id="stacktestcpp" mode="cpp"/>


So, private derivation provides a way to hide the public interface of a base class, for situations where the base class is needed only for implementation purposes. What about protected derivation?

Suppose you want to derive XStack, a particular kind of stack, from this Stack class. With Stack privately derived from QList, you will not be able to make use of any QList member functions in the implementation of XStack. If you need to use some of the QList functions when you implement XStack, then you must use protected derivation when you derive Stack from QList. protected derivation makes the public interface of QList protected in Stack. Internally, this enables classes derived from Stack to make use of the inherited QList protected interface.