We're always interested in getting feedback. E-mail us if you like this guide, if you think that important material is omitted, if you encounter errors in the code examples or in the documentation, if you find any typos, or generally just if you feel like e-mailing. Send your email to Frank Brokken.Please state the document version you're referring to, as found in the title (in this document: 5.2.0a) and please state the paragraph you're referring to.
All mail received is seriously considered, and new (sub)releases of the Annotations will normally reflect your suggestions for improvements. Except for the incidental case I will not otherwise acknowledge the receipt of suggestions for improvements. Please don't misinterpret this for lack of appreciation.
As we have seen in chapter 13, C++ provides the tools to
derive classes from base classes, and to use base class pointers to address
derived objects. As we've seen, when using a
base class pointer to address
an object of a
derived class, the type of the pointer determines which
member function will be used. This means that a Vehicle *vp, pointing
to a Truck object, will incorrectly compute the truck's combined weight in
a statement like vp->getweight(). The reason for this should now be clear:
vp calls Vehicle::getweight() and not Truck::getweight(), even
though vp actually points to a Truck.
Fortunately, a remedy is available. In C++ it is possible for a Vehicle
*vp to call a function Truck::getweight() when the pointer actually
points to a Truck.
The terminology for this feature is
polymorphism: it is as though the
pointer vp changes its type from a base class pointer to a pointer to the
class of the object it actually points to. So, vp might behave like a
Truck * when pointing to a Truck, and like an Auto * when pointing
to an Auto etc.. (In one of the StarTrek movies, Capt. Kirk was
in trouble, as usual. He met an extremely beautiful lady who, however,
later on changed into a hideous troll. Kirk was quite surprised, but the lady
told him: ``Didn't you know I am a polymorph?'')
Polymorphism is realized by a feature called late binding. This refers to the fact that the decision which function to call (a base class function or a function of a derived class) cannot be made compile-time, but is postponed until the program is actually executed: the actual member function to be used is selected run-time.
Vehicle * will activate Vehicle's
member functions, even when pointing to an object of a derived class. This is
referred to as early or
static binding, since the type of function is known
compile-time. The late
or
dynamic binding is
achieved in C++ using virtual member functions.
A member function becomes a
virtual member function when its declaration
starts with the keyword
virtual. Once a function is declared virtual
in a
base class, it remains a virtual member function in all derived
classes; even when the keyword virtual is not repeated in a
derived class.
As far as the vehicle classification system is concerned (see section
13.1) the two member functions getweight() and
setweight() might well be declared virtual. The relevant sections of
the class definitions of the class Vehicle and Truck are shown
below. Also, we show the implementations of the member functions
getweight() of the two classes:
class Vehicle
{
public:
virtual int getweight() const;
virtual void setweight(int wt);
};
class Truck: public Auto
{
public:
void setweight(int engine_wt, int trailer_wt);
int getweight() const;
};
int Vehicle::getweight() const
{
return (weight);
}
int Truck::getweight() const
{
return (Auto::getweight() + trailer_wt);
}
Note that the keyword virtual only needs to appear in the
definition of the Vehicle base class. There is no need (but there is also
no
penalty) to repeat it in derived classes: once virtual, always
virtual. On the other hand, a function may be declared virtual
anywhere in a
class hierarchy: the compiler will be perfectly happy if
getweight() is declared virtual in Auto, rather than in
Vehicle. However, the specific characteristics of virtual member functions
would then, for the member function getweight(), only appear with Auto
(and its derived classes) pointers or references. With a Vehicle pointer,
static binding will remain to be used.
The effect of
late binding is illustrated below:
Vehicle
v(1200); // vehicle with weight 1200
Truck
t(6000, 115, // truck with cabin weight 6000, speed 115,
"Scania", // make Scania, trailer weight 15000
15000);
Vehicle
*vp; // generic vehicle pointer
int main()
{
vp = &v; // see (1) below
cout << vp->getweight() << endl;
vp = &t; // see (2) below
cout << vp->getweight() << endl;
cout << vp->getspeed() << endl; // see (3) below
}
Since the function getweight() is defined virtual,
late binding
is used:
Vehicle::getweight() is called.
Truck::getweight() is called.
getspeed() is no member of Vehicle, and hence not callable via
a Vehicle*.
virtual, but this only influences the type of binding
(early vs. late).
A virtual member function cannot be a
static member function: a virtual
member function is still a ordinary member function in that it has a
this
pointer. As static member functions have no this pointer, they cannot be
declared virtual.
delete releases memory which is occupied by a
dynamically allocated object, or when an object goes
out of scope,
the appropriate
destructor is called to ensure
that memory allocated by the object is also deleted. Now consider
the following code fragment (cf. section 13.1):
Vehicle
*vp = new Land(1000, 120);
delete vp; // object destroyed
In this example an object of a derived class (Land) is destroyed using
a base class pointer (Vehicle *). For a `standard' class definition this
will mean that the destructor of Vehicle is called, instead of the
destructor of the Land object. This not only results in a
memory leak
when memory is allocated in Land, but it will also prevent any other task,
normally performed by the derived class' destructor from being completed (or,
better: started). A Bad Thing.
In C++ this problem is solved using a
virtual destructor. By
applying the keyword virtual to the declaration of a
destructor the
appropriate derived class destructor is activated when the argument of the
delete operator is a
base class pointer. In the following partial class
definition the declaration of such a virtual destructor is shown:
class Vehicle
{
public:
virtual ~Vehicle();
virtual unsigned getweight() const;
};
By declaring a virtual destructor, the above delete operation
(delete vp) will correctly call de the Land's destructor, rather than
the Vehicle's destructor.
From this discussion we are now able to formulate the following situations in which a destructor should be defined:
Vehicle::~Vehicle() may be as simple as:
Vehicle::~Vehicle()
{}
Often this will be part of the
class interface as
an
inline destructor.
Vehicle contained its own, concrete,
implementations of the virtual functions getweight() and
setweight(). In C++ it is however also possible only to mention
virtual member functions in a
base class, without actually defining
them. The functions are concretely implemented in a
derived class. This
approach defines a
protocol, which has to be followed in the derived
classes. This implies that derived classes must take care of the actual
definition: the C++ compiler will not allow the definition of an object of
a class in which one or more member functions are left undefined. The base
class thus enforces a protocol by declaring a function by its name, return
value and arguments. The derived classes must take care of the actual
implementation. The base class itself defines therefore only a model or
mold, to be used when other classes are derived. Such base classes are
also called
abstract classes.
The functions which are only declared in the base class are
called
pure virtual functions. A function is made pure virtual by
preceding its declaration with the keyword
virtual and by postfixing it
with
= 0. An example of a pure virtual function occurs in the following
listing, where the definition of a class Object requires the
implementation of the
conversion operator
operator string():
#include <string>
class Object
{
public:
virtual operator string() const = 0;
};
Now, all classes derived from Object must implement the
operator string() member function, or their objects cannot be
constructed. This is neat: all objects derived from Object can now always
be considered string objects, so they can, e.g., be inserted into
ostream objects.
Should the
virtual destructor of a base class be a pure virtual
function? The answer to this question is no: a class such as Vehicle
should not require derived classes to define a
destructor. In contrast, Object::operator string() can be a pure
virtual function: in this case the base class defines a protocol which must be
adhered to.
Realize what would happen if we would define the destructor of a base
class as a pure virtual destructor: according to the
compiler, the
derived class object can be constructed: as its destructor is defined, the
derived class is not a pure abstract class. However, inside the derived class
destructor, the destructor of its base class is implicitly called. This
destructor was never defined, and the
linker will loudly complain about
an
undefined reference to, e.g., Virtual::~Virtual().
Often, but not necessarily always, pure virtual member functions are
const
member functions. This allows the
construction of constant derived class objects. In other situations this might
not be necessary (or realistic), and
non-constant member functions might be
required. The general rule for const member functions applies also to pure
virtual functions: if the member function will alter the object's data
members, it cannot be a const member function. Often abstract base classes
have
no data members. However, the
prototype of the pure virtual member function must be used again in derived
classes. If the implementation of a pure virtual function in a derived class
alters the data of the derived class object, than that function cannot be
declared as a const member function. Therefore, the constructor of an
abstract base class should well consider whether a pure virtual member
function should be a const member function or not.
A slight difficulty in
multiple inheritance may arise when more than one
`path' leads from the
derived class to the
base class. This is
illustrated in the code example below: a class Derived is doubly derived
from a class Base:
class Base
{
int d_field;
public:
void setfield(int val)
{ d_field = val; }
int getfield() const
{ return d_field; }
};
class Derived: public Base, public Base
{
};
Due to the double derivation, the functionality of Base now occurs
twice in Derived. This leads to
ambiguity: when the function
setfield() is called for a Derived object, which function should
that be, since there are two? In such a duplicate derivation, many C++
compilers will refuse to generate code and will (correctly) identify an error.
The above code clearly duplicates its base class in the derivation. Such a
duplication can here easily be easily. But duplication of a base class can
also occur through
nested inheritance, where an object is derived from,
say, an Auto and from an Air (see the vehicle classification system,
section 13.1). Such a class would be needed to represent, e.g.,
a flying car (such as the one in James Bond vs. the Man with the
Golden Gun...). An AirAuto would ultimately contain two Vehicles, and
hence two weight fields, two setweight() functions and two
getweight() functions.
AirAuto introduces
ambiguity, when
derived from Auto and Air.
AirAuto is an Auto, hence a Land, and hence a
Vehicle.
AirAuto is also an Air, and hence a Vehicle.
Vehicle data is further illustrated in
figure 13.

The internal organization of an AirAuto is shown in
figure 14

AirAuto object.
The C++ compiler will detect the ambiguity in an AirAuto object,
and will therefore fail to compile a statement like:
AirAuto
cool;
cout << cool.getweight() << endl;
The question of which member function getweight() should be called,
cannot be answered by the compiler. The programmer has two possibilities to
resolve the ambiguity explicitly:
// let's hope that the weight is kept in the Auto
// part of the object..
cout << cool.Auto::getweight() << endl;
Note the position of the scope operator and the class name: before the name
of the member function itself.
getweight() could be created for
the class AirAuto:
int AirAuto::getweight() const
{
return(Auto::getweight());
}
AirAuto of special precautions.
However, apart from these explicit solutions, there is a more elegant one, which will be introduced in the next section.
Vehicle is present in an AirAuto. The result is not only an
ambiguity in the functions which access the weight data, but also the
presence of two weight fields. This is somewhat redundant, since we can
assume that an AirAuto has just one weight.
We can achieve the situation that only one Vehicle will be contained
in an AirAuto. This is done by ensuring that the
base class which is
multiply present in a
derived class, is defined as a
virtual base class.
For the class AirAuto this means that the derivation of Land and
Air is changed:
class Land: virtual public Vehicle
{
// etc
};
class Air: virtual public Vehicle
{
// etc
};
class AirAuto: public Land, public Air
{
};
The
virtual derivation ensures that via the Land route, a
Vehicle is only added to a class when a virtual base class was not yet
present. The same holds true for the Air route. This means that we can no
longer say via which route a Vehicle becomes a part of an AirAuto; we
can only say that there is an embedded Vehicle object. The internal
organization of an AirAuto after virtual derivation is shown in
figure 15.

AirAuto object when the base
classes are virtual.
There are several points worth noting when using virtual derivation:
Derived1
and Derived2, both (possibly virtually) derived from Base. We will
address the
question which
constructors will be called when a class Final: public Derived1, public
Derived2 is defined. To distinguish the several constructors that are
involved, we will use Base1() to indicate the Base class constructor
that is called as base class initializer for Derived1 (and analogously:
Base2() belonging to Derived2), while Base() indicates the default
constructor of the class Base. Apart from the Base class constructor,
we use Derived1() and Derived2() to indicate the base class
initializers for the class Final. We now distinguid the following
situation, for constructors of the class Final: public Derived1, public
Derived2:
Derived1: public Base, Derived2: public Base
This is the normal, non virtual multiple derivation. There are twoBaseclasses in theFinalobject, and the following constructors will be called (in the mentioned order):
- Base1(), Derived1(), Base2(), Derived2()
Derived1: public Base, Derived2: virtual public
Base
OnlyDerived2uses virtual derivation. For theDerived2part the base class initializer will be omitted, and the defaultBaseclass constructor will be called. Furthermore, this `detached' base class constructor will be called first:Note that
- Base(), Base1(), Derived1(), Derived2()
Base()is called first, notBase1(). Also note that, as only one derived class uses virtual derivation, there are still twoBaseclass objects in the eventualFinalclass. Merging of base classes only occurs with multiple virtual base classes.
Derived1: virtual public Base, Derived2: public
Base
OnlyDerived1uses virtual derivation. For theDerived1part the base class initializer will now be omitted, and the defaultBaseclass constructor will be called instead. Note the difference with the first case:Base1()is replaced byBase(). ShouldDerived1happen to use the defaultBaseconstructor, no difference would be noted here with the first case:
- Base(), Derived1(), Base2(), Derived2()
Derived1: virtual public Base, Derived2: virtual
public Base
Here both derived classes use virtual derivation, and so only oneBaseclass object will be present in theFinalclass. Note that now only oneBaseclass constructor is called: for the detached (merged)Baseclass object:
- Base(), Derived1(), Derived2()
AirAuto,
situations may arise where the
double
presence of the members of a base class is appropriate. To illustrate this,
consider the definition of a Truck from section 13.4:
class Truck: public Auto
{
int d_trailer_weight;
public:
Truck();
Truck(int engine_wt, int sp, char const *nm,
int trailer_wt);
void setweight(int engine_wt, int trailer_wt);
int getweight() const;
};
Truck::Truck(int engine_wt, int sp, char const *nm,
int trailer_wt)
:
Auto(engine_wt, sp, nm)
{
d_trailer_weight = trailer_wt;
}
int Truck::getweight() const
{
return
( // sum of:
Auto::getweight() + // engine part plus
trailer_wt // the trailer
);
}
This definition shows how a Truck object is constructed to contain two
weight fields: one via its derivation from Auto and one via its own int
d_trailer_weight data member. Such a definition is of course valid, but it
could also be rewritten. We could derive a Truck from an Auto
and from a Vehicle, thereby explicitly requesting the double presence
of a Vehicle; one for the weight of the engine and cabin, and one for the
weight of the trailer.
A small point of interest here is that a derivation like
class Truck: public Auto, public Vehicle
is not accepted by the C++ compiler: a Vehicle is already part of
an Auto, and is therefore not needed. An
intermediate class solves
the problem: we derive a class TrailerVeh from Vehicle, and Truck
from Auto and from TrailerVeh. All ambiguities concerning the member
functions are then be solved for the class Truck:
class TrailerVeh: public Vehicle
{
public:
TrailerVeh(int wt);
};
TrailerVeh::TrailerVeh(int wt)
:
Vehicle(wt)
{
}
class Truck: public Auto, public TrailerVeh
{
public:
Truck();
Truck(int engine_wt, int sp, char const *nm,
int trailer_wt);
void setweight(int engine_wt, int trailer_wt);
int getweight() const;
};
Truck::Truck(int engine_wt, int sp, char const *nm,
int trailer_wt)
:
Auto(engine_wt, sp, nm),
TrailerVeh(trailer_wt)
{
}
int Truck::getweight() const
{
return
( // sum of:
Auto::getweight() + // engine part plus
TrailerVeh::getweight() // the trailer
);
}
typeid operators.
dynamic_cast<>() operator can be used to convert a base
class
base class: coverting to derived class pointer or reference to a
derived class pointer or reference.
typeid operator returns the actual type of an expression.
dynamic_cast<>() operator is used to convert a
base class pointer
or
reference to, respectively, a
derived class pointer or reference.
A dynamic cast is performed run-time. A prerequisite for the use of the dynamic cast operator is the existence of at least one virtual member function in the base class.
In the following example a pointer to the class Derived is obtained from
the Base class pointer bp:
class Base
{
public:
virtual ~Base();
};
class Derived: public Base
{
public:
char const *toString()
{
return ("Derived object");
}
};
int main()
{
Base
*bp;
Derived
*dp,
d;
bp = &d;
dp = dynamic_cast<Derived *>(bp);
if (dp)
cout << dp->toString() << endl;
else
cout << "dynamic cast conversion failed\n";
}
Note the test: in the if condition the success of the dynamic cast is
checked. This must be done run-time, as the compiler can't do this all by
itself. If a base class pointer is provided the dynamic cast operator returns
0 on failure, and a pointer to the requested derived class on
success. Consequently, if there are multiple derived classes, a series of
checks could be performed to find the actual derived class to which the
pointer points (In de next example derived classes are declared only):
class Base
{
public:
virtual ~Base();
};
class Derived1: public Base;
class Derived2: public Base;
int main()
{
Base
*bp;
Derived1
*d1,
d;
Derived2
*d2;
bp = &d;
if ((d1 = dynamic_cast<Derived1 *>(bp)))
cout << *d1 << endl;
else if ((d2 = dynamic_cast<Derived2 *>(bp)))
cout << *d2 << endl;
}
Alternatively, a reference to a base class object may be available. In
this case the dynamic_cast<>() operator will throw an
exception if it
fails. For example:
#include <iostream>
#include <typeinfo>
class Base
{
public:
virtual ~Base()
{}
virtual char const *toString()
{}
};
class Derived1: public Base
{};
class Derived2: public Base
{};
void process(Base &b)
{
try
{
cout << dynamic_cast<Derived1 &>(b).toString() << endl;
return;
}
catch (std::bad_cast)
{}
try
{
cout << dynamic_cast<Derived2 &>(b).toString() << endl;
return;
}
catch (std::bad_cast)
{}
}
int main()
{
Derived1
d;
process(d);
}
In this example the value
std::bad_cast
is
introduced. The std::bad_cast is thrown as an exception if the dynamic
cast of a reference to a base class object fails. Apparently bad_cast is
the name of a type (
). In section EMPTYENUM the construction
of such a type is discussed.
The dynamic cast operator is a handy tool when an existing base class cannot or should not be modified (e.g., when the sources are not available), and a derived class may be modified instead. Code receiving a base class pointer or reference may then perform a dynamic cast to the derived class to be able to use the derived class' functionality.
Casts from a base class reference or pointer to a derived class reference or pointer are called downcasts.
dynamic_cast<>() operator, the
typeid is usually applied
to base class objects, that are actually derived class objects. Similarly, the
base class should contain one or more virtual functions.
In order to use the typeid operator, source files must
#include <typeinfo>
Actually, the typeid operator returns an object of type
type_info,
which may, e.g., be compared to other type_info objects.
The class type_info may be implemented differently by different
implementations, but at the very least it has the following interface:
class type_info
{
public:
virtual ~type_info();
int operator==(const type_info &other) const;
int operator!=(const type_info &other) const;
char const *name() const;
private:
type_info(type_info const &other);
type_info &operator=(type_info const &other);
};
Note that this class has a
private copy constructor and overloaded
assignment operator. This prevents the
normal construction or assignment of a type_info object. Type_info
objects are constructed and returned by the typeid
operator. Implementations, however, may choose to extend or elaborate the
type_info class and provide, e.g., lists of functions that can be called
with a certain class.
If the type_id operator is given a base class reference (where the
base class contains at least one virtual function), it will indicate that the
type of its operand is the derived class. For example:
class Base; // contains >= 1 virtual functions
class Derived: public Base;
Derived
d;
Base
&br = d;
cout << typeid(br).name() << endl;
In this example the typeid operator is given a base class reference.
It will print the text ``Derived'', being the
class name of the class
br actually refers to. If Base does not contain virtual functions, the
text ``Base'' would have been printed.
The typeid operator can be used to determine the name of the actual
type of
expressions, not just of class type
objects. For example:
cout << typeid(12).name() << endl; // prints: int
cout << typeid(12.23).name() << endl; // prints: double
Note, however, that the above example is suggestive at most of the type
that is printed. It may be int and double, but this is not
necessarily the case. If portability is required, make sure no tests against
static, built-in strings are required. Check out what your compiler produces
in case of doubt.
In situations where the typeid operator is applied to determine the
type of a derived class, it is important to realize that a base class
reference
is used as the argument of the typeid
operator. Consider the following example:
class Base; // contains at least one virtual function
class Derived: public Base;
Base
*bp = new Derived; // base class pointer to derived object
if (typeid(bp) == typeid(Derived *)) // 1: false
...
if (typeid(bp) == typeid(Base *)) // 2: true
...
if (typeid(bp) == typeid(Derived)) // 3: false
...
if (typeid(bp) == typeid(Base)) // 4: false
...
Here, (1) returns false as a Base * is not a Derived
*. (2) returns true, as the two pointer types are the same, (3)
and (4) return false as pointers to objects are not the objects
themselves.
On the other hand, if *bp is used in the above expressions, then
(1) and (2) return false as an object (or reference to an object)
is not a pointer to an object, whereas with
if (typeid(*bp) == typeid(Derived)) // 3: true
...
if (typeid(*bp) == typeid(Base)) // 4: false
...
we see that (3) now returns true: *bp actually refers to a
Derived class object, and typeid(*bp) will return typeid(Derived).
A similar result is obtained if a base class reference is used:
Base
&br = *bp;
if (typeid(br) == typeid(Derived)) // 3: true
...
if (typeid(br) == typeid(Base)) // 4: false
...
class
streambuf (see section 5.7 and figure
4) has many (protected)
virtual member functions (see section
5.7.1) that are used by the
stream classes using streambuf
objects. By deriving a class from the class streambuf these member
functions may be overriden in the derived classes, thus implementing a
specialization of the class streambuf for which the standard
istream
and
ostream objects can be used.
Basically, a streambuf interfaces to some
device. The normal behavior
of the stream-class objects remains unaltered. So, a
string extraction
from a streambuf object will still return a consecutive sequence of non
white space delimited characters. If the derived class is used for
input operations, the following member functions are serious
candidates to be overridden. Examples in which some of these functions are
overridden will be given later in this section:
int streambuf::pbackfail(int c):This member is called whenIf
gptr() == 0: no buffering used,gptr() == eback(): no more room to push back,*gptr() != c: a different character than the next character to be read must be pushed back.c == endOfFile()then the input device must be reset one character, otherwisecmust be prepended to the characters to be read. The function returnsEOFon failure. Otherwise 0 can be returned. The function is called when other attempts to push back a character fail.
streamsize streambuf::showmanyc():This member must return a guaranteed lower bound on the number of characters that can be read from the device beforeuflow()orunderflow()returnsEOF. By default 0 is returned (meaning at least 0 characters will be returned before the latter two functions will returnEOF). When a positive value is returned then the next call to theu(nder)flow()member will not returnEOF.
int streambuf::uflow():By default, this function callsunderflow(). Ifunderflow()fails,EOFis returned. Otherwise, the next character available character is returned as*gptr()following agbump(-1). The member moves the pending character that is returned also to the backup sequence. This is different fromunderflow(), which also returns the next available character, but does not alter the input position.
int streambuf::underflow():This member is called whenIt returns the next available input character, which is the character at
- there is no input buffer (
eback() == 0)gptr() >= egptr(): there are no more pending input characters.gptr(), or the first available character from the input device.Since this member is eventually used by other member functions for reading characters from a device, at the very least this member function must be overridden for new classes derived from
streambuf.
streamsize streambuf::xsgetn(char *buffer, streamsize n):This member function should act as if the returnvalues ofncalls ofsnext()are assigned to consecutive locations ofbuffer. IfEOFis returned then reading stops. The actual number of characters read is returned. Overridden versions could optimize the reading process by, e.g., directly accessing the input buffer.
int streambuf::overflow(int c):This member is called to write characters from the pending sequence to the output device. UnlesscisEOF, it is at least logically, appended to the pending sequence. So, if the pending sequence consists of the characters'h', 'e', 'l'and'l', andc == 'o', then eventually `hello' will be written to the output device.Since this member is eventually used by other member functions for writing characters to a device, at the very least this member function must be overridden for new classes derived from
streambuf.
streamsize streambuf::xsputn(char const *buffer, streamsize n):This member function should act as ifnconsecutive locations ofbufferare passed tosputc(). IfEOFis returned by this latter member, then writing stops. The actual number of characters written is returned. Overridden versions could optimize the writing process by, e.g., directly accessing the output buffer.
streambuf *streambuf::setbuf(char *buffer, streamsize n):This member function is called by the pubsetbuf() member
function.
pos_type streambuf::seekoff(off_type offset, ios::seekdir way,
ios::openmode mode = ios::in | ios::out):This member function is called to reset the position of the next
character to be processed. It is called by
pubseekoff(). The
new position or an invalid position (e.g., -1) is returned.
pos_type streambuf::seekpos(pos_type offset,
ios::openmode mode = ios::in | ios::out):This member function acts similarly as seekoff(), but operates
with absolute rather than relative positions.
int sync():This member function flushes all pending characters to the device,
and/or resets an input device to the position of the first pending
character, waiting in the input buffer to be consumed. It returns
0 on success, -1 on failure. As the default streambuf is not
buffered, the default implementation also returns 0.
capsbuf that is derived from streambuf. The problem is to
construct a streambuf which writes its information to the standard output
stream in such a way that all white-space series of characters are
capitalized. The class capsbuf obviously needs an overridden
overflow() member and a minimal awareness of its state. Its state changes
from `Capitalize' to `Literal' as follows:
capsbuf:
#include <iostream>
#include <streambuf>
#include <ctype.h>
class capsbuf: public std::streambuf
{
int d_last;
public:
capsbuf()
:
d_last(' ')
{}
protected:
int overflow(int c) // interface to the device.
{
std::cout.put(isspace(d_last) ? toupper(c) : c);
d_last = c;
return c;
}
};
An example of a program using capsbuf is:
#include "capsbuf1.h"
using namespace std;
int main()
{
capsbuf cb;
ostream out(&cb);
out << hex << "hello " << 32 << " worlds" << endl;
return 0;
}
/*
Generated output:
Hello 20 Worlds
*/
Note the use of the insertion operator, and note that all type and
radix conversions (inserting
hex and the value 32, coming out as the
ASCII-characters '2' and '0') is neatly done by the
ostream
object. The real purpose in life for capsbuf is to capitalize series of
ASCII-characters, and that's what it does very well.
Next, we realize that inserting characters into streams can also be done using a construction like
cout << cin.rdbuf();
or, boiling down to the same thing:
cin >> cout.rdbuf();
Realizing that this is all about streams, we now try, in the above
main() function:
cin >> out.rdbuf();
We compile and link the program to the executable caps, and start:
echo hello world | caps
Unfortunately, nothing happens.... Any reaction is also lacking if we try
the statement cin >> cout.rdbuf(). What's wrong here?
The difference between cout << cin.rdbuf(), which does produce the
expected results and our using of cin >> out.rdbuf() is that the
operator>>(streambuf *) (and its insertion counterpart) member function
only does a streambuf-to-streambuf copy if the respective
stream modes are set up correctly. So, the argument of
the extraction operator must point to a streambuf into which information
can be written. By default, no stream mode is set for a plain streambuf
object. As there is no constructor for a streambuf accepting an
ios::openmode, we force the ios::out mode by defining an output
buffer using setp(). By doing so we define a buffer, but don't want to use
is, so we make its size 0. Note that this is something different than using
0-argument values with setp(), as this would indicate `no buffering',
which would not alter the default situation. Although any non-0 value could be
used for the empty [begin, begin) range, we decided to define a
(dummy) local char variable in the constructor, and use
[&dummy, &dummy) to define the empty buffer. This effectively makes
capsbuf an output buffer, thus activating the
istream::operator>>(streambuf *)
member. Here is the revised constructor of the class capsbuf:
capsbuf::capsbuf()
:
d_last(' ')
{
char
dummy;
setp(&dummy, &dummy);
}
Now the program can use either
out << cin.rdbuf();
or:
cin >> out.rdbuf();
Actually, the ostream
wrapper isn't really needed here:
cin >> &cb;
would have produced the same results.
It is not clear whether the setp() solution proposed here is actually
a
kludge. After all, shouldn't the ostream wrapper around cb
inform the capsbuf that it should act as a streambuf for doing output
operations?
Exception whose process() member
would behave differently, depending on the kind of exception that was
thrown. Now that we've introduced
polymorphism, we can further develop this
example.
It will now probably be clear that our class Exception should be a virtual
base class, from which special exception handling classes can be derived. It
could even be argued that Exception can be an
abstract base class
declaring only
pure virtual member functions. In the discussion in section
8.3.1 a member function severity() was mentioned which might not
be a proper candidate for a purely abstract member function, but for that
member we can now use the completely general
dynamic_cast<>() operator.
The (abstract) base class Exception is designed as follows:
#ifndef _EXCEPTION_H_
#define _EXCEPTION_H_
#include <iostream>
#include <string>
class Exception
{
friend ostream &operator<<(ostream &str, Exception const &e)
{
return str << e.operator string();
}
public:
virtual ~Exception()
{}
virtual void process() const = 0;
virtual operator string() const
{
return d_reason;
}
protected:
Exception(char const *reason)
:
d_reason(reason)
{}
string
d_reason;
};
#endif
The operator string() member function of course replaces the
toString() member used in section 8.3.1. The friend
operator<<() function is using the (virtual) operator string() member so
that we're able to insert an Exception object into an ostream. Apart
from that, notice the use of a
virtual destructor, doing nothing.
A derived class FatalException: public Exception could now be defined
as follows (using a very basic process() implementation indeed):
#ifndef _FATALEXCEPTION_H_
#define _FATALEXCEPTION_H_
#include "exception.h"
class FatalException: public Exception
{
public:
FatalException(char const *reason)
:
Exception(reason)
{}
void process() const
{
exit(1);
}
};
#endif
The translation of the example at the end of section 8.3.1 to
the current situation can now easily be made (using derived classes
WarningException and MessageException), constructed like FatalException:
#include <iostream>
#include "message.h"
#include "warning.h"
void initialExceptionHandler(Exception const *e)
{
cout << *e << endl; // show the plain-text information
if
(
dynamic_cast<MessageException const *>(e)
||
dynamic_cast<WarningException const *>(e)
)
{
e->process(); // Process a message or a warning
delete e;
}
else
throw; // Pass on other types of Exceptions
}
The fundamental idea behind polymorphism is that the compiler does not know
which function to call
compile-time; the appropriate function will be
selected
run-time. That means that the
address of the
function must be stored somewhere, to be looked up prior to the actual
call. This `somewhere' place must be accessible from the object in
question. E.g., when a Vehicle *vp points to a Truck object, then
vp->getweight() calls a member function of Truck; the address of this
function is determined from the actual object which vp points to.
A common implementation is the following: An object containing virtual member functions holds as its first data member a hidden field, pointing to an array of pointers containing the addresses of the virtual member functions. The hidden data member is usually called the vpointer, the array of virtual member function addresses the vtable. Note that the discussed implementation is compiler-dependent, and is by no means dictated by the C++ ANSI/ISO standard.
The table of addresses of virtual functions is shared by all objects of the class. Multiple classes may even share the same table. The overhead in terms of memory consumption is therefore:
vp->getweight() first inspects the
hidden data member of the object pointed to by vp. In the case of the
vehicle classification system, this data member points to a table of two
addresses: one pointer for the function getweight() and one pointer for
the function setweight(). The actual function which is called is
determined from this table.
The internal organization of the objects having virtual functions is further illustrated in figures figure 16 and figure 17.


As can be seen from figures figure 16 and
figure 17, all objects which use virtual functions must have one
(hidden) data member to address a table of function pointers. The objects of
the classes Vehicle and Auto both address the same table. The class
Truck, however, introduces its own version of getweight(): therefore,
this class needs its own table of function pointers.