Tải bản đầy đủ (.pdf) (102 trang)

The C++ Programming Language Third Edition phần 4 potx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (338.67 KB, 102 trang )

Section 11.12

A String Class

297

f ri en d b oo l o pe ra to r!=(c on st S tr in g& x c on st S tr in g& y
fr ie nd bo ol op er at or
co ns t St ri ng x, co ns t St ri ng y)
{ r et ur n s tr cm p(x re p->s y re p->s != 0 }
re tu rn st rc mp x.r ep s, y.r ep s)
0;
};
S tr in g o pe ra to r+(c on st S tr in g&, c on st S tr in g&);
St ri ng op er at or co ns t St ri ng
co ns t St ri ng
S tr in g o pe ra to r+(c on st S tr in g&, c on st c ha r*);
St ri ng op er at or co ns t St ri ng
co ns t ch ar

To save space, I have left the I/O and concatenation operations as exercises.
The main program simply exercises the S tr in g operators a bit:
St ri ng
S tr in g f St ri ng a S tr in g b
St ri ng f(S tr in g a, St ri ng b)
{
a 2] = ´x
a[2
x´;
c ha r c = b 3];
ch ar


b[3
c ou t << "i n f " << a << ´ ´ << b << ´ ´ << c << ´\ n´;
co ut
in f:
\n
r et ur n b
re tu rn b;
}
i nt m ai n()
in t ma in
{
S tr in g x y
St ri ng x, y;
c ou t << "P le as e e nt er t wo s tr in gs \n
co ut
Pl ea se en te r tw o st ri ng s\ n";
c in >> x >> y
ci n
y;
c ou t << "i np ut " << x << ´ ´ << y << ´\ n´;
co ut
in pu t:
\n
S tr in g z = x
St ri ng
x;
y = f x,y ;
f(x y)
i f (x != z c ou t << "x c or ru pt ed \n
if x

z) co ut
x co rr up te d!\ n";
x 0] = ´!´;
x[0
i f (x == z c ou t << "w ri te f ai le d!\ n";
if x
z) co ut
wr it e fa il ed \n
c ou t << "e xi t: " << x << ´ ´ << y << ´ ´ << z << ´\ n´;
co ut
ex it
\n
}

This S tr in g lacks many features that you might consider important or even essential. For example,
St ri ng
it offers no operation of producing a C-string representation of its value (§11.14[10], Chapter 20).

11.13 Advice [class.advice]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]


Define operators primarily to mimic conventional usage; §11.1.
For large operands, use c on st reference argument types; §11.6.
co ns t
For large results, consider optimizing the return; §11.6.
Prefer the default copy operations if appropriate for a class; §11.3.4.
Redefine or prohibit copying if the default is not appropriate for a type; §11.2.2.
Prefer member functions over nonmembers for operations that need access to the representation; §11.5.2.
Prefer nonmember functions over members for operations that do not need access to the representation; §11.5.2.
Use namespaces to associate helper functions with ‘‘their’’ class; §11.2.4.
Use nonmember functions for symmetric operators; §11.3.2.
Use () for subscripting multidimensional arrays; §11.9.

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


298

Operator Overloading

Chapter 11

[11] Make constructors that take a single ‘‘size argument’’ e xp li ci t; §11.7.1.
ex pl ic it
[12] For non-specialized uses, prefer the standard s tr in g (Chapter 20) to the result of your own
st ri ng
exercises; §11.12.
[13] Be cautious about introducing implicit conversions; §11.4.
[14] Use member functions to express operators that require an lvalue as its left-hand operand;
§11.3.5.


11.14 Exercises [over.exercises]
1. (∗2) In the following program, which conversions are used in each expression?
s tr uc t X {
st ru ct
i nt i
in t i;
X in t);
X(i nt
o pe ra to r+(i nt ;
op er at or in t)
};
s tr uc t Y {
st ru ct
i nt i
in t i;
Y X);
Y(X
o pe ra to r+(X ;
op er at or X)
o pe ra to r i nt ;
op er at or in t()
};
e xt er n X o pe ra to r*(X Y ;
ex te rn
op er at or X, Y)
e xt er n i nt f X);
ex te rn in t f(X
X x=1
1;

Y y=x
x;
i nt i = 2
in t
2;
i nt m ai n()
in t ma in
{
i + 1 0;
10
x+y+i
i;
f y);
f(y
}

y + 1 0;
10
y + 10 * y
10 y;
x*x+i
i; f 7);
f(7
y+y
y;
1 06 + y
10 6 y;

Modify the program so that it will run and print the values of each legal expression.
2. (∗2) Complete and test class S tr in g from §11.12.

St ri ng
3. (∗2) Define a class I NT that behaves exactly like an i nt Hint: Define I NT :o pe ra to r i nt
IN T
in t.
IN T: op er at or in t().
4. (∗1) Define a class R IN T that behaves like an i nt except that the only operations allowed are +
RI NT
in t
(unary and binary), - (unary and binary), *, /, and %. Hint: Do not define R IN T::o pe ra to r
RI NT op er at or
i nt
in t().
5. (∗3) Define a class L IN T that behaves like a R IN T, except that it has at least 64 bits of preciLI NT
RI NT
sion.
6. (∗4) Define a class implementing arbitrary precision arithmetic. Test it by calculating the factorial of 1 00 0. Hint: You will need to manage storage in a way similar to what was done for class
10 00
S tr in g.
St ri ng

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


Section 11.14

Exercises

299


7. (∗2) Define an external iterator for class S tr in g:
St ri ng
c la ss S tr in g_ it er {
cl as s St ri ng _i te r
// refer to string and string element
p ub li c:
pu bl ic
S tr in g_ it er St ri ng s ;
St ri ng _i te r(S tr in g& s)
// iterator for s
c ha r& n ex t();
ch ar ne xt
// reference to next element
// more operations of your choice
};

Compare this in utility, programming style, and efficiency to having an internal iterator for
S tr in g (that is, a notion of a current element for the S tr in g and operations relating to that eleSt ri ng
St ri ng
ment).
8. (∗1.5) Provide a substring operator for a string class by overloading (). What other operations
would you like to be able to do on a string?
9. (∗3) Design class S tr in g so that the substring operator can be used on the left-hand side of an
St ri ng
assignment. First, write a version in which a string can be assigned to a substring of the same
length. Then, write a version in which the lengths may differ.
10. (∗2) Define an operation for S tr in g that produces a C-string representation of its value. Discuss
St ri ng
the pros and cons of having that operation as a conversion operator. Discuss alternatives for
allocating the memory for that C-string representation.

11. (∗2.5) Define and implement a simple regular expression pattern match facility for class S tr in g.
St ri ng
12. (∗1.5) Modify the pattern match facility from §11.14[11] to work on the standard library s tr in g.
st ri ng
Note that you cannot modify the definition of s tr in g.
st ri ng
13. (∗2) Write a program that has been rendered unreadable through use of operator overloading
and macros. An idea: Define + to mean - and vice versa for I NT s. Then, use a macro to define
IN Ts
i nt to mean I NT Redefine popular functions using reference type arguments. Writing a few
in t
IN T.
misleading comments can also create great confusion.
14. (∗3) Swap the result of §11.14[13] with a friend. Without running it, figure out what your
friend’s program does. When you have completed this exercise, you’ll know what to avoid.
15. (∗2) Define a type V ec 4 as a vector of four f lo at Define o pe ra to r[] for V ec 4. Define operaVe c4
fl oa ts.
op er at or
Ve c4
tors +, -, *, /, =, +=, -=, *=, and /= for combinations of vectors and floating-point numbers.
16. (∗3) Define a class M at 4 as a vector of four V ec 4s. Define o pe ra to r[] to return a V ec 4 for
Ma t4
Ve c4
op er at or
Ve c4
M at 4. Define the usual matrix operations for this type. Define a function doing Gaussian elimMa t4
ination for a M at 4.
Ma t4
17. (∗2) Define a class V ec to r similar to V ec 4 but with the size given as an argument to the conVe ct or
Ve c4

structor V ec to r::V ec to r(i nt
Ve ct or Ve ct or in t).
18. (∗3) Define a class M at ri x similar to M at 4 but with the dimensions given as arguments to the
Ma tr ix
Ma t4
constructor M at ri x::M at ri x(i nt in t).
Ma tr ix Ma tr ix in t,i nt
19. (∗2) Complete class P tr _t o_ T from §11.11 and test it. To be complete, P tr _t o_ T must have at
Pt r_ to _T
Pt r_ to _T
least the operators *, ->, =, ++, and -- defined. Do not cause a run-time error until a wild
pointer is actually dereferenced.

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


300

Operator Overloading

Chapter 11

20. (∗1) Given two structures:
s tr uc t S { i nt x y };
st ru ct
in t x, y;
s tr uc t T { c ha r* p c ha r* q };
st ru ct
ch ar p; ch ar q;


write a class C that allows the use of x and p from some S and T much as if x and p had been
T,
members of C
C.
21. (∗1.5) Define a class I nd ex to hold the index for an exponentiation function
In de x
m yp ow do ub le In de x). Find a way to have 2 I call m yp ow 2,I
my po w(d ou bl e,I nd ex
2**I
my po w(2 I).
22. (∗2) Define a class I ma gi na ry to represent imaginary numbers. Define class C om pl ex based on
Im ag in ar y
Co mp le x
that. Implement the fundamental arithmetic operators.

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


________________________________________
________________________________________________________________________________________________________________________________________________________________

12
________________________________________
________________________________________________________________________________________________________________________________________________________________

Derived Classes
Do not multiply objects without necessity.
– W. Occam


Concepts and classes — derived classes — member functions — construction and
destruction — class hierarchies — type fields — virtual functions — abstract classes —
traditional class hierarchies — abstract classes as interfaces — localizing object creation
— abstract classes and class hierarchies — advice — exercises.

12.1 Introduction [derived.intro]
From Simula, C++ borrowed the concept of a class as a user-defined type and the concept of class
hierarchies. In addition, it borrowed the idea for system design that classes should be used to
model concepts in the programmer’s and the application’s world. C++ provides language constructs that directly support these design notions. Conversely, using the language features in support of design concepts distinguishes effective use of C++. Using language constructs only as notational props for more traditional types of programming is to miss key strengths of C++.
A concept does not exist in isolation. It coexists with related concepts and derives much of its
power from relationships with related concepts. For example, try to explain what a car is. Soon
you’ll have introduced the notions of wheels, engines, drivers, pedestrians, trucks, ambulances,
roads, oil, speeding tickets, motels, etc. Since we use classes to represent concepts, the issue
becomes how to represent relationships between concepts. However, we can’t express arbitrary
relationships directly in a programming language. Even if we could, we wouldn’t want to. Our
classes should be more narrowly defined than our everyday concepts – and more precise. The
notion of a derived class and its associated language mechanisms are provided to express hierarchical relationships, that is, to express commonality between classes. For example, the concepts of a
circle and a triangle are related in that they are both shapes; that is, they have the concept of a shape
in common. Thus, we must explicitly define class C ir cl e and class T ri an gl e to have class S ha pe in
Ci rc le
Tr ia ng le
Sh ap e

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


302


Derived Classes

Chapter 12

common. Representing a circle and a triangle in a program without involving the notion of a shape
would be to lose something essential. This chapter is an exploration of the implications of this simple idea, which is the basis for what is commonly called object-oriented programming.
The presentation of language features and techniques progress from the simple and concrete to
the more sophisticated and abstract. For many programmers, this will also be a progression from
the familiar towards the less well known. This is not a simple journey from ‘‘bad old techniques’’
towards ‘‘the one right way.’’ When I point out limitations of one technique as a motivation for
another, I do so in the context of specific problems; for different problems or in other contexts, the
first technique may indeed be the better choice. Useful software has been constructed using all of
the techniques presented here. The aim is to help you attain sufficient understanding of the techniques to be able to make intelligent and balanced choices among them for real problems.
In this chapter, I first introduce the basic language features supporting object-oriented programming. Next, the use of those features to develop well-structured programs is discussed in the context of a larger example. Further facilities supporting object-oriented programming, such as multiple inheritance and run-time type identification, are discussed in Chapter 15.

12.2 Derived Classes [derived.derived]
Consider building a program dealing with people employed by a firm. Such a program might have
a data structure like this:
s tr uc t E mp lo ye e {
st ru ct Em pl oy ee
s tr in g f ir st _n am e, f am il y_ na me
st ri ng fi rs t_ na me fa mi ly _n am e;
c ha r m id dl e_ in it ia l;
ch ar mi dd le _i ni ti al
D at e h ir in g_ da te
Da te hi ri ng _d at e;
s ho rt d ep ar tm en t;
sh or t de pa rt me nt
// ...
};


Next, we might try to define a manager:
s tr uc t M an ag er {
st ru ct Ma na ge r
E mp lo ye e e mp
Em pl oy ee em p;
s et Em pl oy ee
se t<E mp lo ye e*> g ro up
gr ou p;
s ho rt l ev el
sh or t le ve l;
// ...
};

// manager’s employee record
// people managed

A manager is also an employee; the E mp lo ye e data is stored in the e mp member of a M an ag er
Em pl oy ee
em p
Ma na ge r
object. This may be obvious to a human reader – especially a careful reader – but there is nothing
that tells the compiler and other tools that M an ag er is also an E mp lo ye e. A M an ag er is not an
Ma na ge r
Em pl oy ee
Ma na ge r*
E mp lo ye e*, so one cannot simply use one where the other is required. In particular, one cannot put
Em pl oy ee
a M an ag er onto a list of E mp lo ye es without writing special code. We could either use explicit
Ma na ge r

Em pl oy ee
type conversion on a M an ag er or put the address of the e mp member onto a list of e mp lo ye es.
Ma na ge r*
em p
em pl oy ee
However, both solutions are inelegant and can be quite obscure. The correct approach is to explicitly state that a M an ag er is an E mp lo ye e, with a few pieces of information added:
Ma na ge r
Em pl oy ee

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


Section 12.2

Derived Classes

303

s tr uc t M an ag er : p ub li c E mp lo ye e {
st ru ct Ma na ge r pu bl ic Em pl oy ee
s et Em pl oy ee
se t<E mp lo ye e*> g ro up
gr ou p;
s ho rt l ev el
sh or t le ve l;
// ...
};

The M an ag er is derived from E mp lo ye e, and conversely, E mp lo ye e is a base class for M an ag er

Ma na ge r
Em pl oy ee
Em pl oy ee
Ma na ge r.
The class M an ag er has the members of class E mp lo ye e (n am e, a ge etc.) in addition to its own
Ma na ge r
Em pl oy ee na me ag e,
members (g ro up l ev el etc.).
gr ou p, le ve l,
Derivation is often represented graphically by a pointer from the derived class to its base class
indicating that the derived class refers to its base (rather than the other way around):
E mp lo ye e
Em pl oy ee
.
M an ag er
Ma na ge r
A derived class is often said to inherit properties from its base, so the relationship is also called
inheritance. A base class is sometimes called a superclass and a derived class a subclass. This terminology, however, is confusing to people who observe that the data in a derived class object is a
superset of the data of an object of its base class. A derived class is larger than its base class in the
sense that it holds more data and provides more functions.
A popular and efficient implementation of the notion of derived classes has an object of the
derived class represented as an object of the base class, with the information belonging specifically
to the derived class added at the end. For example:
E mp lo ye e:
Em pl oy ee

M an ag er
Ma na ge r:
f ir st _n am e
fi rs t_ na me

f am il y_ na me
fa mi ly _n am e
...

f ir st _n am e
fi rs t_ na me
f am il y_ na me
fa mi ly _n am e
...
g ro up
gr ou p
l ev el
le ve l
...

Deriving M an ag er from E mp lo ye e in this way makes M an ag er a subtype of E mp lo ye e so that a
Ma na ge r
Em pl oy ee
Ma na ge r
Em pl oy ee
M an ag er can be used wherever an E mp lo ye e is acceptable. For example, we can now create a list
Ma na ge r
Em pl oy ee
of E mp lo ye es, some of whom are M an ag er
Em pl oy ee
Ma na ge rs:
v oi d f Ma na ge r m 1, E mp lo ye e e 1)
vo id f(M an ag er m1 Em pl oy ee e1
{
l is t<E mp lo ye e*> e li st

li st Em pl oy ee
el is t;
e li st pu sh _f ro nt m1 ;
el is t.p us h_ fr on t(&m 1)
e li st pu sh _f ro nt e1 ;
el is t.p us h_ fr on t(&e 1)
// ...
}

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


304

Derived Classes

Chapter 12

A M an ag er is (also) an E mp lo ye e, so a M an ag er can be used as a E mp lo ye e*. However, an
Ma na ge r
Em pl oy ee
Ma na ge r*
Em pl oy ee
E mp lo ye e is not necessarily a M an ag er so an E mp lo ye e* cannot be used as a M an ag er In genEm pl oy ee
Ma na ge r,
Em pl oy ee
Ma na ge r*.
eral, if a class D er iv ed has a public base class (§15.3) B as e, then a D er iv ed can be assigned to a
De ri ve d

Ba se
De ri ve d*
variable of type B as e* without the use of explicit type conversion. The opposite conversion, from
Ba se
B as e* to D er iv ed must be explicit. For example:
Ba se
De ri ve d*,
v oi d g Ma na ge r m m, E mp lo ye e e e)
vo id g(M an ag er mm Em pl oy ee ee
{
E mp lo ye e* p e = &m m;
Em pl oy ee pe
mm
// ok: every Manager is an Employee
M an ag er p m = &e e;
Ma na ge r* pm
ee
// error: not every Employee is a Manager
p m->l ev el = 2
pm le ve l 2;

// disaster: ee doesn’t have a ‘level’

p m = s ta ti c_ ca st Ma na ge r*>(p e);
pm st at ic _c as tpe
p m->l ev el = 2
pm le ve l 2;

// brute force: works because pe points

// to the Manager mm

// fine: pm points to the Manager mm that has a ‘level’

}

In other words, an object of a derived class can be treated as an object of its base class when manipulated through pointers and references. The opposite is not true. The use of s ta ti c_ ca st and
st at ic _c as t
d yn am ic _c as t is discussed in §15.4.2.
dy na mi c_ ca st
Using a class as a base is equivalent to declaring an (unnamed) object of that class. Consequently, a class must be defined in order to be used as a base (§5.7):
c la ss E mp lo ye e;
cl as s Em pl oy ee

// declaration only, no definition

c la ss M an ag er : p ub li c E mp lo ye e { // error: Employee not defined
cl as s Ma na ge r pu bl ic Em pl oy ee
// ...
};

12.2.1 Member Functions [derived.member]
Simple data structures, such as E mp lo ye e and M an ag er are really not that interesting and often not
Em pl oy ee
Ma na ge r,
particularly useful. We need to give the information as a proper type that provides a suitable set of
operations that present the concept, and we need to do this without tying us to the details of a particular representation. For example:
c la ss E mp lo ye e {
cl as s Em pl oy ee
s tr in g f ir st _n am e, f am il y_ na me

st ri ng fi rs t_ na me fa mi ly _n am e;
c ha r m id dl e_ in it ia l;
ch ar mi dd le _i ni ti al
// ...
p ub li c:
pu bl ic
v oi d p ri nt
vo id pr in t() c on st
co ns t;
s tr in g f ul l_ na me
st ri ng fu ll _n am e() c on st
co ns t
{ r et ur n f ir st _n am e + ´ ´ + m id dl e_ in it ia l + ´ ´ + f am il y_ na me }
re tu rn fi rs t_ na me
mi dd le _i ni ti al
fa mi ly _n am e;
// ...
};

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


Section 12.2.1

Member Functions

305

c la ss M an ag er : p ub li c E mp lo ye e {

cl as s Ma na ge r pu bl ic Em pl oy ee
// ...
p ub li c:
pu bl ic
v oi d p ri nt c on st
vo id pr in t() co ns t;
// ...
};

A member of a derived class can use the public – and protected (see §15.3) – members of its base
class as if they were declared in the derived class itself. For example:
v oi d M an ag er :p ri nt
vo id Ma na ge r: pr in t() c on st
co ns t
{
c ou t << "n am e i s " << f ul l_ na me
co ut
na me is
fu ll _n am e() << ´\ n´;
\n
// ...
}

However, a derived class cannot use a base class’ private names:
v oi d M an ag er :p ri nt
vo id Ma na ge r: pr in t() c on st
co ns t
{
c ou t << " n am e i s " << f am il y_ na me << ´\ n´;
co ut

na me is
fa mi ly _n am e
\n
// ...
}

// error!

This second version of M an ag er :p ri nt
Ma na ge r: pr in t() will not compile. A member of a derived class has no
special permission to access private members of its base class, so f am il y_ na me is not accessible to
fa mi ly _n am e
M an ag er :p ri nt
Ma na ge r: pr in t().
This comes as a surprise to some, but consider the alternative: that a member function of a
derived class could access the private members of its base class. The concept of a private member
would be rendered meaningless by allowing a programmer to gain access to the private part of a
class simply by deriving a new class from it. Furthermore, one could no longer find all uses of a
private name by looking at the functions declared as members and friends of that class. One would
have to examine every source file of the complete program for derived classes, then examine every
function of those classes, then find every class derived from those classes, etc. This is, at best,
tedious and often impractical. Where it is acceptable, p ro te ct ed – rather than p ri va te – members
pr ot ec te d
pr iv at e
can be used. A protected member is like a public member to a member of a derived class, yet it is
like a private member to other functions (see §15.3).
Typically, the cleanest solution is for the derived class to use only the public members of its
base class. For example:
v oi d M an ag er :p ri nt
vo id Ma na ge r: pr in t() c on st

co ns t
{
E mp lo ye e::p ri nt ; // print Employee information
Em pl oy ee pr in t()
c ou t << l ev el
co ut
le ve l;
// ...

// print Manager-specific information

}

Note that :: must be used because p ri nt
pr in t() has been redefined in M an ag er Such reuse of names
Ma na ge r.
is typical. The unwary might write this:

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


306

Derived Classes

Chapter 12

v oi d M an ag er :p ri nt c on st
vo id Ma na ge r: pr in t() co ns t

{
p ri nt ; // oops!
pr in t()
// print Manager-specific information
}

and find the program involved in an unexpected sequence of recursive calls.
12.2.2 Constructors and Destructors [derived.ctor]
Some derived classes need constructors. If a base class has constructors, then a constructor must be
invoked. Default constructors can be invoked implicitly. However, if all constructors for a base
require arguments, then a constructor for that base must be explicitly called. Consider:
c la ss E mp lo ye e {
cl as s Em pl oy ee
s tr in g f ir st _n am e, f am il y_ na me
st ri ng fi rs t_ na me fa mi ly _n am e;
s ho rt d ep ar tm en t;
sh or t de pa rt me nt
// ...
p ub li c:
pu bl ic
E mp lo ye e(c on st s tr in g& n i nt d ;
Em pl oy ee co ns t st ri ng n, in t d)
// ...
};
c la ss M an ag er : p ub li c E mp lo ye e {
cl as s Ma na ge r pu bl ic Em pl oy ee
s et Em pl oy ee
se t<E mp lo ye e*> g ro up
gr ou p;
// people managed

s ho rt l ev el
sh or t le ve l;
// ...
p ub li c:
pu bl ic
M an ag er co ns t s tr in g& n i nt d i nt l vl ;
Ma na ge r(c on st st ri ng n, in t d, in t lv l)
// ...
};

Arguments for the base class’ constructor are specified in the definition of a derived class’ constructor. In this respect, the base class acts exactly like a member of the derived class (§10.4.6).
For example:
E mp lo ye e::E mp lo ye e(c on st s tr in g& n i nt d
Em pl oy ee Em pl oy ee co ns t st ri ng n, in t d)
: f am il y_ na me n), d ep ar tm en t(d
fa mi ly _n am e(n de pa rt me nt d)
{
// ...
}

// initialize members

M an ag er :M an ag er co ns t s tr in g& n i nt d i nt l vl
Ma na ge r: Ma na ge r(c on st st ri ng n, in t d, in t lv l)
: E mp lo ye e(n d),
Em pl oy ee n,d
// initialize base
l ev el lv l)
le ve l(l vl
// initialize members

{
// ...
}

A derived class constructor can specify initializers for its own members and immediate bases only;
it cannot directly initialize members of a base. For example:

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


Section 12.2.2

Constructors and Destructors

307

M an ag er :M an ag er co ns t s tr in g& n i nt d i nt l vl
Ma na ge r: Ma na ge r(c on st st ri ng n, in t d, in t lv l)
: f am il y_ na me n), // error: family_name not declared in manager
fa mi ly _n am e(n
d ep ar tm en t(d ,
de pa rt me nt d)
// error: department not declared in manager
l ev el lv l)
le ve l(l vl
{
// ...
}


This definition contains three errors: it fails to invoke E mp lo ye e´s constructor, and twice it
Em pl oy ee s
attempts to initialize members of E mp lo ye e directly.
Em pl oy ee
Class objects are constructed from the bottom up: first the base, then the members, and then the
derived class itself. They are destroyed in the opposite order: first the derived class itself, then the
members, and then the base. Members and bases are constructed in order of declaration in the class
and destroyed in the reverse order. See also §10.4.6 and §15.2.4.1.
12.2.3 Copying [derived.copy]
Copying of class objects is defined by the copy constructor and assignments (§10.4.4.1). Consider:
c la ss E mp lo ye e {
cl as s Em pl oy ee
// ...
E mp lo ye e& o pe ra to r=(c on st E mp lo ye e&);
Em pl oy ee op er at or co ns t Em pl oy ee
E mp lo ye e(c on st E mp lo ye e&);
Em pl oy ee co ns t Em pl oy ee
};
v oi d f co ns t M an ag er m
vo id f(c on st Ma na ge r& m)
{
E mp lo ye e e = m
Em pl oy ee
m;
// construct e from Employee part of m
e=m
m;
// assign Employee part of m to e
}


Because the E mp lo ye e copy functions do not know anything about M an ag er only the E mp lo ye e
Em pl oy ee
Ma na ge rs,
Em pl oy ee
part of a M an ag er is copied. This is commonly referred to as slicing and can be a source of surMa na ge r
prises and errors. One reason to pass pointers and references to objects of classes in a hierarchy is
to avoid slicing. Other reasons are to preserve polymorphic behavior (§2.5.4, §12.2.6) and to gain
efficiency.
12.2.4 Class Hierarchies [derived.hierarchy]
A derived class can itself be a base class. For example:
c la ss E mp lo ye e { /* ... */ };
cl as s Em pl oy ee
c la ss M an ag er : p ub li c E mp lo ye e { /* ... */ };
cl as s Ma na ge r pu bl ic Em pl oy ee
c la ss D ir ec to r : p ub li c M an ag er { /* ... */ };
cl as s Di re ct or pu bl ic Ma na ge r

Such a set of related classes is traditionally called a class hierarchy. Such a hierarchy is most often
a tree, but it can also be a more general graph structure. For example:
c la ss T em po ra ry { /* ... */ };
cl as s Te mp or ar y
c la ss S ec re ta ry : p ub li c E mp lo ye e { /* ... */ };
cl as s Se cr et ar y pu bl ic Em pl oy ee

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


308


Derived Classes

Chapter 12

c la ss T se c : p ub li c T em po ra ry p ub li c S ec re ta ry { /* ... */ };
cl as s Ts ec pu bl ic Te mp or ar y, pu bl ic Se cr et ar y
c la ss C on su lt an t : p ub li c T em po ra ry p ub li c M an ag er { /* ... */ };
cl as s Co ns ul ta nt pu bl ic Te mp or ar y, pu bl ic Ma na ge r

Or graphically:
T em po ra ry
Te mp or ar y

E mp lo ye e
Em pl oy ee
.
S ec re ta ry
Se cr et ar y

M an ag er
Ma na ge r

T se c
Ts ec
C on su lt an t
Co ns ul ta nt

D ir ec to r
Di re ct or


Thus, as is explained in detail in §15.2, C++ can express a directed acyclic graph of classes.
12.2.5 Type Fields [derived.typefield]
To use derived classes as more than a convenient shorthand in declarations, we must solve the following problem: Given a pointer of type b as e*, to which derived type does the object pointed to
ba se
really belong? There are four fundamental solutions to the problem:
[1] Ensure that only objects of a single type are pointed to (§2.7, Chapter 13).
[2] Place a type field in the base class for the functions to inspect.
[3] Use d yn am ic _c as t (§15.4.2, §15.4.5).
dy na mi c_ ca st
[4] Use virtual functions (§2.5.5, §12.2.6).
Pointers to base classes are commonly used in the design of container classes such as set, vector,
and list. In this case, solution 1 yields homogeneous lists, that is, lists of objects of the same type.
Solutions 2, 3, and 4 can be used to build heterogeneous lists, that is, lists of (pointers to) objects of
several different types. Solution 3 is a language-supported variant of solution 2. Solution 4 is a
special type-safe variation of solution 2. Combinations of solutions 1 and 4 are particularly interesting and powerful; in almost all situations, they yield cleaner code than do solutions 2 and 3.
Let us first examine the simple type-field solution to see why it is most often best avoided. The
manager/employee example could be redefined like this:
s tr uc t E mp lo ye e {
st ru ct Em pl oy ee
e nu m E mp l_ ty pe { M E };
en um Em pl _t yp e M,
E mp l_ ty pe t yp e;
Em pl _t yp e ty pe
E mp lo ye e() : t yp e(E { }
Em pl oy ee
ty pe E)
s tr in g f ir st _n am e, f am il y_ na me
st ri ng fi rs t_ na me fa mi ly _n am e;
c ha r m id dl e_ in it ia l;
ch ar mi dd le _i ni ti al

D at e h ir in g_ da te
Da te hi ri ng _d at e;
s ho rt d ep ar tm en t;
sh or t de pa rt me nt
// ...
};

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


Section 12.2.5

Type Fields

309

s tr uc t M an ag er : p ub li c E mp lo ye e {
st ru ct Ma na ge r pu bl ic Em pl oy ee
M an ag er
Ma na ge r() { t yp e = M }
ty pe M;
s et Em pl oy ee
se t<E mp lo ye e*> g ro up
gr ou p;
s ho rt l ev el
sh or t le ve l;
// ...

// people managed


};

Given this, we can now write a function that prints information about each E mp lo ye e:
Em pl oy ee
v oi d p ri nt _e mp lo ye e(c on st E mp lo ye e* e
vo id pr in t_ em pl oy ee co ns t Em pl oy ee e)
{
s wi tc h (e ty pe {
sw it ch e->t yp e)
c as e E mp lo ye e::E
ca se Em pl oy ee E:
c ou t << e fa mi ly _n am e << ´\ t´ << e de pa rt me nt << ´\ n´;
co ut
e->f am il y_ na me
\t
e->d ep ar tm en t
\n
// ...
b re ak
br ea k;
c as e E mp lo ye e::M
ca se Em pl oy ee M:
{
c ou t << e fa mi ly _n am e << ´\ t´ << e de pa rt me nt << ´\ n´;
co ut
e->f am il y_ na me
\t
e->d ep ar tm en t
\n

// ...
c on st M an ag er p = s ta ti c_ ca st co ns t M an ag er
co ns t Ma na ge r*
st at ic _c as t<c on st Ma na ge r*>(e ;
e)
c ou t << " l ev el " << p le ve l << ´\ n´;
co ut
le ve l
p->l ev el
\n
// ...
b re ak
br ea k;
}
}
}

and use it to print a list of E mp lo ye es, like this:
Em pl oy ee
v oi d p ri nt _l is t(c on st l is t<E mp lo ye e*>& e li st
vo id pr in t_ li st co ns t li st Em pl oy ee
el is t)
{
f or (l is t<E mp lo ye e*>::c on st _i te ra to r p = e li st be gi n(); p el is t.e nd ; ++p
fo r li st Em pl oy ee
co ns t_ it er at or
el is t.b eg in
p!=e li st en d()
p)
p ri nt _e mp lo ye e(*p ;

pr in t_ em pl oy ee p)
}

This works fine, especially in a small program maintained by a single person. However, it has the
fundamental weakness in that it depends on the programmer manipulating types in a way that cannot be checked by the compiler. This problem is usually made worse because functions such as
p ri nt _e mp lo ye e() are organized to take advantage of the commonality of the classes involved.
pr in t_ em pl oy ee
For example:
v oi d p ri nt _e mp lo ye e(c on st E mp lo ye e* e
vo id pr in t_ em pl oy ee co ns t Em pl oy ee e)
{
c ou t << e fa mi ly _n am e << ´\ t´ << e de pa rt me nt << ´\ n´;
co ut
e->f am il y_ na me
\t
e->d ep ar tm en t
\n
// ...
i f (e ty pe == E mp lo ye e::M {
if e->t yp e
Em pl oy ee M)
c on st M an ag er p = s ta ti c_ ca st co ns t M an ag er
co ns t Ma na ge r*
st at ic _c as t<c on st Ma na ge r*>(e ;
e)
c ou t << " l ev el " << p le ve l << ´\ n´;
co ut
le ve l
p->l ev el
\n

// ...
}
}

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


310

Derived Classes

Chapter 12

Finding all such tests on the type field buried in a large function that handles many derived classes
can be difficult. Even when they have been found, understanding what is going on can be difficult.
Furthermore, any addition of a new kind of E mp lo ye e involves a change to all the key functions in
Em pl oy ee
the system – the ones containing the tests on the type field. The programmer must consider every
function that could conceivably need a test on the type field after a change. This implies the need
to access critical source code and the resulting necessary overhead of testing the affected code. The
use of an explicit type conversion is a strong hint that improvement is possible.
In other words, use of a type field is an error-prone technique that leads to maintenance problems. The problems increase in severity as the size of the program increases because the use of a
type field causes a violation of the ideals of modularity and data hiding. Each function using a type
field must know about the representation and other details of the implementation of every class
derived from the one containing the type field.
It also seems that the existence of any common data accessible from every derived class, such
as a type field, tempts people to add more such data. The common base thus becomes the repository of all kinds of ‘‘useful information.’’ This, in turn, gets the implementation of the base and
derived classes intertwined in ways that are most undesirable. For clean design and simpler maintenance, we want to keep separate issues separate and avoid mutual dependencies.
12.2.6 Virtual Functions [derived.virtual]

Virtual functions overcome the problems with the type-field solution by allowing the programmer
to declare functions in a base class that can be redefined in each derived class. The compiler and
loader will guarantee the correct correspondence between objects and the functions applied to them.
For example:
c la ss E mp lo ye e {
cl as s Em pl oy ee
s tr in g f ir st _n am e, f am il y_ na me
st ri ng fi rs t_ na me fa mi ly _n am e;
s ho rt d ep ar tm en t;
sh or t de pa rt me nt
// ...
p ub li c:
pu bl ic
E mp lo ye e(c on st s tr in g& n am e, i nt d ep t);
Em pl oy ee co ns t st ri ng na me in t de pt
v ir tu al v oi d p ri nt c on st
vi rt ua l vo id pr in t() co ns t;
// ...
};

The keyword v ir tu al indicates that p ri nt
vi rt ua l
pr in t() can act as an interface to the p ri nt
pr in t() function defined
in this class and the p ri nt
pr in t() functions defined in classes derived from it. Where such p ri nt
pr in t()
functions are defined in derived classes, the compiler ensures that the right p ri nt
pr in t() for the given
E mp lo ye e object is invoked in each case.

Em pl oy ee
To allow a virtual function declaration to act as an interface to functions defined in derived
classes, the argument types specified for a function in a derived class cannot differ from the argument types declared in the base, and only very slight changes are allowed for the return type
(§15.6.2). A virtual member function is sometimes called a method.
A virtual function must be defined for the class in which it is first declared (unless it is declared
to be a pure virtual function; see §12.3). For example:

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


Section 12.2.6

Virtual Functions

311

v oi d E mp lo ye e::p ri nt
vo id Em pl oy ee pr in t() c on st
co ns t
{
c ou t << f am il y_ na me << ´\ t´ << d ep ar tm en t << ´\ n´;
co ut
fa mi ly _n am e
\t
de pa rt me nt
\n
// ...
}


A virtual function can be used even if no class is derived from its class, and a derived class that
does not need its own version of a virtual function need not provide one. When deriving a class,
simply provide an appropriate function, if it is needed. For example:
c la ss M an ag er : p ub li c E mp lo ye e {
cl as s Ma na ge r pu bl ic Em pl oy ee
s et Em pl oy ee
se t<E mp lo ye e*> g ro up
gr ou p;
s ho rt l ev el
sh or t le ve l;
// ...
p ub li c:
pu bl ic
M an ag er co ns t s tr in g& n am e, i nt d ep t, i nt l vl ;
Ma na ge r(c on st st ri ng na me in t de pt in t lv l)
v oi d p ri nt
vo id pr in t() c on st
co ns t;
// ...
};
v oi d M an ag er :p ri nt
vo id Ma na ge r: pr in t() c on st
co ns t
{
E mp lo ye e::p ri nt ;
Em pl oy ee pr in t()
c ou t << "\ tl ev el " << l ev el << ´\ n´;
co ut
\t le ve l
le ve l

\n
// ...
}

A function from a derived class with the same name and the same set of argument types as a virtual
function in a base is said to override the base class version of the virtual function. Except where
we explicitly say which version of a virtual function is called (as in the call E mp lo ye e::p ri nt
Em pl oy ee pr in t()),
the overriding function is chosen as the most appropriate for the object for which it is called.
The global function p ri nt _e mp lo ye e() (§12.2.5) is now unnecessary because the p ri nt
pr in t_ em pl oy ee
pr in t()
member functions have taken its place. A list of E mp lo ye es can be printed like this:
Em pl oy ee
v oi d p ri nt _l is t(s et Em pl oy ee
vo id pr in t_ li st se t<E mp lo ye e*>& s
s)
{
f or (s et Em pl oy ee
fo r se t<E mp lo ye e*>::c on st _i te ra to r p = s be gi n(); p s.e nd ; ++p / see §2.7.2
co ns t_ it er at or
s.b eg in
p!=s en d()
p)/
(*p
p)->p ri nt ;
pr in t()
}

or even

v oi d p ri nt _l is t(s et Em pl oy ee
vo id pr in t_ li st se t<E mp lo ye e*>& s
s)
{
f or _e ac h(s be gi n(),s en d(),m em _f un Em pl oy ee :p ri nt ;
fo r_ ea ch s.b eg in
s.e nd
me m_ fu n(&E mp lo ye e: pr in t))
}

// see §3.8.5

Each E mp lo ye e will be written out according to its type. For example:
Em pl oy ee

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


312

Derived Classes

Chapter 12

i nt m ai n()
in t ma in
{
E mp lo ye e e Br ow n",1 23 4);
Em pl oy ee e("B ro wn 12 34

M an ag er m Sm it h",1 23 4,2 ;
Ma na ge r m("S mi th 12 34 2)
s et Em pl oy ee
se t<E mp lo ye e*> e mp l;
em pl
e mp l.p us h_ fr on t(&e ;
em pl pu sh _f ro nt e)
// see §2.5.4
e mp l.p us h_ fr on t(&m ;
em pl pu sh _f ro nt m)
p ri nt _l is t(e mp l);
pr in t_ li st em pl
}

produced:
S mi th 1 23 4
Sm it h 12 34
l ev el 2
le ve l
B ro wn 1 23 4
Br ow n 12 34

Note that this will work even if E mp lo ye e::p ri nt _l is t() was written and compiled before the speEm pl oy ee pr in t_ li st
cific derived class M an ag er was even conceived of! This is a key aspect of classes. When used
Ma na ge r
properly, it becomes the cornerstone of object-oriented designs and provides a degree of stability to
an evolving program.
Getting ‘‘the right’’ behavior from E mp lo ye e’s functions independently of exactly what kind of
Em pl oy ee
E mp lo ye e is actually used is called polymorphism. A type with virtual functions is called a

Em pl oy ee
polymorphic type. To get polymorphic behavior in C++, the member functions called must be v ir vi rt ua l and objects must be manipulated through pointers or references. When manipulating an object
tu al
directly (rather than through a pointer or reference), its exact type is known by the compilation so
that run-time polymorphism is not needed.
Clearly, to implement polymorphism, the compiler must store some kind of type information in
each object of class E mp lo ye e and use it to call the right version of the virtual function p ri nt
Em pl oy ee
pr in t(). In
a typical implementation, the space taken is just enough to hold a pointer (§2.5.5). This space is
taken only in objects of a class with virtual functions – not in every object, or even in every object
of a derived class. You pay this overhead only for classes for which you declare virtual functions.
Had you chosen to use the alternative type-field solution, a comparable amount of space would
have been needed for the type field.
Calling a function using the scope resolution operator :: as is done in M an ag er :p ri nt
Ma na ge r: pr in t()
ensures that the virtual mechanism is not used. Otherwise, M an ag er :p ri nt
Ma na ge r: pr in t() would suffer an
infinite recursion. The use of a qualified name has another desirable effect. That is, if a v ir tu al
vi rt ua l
function is also i nl in e (as is not uncommon), then inline substitution can be used for calls specified
in li ne
using ::. This provides the programmer with an efficient way to handle some important special
cases in which one virtual function calls another for the same object. The M an ag er :p ri nt
Ma na ge r: pr in t()
function is an example of this. Because the type of the object is determined in the call of
M an ag er :p ri nt
Ma na ge r: pr in t(), it need not be dynamically determined again for the resulting call of
E mp lo ye e::p ri nt
Em pl oy ee pr in t().

It is worth remembering that the traditional and obvious implementation of a virtual function
call is simply an indirect function call (§2.5.5), so efficiency concerns should not deter anyone from
using a virtual function where an ordinary function call would be acceptably efficient.

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


Section 12.3

Abstract Classes

313

12.3 Abstract Classes [derived.abstract]
Many classes resemble class E mp lo ye e in that they are useful both as themselves and also as bases
Em pl oy ee
for derived classes. For such classes, the techniques described in the previous section suffice.
However, not all classes follow that pattern. Some classes, such as class S ha pe represent abstract
Sh ap e,
concepts for which objects cannot exist. A S ha pe makes sense only as the base of some class
Sh ap e
derived from it. This can be seen from the fact that it is not possible to provide sensible definitions
for its virtual functions:
c la ss S ha pe {
cl as s Sh ap e
p ub li c:
pu bl ic
v ir tu al v oi d r ot at e(i nt { e rr or Sh ap e::r ot at e"); } // inelegant
vi rt ua l vo id ro ta te in t) er ro r("S ha pe ro ta te

v ir tu al v oi d d ra w() { e rr or Sh ap e::d ra w"); }
vi rt ua l vo id dr aw
er ro r("S ha pe dr aw
// ...
};

Trying to make a shape of this unspecified kind is silly but legal:
S ha pe s // silly: ‘‘shapeless shape’’
Sh ap e s;

It is silly because every operation on s will result in an error.
A better alternative is to declare the virtual functions of class S ha pe to be pure virtual functions.
Sh ap e
A virtual function is ‘‘made pure’’ by the initializer = 0
0:
c la ss S ha pe {
cl as s Sh ap e
// abstract class
p ub li c:
pu bl ic
v ir tu al v oi d r ot at e(i nt = 0
vi rt ua l vo id ro ta te in t) 0;
// pure virtual function
v ir tu al v oi d d ra w() = 0
vi rt ua l vo id dr aw
0;
// pure virtual function
v ir tu al b oo l i s_ cl os ed
vi rt ua l bo ol is _c lo se d() = 0
0; // pure virtual function

// ...
};

A class with one or more pure virtual functions is an abstract class, and no objects of that abstract
class can be created:
S ha pe s // error: variable of abstract class Shape
Sh ap e s;

An abstract class can be used only as an interface and as a base for other classes. For example:
c la ss P oi nt { /* ... */ };
cl as s Po in t
c la ss C ir cl e : p ub li c S ha pe {
cl as s Ci rc le pu bl ic Sh ap e
p ub li c:
pu bl ic
v oi d r ot at e(i nt { }
vo id ro ta te in t)
v oi d d ra w();
vo id dr aw
b oo l i s_ cl os ed
bo ol is _c lo se d() { r et ur n t ru e; }
re tu rn tr ue

// override Shape::rotate
// override Shape::draw
// override Shape::is_closed

C ir cl e(P oi nt p i nt r ;
Ci rc le Po in t p, in t r)
p ri va te

pr iv at e:
P oi nt c en te r;
Po in t ce nt er
i nt r ad iu s;
in t ra di us
};

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


314

Derived Classes

Chapter 12

A pure virtual function that is not defined in a derived class remains a pure virtual function, so the
derived class is also an abstract class. This allows us to build implementations in stages:
c la ss P ol yg on : p ub li c S ha pe {
cl as s Po ly go n pu bl ic Sh ap e
// abstract class
p ub li c:
pu bl ic
b oo l i s_ cl os ed { r et ur n t ru e; }
bo ol is _c lo se d() re tu rn tr ue
// override Shape::is_closed
// ... draw and rotate not overridden ...
};
P ol yg on b

Po ly go n b;

// error: declaration of object of abstract class Polygon

c la ss I rr eg ul ar _p ol yg on : p ub li c P ol yg on {
cl as s Ir re gu la r_ po ly go n pu bl ic Po ly go n
l is t

li st Po in t> lp
p ub li c:
pu bl ic
v oi d d ra w();
vo id dr aw
// override Shape::draw
v oi d r ot at e(i nt ;
vo id ro ta te in t)
// override Shape::rotate
// ...
};
I rr eg ul ar _p ol yg on p ol y(s om e_ po in ts ;
Ir re gu la r_ po ly go n po ly so me _p oi nt s)

// fine (assume suitable constructor)

An important use of abstract classes is to provide an interface without exposing any implementation
details. For example, an operating system might hide the details of its device drivers behind an
abstract class:
c la ss C ha ra ct er _d ev ic e {
cl as s Ch ar ac te r_ de vi ce
p ub li c:
pu bl ic


v ir tu al i nt o pe n(i nt o pt = 0
vi rt ua l in t op en in t op t) 0;
v ir tu al i nt c lo se in t o pt = 0
vi rt ua l in t cl os e(i nt op t) 0;
v ir tu al i nt r ea d(c ha r* p i nt n = 0
vi rt ua l in t re ad ch ar p, in t n) 0;
v ir tu al i nt w ri te co ns t c ha r* p i nt n = 0
vi rt ua l in t wr it e(c on st ch ar p, in t n) 0;
v ir tu al i nt i oc tl in t ...) = 0
vi rt ua l in t io ct l(i nt
0;
v ir tu al ~C ha ra ct er _d ev ic e() { }
vi rt ua l Ch ar ac te r_ de vi ce
// virtual destructor
};

We can then specify drivers as classes derived from C ha ra ct er _d ev ic e, and manipulate a variety of
Ch ar ac te r_ de vi ce
drivers through that interface. The importance of virtual destructors is explained in §12.4.2.
With the introduction of abstract classes, we have the basic facilities for writing a complete program in a modular fashion using classes as building blocks.

12.4 Design of Class Hierarchies [derived.design]
Consider a simple design problem: provide a way for a program to get an integer value from a user
interface. This can be done in a bewildering number of ways. To insulate our program from this
variety, and also to get a chance to explore the possible design choices, let us start by defining our
program’s model of this simple input operation. We will leave until later the details of implementing it using a real user-interface system.
The idea is to have a class I va l_ bo x that knows what range of input values it will accept. A
Iv al _b ox
program can ask an I va l_ bo x for its value and ask it to prompt the user if necessary. In addition, a
Iv al _b ox

program can ask an I va l_ bo x if a user changed the value since the program last looked at it.
Iv al _b ox

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


Section 12.4

Design of Class Hierarchies

315

Because there are many ways of implementing this basic idea, we must assume that there will
be many different kinds of I va l_ bo xe s, such as sliders, plain boxes in which a user can type a numIv al _b ox es
ber, dials, and voice interaction.
The general approach is to build a ‘‘virtual user-interface system’’ for the application to use.
This system provides some of the services provided by existing user-interface systems. It can be
implemented on a wide variety of systems to ensure the portability of application code. Naturally,
there are other ways of insulating an application from a user-interface system. I chose this
approach because it is general, because it allows me to demonstrate a variety of techniques and
design tradeoffs, because those techniques are also the ones used to build ‘‘real’’ user-interface systems, and – most important – because these techniques are applicable to problems far beyond the
narrow domain of interface systems.
12.4.1 A Traditional Class Hierarchy [derived.traditional]
Our first solution is a traditional class hierarchy as is commonly found in Simula, Smalltalk, and
older C++ programs.
Class I va l_ bo x defines the basic interface to all I va l_ bo xes and specifies a default implementaIv al _b ox
Iv al _b ox
tion that more specific kinds of I va l_ bo xes can override with their own versions. In addition, we
Iv al _b ox

declare the data needed to implement the basic notion:
c la ss I va l_ bo x {
cl as s Iv al _b ox
p ro te ct ed
pr ot ec te d:
i nt v al
in t va l;
i nt l ow h ig h;
in t lo w, hi gh
b oo l c ha ng ed
bo ol ch an ge d;
p ub li c:
pu bl ic
I va l_ bo x(i nt l l, i nt h h) { c ha ng ed = f al se v al = l ow = l l; h ig h = h h; }
Iv al _b ox in t ll in t hh
ch an ge d fa ls e; va l lo w ll hi gh hh
v ir tu al
vi rt ua l
v ir tu al
vi rt ua l
v ir tu al
vi rt ua l
v ir tu al
vi rt ua l
v ir tu al
vi rt ua l

i nt g et _v al ue
in t ge t_ va lu e() { c ha ng ed = f al se r et ur n v al }
ch an ge d fa ls e; re tu rn va l;

v oi d s et _v al ue in t i { c ha ng ed = t ru e; v al = i }
vo id se t_ va lu e(i nt i) ch an ge d tr ue va l i;
v oi d r es et _v al ue in t i { c ha ng ed = f al se v al = i }
vo id re se t_ va lu e(i nt i) ch an ge d fa ls e; va l i;
v oi d p ro mp t() { }
vo id pr om pt
b oo l w as _c ha ng ed c on st { r et ur n c ha ng ed }
bo ol wa s_ ch an ge d() co ns t re tu rn ch an ge d;

// for user
// for application

};

The default implementation of the functions is pretty sloppy and is provided here primarily to illustrate the intended semantics. A realistic class would, for example, provide some range checking.
A programmer might use these ‘‘i va l classes’’ like this:
iv al
v oi d i nt er ac t(I va l_ bo x* p b)
vo id in te ra ct Iv al _b ox pb
{
p b->p ro mp t(); // alert user
pb pr om pt
// ...
i nt i = p b->g et _v al ue ;
in t
pb ge t_ va lu e()
i f (p b->w as _c ha ng ed
if pb wa s_ ch an ge d()) {
// new value; do something
}


The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


316

Derived Classes

Chapter 12

e ls e {
el se
// old value was fine; do something else
}
// ...
}
v oi d s om e_ fc t()
vo id so me _f ct
{
I va l_ bo x* p 1 = n ew I va l_ sl id er 0,5 ;
Iv al _b ox p1 ne w Iv al _s li de r(0 5)
i nt er ac t(p 1);
in te ra ct p1

// Ival_slider derived from Ival_box

I va l_ bo x* p 2 = n ew I va l_ di al 1,1 2);
Iv al _b ox p2 ne w Iv al _d ia l(1 12
i nt er ac t(p 2);

in te ra ct p2
}

Most application code is written in terms of (pointers to) plain I va l_ bo xes the way i nt er ac t() is.
Iv al _b ox
in te ra ct
That way, the application doesn’t have to know about the potentially large number of variants of
the I va l_ bo x concept. The knowledge of such specialized classes is isolated in the relatively few
Iv al _b ox
functions that create such objects. This isolates users from changes in the implementations of the
derived classes. Most code can be oblivious to the fact that there are different kinds of I va l_ bo xes.
Iv al _b ox
To simplify the discussion, I do not address issues of how a program waits for input. Maybe the
program really does wait for the user in g et _v al ue
ge t_ va lu e(), maybe the program associates the I va l_ bo x
Iv al _b ox
with an event and prepares to respond to a callback, or maybe the program spawns a thread for the
I va l_ bo x and later inquires about the state of that thread. Such decisions are crucial in the design
Iv al _b ox
of user-interface systems. However, discussing them here in any realistic detail would simply distract from the presentation of programming techniques and language facilities. The design techniques described here and the language facilities that support them are not specific to user interfaces. They apply to a far greater range of problems.
The different kinds of I va l_ bo xes are defined as classes derived from I va l_ bo x. For example:
Iv al _b ox
Iv al _b ox
c la ss I va l_ sl id er : p ub li c I va l_ bo x {
cl as s Iv al _s li de r pu bl ic Iv al _b ox
// graphics stuff to define what the slider looks like, etc.
p ub li c:
pu bl ic
I va l_ sl id er in t, i nt ;
Iv al _s li de r(i nt in t)

i nt g et _v al ue ;
in t ge t_ va lu e()
v oi d p ro mp t();
vo id pr om pt
};

The data members of I va l_ bo x were declared p ro te ct ed to allow access from derived classes.
Iv al _b ox
pr ot ec te d
Thus, I va l_ sl id er :g et _v al ue
Iv al _s li de r: ge t_ va lu e() can deposit a value in I va l_ bo x::v al A p ro te ct ed member is
Iv al _b ox va l.
pr ot ec te d
accessible from a class’ own members and from members of derived classes, but not to general
users (see §15.3).
In addition to I va l_ sl id er we would define other variants of the I va l_ bo x concept. These could
Iv al _s li de r,
Iv al _b ox
include I va l_ di al which lets you select a value by turning a knob; f la sh in g_ iv al _s li de r, which
Iv al _d ia l,
fl as hi ng _i va l_ sl id er
flashes when you ask it to p ro mp t(); and p op up _i va l_ sl id er which responds to p ro mp t() by
pr om pt
po pu p_ iv al _s li de r,
pr om pt
appearing in some prominent place, thus making it hard for the user to ignore.
From where would we get the graphics stuff? Most user-interface systems provide a class
defining the basic properties of being an entity on the screen. So, if we use the system from ‘‘Big

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.

Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


Section 12.4.1

A Traditional Class Hierarchy

317

Bucks Inc.,’’ we would have to make each of our I va l_ sl id er I va l_ di al etc., classes a kind of
Iv al _s li de r, Iv al _d ia l,
B Bw in do w. This would most simply be achieved by rewriting our I va l_ bo x so that it derives from
BB wi nd ow
Iv al _b ox
B Bw in do w. In that way, all our classes inherit all the properties of a B Bw in do w. For example,
BB wi nd ow
BB wi nd ow
every I va l_ bo x can be placed on the screen, obey the graphical style rules, be resized, be dragged
Iv al _b ox
around, etc., according to the standard set by the B Bw in do w system. Our class hierarchy would
BB wi nd ow
look like this:
c la ss
cl as s
c la ss
cl as s
c la ss
cl as s
c la ss
cl as s

c la ss
cl as s

I va l_ bo x : p ub li c B Bw in do w { /* ... */ }; // rewritten to use BBwindow
Iv al _b ox pu bl ic BB wi nd ow
I va l_ sl id er : p ub li c I va l_ bo x { /* ... */ };
Iv al _s li de r pu bl ic Iv al _b ox
I va l_ di al : p ub li c I va l_ bo x { /* ... */ };
Iv al _d ia l pu bl ic Iv al _b ox
F la sh in g_ iv al _s li de r : p ub li c I va l_ sl id er { /* ... */ };
Fl as hi ng _i va l_ sl id er pu bl ic Iv al _s li de r
P op up _i va l_ sl id er : p ub li c I va l_ sl id er { /* ... */ };
Po pu p_ iv al _s li de r pu bl ic Iv al _s li de r

or graphically:
B Bw in do w
BB wi nd ow
.
I va l_ bo x
Iv al _b ox
I va l_ sl id er
Iv al _s li de r
P op up _i va l_ sl id er
Po pu p_ iv al _s li de r

I va l_ di al
Iv al _d ia l

F la sh in g_ iv al _s li de r
Fl as hi ng _i va l_ sl id er


12.4.1.1 Critique [derived.critique]
This design works well in many ways, and for many problems this kind of hierarchy is a good solution. However, there are some awkward details that could lead us to look for alternative designs.
We retrofitted B Bw in do w as the base of I va l_ bo x. This is not quite right. The use of B Bw in BB wi nd ow
Iv al _b ox
BB wi nd ow isn’t part of our basic notion of an I va l_ bo x; it is an implementation detail. Deriving I va l_ bo x
do w
Iv al _b ox
Iv al _b ox
from B Bw in do w elevated an implementation detail to a first-level design decision. That can be
BB wi nd ow
right. For example, using the environment defined by ‘‘Big Bucks Inc.’’ may be a key decision of
how our organization conducts its business. However, what if we also wanted to have implementations of our I va l_ bo xes for systems from ‘‘Imperial Bananas,’’ ‘‘Liberated Software,’’ and ‘‘ComIv al _b ox
piler Whizzes?’’ We would have to maintain four distinct versions of our program:
c la ss
cl as s
c la ss
cl as s
c la ss
cl as s
c la ss
cl as s

I va l_ bo x : p ub li c
Iv al _b ox pu bl ic
I va l_ bo x : p ub li c
Iv al _b ox pu bl ic
I va l_ bo x : p ub li c
Iv al _b ox pu bl ic
I va l_ bo x : p ub li c

Iv al _b ox pu bl ic

B Bw in do w { /* ... */ };
BB wi nd ow
C Ww in do w { /* ... */ };
CW wi nd ow
I Bw in do w { /* ... */ };
IB wi nd ow
L Sw in do w { /* ... */ };
LS wi nd ow

// BB version
// CW version
// IB version
// LS version

Having many versions could result in a version-control nightmare.
Another problem is that every derived class shares the basic data declared in I va l_ bo x. That
Iv al _b ox
data is, of course, an implementation detail that also crept into our I va l_ bo x interface. From a
Iv al _b ox
practical point of view, it is also the wrong data in many cases. For example, an I va l_ sl id er
Iv al _s li de r
doesn’t need the value stored specifically. It can easily be calculated from the position of the slider
when someone executes g et _v al ue
ge t_ va lu e(). In general, keeping two related, but different, sets of data is

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.



318

Derived Classes

Chapter 12

asking for trouble. Sooner or later someone will get them out of sync. Also, experience shows that
novice programmers tend to mess with protected data in ways that are unnecessary and that cause
maintenance problems. Data is better kept private so that writers of derived classes cannot mess
with them. Better still, data should be in the derived classes, where it can be defined to match
requirements exactly and cannot complicate the life of unrelated derived classes. In almost all
cases, a protected interface should contain only functions, types, and constants.
Deriving from B Bw in do w gives the benefit of making the facilities provided by B Bw in do w
BB wi nd ow
BB wi nd ow
available to users of I va l_ bo x. Unfortunately, it also means that changes to class B Bw in do w may
Iv al _b ox
BB wi nd ow
force users to recompile or even rewrite their code to recover from such changes. In particular, the
way most C++ implementations work implies that a change in the size of a base class requires a
recompilation of all derived classes.
Finally, our program may have to run in a mixed environment in which windows of different
user-interface systems coexist. This could happen either because two systems somehow share a
screen or because our program needs to communicate with users on different systems. Having our
user-interface systems ‘‘wired in’’ as the one and only base of our one and only I va l_ bo x interface
Iv al _b ox
just isn’t flexible enough to handle those situations.
12.4.2 Abstract Classes [derived.interface]
So, let’s start again and build a new class hierarchy that solves the problems presented in the critique of the traditional hierarchy:

[1] The user-interface system should be an implementation detail that is hidden from users who
don’t want to know about it.
[2] The I va l_ bo x class should contain no data.
Iv al _b ox
[3] No recompilation of code using the I va l_ bo x family of classes should be required after a
Iv al _b ox
change of the user-interface system.
[4] I va l_ bo xes for different interface systems should be able to coexist in our program.
Iv al _b ox
Several alternative approaches can be taken to achieve this. Here, I present one that maps cleanly
into the C++ language.
First, I specify class I va l_ bo x as a pure interface:
Iv al _b ox
c la ss I va l_ bo x {
cl as s Iv al _b ox
p ub li c:
pu bl ic
v ir tu al i nt g et _v al ue = 0
vi rt ua l in t ge t_ va lu e() 0;
v ir tu al v oi d s et _v al ue in t i = 0
vi rt ua l vo id se t_ va lu e(i nt i) 0;
v ir tu al v oi d r es et _v al ue in t i = 0
vi rt ua l vo id re se t_ va lu e(i nt i) 0;
v ir tu al v oi d p ro mp t() = 0
vi rt ua l vo id pr om pt
0;
v ir tu al b oo l w as _c ha ng ed c on st = 0
vi rt ua l bo ol wa s_ ch an ge d() co ns t 0;
v ir tu al ~I va l_ bo x() { }
vi rt ua l Iv al _b ox

};

This is much cleaner than the original declaration of I va l_ bo x. The data is gone and so are the simIv al _b ox
plistic implementations of the member functions. Gone, too, is the constructor, since there is no
data for it to initialize. Instead, I added a virtual destructor to ensure proper cleanup of the data that
will be defined in the derived classes.
The definition of I va l_ sl id er might look like this:
Iv al _s li de r

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


Section 12.4.2

Abstract Classes

319

c la ss I va l_ sl id er : p ub li c I va l_ bo x, p ro te ct ed B Bw in do w {
cl as s Iv al _s li de r pu bl ic Iv al _b ox pr ot ec te d BB wi nd ow
p ub li c:
pu bl ic
I va l_ sl id er in t,i nt ;
Iv al _s li de r(i nt in t)
~I va l_ sl id er ;
Iv al _s li de r()
i nt g et _v al ue ;
in t ge t_ va lu e()
v oi d s et _v al ue in t i ;

vo id se t_ va lu e(i nt i)
// ...
p ro te ct ed
pr ot ec te d:
// functions overriding BBwindow virtual functions
// e.g. BBwindow::draw(), BBwindow::mouse1hit()
p ri va te
pr iv at e:
// data needed for slider
};

The derived class I va l_ sl id er inherits from an abstract class (I va l_ bo x) that requires it to impleIv al _s li de r
Iv al _b ox
ment the base class’ pure virtual functions. It also inherits from B Bw in do w that provides it with
BB wi nd ow
the means of doing so. Since I va l_ bo x provides the interface for the derived class, it is derived
Iv al _b ox
using p ub li c. Since B Bw in do w is only an implementation aid, it is derived using p ro te ct ed
pu bl ic
BB wi nd ow
pr ot ec te d
(§15.3.2). This implies that a programmer using I va l_ sl id er cannot directly use facilities defined
Iv al _s li de r
by B Bw in do w. The interface provided by I va l_ sl id er is the one inherited by I va l_ bo x, plus what
BB wi nd ow
Iv al _s li de r
Iv al _b ox
I va l_ sl id er explicitly declares. I used p ro te ct ed derivation instead of the more restrictive (and usuIv al _s li de r
pr ot ec te d
ally safer) p ri va te derivation to make B Bw in do w available to classes derived from I va l_ sl id er

pr iv at e
BB wi nd ow
Iv al _s li de r.
Deriving directly from more than one class is usually called multiple inheritance (§15.2). Note
that I va l_ sl id er must override functions from both I va l_ bo x and B Bw in do w. Therefore, it must be
Iv al _s li de r
Iv al _b ox
BB wi nd ow
derived directly or indirectly from both. As shown in §12.4.1.1, deriving I va l_ sl id er indirectly
Iv al _s li de r
from B Bw in do w by making B Bw in do w a base of I va l_ bo x is possible, but doing so has undesirable
BB wi nd ow
BB wi nd ow
Iv al _b ox
side effects. Similarly, making the ‘‘implementation class’’ B Bw in do w a member of I va l_ bo x is
BB wi nd ow
Iv al _b ox
not a solution because a class cannot override virtual functions of its members (§24.3.4). Representing the window by a B Bw in do w* member in I va l_ bo x leads to a completely different design
BB wi nd ow
Iv al _b ox
with a separate set of tradeoffs (§12.7[14], §25.7).
Interestingly, this declaration of I va l_ sl id er allows application code to be written exactly as
Iv al _s li de r
before. All we have done is to restructure the implementation details in a more logical way.
Many classes require some form of cleanup for an object before it goes away. Since the abstract
class I va l_ bo x cannot know if a derived class requires such cleanup, it must assume that it does
Iv al _b ox
require some. We ensure proper cleanup by defining a virtual destructor I va l_ bo x::~I va l_ bo x()
Iv al _b ox
Iv al _b ox

in the base and overriding it suitably in derived classes. For example:
v oi d f Iv al _b ox p
vo id f(I va l_ bo x* p)
{
// ...
d el et e p
de le te p;
}

The d el et e operator explicitly destroys the object pointed to by p We have no way of knowing
de le te
p.
exactly to which class the object pointed to by p belongs, but thanks to I va l_ bo x’s virtual
Iv al _b ox
destructor, proper cleanup as (optionally) defined by that class’ destructor will be called.

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


320

Derived Classes

Chapter 12

The I va l_ bo x hierarchy can now be defined like this:
Iv al _b ox
c la ss
cl as s

c la ss
cl as s
c la ss
cl as s
c la ss
cl as s
c la ss
cl as s

I va l_ bo x { /* ... */ };
Iv al _b ox
I va l_ sl id er : p ub li c I va l_ bo x, p ro te ct ed B Bw in do w { /* ... */ };
Iv al _s li de r pu bl ic Iv al _b ox pr ot ec te d BB wi nd ow
I va l_ di al : p ub li c I va l_ bo x, p ro te ct ed B Bw in do w { /* ... */ };
Iv al _d ia l pu bl ic Iv al _b ox pr ot ec te d BB wi nd ow
F la sh in g_ iv al _s li de r : p ub li c I va l_ sl id er { /* ... */ };
Fl as hi ng _i va l_ sl id er pu bl ic Iv al _s li de r
P op up _i va l_ sl id er : p ub li c I va l_ sl id er { /* ... */ };
Po pu p_ iv al _s li de r pu bl ic Iv al _s li de r

or graphically using obvious abbreviations:
I va l_ bo x
Iv al _b ox

B Bw in do w
BB wi nd ow
..
I va l_ sl id er
Iv al _s li de r
P op up _s li de r

Po pu p_ sl id er

B Bw in do w
BB wi nd ow
I va l_ di al
Iv al _d ia l

F la sh in g_ sl id er
Fl as hi ng _s li de r

I used a dashed line to represent protected inheritance. As far as general users are concerned, doing
that is simply an implementation detail.
12.4.3 Alternative Implementations [derived.alt]
This design is cleaner and more easily maintainable than the traditional one – and no less efficient.
However, it still fails to solve the version control problem:
c la ss I va l_ bo x { /* ... */ };
cl as s Iv al _b ox
// common
c la ss I va l_ sl id er : p ub li c I va l_ bo x, p ro te ct ed B Bw in do w { /* ... */ }; // for BB
cl as s Iv al _s li de r pu bl ic Iv al _b ox pr ot ec te d BB wi nd ow
c la ss I va l_ sl id er : p ub li c I va l_ bo x, p ro te ct ed C Ww in do w { /* ... */ }; // for CW
cl as s Iv al _s li de r pu bl ic Iv al _b ox pr ot ec te d CW wi nd ow
// ...

In addition, there is no way of having an I va l_ sl id er for B Bw in do ws coexist with an I va l_ sl id er
Iv al _s li de r
BB wi nd ow
Iv al _s li de r
for C Ww in do ws, even if the two user-interface systems could themselves coexist.
CW wi nd ow

The obvious solution is to define several different I va l_ sl id er classes with separate names:
Iv al _s li de r
c la ss I va l_ bo x { /* ... */ };
cl as s Iv al _b ox
c la ss B B_ iv al _s li de r : p ub li c I va l_ bo x, p ro te ct ed B Bw in do w { /* ... */ };
cl as s BB _i va l_ sl id er pu bl ic Iv al _b ox pr ot ec te d BB wi nd ow
c la ss C W_ iv al _s li de r : p ub li c I va l_ bo x, p ro te ct ed C Ww in do w { /* ... */ };
cl as s CW _i va l_ sl id er pu bl ic Iv al _b ox pr ot ec te d CW wi nd ow
// ...

or graphically:
B Bw in do w
BB wi nd ow

I va l_ bo x
Iv al _b ox

..
B B_ iv al _s li de r
BB _i va l_ sl id er

C Ww in do w
CW wi nd ow

C W_ iv al _s li de r
CW _i va l_ sl id er

To further insulate our application-oriented I va l_ bo x classes from implementation details, we can
Iv al _b ox
derive an abstract I va l_ sl id er class from I va l_ bo x and then derive the system-specific I va l_ sl id er s

Iv al _s li de r
Iv al _b ox
Iv al _s li de rs
from that:
c la ss I va l_ bo x { /* ... */ };
cl as s Iv al _b ox
c la ss I va l_ sl id er : p ub li c I va l_ bo x { /* ... */ };
cl as s Iv al _s li de r pu bl ic Iv al _b ox

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


Section 12.4.3

Alternative Implementations

321

c la ss B B_ iv al _s li de r : p ub li c I va l_ sl id er p ro te ct ed B Bw in do w { /* ... */ };
cl as s BB _i va l_ sl id er pu bl ic Iv al _s li de r, pr ot ec te d BB wi nd ow
c la ss C W_ iv al _s li de r : p ub li c I va l_ sl id er p ro te ct ed C Ww in do w { /* ... */ };
cl as s CW _i va l_ sl id er pu bl ic Iv al _s li de r, pr ot ec te d CW wi nd ow
// ...

or graphically:
I va l_ bo x
Iv al _b ox
.
B Bw in do w

BB wi nd ow

I va l_ sl id er
Iv al _s li de r

B B_ iv al _s li de r
BB _i va l_ sl id er

C Ww in do w
CW wi nd ow

C W_ iv al _s li de r
CW _i va l_ sl id er

Usually, we can do better yet by utilizing more-specific classes in the implementation hierarchy.
For example, if the ‘‘Big Bucks Inc.’’ system has a slider class, we can derive our I va l_ sl id er
Iv al _s li de r
directly from the B Bs li de r:
BB sl id er
c la ss B B_ iv al _s li de r : p ub li c I va l_ sl id er p ro te ct ed B Bs li de r { /* ... */ };
cl as s BB _i va l_ sl id er pu bl ic Iv al _s li de r, pr ot ec te d BB sl id er
c la ss C W_ iv al _s li de r : p ub li c I va l_ sl id er p ro te ct ed C Ws li de r { /* ... */ };
cl as s CW _i va l_ sl id er pu bl ic Iv al _s li de r, pr ot ec te d CW sl id er

or graphically:
B Bw in do w
BB wi nd ow
.

I va l_ bo x

Iv al _b ox
.

C Ww in do w
CW wi nd ow

B Bs li de r
BB sl id er

I va l_ sl id er
Iv al _s li de r

C Ws li de r
CW sl id er

B B_ iv al _s li de r
BB _i va l_ sl id er

C W_ iv al _s li de r
CW _i va l_ sl id er

This improvement becomes significant where – as is not uncommon – our abstractions are not too
different from the ones provided by the system used for implementation. In that case, programming is reduced to mapping between similar concepts. Derivation from general base classes, such
as B Bw in do w, is then done only rarely.
BB wi nd ow
The complete hierarchy will consist of our original application-oriented conceptual hierarchy of
interfaces expressed as derived classes:
c la ss
cl as s
c la ss

cl as s
c la ss
cl as s
c la ss
cl as s
c la ss
cl as s

I va l_ bo x { /* ... */ };
Iv al _b ox
I va l_ sl id er : p ub li c I va l_ bo x { /* ... */ };
Iv al _s li de r pu bl ic Iv al _b ox
I va l_ di al : p ub li c I va l_ bo x { /* ... */ };
Iv al _d ia l pu bl ic Iv al _b ox
F la sh in g_ iv al _s li de r : p ub li c I va l_ sl id er { /* ... */ };
Fl as hi ng _i va l_ sl id er pu bl ic Iv al _s li de r
P op up _i va l_ sl id er : p ub li c I va l_ sl id er { /* ... */ };
Po pu p_ iv al _s li de r pu bl ic Iv al _s li de r

followed by the implementations of this hierarchy for various graphical user-interface systems,
expressed as derived classes:
c la ss B B_ iv al _s li de r : p ub li c I va l_ sl id er p ro te ct ed B Bs li de r { /* ... */ };
cl as s BB _i va l_ sl id er pu bl ic Iv al _s li de r, pr ot ec te d BB sl id er
c la ss B B_ fl as hi ng _i va l_ sl id er : p ub li c F la sh in g_ iv al _s li de r,
cl as s BB _f la sh in g_ iv al _s li de r pu bl ic Fl as hi ng _i va l_ sl id er
p ro te ct ed B Bw in do w_ wi th _b el ls _a nd _w hi st le s { /* ... */ };
pr ot ec te d BB wi nd ow _w it h_ be ll s_ an d_ wh is tl es
c la ss B B_ po pu p_ iv al _s li de r : p ub li c P op up _i va l_ sl id er p ro te ct ed B Bs li de r { /* ... */ };
cl as s BB _p op up _i va l_ sl id er pu bl ic Po pu p_ iv al _s li de r, pr ot ec te d BB sl id er
c la ss C W_ iv al _s li de r : p ub li c I va l_ sl id er p ro te ct ed C Ws li de r { /* ... */ };

cl as s CW _i va l_ sl id er pu bl ic Iv al _s li de r, pr ot ec te d CW sl id er
// ...

The C++ Programming Language, Third Edition by Bjarne Stroustrup. Copyright ©1997 by AT&T.
Published by Addison Wesley Longman, Inc. ISBN 0-201-88954-4. All rights reserved.


×