There are 3 types of inheritances in classes inheritance in C++; they are
- public inheritance
- protected inheritance
- protected inheritance
as for the synonym of the type of inheritances, public inheritance is also know as type inheritance, while the private inheritance is also know as implementation inheritancfe.
A public derivation is refered to as type inhertitance, the dervied class is a subtype of the bse classes; it overrides the implementation of all type-specific member funcitons. of the base class while inheriting those that are shared. The derived class in general reflects is-a relationship
While we know a lots can be found abou the public inheritance, here we are not going into details further.
A private derivation is refered to as implementation inheritance, the derived class does not support the public interface of hte base directly. rather, it wishes to reuses the implementation of the base class while providing its own interface. To illustrate the issues involved, let's implements a PeekbackStack.
Let's see an examle.
suppose that we are subclass a IntArray class (a custom class) and provide additional functionality such as the ability to peek the back of the stack, which we call the subclass PeekbackStack.
for illustration purpose, this is the simplified version of IntArray code.
class IntArray
{
public:
IntArray(int size) : _size(size) {
init(size, 0);
}
IntArray(const IntArray & rhs) {
init(rhs.size(), rhs.ia);
}
int size() const { return _size; }
int& operator[](int index) { return ia[index]; }
virtual ~IntArray() {
delete[] ia;
}
private:
void init(int sz, int * array_) {
_size = sz;
ia = new int[_size];
for (int i = 0; i < _size; ++i) {
if (! array_)
ia[i] = 0;
else ia[i] = array_[i];
}
}
protected:
int _size;
int *ia;
};
if you are using the public inheritance, you might implement the code as follow.
class PeekbackStack : public IntArray
{
private:
const int static bos = -1;
public:
explicit PeekbackStack(int size) : IntArray(size), _top(bos) { }
bool empty() const { return _top == bos; }
bool full() const { return _top == size() - 1; }
bool top() const { return _top; }
int pop() {
if (empty()) {
/* handle error condiiton */
return ia[_top --];
}
}
void push(int value ) {
if (full() ) {
/* handle error condition */
ia[++_top] = value;
}
}
bool peekback(int index, int & value) const;
private:
int _top;
};
inline bool
PeekbackStack::peekback(int index, int & value) const {
if (empty() ) {
/* handle error condition */
}
if (index < 0 || index > _top)
{
value = ia[_top];
return false;
}
value = ia[index];
return true;
}
This is all good, except that there is a by-prpduct, whereas , Program using the new PeekbackStack class may also make inappropriate use of its IntArray base class public interface, for example.
extern void swap(IntArray&, int, int);
Peekbackstack is(1024);
// oops: unexpected misuse of the PeekbackStack
swap(is, i , j);
is.sort();
is[0] = is[512];
So, the public inheritance represents a is-a relationship, while reflected back in our case, we can see that PeekbackStack is a special kind of IntArray, so you can expect that the operation that is applicable to PeekbackStack as well. so we can see that public inheritance is not the best fit for our case, what we want to is to restrict the user to the interfaces that is defined by us.
So how to address that, here comes the private inheritance. Let's see how we impl the private inheritance through the use of private inheritance
the solution is quite easy, you just change the public keyword with the private in the class derivation list.
class PeekbackStack: private IntArray { ... }
as a side note, a private base class reflects a form of inheritance that is not based on subtype relationship. The entire public interface of the base class becomes private in the derived class. Each of the preceding misuses of a PeekbackStack class instance is now illegal except witin in friend and member functions of the derived class.
Thus, it may as well deserve to talk about composition versus inheritance as far as we have private inheritance covered.
Since, after all with the private inheritance, we have achieved a kind of has-as relationship with regards to the IntArray classes.
Here is the new PeekbackStack implementaion with Composition pattern .
class PeekbackStack
{
private:
const int static bos = -1;
public:
explicit PeekbackStack (int size): stack (size), _top(bos) {}
bool empty() const { return _top == bos; }
bool full() const { return _top == stack.size() - 1; }
int top() const { return _top; }
int pop() {
if (empty() ) {
// handling error
}
return stack[_top -- ];
}
void push(int value)
{
if ( full() ) {
// handle error condition
}
stack[++_top] = value;
}
bool peekback(int index, int & value) const;
private:
int _top;
IntArray stack;
};
so you may wonder when to use private inheritance and when to use the composition by references/value?
so this is a very good guidelines as whether to use composition or private inheritance in a class design in in which a has-a relationship exists
- If we wish to override any of the virtual functions of a class, we must inherit from it privately.
- If we wish to allow the class to refer to one of a hierarchy of possible types, we must use composition by reference
- If as with our PeekbackStack class, we wish simply to reuse the implementation, composition by value is to be preferred over inheritance. If a lazy allocation of the object is desired, composition by reference (useing a pointer) is the generally preferred design choice.
So we have covered the private and public inhertance, let's see why we have protected inheritance...
Before we introduce the protected inheritance, let's first examing the "exemping individual members"... since with private inhertiance, protected and public members are now all becomes private, in our example, you cannot allow the client to access the IntArray::size() method (which should be the same if the PeekbackStack class need to provide a size() method).
So what you can do is as follow.
class PeekbackStack : private IntArray
{
//....
public:
// you can allow the client to access the IntArray size method by exempting the size method in the public section
using IntArray::size;
// you can allow further subsequent derivation access to the protected members. you can exempt individual members in the protected section
protected:
using IntArray::_size;
using IntArray::ia;
//....
};
However, this is tedious work and further, suppose that if we change the hierarchy so that
Stack inherits IntArray, => PeekbackStack inherits Stack, if we were using the private inheritancfe, we cannot allow the PeekbackStack to override some members, while protected inheritaince provides a perfect balance/compromise.
so if you do
class Stack : protected IntArray { ... }
then it you can easilty do
class PeekbackStack : public Stack { ... }