"So what are the pros and cons of private inheritance and composition?\n",
" \n",
"- No granularity with private inheritance: you get all the public interface available *privately* in the derived class - as mentioned above you may choose which methods to make available publicly.\n",
"- No granularity with private inheritance: you get **all** of the public interface available *privately* in the derived class. As mentioned above, you may choose which methods to make available publicly through the `using` statement.\n",
"- With composition you can cherry-pick which method you want to expose in your new class (only `Print()` here).\n",
"- Composition is more verbose: if you want most of the interface, you need for each method to define a method which under the hood calls your data attribute (as did `Square4::Print()` above). So if you need most of the API of the base class, private inheritance is less work.\n",
"\n",
...
...
%% Cell type:markdown id: tags:
# [Getting started in C++](./) - [Object programming](./0-main.ipynb) - [Inheritance](./6-inheritance.ipynb)
%% Cell type:markdown id: tags:
## Introduction to inheritance
Sometimes, you might want to define two types that are related: one might be an extension of the other, or they may share some similarities we would like to put in common.
Let's suppose for instance you are asked to register all vehicles owned by your company. You could define independent classes `Bicycle`, `Scooter` and `Car`, but as a result storing them in a same array (or even better `std::vector`) would be impossible.
The idea of inheritance is to provide a **base class** from which our classes are derived:
%% Cell type:code id: tags:
``` c++
#include<iostream>
enumclassmotor_type{none,thermic,electric};
classVehicle
{
public:
Vehicle(motor_typetype);
voidPrint()const{std::cout<<"I'm a Vehicle!"<<std::endl;}
private:
motor_typetype_;
};
```
%% Cell type:code id: tags:
``` c++
Vehicle::Vehicle(motor_typetype)
:type_(type)
{}
```
%% Cell type:code id: tags:
``` c++
classElectricBicycle:publicVehicle
{
public:
ElectricBicycle();
};
```
%% Cell type:code id: tags:
``` c++
classThermicCar:publicVehicle
{
public:
ThermicCar();
};
```
%% Cell type:code id: tags:
``` c++
ElectricBicycle::ElectricBicycle()
:Vehicle(motor_type::electric)
{}
```
%% Cell type:code id: tags:
``` c++
ThermicCar::ThermicCar()
:Vehicle(motor_type::thermic)
{}
```
%% Cell type:markdown id: tags:
A bit of syntax first:
* See the structure in declaring the derived classes: there is a `:` followed by the keyword `public` and the name of the class.
* The derived constructors must first call one of the base class constructor. If none specified, the default one without arguments is called *if existing*...
The base class part is constructed first, and then the elements specific to the derived class are added (that will be important - we will come back to that).
Likewise, destruction is performed in reverse order: first the specific parts of the derived class, then the base class (in most cases you don't have to care about that).
### Multiple layer of inheritance
A child class may also be the parent of another class (unless `final` keyword is used - we'll see this keyword in [polymorphism notebook](./7-polymorphism.ipynb#final-keyword)).
%% Cell type:code id: tags:
``` c++
classElectricVehicle:publicVehicle
{
public:
ElectricVehicle();
};
```
%% Cell type:code id: tags:
``` c++
ElectricVehicle::ElectricVehicle()
:Vehicle(motor_type::electric)
{}
```
%% Cell type:code id: tags:
``` c++
classElectricCar:publicElectricVehicle
{
public:
ElectricCar()=default;
};
```
%% Cell type:markdown id: tags:
### Multiple inheritance
It is also possible for a class to inherit from several parents:
%% Cell type:code id: tags:
``` c++
classBlueObjects
{
public:
BlueObjects()=default;
intPrint()const
{
std::cout<<"I'm blue!"<<std::endl;
return42;
}
}
```
%% Cell type:code id: tags:
``` c++
classBlueVehicle:publicVehicle,publicBlueObjects
{
public:
BlueVehicle(motor_typetype);
};
```
%% Cell type:code id: tags:
``` c++
BlueVehicle::BlueVehicle(motor_typetype)
:Vehicle(type),// mandatory call of the non-default constructor
BlueObjects()// not mandatory: default constructor called anyway if not specified explicitly
{}
```
%% Cell type:code id: tags:
``` c++
BlueVehicleblue_bike(motor_type::none);
```
%% Cell type:markdown id: tags:
**Beware:** there might be ambiguity between some methods names, regardless of prototype:
%% Cell type:code id: tags:
``` c++
blue_bike.Print();// COMPILATION ERROR: should it call int BlueObjects::Print() or void Vehicle::Print() const ?
```
%% Cell type:markdown id: tags:
It is possible to lift the ambiguity, but the syntax is not very pretty and you should strive to avoid the case that requires it in the first place:
%% Cell type:code id: tags:
``` c++
blue_bike.BlueObjects::Print();
```
%% Cell type:code id: tags:
``` c++
blue_bike.Vehicle::Print();
```
%% Cell type:markdown id: tags:
### Diamond inheritance
In C++ you may also define so-called diamond inheritance, where an object `D` inherits from the same base class `A` twice through different parents `B` and `C`:
%% Cell type:markdown id: tags:
<imgsrc="./Images/DiamondInheritance.png"alt="Diamond inheritance diagram, from Wikipedia page (public domain)"width="150"/>
%% Cell type:markdown id: tags:
I really advise you to avoid this kind of things whenever possible, but if you _really_ need this:
- First think again, do you _really really_ need this, or could you reorganize your code differently to avoid this situation? (keep in mind other languages chose - wisely to my mind - not to support this, so it is possible to work around it!)
- Have a look [here](https://www.cprogramming.com/tutorial/virtual_inheritance.html) to understand the caveats and do it properly (one key is not to forget the keywords `virtual` in the inheritance line).
%% Cell type:markdown id: tags:
## Public inheritance, private inheritance and composition
### IS-A relationship of public inheritance
So far, we have derived **publicly** the base class (hence the **public** keyword in the inheritance declaration), and this defines a **IS-A** relationship: the derived object is expected to be acceptable *everywhere* a base object is deemed acceptable.
This might seem trivial, but is not as obvious at it seems. Let's consider a counter-intuitive example:
%% Cell type:code id: tags:
``` c++
#include<string>
classRectangle
{
public:
Rectangle(doublewidth,doublelength);
voidSetWidth(doublex);
voidSetLength(doublex);
doubleGetWidth()const;
doubleGetLength()const;
voidPrint()const;
private:
doublewidth_{-1.e20};// Stupid value that would play havoc if not properly initialized
// - std::optional (C++17) would probably a better choice.
doublelength_{-1.e20};
};
```
%% Cell type:code id: tags:
``` c++
Rectangle::Rectangle(doublewidth,doublelength)
:width_(width),
length_(length)
{}
```
%% Cell type:code id: tags:
``` c++
doubleRectangle::GetWidth()const
{
returnwidth_;
}
```
%% Cell type:code id: tags:
``` c++
doubleRectangle::GetLength()const
{
returnlength_;
}
```
%% Cell type:code id: tags:
``` c++
voidRectangle::SetWidth(doublex)
{
width_=x;
}
```
%% Cell type:code id: tags:
``` c++
voidRectangle::SetLength(doublex)
{
length_=x;
}
```
%% Cell type:code id: tags:
``` c++
#include<iostream>
voidRectangle::Print()const
{
std::cout<<"My rectangle gets a length of "<<GetLength()<<" and a width of "<<GetWidth()<<std::endl;
}
```
%% Cell type:code id: tags:
``` c++
classSquare:publicRectangle// BAD IDEA!
{
public:
Square(doubleside_dimension);
};
```
%% Cell type:code id: tags:
``` c++
Square::Square(doubleside_dimension)
:Rectangle(side_dimension,side_dimension)
{}
```
%% Cell type:markdown id: tags:
This is perfectly valid C++ code, and you might be happy with that... Now let's add a free-function that changes the shape of a rectangle:
%% Cell type:code id: tags:
``` c++
voidModifyRectangle(Rectangle&r)
{
r.SetWidth(r.GetWidth()*2.);
r.SetLength(r.GetLength()*.5);
}
```
%% Cell type:code id: tags:
``` c++
{
std::cout<<" ==== RECTANGLE ==== "<<std::endl;
Rectangler(3.,5.);
r.Print();
ModifyRectangle(r);// ok
r.Print();
std::cout<<" ==== SQUARE ==== "<<std::endl;
Squarec(4.);
c.Print();// ok
ModifyRectangle(c);// ok from compiler standpoint... not so much from consistency!
c.Print();// urgh...
}
```
%% Cell type:markdown id: tags:
So the language allows you to define this public relationship between `Rectangle` and `Square`, but you can see it is not a very bright idea... (this example is more detailed in item 32 of [Effective C++](../bibliography.ipynb#Effective-C++-/-More-Effective-C++)).
Don't get me wrong: public inheritance is very handy, as we shall see more below with the introduction of polymorphism. It's just that you need to assess properly first what your needs are, and decide which is the more appropriate answer - and sometimes the most obvious one is not the best.
The public inheritance is an application of the [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle).
We will now "fix" our `Square` problem with two different idioms: private inheritance and composition.
### IS-IMPLEMENTED-IN-TERMS-OF relationship of private inheritance
What you might look for in fact is **private** inheritance, in which all the inherited attributes are considered private:
%% Cell type:code id: tags:
``` c++
classSquare2:privateRectangle
{
public:
Square2(doubleside_dimension);
};
```
%% Cell type:code id: tags:
``` c++
Square2::Square2(doubleside_dimension)
:Rectangle(side_dimension,side_dimension)
{}
```
%% Cell type:code id: tags:
``` c++
{
Square2square(4.);
square.SetWidth(5.);// COMPILATION ERROR!
}
```
%% Cell type:markdown id: tags:
And from there:
%% Cell type:code id: tags:
``` c++
{
Square2square(4.);
ModifyRectangle(square);// COMPILATION ERROR!
}
```
%% Cell type:markdown id: tags:
So this way, there is no assumption a `Square2` might pass any call a `Rectangle` would accept.
And of course the point is to avoid redefining stuff and relying upon what was already implemented in the first class (it won't be impressive here but in other more complex cases it might prove extremely handy):
%% Cell type:code id: tags:
``` c++
classSquare3:privateRectangle
{
public:
Square3(doubleside_dimension);
voidSetSideDimension(doublex);
usingRectangle::Print;// special syntax to make explicitly available the Print() method from Rectangle
};
```
%% Cell type:code id: tags:
``` c++
Square3::Square3(doubleside_dimension)
:Rectangle(side_dimension,side_dimension)
{}
```
%% Cell type:code id: tags:
``` c++
voidSquare3::SetSideDimension(doublex)
{
SetWidth(x);// the methods from Rectangle, accessible privately
SetLength(x);
}
```
%% Cell type:code id: tags:
``` c++
{
Square3square(4.);
square.Print();
square.SetSideDimension(3.);
square.Print();
}
```
%% Cell type:markdown id: tags:
### CONTAINS-A relationship of composition
Private inheritance is not the only way to provide this kind of behaviour; it is possible as well to use **composition**, which is defining a data attribute object that handles part of the computation.
I won't dwelve into too much details here, but the general idea is that composition is often preferable as the binding is less strong and you should strive for the looser relationship possible. Here is the same example as above implemented with composition:
%% Cell type:code id: tags:
``` c++
classSquare4
{
public:
Square4(doubleside_dimension);
~Square4()=default;
voidPrint()const;
voidSetSideDimension(doublex);
private:
constRectangle&GetRectangle()const;
Rectangle&GetNonConstantRectangle();// we could have named it `GetRectangle()` as well, but a really distinct name is more expressive!
private:
Rectanglerectangle_;
};
```
%% Cell type:code id: tags:
``` c++
constRectangle&Square4::GetRectangle()const
{
returnrectangle_;
}
```
%% Cell type:code id: tags:
``` c++
Rectangle&Square4::GetNonConstantRectangle()
{
returnrectangle_;
}
```
%% Cell type:code id: tags:
``` c++
Square4::Square4(doubleside_dimension)
:rectangle_(Rectangle(side_dimension,side_dimension))// you need the `Rectangle` constructor call here, as there are no default constructor in `Rectangle` class
{}
```
%% Cell type:code id: tags:
``` c++
voidSquare4::SetSideDimension(doublex)
{
auto&rectangle=GetNonConstantRectangle();
rectangle.SetWidth(x);
rectangle.SetLength(x);
}
```
%% Cell type:code id: tags:
``` c++
voidSquare4::Print()const
{
GetRectangle().Print();
}
```
%% Cell type:code id: tags:
``` c++
{
Square4square(4.);
square.Print();
square.SetSideDimension(3.);
square.Print();
}
```
%% Cell type:markdown id: tags:
### Private inheritance vs composition
So what are the pros and cons of private inheritance and composition?
- No granularity with private inheritance: you get all the public interface available *privately* in the derived class - as mentioned above you may choose which methods to make available publicly.
- No granularity with private inheritance: you get **all** of the public interface available *privately* in the derived class. As mentioned above, you may choose which methods to make available publicly through the `using` statement.
- With composition you can cherry-pick which method you want to expose in your new class (only `Print()` here).
- Composition is more verbose: if you want most of the interface, you need for each method to define a method which under the hood calls your data attribute (as did `Square4::Print()` above). So if you need most of the API of the base class, private inheritance is less work.
As indicated before, I tend to use composition more, but it's still nice to know both are available.
%% Cell type:markdown id: tags:
## Protected status
Our discussion about public and private inheritance has highlighted the importance of the propagation of the status of public members:
* With public inheritance, public members of the base class remains public.
* With private inheritance, public members of the base class become private.
So far, we have not talked about what happens to private members... Now is the time; let's find out!
%% Cell type:code id: tags:
``` c++
classBaseClass
{
public:
BaseClass()=default;
private:
voidDoNothing(){}
};
```
%% Cell type:code id: tags:
``` c++
classDerivedClass:publicBaseClass
{
public:
DerivedClass();
};
```
%% Cell type:code id: tags:
``` c++
DerivedClass::DerivedClass()
{
DoNothing();// COMPILATION ERROR
}
```
%% Cell type:markdown id: tags:
So a private method is not accessible, even to the derived class. If you need such a functionality (and you should!), there is actually a third keyword: **protected**. This status means basically private except for the derived classes:
%% Cell type:code id: tags:
``` c++
classBaseClass2
{
public:
BaseClass2()=default;
protected:
voidDoNothing(){}
};
```
%% Cell type:code id: tags:
``` c++
classDerivedClass2:publicBaseClass2
{
public:
DerivedClass2();
};
```
%% Cell type:code id: tags:
``` c++
DerivedClass2::DerivedClass2()
{
DoNothing();// Ok
}
```
%% Cell type:code id: tags:
``` c++
autoobject=DerivedClass2();
```
%% Cell type:code id: tags:
``` c++
object.DoNothing();// COMPILATION ERROR!
```
%% Cell type:markdown id: tags:
Here `DoNothing` behaves exactly as a private method of `DerivedClass2`, but it was defined in `BaseClass2`.
%% Cell type:markdown id: tags:
There is as well a **protected** status for inheritance even if I must admit I have never needed it. A contributor to [StackOverflow](https://stackoverflow.com/questions/860339/difference-between-private-public-and-protected-inheritance) summed this up with this nice cheatsheet:
%% Cell type:markdown id: tags:
")
%% Cell type:markdown id: tags:
## **[WARNING]** Be wary of the slicing effect
A note of caution about public inheritance:
%% Cell type:code id: tags:
``` c++
classBase{};
```
%% Cell type:code id: tags:
``` c++
#include<iostream>
classDerived:publicBase
{
public:
Derived(intval)
:value_{val}
{}
private:
intvalue_;
}
```
%% Cell type:code id: tags:
``` c++
{
Basebase;
Derivedderived{5};
base=derived;// dangerous line...
}
```
%% Cell type:markdown id: tags:
This code is perfectly valid, but there is a fat chance it's absolutely not what you intended: all the information that is in `Derived` but not in `Base` is lost in `base` object...
This is the so-called **slicing effect**.
%% Cell type:markdown id: tags:
Here it may seem obvious, but it might be insidious if you use functions with pass-by-value:
%% Cell type:code id: tags:
``` c++
voidComputePoorlyStuff(Basebase)
{
// ...
}
```
%% Cell type:code id: tags:
``` c++
{
Derivedderived{5};
ComputePoorlyStuff(derived);// slicing effect: all defined only in `Derived` class is lost as far as `ComputePoorlyStuff` function is concerned!
}
```
%% Cell type:markdown id: tags:
you're actually slicing the `derived` object and only the `Base` part of it is copied and passed in the function!
The patch is rather easy and match what we already told for different reasons previously: don't pass objects by copy!
Indeed, the same with pass-by-reference (or pass-by-pointer if you like these sort of things...) works like a charm:
%% Cell type:code id: tags:
``` c++
voidComputeStuff(constBase&base)// ok, no slicing effect!
{
// ...
}
```
%% Cell type:markdown id: tags:
If you want to learn more about this effect, you should read this [great (and short) tutorial](https://www.learncpp.com/cpp-tutorial/object-slicing).