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

The C++ Programming Language Third Edition phần 5 doc

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 (359.72 KB, 102 trang )

Section 15.2.4.1

Programming Virtual Bases

399

v oi d W in do w_ wi th _b or de r::d ra w()
vo id Wi nd ow _w it h_ bo rd er dr aw
{
W in do w::d ra w();
Wi nd ow dr aw
o wn _d ra w(); // display the border
ow n_ dr aw
}
v oi d W in do w_ wi th _m en u::d ra w()
vo id Wi nd ow _w it h_ me nu dr aw
{
W in do w::d ra w();
Wi nd ow dr aw
o wn _d ra w(); // display the menu
ow n_ dr aw
}
v oi d C lo ck :d ra w()
vo id Cl oc k: dr aw
{
W in do w::d ra w();
Wi nd ow dr aw
W in do w_ wi th _b or de r::o wn _d ra w();
Wi nd ow _w it h_ bo rd er ow n_ dr aw
W in do w_ wi th _m en u::o wn _d ra w();
Wi nd ow _w it h_ me nu ow n_ dr aw


o wn _d ra w(); // display the clock face and hands
ow n_ dr aw
}

Casting from a v ir tu al base class to a derived class is discussed in §15.4.2.
vi rt ua l
15.2.5 Using Multiple Inheritance [hier.using.mi]
The simplest and most obvious use of multiple inheritance is to ‘‘glue’’ two otherwise unrelated
classes together as part of the implementation of a third class. The S at el li te class built out of the
Sa te ll it e
T as k and D is pl ay ed classes in §15.2 is an example of this. This use of multiple inheritance is
Ta sk
Di sp la ye d
crude, effective, and important, but not very interesting. Basically, it saves the programmer from
writing a lot of forwarding functions. This technique does not affect the overall design of a program significantly and can occasionally clash with the wish to keep implementation details hidden.
However, a technique doesn’t have to be clever to be useful.
Using multiple inheritance to provide implementations for abstract classes is more fundamental
in that it affects the way a program is designed. Class B B_ iv al _s li de r (§12.3) is an example:
BB _i va l_ sl id er
c la ss B B_ iv al _s li de r
cl as s BB _i va l_ sl id er
: p ub li c I va l_ sl id er // interface
pu bl ic Iv al _s li de r
, p ro te ct ed B Bs li de r // implementation
pr ot ec te d BB sl id er
{
// implementation of functions required by ‘Ival_slider’ and ‘BBslider’
// using the facilities provided by ‘BBslider’
};


In this example, the two base classes play logically distinct roles. One base is a public abstract
class providing the interface and the other is a protected concrete class providing implementation
‘‘details.’’ These roles are reflected in both the style of the classes and in the access control provided. The use of multiple inheritance is close to essential here because the derived class needs to
override virtual functions from both the interface and the implementation.
Multiple inheritance allows sibling classes to share information without introducing a dependence on a unique common base class in a program. This is the case in which the so-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.


400

Class Hierarchies

Chapter 15

diamond-shaped inheritance occurs (for example, the R ad io (§15.2.4) and C lo ck (§15.2.4.1)). A
Ra di o
Cl oc k
virtual base class, as opposed to an ordinary base class, is needed if the base class cannot be replicated.
I find that a diamond-shaped inheritance lattice is most manageable if either the virtual base
class or the classes directly derived from it are abstract classes. For example, consider again the
I va l_ bo x classes from §12.4. In the end, I made all the I va l_ bo x classes abstract to reflect their
Iv al _b ox
Iv al _b ox
role as pure interfaces. Doing that allowed me to place all implementation details in specific implementation classes. Also, all sharing of implementation details was done in the classical hierarchy
of the windows system used for the implementation.
It would make sense for the class implementing a P op up _i va l_ sl id er to share most of the
Po pu p_ iv al _s li de r
implementation of the class implementing a plain I va l_ sl id er After all, these implementation

Iv al _s li de r.
classes would share everything except the handling of prompts. However, it would then seem natural to avoid replication of I va l_ sl id er objects within the resulting slider implementation objects.
Iv al _s li de r
Therefore, we could make I va l_ sl id er a virtual base:
Iv al _s li de r
c la ss B B_ iv al _s li de r : p ub li c v ir tu al 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 vi rt ua l Iv al _s li de r, pr ot ec te d BB sl id er
c la ss P op up _i va l_ sl id er : p ub li c v ir tu al I va l_ sl id er { /* ... */ };
cl as s Po pu p_ iv al _s li de r pu bl ic vi rt ua l Iv al _s li de r
c la ss B B_ po pu p_ iv al _s li de r
cl as s BB _p op up _i va l_ sl id er
: p ub li c v ir tu al P op up _i va l_ sl id er p ro te ct ed B B_ iv al _s li de r { /* ... */ };
pu bl ic vi rt ua l Po pu p_ iv al _s li de r, pr ot ec te d BB _i va l_ sl id er

or graphically:
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

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

B B_ po pu p_ iv al _s li de r
BB _p op up _i va l_ sl id er
It is easy to imagine further interfaces derived from P op up _i va l_ sl id er and further implementation
Po pu p_ iv al _s li de r
classes derived from such classes and B B_ po pu p_ sl id er

BB _p op up _s li de r.
If we take this idea to its logical conclusion, all of the derivations from the abstract classes that
constitute our application’s interfaces would become virtual. This does indeed seem to be the most
logical, general, and flexible approach. The reason I didn’t do that was partly historical and partly
because the most obvious and common techniques for implementing virtual bases impose time and
space overhead that make their extensive use within a class unattractive. Should this overhead
become an issue for an otherwise attractive design, note that an object representing an I va l_ sl id er
Iv al _s li de r
usually holds only a virtual table pointer. As noted in §15.2.4, such an abstract class holding no
variable data can be replicated without ill effects. Thus, we can eliminate the virtual base in favor
of ordinary ones:
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 P op up _i va l_ sl id er : p ub li c I va l_ sl id er { /* ... */ };
cl as s Po pu p_ iv al _s li de r pu bl ic Iv al _s li de r
c la ss B B_ po pu p_ iv al _s li de r
cl as s BB _p op up _i va l_ sl id er
: p ub li c P op up _i va l_ sl id er p ro te ct ed B B_ iv al _s li de r { /* ... */ };
pu bl ic Po pu p_ iv al _s li de r, pr ot ec te d BB _i va l_ sl id er

or graphically:

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 15.2.5

Using Multiple Inheritance


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

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

401

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

B B_ po pu p_ iv al _s li de r
BB _p op up _i va l_ sl id er
This is most likely a viable optimization to the admittedly cleaner alternative presented previously.
15.2.5.1 Overriding Virtual Base Functions [hier.dominance]
A derived class can override a virtual function of its direct or indirect virtual base class. In particular, two different classes might override different virtual functions from the virtual base. In that
way, several derived classes can contribute implementations to the interface presented by a virtual
base class. For example, the W in do w class might have functions s et _c ol or
Wi nd ow
se t_ co lo r() and p ro mp t(). In
pr om pt
that case, W in do w_ wi th _b or de r might override s et _c ol or
Wi nd ow _w it h_ bo rd er
se t_ co lo r() as part of controlling the color
scheme and W in do w_ wi th _m en u might override p ro mp t() as part of its control of user interacWi nd ow _w it h_ me nu

pr om pt
tions:
c la ss W in do w {
cl as s Wi nd ow
// ...
v ir tu al s et _c ol or Co lo r) = 0
vi rt ua l se t_ co lo r(C ol or
0;
v ir tu al v oi d p ro mp t() = 0
vi rt ua l vo id pr om pt
0;
};

// set background color

c la ss W in do w_ wi th _b or de r : p ub li c v ir tu al W in do w {
cl as s Wi nd ow _w it h_ bo rd er pu bl ic vi rt ua l Wi nd ow
// ...
s et _c ol or Co lo r);
se t_ co lo r(C ol or
// control background color
};
c la ss W in do w_ wi th _m en u : p ub li c v ir tu al W in do w {
cl as s Wi nd ow _w it h_ me nu pu bl ic vi rt ua l Wi nd ow
// ...
v oi d p ro mp t(); // control user interactions
vo id pr om pt
};
c la ss M y_ wi nd ow : p ub li c W in do w_ wi th _m en u, p ub li c W in do w_ wi th _b or de r {
cl as s My _w in do w pu bl ic Wi nd ow _w it h_ me nu pu bl ic Wi nd ow _w it h_ bo rd er

// ...
};

What if different derived classes override the same function? This is allowed if and only if some
overriding class is derived from every other class that overrides the function. That is, one function
must override all others. For example, M y_ wi nd ow could override p ro mp t() to improve on what
My _w in do w
pr om pt
W in do w_ wi th _m en u provides:
Wi nd ow _w it h_ me nu
c la ss M y_ wi nd ow : p ub li c W in do w_ wi th _m en u, p ub li c W in do w_ wi th _b or de r {
cl as s My _w in do w pu bl ic Wi nd ow _w it h_ me nu pu bl ic Wi nd ow _w it h_ bo rd er
// ...
v oi d p ro mp t(); // don’t leave user interactions to base
vo id pr om pt
};

or graphically:

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.


402

Class Hierarchies

Chapter 15

W in do w { s et _c ol or

Wi nd ow se t_ co lo r(), p ro mp t() }
pr om pt
W in do w_ wi th _b or de r { s et _c ol or }
Wi nd ow _w it h_ bo rd er se t_ co lo r()

W in do w_ wi th _m en u { p ro mp t() }
Wi nd ow _w it h_ me nu pr om pt

M y_ wi nd ow { p ro mp t() }
My _w in do w pr om pt
If two classes override a base class function, but neither overrides the other, the class hierarchy is
an error. No virtual function table can be constructed because a call to that function on the complete object would have been ambiguous. For example, had R ad io in §15.2.4 not declared
Ra di o
w ri te
wr it e(), the declarations of w ri te
wr it e() in R ec ei ve r and T ra ns mi tt er would have caused an error
Re ce iv er
Tr an sm it te r
when defining R ad io As with R ad io such a conflict is resolved by adding an overriding function
Ra di o.
Ra di o,
to the most derived class.
A class that provides some – but not all – of the implementation for a virtual base class is often
called a ‘‘mixin.’’

15.3 Access Control [hier.access]
A member of a class can be p ri va te p ro te ct ed or p ub li c:
pr iv at e, pr ot ec te d, pu bl ic
– If it is p ri va te its name can be used only by member functions and friends of the class in
pr iv at e,

which it is declared.
– If it is p ro te ct ed its name can be used only by member functions and friends of the class in
pr ot ec te d,
which it is declared and by member functions and friends of classes derived from this class
(see §11.5).
– If it is p ub li c, its name can be used by any function.
pu bl ic
This reflects the view that there are three kinds of functions accessing a class: functions implementing the class (its friends and members), functions implementing a derived class (the derived class’
friends and members), and other functions. This can be presented graphically:
general users
derived class’ member functions and friends
own member functions and friends
.....................................................................
.
.
.
.
.
.
public:
.
.
.
.
.
.
protected:
.
.
.

.
.
.
.
.
private:
.
.
.....................................................................

The access control is applied uniformly to names. What a name refers to does not affect the control
of its use. This means that we can have private member functions, types, constants, etc., as well as
private data members. For example, an efficient non-intrusive (§16.2.1) list class often requires
data structures to keep track of elements. Such information is best kept private:

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 15.3

Access Control

403

t em pl at ete mp la te cl as s T> cl as s Li st
p ri va te
pr iv at e:
s tr uc t L in k { T v al L in k* n ex t; };

st ru ct Li nk
va l; Li nk ne xt
s tr uc t C hu nk {
st ru ct Ch un k
e nu m { c hu nk _s iz e = 1 5 };
en um ch un k_ si ze 15
L in k v ch un k_ si ze ;
Li nk v[c hu nk _s iz e]
C hu nk n ex t;
Ch un k* ne xt
};
c la ss U nd er fl ow { };
cl as s Un de rf lo w
C hu nk a ll oc at ed
Ch un k* al lo ca te d;
L in k* f re e;
Li nk fr ee
L in k* g et _f re e();
Li nk ge t_ fr ee
L in k* h ea d;
Li nk he ad
p ub li c:
pu bl ic
v oi d i ns er t(T ;
vo id in se rt T)
T g et ;
ge t()
// ...
};
t em pl at e

te mp la te cl as s T> vo id Li st T>: in se rt T va l)
{
L in k* l nk = g et _f re e();
Li nk ln k ge t_ fr ee
l nk va l = v al
ln k->v al va l;
l nk ne xt = h ea d;
ln k->n ex t he ad
h ea d = l nk
he ad ln k;
}
t em pl at ete mp la te cl as s T> Li st T>: Li nk Li st T>: ge t_ fr ee
{
i f (f re e == 0 {
if fr ee
0)
// allocate a new chunk and place its Links on the free list
}
L in k* p = f re e;
Li nk
fr ee
f re e = f re e->n ex t;
fr ee fr ee ne xt
r et ur n p
re tu rn p;
}
t em pl at ete mp la te cl as s T> Li st T>: ge t()
{

i f (h ea d == 0 t hr ow U nd er fl ow ;
if he ad
0) th ro w Un de rf lo w()
L in k* p h ea d;
Li nk p= he ad
h ea d = p ne xt
he ad p->n ex t;
p ne xt = f re e;
p->n ex t fr ee
f re e = p
fr ee p;
r et ur n p va l;
re tu rn p->v al
}

The L is tLi st T>
Li st T>:
return type of g et _f re e() is mentioned before the name L is tge t_ fr ee
Li st T>: ge t_ fr ee
full name L is tLi st T>: Li nk
Li nk T>.

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.


404


Class Hierarchies

Chapter 15

Nonmember functions (except friends) do not have such access:
v oi d w ou ld _b e_ me dd le r(L is tvo id wo ul d_ be _m ed dl er Li st T>* p
p)
{
L is tLi st T>: Li nk
0;
// ...
q = p fr ee
p->f re e;
// ...
i f (L is t<T :C hu nk :c hu nk _s iz e > 3 1) {
if Li st T>: Ch un k: ch un k_ si ze 31
// ...
}
}

// error: List<T>::Link is private
// error: List<T>::free is private
// error: List<T>::Chunk::chunk_size is private

In a c la ss a member is by default private; in a s tr uc t, a member is by default public (§10.2.8).
cl as s,
st ru ct

15.3.1 Protected Members [hier.protected]
As an example of how to use p ro te ct ed members, consider the W in do w example from §15.2.4.1.
pr ot ec te d
Wi nd ow
The o wn _d ra w() functions were (deliberately) incomplete in the service they provided. They
ow n_ dr aw
were designed as building blocks for use by derived classes (only) and are not safe or convenient
for general use. The d ra w() operations, on the other hand, were designed for general use. This
dr aw
distinction can be expressed by separating the interface of the W in do w classes in two, the p ro te ct ed
Wi nd ow
pr ot ec te d
interface and the p ub li c interface:
pu bl ic
c la ss W in do w_ wi th _b or de r {
cl as s Wi nd ow _w it h_ bo rd er
p ub li c:
pu bl ic
v ir tu al v oi d d ra w();
vi rt ua l vo id dr aw
// ...
p ro te ct ed
pr ot ec te d:
v oi d o wn _d ra w();
vo id ow n_ dr aw
// other tool-building stuff
p ri va te
pr iv at e:
// representation, etc.
};


A derived class can access a base class’ protected members only for objects of its own type:
c la ss B uf fe r {
cl as s Bu ff er
p ro te ct ed
pr ot ec te d:
c ha r a 12 8];
ch ar a[1 28
// ...
};
c la ss L in ke d_ bu ff er : p ub li c B uf fe r { /* ... */ };
cl as s Li nk ed _b uf fe r pu bl ic Bu ff er
c la ss C yc li c_ bu ff er : p ub li c B uf fe r {
cl as s Cy cl ic _b uf fe r pu bl ic Bu ff er
// ...
v oi d f Li nk ed _b uf fe r* p {
vo id f(L in ke d_ bu ff er p)
a 0] = 0
a[0
0;
// ok: access to cyclic_buffer’s own protected member
p a[0 = 0
p->a 0] 0; // error: access to protected member of different type
}
};

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 15.3.1

Protected Members

405

This prevents subtle errors that would otherwise occur when one derived class corrupts data
belonging to other derived classes.
15.3.1.1 Use of Protected Members [hier.protected.use]
The simple private/public model of data hiding serves the notion of concrete types (§10.3) well.
However, when derived classes are used, there are two kinds of users of a class: derived classes and
‘‘the general public.’’ The members and friends that implement the operations on the class operate
on the class objects on behalf of these users. The private/public model allows the programmer to
distinguish clearly between the implementers and the general public, but it does not provide a way
of catering specifically to derived classes.
Members declared p ro te ct ed are far more open to abuse than members declared p ri va te In
pr ot ec te d
pr iv at e.
particular, declaring data members protected is usually a design error. Placing significant amounts
of data in a common class for all derived classes to use leaves that data open to corruption. Worse,
protected data, like public data, cannot easily be restructured because there is no good way of finding every use. Thus, protected data becomes a software maintenance problem.
Fortunately, you don’t have to use protected data; p ri va te is the default in classes and is usually
pr iv at e
the better choice. In my experience, there have always been alternatives to placing significant
amounts of information in a common base class for derived classes to use directly.
Note that none of these objections are significant for protected member functions; p ro te ct ed is a
pr ot ec te d
fine way of specifying operations for use in derived classes. The I va l_ sl id er in §12.4.2 is an examIv al _s li de r
ple of this. Had the implementation class been p ri va te in this example, further derivation would
pr iv at e

have been infeasible.
Technical examples illustrating access to members can be found in §C.11.1.
15.3.2 Access to Base Classes [hier.base.access]
Like a member, a base class can be declared p ri va te p ro te ct ed or p ub li c. For example:
pr iv at e, pr ot ec te d, pu bl ic
c la ss X : p ub li c B { /* ... */ };
cl as s
pu bl ic
c la ss Y : p ro te ct ed B { /* ... */ };
cl as s
pr ot ec te d
c la ss Z : p ri va te B { /* ... */ };
cl as s
pr iv at e

Public derivation makes the derived class a subtype of its base; this is the most common form of
derivation. Protected and private derivation are used to represent implementation details. Protected
bases are useful in class hierarchies in which further derivation is the norm; the I va l_ sl id er from
Iv al _s li de r
§12.4.2 is a good example of that. Private bases are most useful when defining a class by restricting the interface to a base so that stronger guarantees can be provided. For example, V ec adds
Ve c
range checking to its private base v ec to r (§3.7.1) and the l is t of pointers template adds type checkve ct or
li st
ing to its l is t<v oi d*> base (§13.5).
li st vo id
The access specifier for a base class can be left out. In that case, the base defaults to a private
base for a c la ss and a public base for a s tr uc t. For example:
cl as s
st ru ct
c la ss X X : B { /* ... */ };

cl as s XX
s tr uc t Y Y : B { /* ... */ };
st ru ct YY

// B is a private base
// B is a public base

For readability, it is best always to use an explicit access specifier.

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.


406

Class Hierarchies

Chapter 15

The access specifier for a base class controls the access to members of the base class and the
conversion of pointers and references from the derived class type to the base class type. Consider a
class D derived from a base class B
B:
– If B is a p ri va te base, its public and protected members can be used only by member funcpr iv at e
tions and friends of D Only friends and members of D can convert a D to a B
D.
D*
B*.
– If B is a p ro te ct ed base, its public and protected members can be used only by member
pr ot ec te d

functions and friends of D and by member functions and friends of classes derived from D
D.
Only friends and members of D and friends and members of classes derived from D can
convert a D to a B
D*
B*.
– If B is a p ub li c base, its public members can be used by any function. In addition, its propu bl ic
tected members can be used by members and friends of D and members and friends of
classes derived from D Any function can convert a D to a B
D.
D*
B*.
This basically restates the rules for member access (§15.3). We choose access for bases in the same
way as for members. For example, I chose to make B Bw in do w a p ro te ct ed base of I va l_ sl id er
BB wi nd ow pr ot ec te d
Iv al _s li de r
(§12.4.2) because B Bw in do w was part of the implementation of I va l_ sl id er rather than part of its
BB wi nd ow
Iv al _s li de r
interface. However, I couldn’t completely hide B Bw in do w by making it a private base because I
BB wi nd ow
wanted to be able to derive further classes from I va l_ sl id er and those derived classes would need
Iv al _s li de r,
access to the implementation.
Technical examples illustrating access to bases can be found in §C.11.2.
15.3.2.1 Multiple Inheritance and Access Control [hier.mi.access]
If a name or a base class can be reached through multiple paths in a multiple inheritance lattice, it is
accessible if it is accessible through any path. For example:
s tr uc t B {
st ru ct

i nt m
in t m;
s ta ti c i nt s m;
st at ic in t sm
// ...
};
c la ss D 1 : p ub li c v ir tu al B { /* ... */ } ;
cl as s D1 pu bl ic vi rt ua l
c la ss D 2 : p ub li c v ir tu al B { /* ... */ } ;
cl as s D2 pu bl ic vi rt ua l
c la ss D D : p ub li c D 1, p ri va te D 2 { /* ... */ };
cl as s DD pu bl ic D1 pr iv at e D2
D D* p d = n ew D D;
DD pd ne w DD
B p b = p d;
B* pb pd
i nt i 1 = p d->m
in t i1 pd m;

// ok: accessible through D1
// ok: accessible through D1

If a single entity is reachable through several paths, we can still refer to it without ambiguity. For
example:
c la ss X 1 : p ub li c B { /* ... */ } ;
cl as s X1 pu bl ic
c la ss X 2 : p ub li c B { /* ... */ } ;
cl as s X2 pu bl ic
c la ss X X : p ub li c X 1, p ub li c X 2 { /* ... */ };
cl as s XX pu bl ic X1 pu bl ic X2

X X* p xx = n ew X X;
XX px x ne w XX
i nt i 1 = p xx m;
in t i1 px x->m
// error, ambiguous: XX::X1::B::m or XX::X2::B::m
i nt i 2 = p xx sm
in t i2 px x->s m;
// ok: there is only one B::sm in an XX

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 15.3.2.1

Multiple Inheritance and Access Control

407

15.3.2.2 Using-Declarations and Access Control [hier.access.using]
A using-declaration cannot be used to gain access to additional information. It is simply a mechanism for making accessible information more convenient to use. On the other hand, once access is
available, it can be granted to other users. For example:
c la ss B {
cl as s
p ri va te
pr iv at e:
i nt a
in t a;
p ro te ct ed
pr ot ec te d:

i nt b
in t b;
p ub li c:
pu bl ic
i nt c
in t c;
};
c la ss D : p ub li c B {
cl as s
pu bl ic
p ub li c:
pu bl ic
u si ng B :a
us in g B: a;
// error: B::a is private
u si ng B :b
us in g B: b;
// make B::b publically available through D
};

When a using-declaration is combined with private or protected derivation, it can be used to specify interfaces to some, but not all, of the facilities usually offered by a class. For example:
c la ss B B : p ri va te B {
cl as s BB pr iv at e
u si ng B :b
us in g B: b;
u si ng B :c
us in g B: c;
};

// give access to B::b and B::c, but not B::a


See also §15.2.2.

15.4 Run-Time Type Information [hier.rtti]
A plausible use of the I va l_ bo xes defined in §12.4 would be to hand them to a system that conIv al _b ox
trolled a screen and have that system hand objects back to the application program whenever some
activity had occurred. This is how many user-interfaces work. However, a user-interface system
will not know about our I va l_ bo xes. The system’s interfaces will be specified in terms of the
Iv al _b ox
system’s own classes and objects rather than our application’s classes. This is necessary and
proper. However, it does have the unpleasant effect that we lose information about the type of
objects passed to the system and later returned to us.
Recovering the ‘‘lost’’ type of an object requires us to somehow ask the object to reveal its
type. Any operation on an object requires us to have a pointer or reference of a suitable type for the
object. Consequently, the most obvious and useful operation for inspecting the type of an object at
run time is a type conversion operation that returns a valid pointer if the object is of the expected
type and a null pointer if it isn’t. The d yn am ic _c as t operator does exactly that. For example,
dy na mi c_ ca st
assume that ‘‘the system’’ invokes m y_ ev en t_ ha nd le r() with a pointer to a B Bw in do w, where an
my _e ve nt _h an dl er
BB wi nd ow
activity has occurred. I then might invoke my application code using I va l_ bo x’s d o_ so me th in g():
Iv al _b ox do _s om et hi 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.


408


Class Hierarchies

Chapter 15

v oi d m y_ ev en t_ ha nd le r(B Bw in do w* p w)
vo id my _e ve nt _h an dl er BB wi nd ow pw
{
i f (I va l_ bo x* p b = d yn am ic _c as t<I va l_ bo x*>(p w))
if Iv al _b ox pb dy na mi c_ ca st Iv al _b ox
pw
p b->d o_ so me th in g();
pb do _s om et hi ng
e ls e {
el se
// Oops! unexpected event
}
}

// does pw point to an Ival_box?

One way of explaining what is going on is that d yn am ic _c as t translates from the implementationdy na mi c_ ca st
oriented language of the user-interface system to the language of the application. It is important to
note what is not mentioned in this example: the actual type of the object. The object will be a particular kind of I va l_ bo x, say an I va l_ sl id er implemented by a particular kind of B Bw in do w, say a
Iv al _b ox
Iv al _s li de r,
BB wi nd ow
B Bs li de r. It is neither necessary nor desirable to make the actual type of the object explicit in this
BB sl id er
interaction between ‘‘the system’’ and the application. An interface exists to represent the essentials of an interaction. In particular, a well-designed interface hides inessential details.
Graphically, the action of

p b = d yn am ic _c as t<I va l_ bo x*>(p w)
pb dy na mi c_ ca st Iv al _b ox
pw

can be represented like this:
p w. . . . . . . . . . . B Bw in do w
pw
BB wi nd ow
B Bs li de r
BB sl id er

I va l_ bo x
Iv al _b ox

. . . . . . . . . . . pb
pb

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
The arrows from p w and p b represent the pointers into the object passed, whereas the rest of the
pw
pb
arrows represent the inheritance relationships between the different parts of the object passed.
The use of type information at run time is conventionally referred to as ‘‘run-time type information’’ and often abbreviated to RTTI.
Casting from a base class to a derived class is often called a downcast because of the convention
of drawing inheritance trees growing from the root down. Similarly, a cast from a derived class to

a base is called an upcast. A cast that goes from a base to a sibling class, like the cast from B Bw in BB wi nd ow to I va l_ bo x, is called a crosscast.
do w Iv al _b ox
15.4.1 Dynamic_cast [hier.dynamic.cast]
The d yn am ic _c as t operator takes two operands, a type bracketed by < and >, and a pointer or referdy na mi c_ ca st
ence bracketed by ( and ).
Consider first the pointer case:
d yn am ic _c as tdy na mi c_ ca st T*>(p
p)

If p is of type T or an accessible base class of T the result is exactly as if we had simply assigned
T*
T,
p to a T For example:
T*.

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 15.4.1

Dynamic_cast

409

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
// ...
};

v oi d f BB _i va l_ sl id er p
vo id f(B B_ iv al _s li de r* p)
{
I va l_ sl id er p i1 = p
Iv al _s li de r* pi 1 p;
// ok
I va l_ sl id er p i2 = d yn am ic _c as tIv al _s li de r* pi 2 dy na mi c_ ca st Iv al _s li de r*>(p ;
p)

// ok

B Bs li de r* p bb 1 = p
BB sl id er pb b1 p;
// error: BBslider is a protected base
B Bs li de r* p bb 2 = d yn am ic _c as t<B Bs li de r*>(p ;
BB sl id er pb b2 dy na mi c_ ca st BB sl id er
p)
// ok: pbb2 becomes 0
}

That is the uninteresting case. However, it is reassuring to know that d yn am ic _c as t doesn’t allow
dy na mi c_ ca st
accidental violation of the protection of private and protected base classes.
The purpose of d yn am ic _c as t is to deal with the case in which the correctness of the conversion
dy na mi c_ ca st
cannot be determined by the compiler. In that case,
d yn am ic _c as tdy na mi c_ ca st T*>(p
p)


looks at the object pointed to by p (if any). If that object is of class T or has a unique base class of
type T then d yn am ic _c as t returns a pointer of type T to that object; otherwise, 0 is returned. If
T,
dy na mi c_ ca st
T*
the value of p is 0 d yn am ic _c as t0, dy na mi c_ ca st T*>(p returns 0 Note the requirement that the conversion
p)
0.
must be to a uniquely identified object. It is possible to construct examples where the conversion
fails and 0 is returned because the object pointed to by p has more than one sub-object representing
bases of type T (see §15.4.2).
A d yn am ic _c as t requires a pointer or a reference to a polymorphic type to do a downcast or a
dy na mi c_ ca st
crosscast. For example:
c la ss M y_ sl id er p ub li c I va l_ sl id er { // polymorphic base (Ival_slider has virtual functions)
cl as s My _s li de r: pu bl ic Iv al _s li de r
// ...
};
c la ss M y_ da te : p ub li c D at e { // base not polymorphic (Date has no virtual functions)
cl as s My _d at e pu bl ic Da te
// ...
};
v oi d g Iv al _b ox p b, D at e* p d)
vo id g(I va l_ bo x* pb Da te pd
{
M y_ sl id er p d1 = d yn am ic _c as tMy _s li de r* pd 1 dy na mi c_ ca st My _s li de r*>(p b);
pb

M y_ da te p d2 = d yn am ic _c as tMy _d at e* pd 2 dy na mi c_ ca st My _d at e*>(p d);
pd
}

// ok
// error: Date not polymorphic

Requiring the pointer’s type to be polymorphic simplifies the implementation of d yn am ic _c as t
dy na mi c_ ca st
because it makes it easy to find a place to hold the necessary information about the object’s type. A
typical implementation will attach a ‘‘type information object’’ to an object by placing a pointer to
the type information in the object’s virtual function table (§2.5.5). 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.


410

Class Hierarchies

Chapter 15

M y_ bo x:
My _b ox :

. ..
.. .
v pt r

vp tr
. ..
.. .

...

...

...

v tb l:
vt bl :
.

.....

t yp e_ in fo :
ty pe _i nf o:
.
" My _b ox "
"M y_ bo x"
t yp.e_ in fo :
ty pe _i nf o:
b as es . . . . . . . . . . .
ba se s
" Iv al _s li de r"
"I va l_ sl id er "

.....


.

The dashed arrow represents an offset that allows the start of the complete object to be found given
only a pointer to a polymorphic sub-object. It is clear that d yn am ic _c as t can be efficiently impledy na mi c_ ca st
mented. All that is involved are a few comparisons of t yp e_ in fo objects representing base classes;
ty pe _i nf o
no expensive lookups or string comparisons are needed.
Restricting d yn am ic _c as t to polymorphic types also makes sense from a logical point of view.
dy na mi c_ ca st
This is, if an object has no virtual functions, it cannot safely be manipulated without knowledge of
its exact type. Consequently, care should be taken not to get such an object into a context in which
its type isn’t known. If its type is known, we don’t need to use d yn am ic _c as t.
dy na mi c_ ca st
The target type of d yn am ic _c as t need not be polymorphic. This allows us to wrap a concrete
dy na mi c_ ca st
type in a polymorphic type, say for transmission through an object I/O system (see §25.4.1), and
then ‘‘unwrap’’ the concrete type later. For example:
c la ss I o_ ob j {
cl as s Io _o bj
// base class for object I/O system
v ir tu al I o_ ob j* c lo ne
vi rt ua l Io _o bj cl on e() = 0
0;
};
c la ss I o_ da te : p ub li c D at e, p ub li c I o_ ob j { };
cl as s Io _d at e pu bl ic Da te pu bl ic Io _o bj
v oi d f Io _o bj p io
vo id f(I o_ ob j* pi o)
{
D at e* p d = d yn am ic _c as t<D at e*>(p io ;

Da te pd dy na mi c_ ca st Da te
pi o)
// ...
}

A d yn am ic _c as t to v oi d* can be used to determine the address of the beginning of an object of
dy na mi c_ ca st
vo id
polymorphic type. For example:
v oi d g Iv al _b ox p b, D at e* p d)
vo id g(I va l_ bo x* pb Da te pd
{
v oi d* p d1 = d yn am ic _c as t<v oi d*>(p b);
vo id pd 1 dy na mi c_ ca st vo id
pb
v oi d* p d2 = d yn am ic _c as t<v oi d*>(p d);
vo id pd 2 dy na mi c_ ca st vo id
pd
}

// ok
// error: Date not polymorphic

This is only useful for interaction with very low-level functions.
15.4.1.1 Dynamic_cast of References [hier.re.cast]
To get polymorphic behavior, an object must be manipulated through a pointer or a reference.
When a d yn am ic _c as t is used for a pointer type, a 0 indicates failure. That is neither feasible nor
dy na mi c_ ca st
desirable for references.


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 15.4.1.1

Dynamic_cast of References

411

Given a pointer result, we must consider the possibility that the result is 0 that is, that the
0;
pointer doesn’t point to an object. Consequently, the result of a d yn am ic _c as t of a pointer should
dy na mi c_ ca st
always be explicitly tested. For a pointer p d yn am ic _c as tp, dy na mi c_ ca st T*>(p can be seen as the question,
p)
‘‘Is the object pointed to by p of type T
T?’’
On the other hand, we may legitimately assume that a reference refers to an object. Consequently, d yn am ic _c as tdy na mi c_ ca st T&>(r of a reference r is not a question but an assertion: ‘‘The object
r)
referred to by r is of type T The result of a d yn am ic _c as t for a reference is implicitly tested by
T.’’
dy na mi c_ ca st
the implementation of d yn am ic _c as t itself. If the operand of a d yn am ic _c as t to a reference isn’t of
dy na mi c_ ca st
dy na mi c_ ca st
the expected type, a b ad _c as t exception is thrown. For example:
ba d_ ca st

v oi d f Iv al _b ox p I va l_ bo x& r
vo id f(I va l_ bo x* p, Iv al _b ox r)
{
i f (I va l_ sl id er i s = d yn am ic _c as tif Iv al _s li de r* is dy na mi c_ ca st Iv al _s li de r*>(p
p)) {
// use ‘is’
}
e ls e {
el se
// *p not a slider
}
I va l_ sl id er i s = d yn am ic _c as tIv al _s li de r& is dy na mi c_ ca st Iv al _s li de r&>(r ;
r)
// use ‘is’

// does p point to an Ival_slider?

// r references an Ival_slider!

}

The difference in results of a failed dynamic pointer cast and a failed dynamic reference cast
reflects a fundamental difference between references and pointers. If a user wants to protect against
bad casts to references, a suitable handler must be provided. For example:
v oi d g
vo id g()
{
t ry {

tr y

f ne w B B_ iv al _s li de r,*n ew B B_ iv al _s li de r);
f(n ew BB _i va l_ sl id er ne w BB _i va l_ sl id er
f ne w B Bd ia l,*n ew B Bd ia l);
f(n ew BB di al ne w BB di al

}
c at ch (b ad _c as t) {
ca tc h ba d_ ca st
// ...
}

// arguments passed as Ival_boxs
// arguments passed as Ival_boxs

// §14.10

}

The first call to f
f() will return normally, while the second will cause a b ad _c as t exception that
ba d_ ca st
will be caught by g
g().
Explicit tests against 0 can be – and therefore occasionally will be – accidentally omitted. If
that worries you, you can write a conversion function that throws an exception instead returning 0
(§15.8[1]) in case of failure.
15.4.2 Navigating Class Hierarchies [hier.navigate]
When only single inheritance is used, a class and its base classes constitute a tree rooted in a single

base class. This is simple but often constraining. When multiple inheritance is used, there is no
single root. This in itself doesn’t complicate matters much. However, if a class appears more than

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.


412

Class Hierarchies

Chapter 15

once in a hierarchy, we must be a bit careful when we refer to the object or objects that represent
that class.
Naturally, we try to keep hierarchies as simple as our application allows (and no simpler).
However, once a nontrivial hierarchy has been made we soon need to navigate it to find an appropriate class to use as an interface. This need occurs in two variants. That is, sometimes, we want to
explicitly name an object of a base class or a member of a base class; §15.2.3 and §15.2.4.1 are
examples of this. At other times, we want to get a pointer to the object representing a base or
derived class of an object given a pointer to a complete object or some sub-object; §15.4 and
§15.4.1 are examples of this.
Here, we consider how to navigate a class hierarchy using type conversions (casts) to gain a
pointer of the desired type. To illustrate the mechanisms available and the rules that guide them,
consider a lattice containing both a replicated base and a virtual base:
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 om po ne nt : p ub li c v ir tu al S to ra bl e { /* ... */ };
Co mp on en t pu bl ic vi rt ua l St or ab le
R ec ei ve r : p ub li c C om po ne nt { /* ... */ };
Re ce iv er pu bl ic Co mp on en t
T ra ns mi tt er : p ub li c C om po ne nt { /* ... */ };
Tr an sm it te r pu bl ic Co mp on en t
R ad io : p ub li c R ec ei ve r, p ub li c T ra ns mi tt er { /* ... */ };
Ra di o pu bl ic Re ce iv er pu bl ic Tr an sm it te r

Or graphically:
S to.ra bl e
St or ab le
C om po ne nt
Co mp on en t

C om po ne nt
Co mp on en t

R ec ei ve r
Re ce iv er

T ra ns mi tt er
Tr an sm it te r
R ad io
Ra di o

Here, a R ad io object has two sub-objects of class C om po ne nt Consequently, a d yn am ic _c as t

Ra di o
Co mp on en t.
dy na mi c_ ca st
from S to ra bl e to C om po ne nt within a R ad io will be ambiguous and return a 0 There is simply no
St or ab le Co mp on en t
Ra di o
0.
way of knowing which C om po ne nt the programmer wanted:
Co mp on en t
v oi d h 1(R ad io r
vo id h1 Ra di o& r)
{
S to ra bl e* p s = &r
St or ab le ps
r;
// ...
C om po ne nt p c = d yn am ic _c as tCo mp on en t* pc dy na mi c_ ca st Co mp on en t*>(p s); // pc = 0
ps
}

This ambiguity is not in general detectable at compile time:
v oi d h 2(S to ra bl e* p s)
vo id h2 St or ab le ps
// ps might or might not point to a Component
{
C om po ne nt p c = d yn am ic _c as tCo mp on en t* pc dy na mi c_ ca st Co mp on en t*>(p s);
ps
// ...

}

This kind of run-time ambiguity detection is needed only for virtual bases. For ordinary bases,

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 15.4.2

Navigating Class Hierarchies

413

there is always a unique sub-object of a given cast (or none) when downcasting (that is, towards a
derived class; §15.4). The equivalent ambiguity occurs when upcasting (that is, towards a base;
§15.4) and such ambiguities are caught at compile time.
15.4.2.1 Static and Dynamic Casts [hier.static.cast]
A d yn am ic _c as t can cast from a polymorphic virtual base class to a derived class or a sibling class
dy na mi c_ ca st
(§15.4.1). A s ta ti c_ ca st (§6.2.7) does not examine the object it casts from, so it cannot:
st at ic _c as t
v oi d g Ra di o& r
vo id g(R ad io r)
{
R ec ei ve r* p re c = &r
Re ce iv er pr ec
r;
R ad io p r = s ta ti c_ ca st Ra di o*>(p re c);
Ra di o* pr st at ic _c as t

pr ec
p r = d yn am ic _c as tpr dy na mi c_ ca st Ra di o*>(p re c);
pr ec
S to ra bl e* p s = &r
St or ab le ps
r;
p r = s ta ti c_ ca st Ra di o*>(p s);
pr st at ic _c as tps
p r = d yn am ic _c as tpr dy na mi c_ ca st Ra di o*>(p s);
ps

// Receiver is ordinary base of Radio
// ok, unchecked
// ok, run-time checked
// Storable is virtual base of Radio
// error: cannot cast from virtual base
// ok, run-time checked

}

The d yn am ic _c as t requires a polymorphic operand because there is no information stored in a nondy na mi c_ ca st
polymorphic object that can be used to find the objects for which it represents a base. In particular,
an object of a type with layout constraints determined by some other language – such as Fortran or
C – may be used as a virtual base class. For objects of such types, only static type information will
be available. However, the information needed to provide run-time type identification includes the
information needed to implement the d yn am ic _c as t.
dy na mi c_ ca st

Why would anyone want to use a s ta ti c_ ca st for class hierarchy navigation? There is a small
st at ic _c as t
run-time cost associated with the use of a d yn am ic _c as t (§15.4.1). More significantly, there are
dy na mi c_ ca st
millions of lines of code that were written before d yn am ic _c as t became available. This code relies
dy na mi c_ ca st
on alternative ways of making sure that a cast is valid, so the checking done by d yn am ic _c as t is
dy na mi c_ ca st
seen as redundant. However, such code is typically written using the C-style cast (§6.2.7); often
obscure errors remain. Where possible, use the safer d yn am ic _c as t.
dy na mi c_ ca st
The compiler cannot assume anything about the memory pointed to by a v oi d*. This implies
vo id
that d yn am ic _c as t – which must look into an object to determine its type – cannot cast from a
dy na mi c_ ca st
v oi d*. For that, a s ta ti c_ ca st is needed. For example:
vo id
st at ic _c as t
R ad io f vo id p
Ra di o* f(v oi d* p)
{
S to ra bl e* p s = s ta ti c_ ca st St or ab le
St or ab le ps st at ic _c as t<S to ra bl e*>(p ; // trust the programmer
p)
r et ur n d yn am ic _c as tre tu rn dy na mi c_ ca st Ra di o*>(p s);
ps
}

Both d yn am ic _c as t and s ta ti c_ ca st respect c on st and access controls. For example:

dy na mi c_ ca st
st at ic _c as t
co ns t
c la ss U se rs : p ri va te s et Pe rs on { /* ... */ };
cl as s Us er s pr iv at e se t<P er so 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.


414

Class Hierarchies

v oi d f Us er s* p u, c on st R ec ei ve r* p cr
vo id f(U se rs pu co ns t Re ce iv er pc r)
{
s ta ti c_ ca st se t<P er so n>*>(p u);
st at ic _c as tpu
d yn am ic _c as tdy na mi c_ ca st se t<P er so n>*>(p u);
pu
s ta ti c_ ca st Re ce iv er
st at ic _c as t<R ec ei ve r*>(p cr ;
pc r)
d yn am ic _c as t<R ec ei ve r*>(p cr ;
dy na mi c_ ca st Re ce iv er
pc r)


Chapter 15

// error: access violation
// error: access violation
// error: can’t cast away const
// error: can’t cast away const

R ec ei ve r* p r = c on st _c as t<R ec ei ve r*>(p cr ;
Re ce iv er pr co ns t_ ca st Re ce iv er
pc r)
// ...

// ok

}

It is not possible to cast to a private base class, and ‘‘casting away c on st requires a c on st _c as t
co ns t’’
co ns t_ ca st
(§6.2.7). Even then, using the result is safe only provided the object wasn’t originally declared
c on st (§10.2.7.1) .
co ns t
15.4.3 Class Object Construction and Destruction [hier.class.obj]
A class object is more than simply a region of memory (§4.9.6). A class object is built from ‘‘raw
memory’’ by its constructors and it reverts to ‘‘raw memory’’ as its destructors are executed. Construction is bottom up, destruction is top down, and a class object is an object to the extent that it
has been constructed or destroyed. This is reflected in the rules for RTTI, exception handling
(§14.4.7), and virtual functions.
It is extremely unwise to rely on details of the order of construction and destruction, but that
order can be observed by calling virtual functions, d yn am ic _c as t, or t yp ei d (§15.4.4) at a point
dy na mi c_ ca st

ty pe id
where the object isn’t complete. For example, if the constructor for C om po ne nt in the hierarchy
Co mp on en t
from §15.4.2 calls a virtual function, it will invoke a version defined for S to ra bl e or C om po ne nt
St or ab le Co mp on en t,
but not one from R ec ei ve r, T ra ns mi tt er or R ad io At that point of construction, the object isn’t
Re ce iv er Tr an sm it te r,
Ra di o.
yet a R ad io it is merely a partially constructed object. It is best to avoid calling virtual functions
Ra di o;
during construction and destruction.
15.4.4 Typeid and Extended Type Information [hier.typeid]
The d yn am ic _c as t operator serves most needs for information about the type of an object at run
dy na mi c_ ca st
time. Importantly, it ensures that code written using it works correctly with classes derived from
those explicitly mentioned by the programmer. Thus, d yn am ic _c as t preserves flexibility and
dy na mi c_ ca st
extensibility in a manner similar to virtual functions.
However, it is occasionally essential to know the exact type of an object. For example, we
might like to know the name of the object’s class or its layout. The t yp ei d operator serves this purty pe id
pose by yielding an object representing the type of its operand. Had t yp ei d() been a function, its
ty pe id
declaration would have looked something like this:
c la ss t yp e_ in fo
cl as s ty pe _i nf o;
c on st t yp e_ in fo t yp ei d(t yp e_ na me t hr ow ba d_ ty pe id ;
co ns t ty pe _i nf o& ty pe id ty pe _n am e) th ro w(b ad _t yp ei d)
c on st t yp e_ in fo t yp ei d(e xp re ss io n);
co ns t ty pe _i nf o& ty pe id ex pr es si on


// pseudo declaration
// pseudo declaration

That is, t yp ei d() returns a reference to a standard library type called t yp e_ in fo defined in ty pe _i nf o
ty pe i nf o>. Given a type-name as its operand, t yp ei d() returns a reference to a t yp e_ in fo that reprein fo
ty pe id
ty pe _i nf o
sents the type-name. Given an expression as its operand, t yp ei d() returns a reference to a
ty pe id

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 15.4.4

Typeid and Extended Type Information

415

t yp e_ in fo that represents the type of the object denoted by the expression. A t yp ei d() is most
ty pe _i nf o
ty pe id
commonly used to find the type of an object referred to by a reference or a pointer:
v oi d f Sh ap e& r S ha pe p
vo id f(S ha pe r, Sh ap e* p)
{
t yp ei d(r ;
ty pe id r)

// type of object referred to by r
t yp ei d(*p ;
ty pe id p)
// type of object pointed to by p
t yp ei d(p ;
ty pe id p)
// type of pointer, that is, Shape* (uncommon, except as a mistake)
}

If the value of a pointer or a reference operand is 0 t yp ei d() throws a b ad _t yp ei d exception.
0, ty pe id
ba d_ ty pe id
The implementation-independent part of t yp e_ in fo looks like this:
ty pe _i nf o
c la ss t yp e_ in fo {
cl as s ty pe _i nf o
p ub li c:
pu bl ic
v ir tu al ~t yp e_ in fo ;
vi rt ua l ty pe _i nf o()

// is polymorphic

b oo l o pe ra to r==(c on st t yp e_ in fo
bo ol op er at or
co ns t ty pe _i nf o&) c on st // can be compared
co ns t;
b oo l o pe ra to r!=(c on st t yp e_ in fo
bo ol op er at or
co ns t ty pe _i nf o&) c on st

co ns t;
b oo l b ef or e(c on st t yp e_ in fo
bo ol be fo re co ns t ty pe _i nf o&) c on st
co ns t;
// ordering
c on st c ha r* n am e() c on st
co ns t ch ar na me
co ns t;
p ri va te
pr iv at e:
t yp e_ in fo co ns t t yp e_ in fo ;
ty pe _i nf o(c on st ty pe _i nf o&)
t yp e_ in fo o pe ra to r=(c on st t yp e_ in fo ;
ty pe _i nf o& op er at or co ns t ty pe _i nf o&)
// ...
};

// name of type
// prevent copying
// prevent copying

The b ef or e() function allows t yp e_ in fo to be sorted. There is no relation between the relationbe fo re
ty pe _i nf os
ships defined by b ef or e and inheritance relationships.
be fo re
It is not guaranteed that there is only one t yp e_ in fo object for each type in the system. In fact,
ty pe _i nf o
where dynamically linked libraries are used it can be hard for an implementation to avoid duplicate
t yp e_ in fo objects. Consequently, we should use == on t yp e_ in fo objects to test equality, rather
ty pe _i nf o

ty pe _i nf o
than == on pointers to such objects.
We sometimes want to know the exact type of an object so as to perform some standard service
on the whole object (and not just on some base of the object). Ideally, such services are presented
as virtual functions so that the exact type needn’t be known. In some cases, no common interface
can be assumed for every object manipulated, so the detour through the exact type becomes necessary (§15.4.4.1). Another, much simpler, use has been to obtain the name of a class for diagnostic
output:
#i nc lu de ty pe in fo
in cl ud e<t yp ei nf o>
v oi d g Co mp on en t* p
vo id g(C om po ne nt p)
{
c ou t << t yp ei d(*p na me ;
co ut
ty pe id p).n am e()
}

The character representation of a class’ name is implementation-defined. This C-style string
resides in memory owned by the system, so the programmer should not attempt to d el et e[] it.
de le te

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.


416

Class Hierarchies

Chapter 15


15.4.4.1 Extended Type Information [hier.extended]
Typically, finding the exact type of an object is simply the first step to acquiring and using moredetailed information about that type.
Consider how an implementation or a tool could make information about types available to
users at run time. Suppose I have a tool that generates descriptions of object layouts for each class
used. I can put these descriptors into a m ap to allow user code to find the layout information:
ma p
m ap co ns t c ha r*, L ay ou t> l ay ou t_ ta bl e;
ma pLa yo ut la yo ut _t ab le
v oi d f B* p
vo id f(B p)
{
L ay ou t& x = l ay ou t_ ta bl e[t yp ei d(*p na me
La yo ut
la yo ut _t ab le ty pe id p).n am e()];
// use x
}

Someone else might provide a completely different kind of information:
s tr uc t T I_ eq {
st ru ct TI _e q
b oo l o pe ra to r()(c on st t yp e_ in fo p c on st t yp e_ in fo q { r et ur n *p
bo ol op er at or
co ns t ty pe _i nf o* p, co ns t ty pe _i nf o* q) re tu rn p==*q }
q;
};
s tr uc t T I_ ha sh {
st ru ct TI _h as h
i nt o pe ra to r()(c on st t yp e_ in fo p ; // compute hash value (§17.6.2.2)

in t op er at or
co ns t ty pe _i nf o* p)
};
h as h_ ma pha sh _m ap ty pe _i nf o*,I co n,h as h_ fc t,T I_ ha sh TI _e q> ic on _t ab le

// §17.6

v oi d g B* p
vo id g(B p)
{
I co n& i = i co n_ ta bl e[&t yp ei d(*p ;
Ic on
ic on _t ab le ty pe id p)]
// use i
}

This way of associating t yp ei ds with information allows several people or tools to associate differty pe id
ent information with types totally independently of each other:
l ay ou t_ ta bl e:
la yo ut _t ab le :
............

" T"
"T "
...

i co n_ ta bl e:
ic on _t ab le :
.

...
& ty pe id (T )
&t yp ei d( T)
...

.............

.
........

........

.......

.

object
layout

icon
representation
of
type

This is most important because the likelihood is zero that someone can come up with a single set of
information that satisfies every user.

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 15.4.5

Uses and Misuses of RTTI

417

15.4.5 Uses and Misuses of RTTI [hier.misuse]
One should use explicit run-time type information only when necessary. Static (compile-time)
checking is safer, implies less overhead, and – where applicable – leads to better-structured programs. For example, RTTI can be used to write thinly disguised switch-statements:
// misuse of run-time type information:
v oi d r ot at e(c on st S ha pe r
vo id ro ta te co ns t Sh ap e& r)
{
i f (t yp ei d(r == t yp ei d(C ir cl e)) {
if ty pe id r)
ty pe id Ci rc le
// do nothing
}
e ls e i f (t yp ei d(r == t yp ei d(T ri an gl e)) {
el se if ty pe id r)
ty pe id Tr ia ng le
// rotate triangle
}
e ls e i f (t yp ei d(r == t yp ei d(S qu ar e)) {
el se if ty pe id r)
ty pe id Sq ua re
// rotate square
}
// ...

}

Using d yn am ic _c as t rather than t yp ei d would improve this code only marginally.
dy na mi c_ ca st
ty pe id
Unfortunately, this is not a strawman example; such code really does get written. For many
people trained in languages such as C, Pascal, Modula-2, and Ada, there is an almost irresistible
urge to organize software as a set of switch-statements. This urge should usually be resisted. Use
virtual functions (§2.5.5, §12.2.6) rather than RTTI to handle most cases when run-time discrimination based on type is needed.
Many examples of proper use of RTTI arise when some service code is expressed in terms of
one class and a user wants to add functionality through derivation. The use of I va l_ bo x in §15.4 is
Iv al _b ox
an example of this. If the user is willing and able to modify the definitions of the library classes,
say B Bw in do w, then the use of RTTI can be avoided; otherwise, it is needed. Even if the user is
BB wi nd ow
willing to modify the base classes, such modification may cause its own problems. For example, it
may be necessary to introduce dummy implementations of virtual functions in classes for which
those functions are not needed or not meaningful. This problem is discussed in some detail in
§24.4.3. A use of RTTI to implement a simple object I/O system can be found in §25.4.1.
For people with a background in languages that rely heavily on dynamic type checking, such as
Smalltalk or Lisp, it is tempting to use RTTI in conjunction with overly general types. Consider:
// misuse of run-time type information:
c la ss O bj ec t { /* ... */ }; // polymorphic
cl as s Ob je ct
c la ss C on ta in er : p ub li c O bj ec t {
cl as s Co nt ai ne r pu bl ic Ob je ct
p ub li c:
pu bl ic
v oi d p ut Ob je ct ;
vo id pu t(O bj ec t*)

O bj ec t* g et ;
Ob je ct ge t()
// ...
};

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.


418

Class Hierarchies

Chapter 15

c la ss S hi p : p ub li c O bj ec t { /* ... */ };
cl as s Sh ip pu bl ic Ob je ct
S hi p* f Sh ip p s, C on ta in er c
Sh ip f(S hi p* ps Co nt ai ne r* c)
{
c pu t(p s);
c->p ut ps
// ...
O bj ec t* p = c ge t();
Ob je ct
c->g et
i f (S hi p* q = d yn am ic _c as t<S hi p*>(p
if Sh ip
dy na mi c_ ca st Sh ip
p)) { // run-time check

r et ur n q
re tu rn q;
}
e ls e {
el se
// do something else (typically, error handling)
}
}

Here, class O bj ec t is an unnecessary implementation artifact. It is overly general because it does
Ob je ct
not correspond to an abstraction in the application domain and forces the application programmer
to use an implementation-level abstraction. Problems of this kind are often better solved by using
container templates that hold only a single kind of pointer:
S hi p* f Sh ip p s, l is t<S hi p*>& c
Sh ip f(S hi p* ps li st Sh ip
c)
{
c pu sh _f ro nt ps ;
c.p us h_ fr on t(p s)
// ...
r et ur n c po p_ fr on t();
re tu rn c.p op _f ro nt
}

Combined with the use of virtual functions, this technique handles most cases.

15.5 Pointers to Members [hier.ptom]
Many classes provide simple, very general interfaces intended to be invoked in several different
ways. For example, many ‘‘object-oriented’’ user-interfaces define a set of requests to which every

object represented on the screen should be prepared to respond. In addition, such requests can be
presented directly or indirectly from programs. Consider a simple variant of this idea:
c la ss S td _i nt er fa ce {
cl as s St d_ in te rf ac e
p ub li c:
pu bl ic
v ir tu al v oi d s ta rt
vi rt ua l vo id st ar t() = 0
0;
v ir tu al v oi d s us pe nd
vi rt ua l vo id su sp en d() = 0
0;
v ir tu al v oi d r es um e() = 0
vi rt ua l vo id re su me
0;
v ir tu al v oi d q ui t() = 0
vi rt ua l vo id qu it
0;
v ir tu al v oi d f ul l_ si ze
vi rt ua l vo id fu ll _s iz e() = 0
0;
v ir tu al v oi d s ma ll = 0
vi rt ua l vo id sm al l() 0;
v ir tu al ~S td _i nt er fa ce {}
vi rt ua l St d_ in te rf ac e()
};

The exact meaning of each operation is defined by the object on which it is invoked. Often, there is
a layer of software between the person or program issuing the request and the object receiving it.


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 15.5

Pointers to Members

419

Ideally, such intermediate layers of software should not have to know anything about the individual
operations such as r es um e() and f ul l_ si ze
re su me
fu ll _s iz e(). If they did, the intermediate layers would have to
be updated each time the set of operations changed. Consequently, such intermediate layers simply
transmit some data representing the operation to be invoked from the source of the request to its
recipient.
One simple way of doing that is to send a s tr in g representing the operation to be invoked. For
st ri ng
example, to invoke s us pe nd we could send the string " su sp en d" However, someone has to cresu sp en d()
"s us pe nd ".
ate that string and someone has to decode it to determine to which operation it corresponds – if
any. Often, that seems indirect and tedious. Instead, we might simply send an integer representing
the operation. For example, 2 might be used to mean s us pe nd
su sp en d(). However, while an integer may
be convenient for machines to deal with, it can get pretty obscure for people. We still have to write
code to determine that 2 means s us pe nd and to invoke s us pe nd
su sp en d()
su sp en d().
C++ offers a facility for indirectly referring to a member of a class. A pointer to a member is a

value that identifies a member of a class. You can think of it as the position of the member in an
object of the class, but of course an implementation takes into account the differences between data
members, virtual functions, non-virtual functions, etc.
Consider S td _i nt er fa ce If I want to invoke s us pe nd
St d_ in te rf ac e.
su sp en d() for some object without mentioning
s us pe nd
su sp en d() directly, I need a pointer to member referring to S td _i nt er fa ce :s us pe nd
St d_ in te rf ac e: su sp en d(). I also
need a pointer or reference to the object I want to suspend. Consider a trivial example:
t yp ed ef v oi d (S td _i nt er fa ce :* P st d_ me m)();
ty pe de f vo id St d_ in te rf ac e: Ps td _m em

// pointer to member type

v oi d f St d_ in te rf ac e* p
vo id f(S td _i nt er fa ce p)
{
P st d_ me m s = &S td _i nt er fa ce :s us pe nd
Ps td _m em
St d_ in te rf ac e: su sp en d;
p su sp en d();
p->s us pe nd

// direct call

(p
p->*s
s)();


// call through pointer to member

}

A pointer to member can be obtained by applying the address-of operator & to a fully qualified
class member name, for example, &S td _i nt er fa ce :s us pe nd A variable of type ‘‘pointer to memSt d_ in te rf ac e: su sp en d.
ber of class X is declared using a declarator of the form X :*.
X’’
X:
The use of t yp ed ef to compensate for the lack of readability of the C declarator syntax is typity pe de f
cal. However, please note how the X :* declarator matches the traditional * declarator exactly.
X:
A pointer to member m can be used in combination with an object. The operators ->* and .*
allow the programmer to express such combinations. For example, p
p->*m binds m to the object
m
pointed to by p and o bj m binds m to the object o bj The result can be used in accordance with
p,
ob j.*m
ob j.
m type. It is not possible to store the result of a ->* or a .* operation for later use.
m’s
Naturally, if we knew which member we wanted to call we would invoke it directly rather than
mess with pointers to members. Just like ordinary pointers to functions, pointers to member functions are used when we need to refer to a function without having to know its name. However, a
pointer to member isn’t a pointer to a piece of memory the way a pointer to a variable or a pointer
to a function is. It is more like an offset into a structure or an index into an array. When a pointer
to member is combined with a pointer to an object of the right type, it yields something that identifies a particular member of a particular object.

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.



420

Class Hierarchies

Chapter 15

This can be represented graphically like this:
p

.

s
.
.
.
.
.

v tb l:
vt bl :
.

X :: st ar t
X: :s ta rt
.

.
X :: su sp en d

X: :s us pe nd

Because a pointer to a virtual member (s in this example) is a kind of offset, it does not depend on
s
an object’s location in memory. A pointer to a virtual member can therefore safely be passed
between different address spaces as long as the same object layout is used in both. Like pointers to
ordinary functions, pointers to non-virtual member functions cannot be exchanged between address
spaces.
Note that the function invoked through the pointer to function can be v ir tu al For example,
vi rt ua l.
when we call s us pe nd through a pointer to function, we get the right s us pe nd for the object to
su sp en d()
su sp en d()
which the pointer to function is applied. This is an essential aspect of pointers to functions.
An interpreter might use pointers to members to invoke functions presented as strings:
m ap st ri ng St d_ in te rf ac e*> v ar ia bl e;
ma pva ri ab le
m ap st ri ng Ps td _m em o pe ra ti on
ma p<s tr in g,P st d_ me m> op er at io n;
v oi d c al l_ me mb er st ri ng v ar s tr in g o pe r)
vo id ca ll _m em be r(s tr in g va r, st ri ng op er
{
(v ar ia bl e[v ar
va ri ab le va r]->*o pe ra ti on op er
op er at io n[o pe r])();
}

// var.oper()


A critical use of pointers to member functions is found in m em _f un (§3.8.5, §18.4).
me m_ fu n()
A static member isn’t associated with a particular object, so a pointer to a static member is simply an ordinary pointer. For example:
c la ss T as k {
cl as s Ta sk
// ...
s ta ti c v oi d s ch ed ul e();
st at ic vo id sc he du le
};
v oi d (*p
vo id
p)() = &T as k::s ch ed ul e;
Ta sk sc he du le
// ok
v oi d (T as k::* p m)() = &T as k::s ch ed ul e; // error: ordinary pointer assigned
vo id Ta sk
pm
Ta sk sc he du le
// to pointer to member

Pointers to data members are described in §C.12.
15.5.1 Base and Derived Classes [hier.contravariance]
A derived class has at least the members that it inherits from its base classes. Often it has more.
This implies that we can safely assign a pointer to a member of a base class to a pointer to a member of a derived class, but not the other way around. This property is often called contravariance.
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 15.5.1

Base and Derived Classes

421

c la ss t ex t : p ub li c S td _i nt er fa ce {
cl as s te xt pu bl ic St d_ in te rf ac e
p ub li c:
pu bl ic
v oi d s ta rt ;
vo id st ar t()
v oi d s us pe nd ;
vo id su sp en d()
// ...
v ir tu al v oi d p ri nt ;
vi rt ua l vo id pr in t()
p ri va te
pr iv at e:
v ec to r s
ve ct or s;
};
v oi d (S td _i nt er fa ce :* p mi
vo id St d_ in te rf ac e: pm i)() = &t ex t::p ri nt
te xt pr in t; // error
v oi d (t ex t::*p mt
vo id te xt
pm t)() = &S td _i nt er fa ce :s ta rt
St d_ in te rf ac e: st ar t; // ok


This contravariance rule appears to be the opposite of the rule that says we can assign a pointer to a
derived class to a pointer to its base class. In fact, both rules exist to preserve the fundamental
guarantee that a pointer may never point to an object that doesn’t at least have the properties that
the pointer promises. In this case, S td _i nt er fa ce :* can be applied to any S td _i nt er fa ce and most
St d_ in te rf ac e:
St d_ in te rf ac e,
such objects presumably are not of type t ex t. Consequently, they do not have the member
te xt
t ex t::p ri nt with which we tried to initialize p mi By refusing the initialization, the compiler saves
te xt pr in t
pm i.
us from a run-time error.

15.6 Free Store [hier.free]
It is possible to take over memory management for a class by defining o pe ra to r n ew and o pe ra op er at or ne w()
op er at or d el et e() (§6.2.6.2). However, replacing the global o pe ra to r n ew and o pe ra to r d el et e() is
to r de le te
op er at or ne w()
op er at or de le te
not for the fainthearted. After all, someone else might rely on some aspect of the default behavior
or might even have supplied other versions of these functions.
A more selective, and often better, approach is to supply these operations for a specific class.
This class might be the base for many derived classes. For example, we might like to have the
E mp lo ye e class from §12.2.6 provide a specialized allocator and deallocator for itself and all of its
Em pl oy ee
derived classes:
c la ss E mp lo ye e {
cl as s Em pl oy ee
// ...
p ub li c:

pu bl ic
// ...
v oi d* o pe ra to r n ew si ze _t ;
vo id op er at or ne w(s iz e_ t)
v oi d o pe ra to r d el et e(v oi d*, s iz e_ t);
vo id op er at or de le te vo id
si ze _t
};

Member o pe ra to r n ew
op er at or ne w()s and o pe ra to r d el et e()s are implicitly s ta ti c members. Consequently,
op er at or de le te
st at ic
they don’t have a t hi s pointer and do not modify an object. They provide storage that a constructor
th is
can initialize and a destructor can clean up.
v oi d* E mp lo ye e::o pe ra to r n ew si ze _t s
vo id Em pl oy ee op er at or ne w(s iz e_ t s)
{
// allocate ‘s’ bytes of memory and return a pointer to it
}

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.


422

Class Hierarchies


Chapter 15

v oi d E mp lo ye e::o pe ra to r d el et e(v oi d* p s iz e_ t s
vo id Em pl oy ee op er at or de le te vo id p, si ze _t s)
{
// assume ‘p’ points to ‘s’ bytes of memory allocated by Employee::operator new()
// and free that memory for reuse
}

The use of the hitherto mysterious s iz e_ t argument now becomes obvious. It is the size of the
si ze _t
object actually deleted. Deleting a ‘‘plain’’ E mp lo ye e gives an argument value of
Em pl oy ee
s iz eo f(E mp lo ye e); deleting a M an ag er gives an argument value of s iz eo f(M an ag er
si ze of Em pl oy ee
Ma na ge r
si ze of Ma na ge r). This
allows a class-specific allocator to avoid storing size information with each allocation. Naturally, a
class-specific allocator can store such information (like a general-purpose allocator must) and
ignore the s iz e_ t argument to o pe ra to r d el et e(). However, that makes it harder to improve signifsi ze _t
op er at or de le te
icantly on the speed and memory consumption of a general-purpose allocator.
How does a compiler know how to supply the right size to o pe ra to r d el et e()? As long as the
op er at or de le te
type specified in the d el et e operation matches the actual type of the object, this is easy. However,
de le te
that is not always the case:
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
i nt l ev el

in t le ve l;
// ...
};
v oi d f
vo id f()
{
E mp lo ye e* p = n ew M an ag er
Em pl oy ee
ne w Ma na ge r;
d el et e p
de le te p;
}

// trouble (the exact type is lost)

In this case, the compiler will not get the size right. As when an array is deleted, the user must help.
This is done by adding a virtual destructor to the base class, E mp lo ye e:
Em pl oy ee
c la ss E mp lo ye e {
cl as s Em pl oy ee
p ub li c:
pu bl ic
v oi d* o pe ra to r n ew si ze _t ;
vo id op er at or ne w(s iz e_ t)
v oi d o pe ra to r d el et e(v oi d*, s iz e_ t);
vo id op er at or de le te vo id
si ze _t
v ir tu al ~E mp lo ye e();
vi rt ua l Em pl oy ee
// ...

};

Even an empty destructor will do:
E mp lo ye e::~E mp lo ye e() { }
Em pl oy ee
Em pl oy ee

In principle, deallocation is then done from within the destructor (which knows the size). Furthermore, the presence of a destructor in E mp lo ye e ensures that every class derived from it will be supEm pl oy ee
plied with a destructor (thus getting the size right), even if the derived class doesn’t have a userdefined destructor. 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 15.6

Free Store

423

v oi d f
vo id f()
{
E mp lo ye e* p = n ew M an ag er
Em pl oy ee
ne w Ma na ge r;
d el et e p
de le te p;
// now fine (Employee is polymorphic)
}


Allocation is done by a (compiler-generated) call:
E mp lo ye e::o pe ra to r n ew si ze of Ma na ge r))
Em pl oy ee op er at or ne w(s iz eo f(M an ag er

and deallocation by a (compiler-generated) call:
E mp lo ye e::o pe ra to r d el et e(p si ze of Ma na ge r))
Em pl oy ee op er at or de le te p,s iz eo f(M an ag er

In other words, if you want to supply an allocator/deallocator pair that works correctly for derived
classes, you must either supply a virtual destructor in the base class or refrain from using the s iz e_ t
si ze _t
argument in the deallocator. Naturally, the language could have been designed to save you from
such concerns. However, that can be done only by also ‘‘saving’’ you from the benefits of the optimizations possible in the less safe system.
15.6.1 Array Allocation [hier.array]
The o pe ra to r n ew
op er at or ne w() and o pe ra to r d el et e() functions allow a user to take over allocation and
op er at or de le te
deallocation of individual objects; o pe ra to r n ew
op er at or ne w[]() and o pe ra to r d el et e[]() serve exactly the
op er at or de le te
same role for the allocation and deallocation of arrays. For example:
c la ss E mp lo ye e {
cl as s Em pl oy ee
p ub li c:
pu bl ic
v oi d* o pe ra to r n ew
vo id op er at or ne w[](s iz e_ t);
si ze _t
v oi d o pe ra to r d el et e[](v oi d*, s iz e_ t);

vo id op er at or de le te
vo id
si ze _t
// ...
};
v oi d f in t s
vo id f(i nt s)
{
E mp lo ye e* p = n ew E mp lo ye e[s ;
Em pl oy ee
ne w Em pl oy ee s]
// ...
d el et e[] p
de le te
p;
}

Here, the memory needed will be obtained by a call,
E mp lo ye e::o pe ra to r n ew
Em pl oy ee op er at or ne w[](s iz eo f(E mp lo ye e)*s de lt a)
si ze of Em pl oy ee s+d el ta

where d el ta is some minimal implementation-defined overhead, and released by a call:
de lt a
E mp lo ye e::o pe ra to r d el et e[](p s*s iz eo f(E mp lo ye e)+d el ta
Em pl oy ee op er at or de le te
p,s si ze of Em pl oy ee de lt a)

The number of elements (s is ‘‘remembered’’ by the system.
s)


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.