File: multiple.yo

package info (click to toggle)
c%2B%2B-annotations 13.02.02-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 13,576 kB
  • sloc: cpp: 25,297; makefile: 1,523; ansic: 165; sh: 126; perl: 90; fortran: 27
file content (177 lines) | stat: -rw-r--r-- 7,634 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
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.

    But then, consider em(the) prototype of an object for which
multiple inheritance was used to its extreme: the
    emi(Swiss army knife)! This object em(is) a knife, it em(is) a pair of
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) 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
allowing us to construct an instrument panel of an aircraft's cockpit. We
design all kinds of instruments, like an artificial horizon and an
altimeter. One of the components that is often seen in aircraft is a
em(nav-com set): a combination of a navigational beacon receiver (the `nav'
part) and a radio communication unit (the `com'-part). To define the nav-com
set, we start by designing the tt(NavSet) class (assume the existence of the
classes tt(Intercom, VHF_Dial) and tt(Message)):
        verb(    class NavSet
    {
        public:
            NavSet(Intercom &intercom, VHF_Dial &dial);

            size_t activeFrequency() const;
            size_t standByFrequency() const;

            void setStandByFrequency(size_t freq);
            size_t toggleActiveStandby();
            void setVolume(size_t level);
            void identEmphasis(bool on_off);
    };)

Next we design the class  tt(ComSet):
        verb(    class ComSet
    {
        public:
            ComSet(Intercom &intercom);

            size_t frequency() const;
            size_t passiveFrequency() const;

            void setPassiveFrequency(size_t freq);
            size_t toggleFrequencies();

            void setAudioLevel(size_t level);
            void powerOn(bool on_off);
            void testState(bool on_off);
            void transmit(Message &message);
    };)

Using objects of this class we can receive messages, transmitted
though the tt(Intercom), but we can also em(transmit) messages using a
tt(Message) object that's passed to the tt(ComSet) object using its
tt(transmit) member function.

    Now we're ready to construct our tt(NavCom) set:
        verb(    class NavComSet: public ComSet, public NavSet
    {
        public:
            NavComSet(Intercom &intercom, VHF_Dial &dial);
    };)

Done. Now we have defined a tt(NavComSet) which is em(both) a tt(NavSet)
em(and) a tt(ComSet): the facilities of both base classes  are now
available in the derived class using multiple inheritance.

    Please note the following:
    itemization(
    it() The keyword ti(public) is present before both base class names
(tt(NavSet) and tt(ComSet)). By default inheritance uses
 emi(private derivation) and the keyword tt(public) must be repeated before
each of the base class specifications. Base classes are not required to use
the same derivation type. One base class could have tt(public) derivation and
another base class could use tt(private) derivation.
    it() The multiply derived class tt(NavComSet) introduces no additional
functionality of its own, but merely combines two existing classes into a new
aggregate class. Thus, bf(C++) offers the possibility to simply sweep
multiple simple classes into one more complex class.
    it() Here is the implementation of The tt(NavComSet) i(constructor):
    verb(NavComSet::NavComSet(Intercom &intercom, VHF_Dial &dial)
:
    ComSet(intercom),
    NavSet(intercom, dial)
{})

The constructor requires no extra code: Its purpose is to activate
the constructors of its base classes. The order in which the base class
initializers hi(base class initializers: calling order)
    hi(calling order of base class initializers) are called is em(not)
dictated by their calling order in the constructor's code, but by the ordering
of the base classes in the class interface.
    it() The tt(NavComSet) class definition requires no additional data
members or member functions: here (and often) the inherited interfaces provide
all the required functionality and data for the multiply derived class to
operate properly.
    )
    Of course, while defining the base classes, we made life easy on ourselves
by strictly using different member function names. So, there is a function
tt(setVolume) in the tt(NavSet) class and a function tt(setAudioLevel) in the
tt(ComSet) class. A bit cheating, since we could expect that both units in
fact have a composed object tt(Amplifier), handling the volume setting. A
revised class might offer an tt(Amplifier &amplifier() const) member function,
and leave it to the application to set up its own interface to the
amplifier. Alternatively, a revised class could define members for setting the
volume of either the tt(NavSet) or the tt(ComSet) parts.

In situations where two base classes offer identically named members
 hi(identically named member functions)hi(member function: identically named)
special provisions need to be made to prevent ambiguity:
    itemization(
    it() The intended base class can explicitly be specified using the base
class name and i(scope resolution operator):
    verb(NavComSet navcom(intercom, dial);

navcom.NavSet::setVolume(5);    // sets the NavSet volume level
navcom.ComSet::setVolume(5);    // sets the ComSet volume level)

it() The class interface is provided with member functions that can be
called unambiguously. These additional members are usually defined tt(inline):
    verb(class NavComSet: public ComSet, public NavSet
{
    public:
        NavComSet(Intercom &intercom, VHF_Dial &dial);
        void comVolume(size_t volume);
        void navVolume(size_t volume);
};
inline void NavComSet::comVolume(size_t volume)
{
    ComSet::setVolume(volume);
}
inline void NavComSet::navVolume(size_t volume)
{
    NavSet::setVolume(volume);
})

it() If the tt(NavComSet) class is obtained from a third party, and cannot
be modified, a disambiguating i(wrapper class) may be used:
    verb(class MyNavComSet: public NavComSet
{
    public:
        MyNavComSet(Intercom &intercom, VHF_Dial &dial);
        void comVolume(size_t volume);
        void navVolume(size_t volume);
};
inline MyNavComSet::MyNavComSet(Intercom &intercom, VHF_Dial &dial)
:
    NavComSet(intercom, dial);
{}
inline void MyNavComSet::comVolume(size_t volume)
{
    ComSet::setVolume(volume);
}
inline void MyNavComSet::navVolume(size_t volume)
{
    NavSet::setVolume(volume);
})

)