Defining things this way enables a simple conversion of an emergency vehicle to an ordinary
and vice versa:
v o i d f (V e h i c l e * p )
e p t r = 0 ; / no longer an emergency vehicle
/ / .
e p t r =
those must themselves be templates so as to maintain generality and efficiency. In that way,
become generic over a range of types. This style of template use is called generic
programming (2.7). When we call s t d : s o r t () on a v e c t o r
depend on the classes used to implement it and need not include them to compile.
25.4.1 Changing Interfaces [role.io]
By definition, a node class is part of a class hierarchy. Not every class in a hierarchy needs to
the same interface. In particular
S t r i n g (c o n s t c h a r * q );
~S t r i n g ();
/ / .
A S t r i n g object can be represented graphically like this:
. elements . \0
126.96.36.199 Invariants [lang.invariant]
The values of the members and the ob
dependency because just doing that for example, in declaring a Y * or mentioning Y in the
of an external function doesnt require access to the declaration of Y at all (5.7):
746 Design and Programming Chapter 24
c l a s s Y ; / Y is the name o
Here, the N D E B U G macro is used in a way similar to the way it is used in the standard C a s
s e r t ()
macro. N D E B U G is conventionally set to indicate that debugging is not being done.
The simple act of defining invariants and using them during
The interface uses the types Y and Z in a way that requires the declarations of Y and Z to be
known to compile it.
The function X : f () takes an arbitrary number of arguments of unknown types (probably
somehow controlled by a format string supplied as
Note, however, that the ideal is still to hide as much of the implementation as is feasible without
seriously hurting performance. Inline functions can be a great win in this context. Exposing
variables by making them public or by providing set and
/ / .
v i r t u a l i n t f ();
i n t g () cfw_ i n t x = f (); r e t u r n x a
What does g () do? The answer critically depends on the definition of f () in some derived class.
Here is a version that will ensure th
f o r (i n t i = 0 ; i <v .s i z e (); i +) cfw_ / natural vector iteration
/ / your stuff
/ / .
The difference in iteration style is natural in the sense that a getnextelement
operation is essential
to the notion of a list (but not that common for a ve
performance lead to the use of templates (16.2).
To make the template/hierarchy tradeoff more concrete, consider how to generalize a simple
v o i d p r i n t _ a l l (I t e r _ f o r _ T x )
f o r (T * p = x .f i r s t (); p ; p =
u s i n g n a m e s p a c e A ; / dependent on declarations from A
/ / .
v o i d f ();
756 Design and Programming Chapter 24
n a m e s p a c e X _ i m p l cfw_ / facilities needed by Xs implementation
/ / .
v o i d X : f ()
u s i n
To make writing overriding functions easy and efficient, the representation is declared p r o t e c
This achieves the objective of making D a t e 2 arbitrarily malleable by derivation, yet keeping
user interface unchanged. However, there are cost
/ / .
If the provision of the object I/O scaffolding becomes tedious, a template might help:
t e m p l a t e <c l a s s T > c l a s s I o : p u b l i c T , p u b l i c I o _ o b j cfw_
I o * c l o
string prefix and call a function capable of creating and initializing an object of the right class.
t y p e d e f I o _ o b j * (*P F )(i s t r e a m &); / pointer to function returning an Io_obj*
m a p <s t r i n g ,P F > i o _ m a p ; / ma
i f (p = t h i s ) r e t u r n t r u e ;
i f (n e x t =0 ) r e t u r n f a l s e ;
o n (p );
However, this turns iteration into recursion, and doing that can cause a major performance hit
a compiler isnt able to optimize the recursion ba
Typically, the invariant is not maintained during the execution of a member function. Functions
that may be called while the invariant is invalid should not be part of the public interface. Private
and protected functions can serve that purpose.
How can w
functions. A typical node class provides not just an implementation of the interface specified by
base class (the way an implementation class does for an abstract type). It also adds new functions
itself, thus providing a wider interface. Consider C a
H o r i z o n t a l _ s c r o l l b a r and V e r t i c a l _ s c r o l l b a r or by a single S c r o l l b a
r type that takes an argument
that says whether its layout is horizontal or vertical. The former choice implies the need for a
third type, the p
My preference is to have a window contain a scrollbar. I find it easier to think of a window
a scrollbar than of a window being a scrollbar in addition to being a window. In fact, my favorite
design strategy involves a scrollbar being a special ki
v i r t u a l v o i d d r a w (); / pull gun from holster
Section 25.6 Interface Classes 779
/ / .
A C o w b o y _ w i n d o w represents the animation of a cowboy in the game and handles the
issues and the way you write constructors and destructors:
X (i n t );
/ / .
C (i n t i , i n t j , i n t k ) : a (i ), p (n e w X (j ), r (*n e w X (k ) cfw_
~C () cfw_ d e l e t e p ; d e l e t e &r
implementation. Derivation is most often used just to supply implementation. However, a new
interface can be constructed from an abstract class by deriving a more extensive abstract class
it. This new abstract class must then in turn be implemented t
c l a s s V e h i c l e cfw_ /* . */ ;
c l a s s E m e r g e n c y cfw_ /* . */ ;
c l a s s C a r : p u b l i c V e h i c l e cfw_ /* . */ ;
c l a s s T r u c k : p u b l i c V e h i c l e cfw_ /* . */ ;
c l a s s P o l i c e _ c a r : p u b l i c C a r ,
introduced. All dependencies are contained in functions that explicitly use a class derived from S
In particular, assuming the conventional use of header files the programmer writing f (S e t &)
only include S e t .h and not L i s t _ s e t .h o
778 Roles of Classes Chapter 25
This is a powerful technique that should be treated with some care by people with a background
in functional decomposition. If too many classes start looking like A c t i o n , the overall design
system may have dete
(l a s t f
i r s t <2 | (*f i r s t <=l a s t [1
& *f i r s t <=f i r s t [(l a s t f
i r s t )/2 ] & f i r s t [(l a s t f
i r s t )/2 ]<=l a s t [1
I often find writing ordinary codechecking
arguments and results simpler than composing assertions
/ / .
p = n e w S c r o l l b a r (o o );
/ / .
v o i d m e ()
h e l p e r (h o r i z o n t a l );
/ / .
This representation would also make it easy to reorient
a scrollbar at run time. This is unlikely to
be of major importance in the case of
v i r t u a l v o i d r e m o v e (T *) = 0 ;
v i r t u a l i n t i s _ m e m b e r (T *) = 0 ;
v i r t u a l T * f i r s t () = 0 ;
v i r t u a l T * n e x t () = 0 ;
v i r t u a l ~S e t () cfw_
This defines an interface to a set with a builtin
v o i d h ();
/ / .
v o i d f ();
v o i d f f ();
A specification that A delegated to B through A : p would result in code like this:
Section 24.3.6 ProgrammedIn
B * p ; / delegation through p
/ / .
v o i d
by specifying and implementing only what needs to differ from V e h i c l e . This is often
referred to as
programming by difference or programming by extension.
Like many node classes, a C a r is itself a good candidate for further derivation. For exampl
g (); / error: D1 doesnt have a member g()
b .g ();
f (); / calls B:f (not overridden by D1:f()
f (); / calls D1:f()
Note that there is no implicit conversion from a class to one of its members and that a class
a member of anot
However, few data structures support both the subscripting and the liststyle
operations well. Consequently,
it is probably not a good idea to specify an interface that requires both. Doing so leads
to the use of runtime
(15.4) or exception ha
i n l i n e v o i d S t r i n g : c h e c k ()
A s s e r t <I n v a r i a n t >(!s t r i n g _ c h e c k | (p & 0 <=s z & s z <T O O _ L A R G E
& p [s z ]=0 );
v o i d f ()
S t r i n g s = "w o n d e r ";
/ / strings are checked here
evaluated at compile time. Unfortunately, some compilers are unable to achieve this for the
A s s e r t (). Consequently, the twoargument
A s s e r t () should be used only when the exception
is not of the form E () and it is also acceptable f