updated through ch. 13, Inheritance

This commit is contained in:
Frank B. Brokken 2017-05-28 16:17:24 +02:00
parent 79569f76f0
commit 99805ea7de
14 changed files with 395 additions and 799 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
Early in the annotations() (cf. section ref(HIDING)) we encountered two
important design principles when developing classes: em(data hiding) and
em(encapsulation). Data hiding restricts control over an object's data to the
members of its class, encapsulating is used to restrict access to the
members of its class, encapsulation is used to restrict access to the
functionality of objects. Both principles are invaluable tools for maintaining
data integrity.
@ -15,13 +15,13 @@ requests sent to objects are handled. In a well-designed class its objects are
in full control of their data.
Inheritance doesn't change these principles, nor does it change the way the
tt(private) and tt(protected) keywords operate. A derived class does not have
access to a base class's private section.
`tt(private)' and `tt(protected)' keywords operate. A derived class does not
have access to a base class's private section.
Sometimes this is a bit too restrictive. Consider a class implementing a
random number generating tt(streambuf) (cf. chapter ref(IOStreams)). Such a
tt(streambuf) can be used to construct an tt(istream irand), after which
extractions from tt(irand) produces the next random number, like in the next
extractions from tt(irand) produces series of random numbers, like in the next
example in which 10 random numbers are generated using stream I/O:
verb(
RandBuf buffer;
@ -43,8 +43,9 @@ number. RandBuf, therefore, operates as follows:
it() It is passed in textual form to its base class tt(streambuf);
it() The tt(istream) object extracts this random number, merely
using tt(streambuf)'s interface;
it() this process is repeated for subsequent random numbers;
)
(this process is repeated for subsequent random numbers).
Once tt(RandBuf) has stored the text representation of the next
random number in some buffer, it must tell its base class (tt(streambuf))
where to find the random number's characters. For this tt(streambuf) offers a

View file

@ -15,7 +15,7 @@ follows:
Land::Land(size_t mass, size_t speed)
{
setMass(mass);
setspeed(speed);
setSpeed(speed);
}
)
However, this implementation has several disadvantages.
@ -27,7 +27,7 @@ called.
it() Using the base class constructor only to reassign new values to its
data members in the derived class constructor's body usually is inefficient,
but sometimes sheer impossible as in situations where base class reference or
const data members must be initialized. In those cases a specialized base
tt(const) data members must be initialized. In those cases a specialized base
class constructor must be used instead of the base class default constructor.
)
A derived class's base class may be initialized using a dedicated base

View file

@ -23,7 +23,7 @@ features are used, but others need to be shielded off. Consider the tt(stack)
container: it is commonly implemented in-terms-of a tt(deque), returning
tt(deque::back)'s value as tt(stack::top)'s value.
When using inheritance to implement an tt(is-a) relationship make sure to get
When using inheritance to implement an em(is-a) relationship make sure to get
the `direction of use' right: inheritance aiming at implementing an em(is-a)
relationship should focus on the base class: the base class facilities aren't
there to be used by the derived class, but the derived class facilities should

View file

@ -26,16 +26,15 @@ private members in the derived class. The derived class members may access
all base class public and protected members but base class members cannot be
used elsewhere.
Public derivation should be used to define an tt(is-a) relationship
between a derived class and a base class: the derived class object
em(is-a) base class object allowing the derived class object to be used
polymorphically as a base class object in code expecting a base class
object. Private inheritance is used in situations where a derived class object
is defined in-terms-of the base class where composition cannot be
used. There's little documented use for protected inheritance, but one could
maybe encounter protected inheritance when defining a base class that is
itself a derived class and needs to make its base class members available to
classes derived from itself.
Public derivation should be used to define an em(is-a) relationship
between a derived class and a base class: the derived class object em(is-a)
base class object allowing the derived class object to be used polymorphically
as a base class object in code expecting a base class object. Private
inheritance is used in situations where a derived class object is defined
in-terms-of the base class where composition cannot be used. There's little
documented use for protected inheritance, but one could maybe encounter
protected inheritance when defining a base class that is itself a derived
class making its base class members available to classes derived from it.
Combinations of inheritance types do occur. For example, when designing a
stream-class it is usually derived from tt(std::istream) or

View file

@ -57,11 +57,11 @@ int main()
/*
After providing 5 lines containing, respectively
alfa, bravo, charlie, delta, echo
alpha, bravo, charley, delta, echo
the program displays:
destructor: alfa
destructor: alpha
destructor: bravo
destructor: charlie
destructor: charley
destructor: delta
destructor: echo
*/

View file

@ -1,17 +1,17 @@
Up to now, a class has always been derived from a single base class. In
addition to i(single inheritance) hi(inheritance: multiple) bf(C++) also
supports emi(multiple inheritance). In multiple inheritance a class is derived
from several base classes and hence inherits functionality from multiple
parent classes at the same time.
Except for the class tt(Randbuf) classes thus far have always been derived
from a single base class. In addition to i(single inheritance) hi(inheritance:
multiple) bf(C++) also supports emi(multiple inheritance). In multiple
inheritance a class is derived from several base classes and hence inherits
functionality from multiple parent classes at the same time.
When using multiple inheritance it should be
defensible to consider the newly derived class an instantiation of both base
classes. Otherwise, i(composition) is more appropriate. In general,
linear derivation (using only one base class) is used much more
frequently than multiple derivation. Good class design dictates that a class
should have a single, well described responsibility and that principle often
conflicts with multiple inheritance where we can state that objects of class
tt(Derived) are em(both) tt(Base1) em(and) tt(Base2) objects.
When using multiple inheritance it should be defensible to consider the
newly derived class an instantiation of both base classes. Otherwise,
i(composition) is more appropriate. In general, linear derivation (using only
one base class) is used much more frequently than multiple derivation. Good
class design dictates that a class should have a single, well described
responsibility and that principle often conflicts with multiple inheritance
where we can state that objects of class tt(Derived) are em(both) tt(Base1)
em(and) tt(Base2) objects.
But then, consider em(the) prototype of an object for which
multiple inheritance was used to its extreme: the
@ -19,10 +19,11 @@ multiple inheritance was used to its extreme: the
scissors, it em(is) a can-opener, it em(is) a corkscrew, it em(is) ....
The `Swiss army knife' is an extreme example of multiple inheritance. In
bf(C++) there em(are) some good reasons, not violating the `one class, one
responsibility' principle that is covered in the
link(next chapter)(POLYMORPHISM). In this section the technical details of
constructing classes using multiple inheritance are discussed.
bf(C++) there em(are) various good arguments for using multiple inheritance as
well, without violating the `one class, one responsibility' principle. We
postpone those arguments until the link(next chapter)(POLYMORPHISM). The
current section concentrates on the technical details of constructing classes
using multiple inheritance.
How to construct a `Swiss army knife' in bf(C++)? First we need (at least)
two base classes. For example, let's assume we are designing a toolkit

View file

@ -2,7 +2,7 @@
pointer variable:
verb(
Land land(1200, 130);
Car auto(500, 75, "Daf");
Car car(500, 75, "Daf");
Truck truck(2600, 120, "Mercedes", 6000);
Vehicle *vp;
)
@ -10,7 +10,7 @@ pointer variable:
the derived classes to the tt(Vehicle) pointer:
verb(
vp = &land;
vp = &auto;
vp = &car;
vp = &truck;
)
Each of these assignments is acceptable. However, an

View file

@ -27,10 +27,9 @@ member:
Truck::Truck(size_t tractor_mass, size_t speed, char const *name,
size_t trailer_mass)
:
Car(tractor_mass, speed, name)
{
d_mass = trailer_mass + trailer_mass;
}
Car(tractor_mass, speed, name),
d_mass(tractor_mass + trailer_mass)
{}
)
Note that the class tt(Truck) now contains two functions already
present in the base class tt(Car): tt(setMass) and tt(mass).
@ -135,7 +134,7 @@ size_t Truck::mass() const
)
The class tt(Truck) was derived from tt(Car). However, one might question
this class design. Since a truck is conceived of as a combination of an
this class design. Since a truck is conceived of as a combination of a
tractor and a trailer it is probably better defined using a mixed design,
using inheritance for the tractor part (inheriting from tt(Car), and
composition for the trailer part).

View file

@ -73,7 +73,7 @@ tt(Vehicle):
Land();
Land(size_t mass, size_t speed);
void setspeed(size_t speed);
void setSpeed(size_t speed);
size_t speed() const;
};
)

Binary file not shown.

Binary file not shown.

Binary file not shown.