derived
has all the data members and member functions that class
base
has, plus more.
base
has a rudimentary member function
print
.
derived
has a bigger and better member function
print
.
announcer.h
base.h
:
has two data membersderived.h
:
has two more data membersmain.C
c++ main.C
cricket.h
metric_cricket.h
:
a child class.kelvin_cricket.h
:
a grandchild class.main1.C
main2.C
:
a pointer and a reference to an objectc++ main1.C
c++ main.C
virtual
our choice of which function to call is held hostage to the data type of
the pointer
p
or the reference
r
.
c++ main1.C
10 20 10 10To achieve late binding, a.k.a. dynamic binding, add they keyword
virtual
to the declaration of the
print
member function of class
base.h
.
//Adequate for class base, can be overridden later. virtual void print() const { cout << i1; }
//If a class has a virtual member function, it must //also have a virtual destructor. virtual ~base() {}You can also add they keyword
override
to the declaration of the
print
member function of class
derived.h
.
//A bigger and better print member function. void print() const override { base::print(); cout << " " << i2; }
10 20 10 20 10 20
base.h
and
derived.h
from the previous example, assuming the keyword
virtual
inserted before base::print
.
c++ main2.C
10
20 30
c++ main3.C
10 20 30 21 31 11
f
points to an object of a different class.
g
is the same example, but with a reference argument instead of a
pointer argument.
c++ main4.C
10 20 30 10 20 30
base
or
derived
object to the function f
in this
main5.C
,
but a
derived
object will be
sliced
down to a
base
when it arrives in f
.
c++ main5.C
10 20
virtual
.
Here’s the workaround that gets the effect of having a
virtual friend
operator<<
.
c++ main.C
10 20 30
vehicle
is
a base class with two derived classes.
c++ main.C
The car goes beep beep! Fun fact: The world's first speeding ticket was issued in 1902! The motorcycle goes vroom vroom! Fun fact: The first motorcycle was invented in 1885!
tax
is a base class with five derived classes.
main1.C
.
c++ main1.C
constructing base ---------------------------------- destructing baseBut something can go wrong in the following
main2.C
.
c++ main2.CIf we constructed an object that was merely a
base
,
everything is okay:
constructing base ---------------------------------- destructing baseBut if we constructed a
derived
object,
i.e., an object that was both a base
and a derived
,
the delete
statement will call the wrong destructor:
constructing base constructing derived ---------------------------------- destructing baseThe problem goes away if the
derived
destructor is virtual:
constructing base constructing derived ---------------------------------- destructing derived destructing base
virtual
.
(Silly example.)
rocket.h
.
The base class,
rocket
,
was written in 1904,
on the eve of Special Relativity.
No one could have predicted that the member function
length
would need to be virtual.
A class can’t have a data member and a member function with the same name,
so I added underscores.
relativistic_rocket.h
and
relativistic_rocket.C
,
the derived class,
has a bigger and better version of the length
function.
main.C
c++ main.C relativistic_rocket.C
velocity == 0.000000e+00, length == 1.000000 velocity == 2.596279e+08, length == 0.500000 velocity == 2.902728e+08, length == 0.250000 velocity == 2.974411e+08, length == 0.125000 Velocity 2.99792e+08 can't be >= the speed of light 2.99792e+08.
As shown above, there may be no way to tell in advance which member functions of the base class should be marked as virtual. But the real difficulty is much worse. There may be no way to tell in advance how the code in the base class should be divided up into member functions. The correct partitioning becomes obvious only when it is too late, after the incorrect design has been engraved in granite.
—Mark Meretzky
The following class
date
can’t tell the difference between leap years and non-leap years.
But it is intended to be the base class for a smarter derived class
that can tell the difference.
It looks like the three big member functions
install
,
operator++
,
operator--
will have to be replaced by smarter versions of themselves in the
derived class,
so these three functions have been declared to be
virtual
.
c++ main.C date.C
2/29/2025 (wrong output)
But we don’t need to write all of
install
,
operator++
,
operator--
in the derived class.
Only one little expression in these three member functions needs to be
rewritten:
length[m]
.
Let’s package this little expression in its own member function,
which will be the only member function of the base class that will need to
be virtual
.
c++ main.C date.C leapdate.C
2/29/2025 (Class date still doesn't know about leap years.) 3/1/2025 (Class leapdate does know.)
date
that knows there was no Year Zero between 1 BC and 1 AD.
date
that knows about the month in which the English speaking world
converted
from the Julian to the Gregorian calendar.
cal 9 1752 September 1752 Su Mo Tu We Th Fr Sa 1 2 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
term.h
.
This header file is written to be legal in both C and C++.term.c
lowercase .c
for the language Cterminal.h
terminal.C
wolf.h
wolf.C
rabbit.h
rabbit.C
main1.C
storm.cis.fordham.edu
.
cc -DUNIX= -c term.c ls -l term.o c++ main1.C wolf.C rabbit.C terminal.C term.o -lcurses ls -l a.out ./a.outPress
h
,
j
,
k
,
l
to move the wolf
.
wolf
and a destructor for class rabbit
.
Here’s what they should do:
terminal
which the dying animal inhabits.
(See wolf::move
in
wolf.C
for an example of how to beep.)
main
function in
main1.C
for an example of how to wait.)
get
member function of the dying animal’s
terminal
to see if the animal’s location on the screen is
occupied by the animal’s character.
If so, call the
put
member function of the animal’s
terminal
to
wipe the animal’s character off the screen
by displaying the
terminal
’s background character there.
(See wolf::move
in
wolf.C
for an example of put
and get
,
and the constructor for class wolf
in
wolf.C
for an example of getting the background character.)
If the dying animal’s location on the screen is not occupied by
the animal’s character,
do not call
put
,
because the location is already occupied by another animal.
Remember,
there is one occasion when two animals are momentarily
at the same place at the same time: right after the
wolf
stomps on the
rabbit.
array.C
:
three ways to loop through an array, a vector
,
or a list
.
main
function in
main1.C
,
replace the
rabbit
object with an array
(or a
vector<rabbit>
)
of three
rabbit
objects (or as many as you want).
For example,
rabbit a[] { {term, 10, 20}, //Please use different numbers, not mine. {term, 30, 40}, {term, 50, 60} }; const size_t n {size(a)}; //need <iostream> for size
vector<rabbit> v { //and #include <vector> {term, 10, 20}, //Please use different numbers, not mine. {term, 30, 40}, {term, 50, 60} };In place of the existing statement that calls the
move
member function of the rabbit
,
write a loop that will
call the move
member function of each
rabbit
in the array
(or in the
vector<rabbit>
).
You can let the game end as soon as any
rabbit
is eaten by the
wolf
.
And now that you have more than one
rabbit
,
change the victory message in
main1.C
to “You killed a rabbit!”.
If you’re totally mystified, you can peek at
main2.C
.
vector
or a list
with an iterator.
You would have to do this if you were inserting or deleting items
while you were looping.
date
with three
int
data members
here
(year
,
month
,
day
),
or with only one int
data member
here
(day
).
Some of the code in these two classes was the same.
To avoid writing the same code twice,
let’s derive two classes
(named
date3
and
date1
)
from a common base class named
date0
that has no data members.
Class
date0
is intended only as a building block for the derived classes.
A
date0
object would be almost totally useless.
date0.h
date0.C
.
Most of the member functions
(prefix operator++
,
print
)
simply output error messages.
It would be premature to attemptto write these member functions here in class
date0
.
Oddly, there is one member function
(operator+=
)
that we can write,
even though we have no data members to work with yet.
main0.C
.
The only thing we can do with a date0
object is to construct it,
apply += 0
to it,
and destruct it.
c++ main0.C date0.C
c++ main2.C date0.C date1.C date3.C
4/10/2025 1/15/2026 4/10/2025 1/15/2026 sizeof (int) == 4 sizeof (date0) == 8 sizeof (date1) == 16 sizeof (date3) == 24A nicer way to declare the two “premature” member functions prefix
operator++
and
print
of class date0
:
//In date0.h. Remove the definitions of these two functions from date0.C. //Class date0 is now an "abstract" base class //becaue it has "pure" virtual functions: virtual void print(ostream& ost) const = 0; virtual date0& operator++() = 0;
date0.h
.
Assume that prefix
operator++
and
print
are now pure virtual functions.
date0.C
.
Assume that prefix
operator++
and
print
are now pure virtual functions.
date1.h
date1.C
date3.h
date3.C
main3.C
g++ main3.C date0.C date1.C date3.C
4/10/2025 4/10/2025How long does a class stay abstract?
wolf
and
rabbit
from a base class
wabbit
.
term.h
term.c
in the language Cterminal.h
terminal.C
wabbit.h
wabbit.C
wolf.h
wolf.C
rabbit.h
rabbit.C
main.C
cc -DUNIX= -c term.c ls -l term.o c++ main.C wabbit.C wolf.C rabbit.C terminal.C term.o -lcurses ls -l a.out ./a.out
cowboy.h
: a base classbank.h
:
another base classcowboybank.h
:
the derived class.main.C
c++ main.C
Gimme a chaw 'a 'baccy. Please take a deposit slip. Time to clear out of town. Put 'em up, pardner! Your account is overdrawn.
virtual
in the mother and father.
window.h
:
the grandparent classwindow_with_horizontal.h
:
the mother classwindow_with_vertical.h
:
the father classwindow_with_horizontal_and_vertical.h
:
the grandchild class.
Its constructor can call the constructor for the grandparent.main.C
c++ main.C
construct window 10 construct window_with_horizontal 10 20 construct window_with_vertical 10 30 construct window_with_horizontal_and_vertical 10 20 30 40 destruct window_with_horizontal_and_vertical 40 destruct window_with_vertical 30 destruct window_with_horizontal 20 destruct window 10If you remove the
virtual
s
from the mother and father,
the grandchild will inherit two copies of the grandparent.
See the diagrams on
page 553
(page 81 of the PDF file).
In this case you must also remove the grandparent’s call to
the constructor for the granschild.
construct window 10 construct window_with_horizontal 10 20 construct window 10 construct window_with_vertical 10 30 construct window_with_horizontal_and_vertical 10 20 30 40 destruct window_with_horizontal_and_vertical 40 destruct window_with_vertical 30 destruct window 10 destruct window_with_horizontal 20 destruct window 10Also consider:
stack
)
with two derived classes.
We will expand class
stack
in two different directions.
stack.h
.
The base class is a stack that stores and retrieves
int
s.
It has two static data members, like the
length
static data member of class
date
.
Its static member function
capacity
receives no invisible pointer.
main1.C
.
Demonstrate class
stack
.
c++ main1.C
stackt.h
.
Class stack
augmented with tracing.
stacke.h
and
stacke.C
.
Class stack
augmented with error checking.
main2.C
.
Demonstrate classes
stack
,
stackt
,
stacke
.
c++ main2.C stacke.C
stackt() 10 push(10) pop returns 10 10 10 20 push(20) pop returns 20 20 20 ~stackt()
stackt
and stacke into two major member functions,
push
and
pop
.
But if we kept this division,
there would be no way to write the member functions of the
grandchild class correctly.
For example,
the following grandchild function
stackte::push
would accidentally call the gransparent function
stack::push
twice.
void stackte::push(int i) //Doesn't work correctly! { stacke::push(i); //Call the mother's push. stackt::push(i); //Call the father's push. }
stack.h
.
The C++ standard library already has a class named
std::stack<>
.stackt.h
:
the mother class has the keyword virtual
stacke.h
:
the father class has the keyword virtual
stacke.C
stackte.h
stackte.C
main.C
c++ main.C
stackt() push(10) pop(10) 10 can't pop stack with size 0
cricket.h
metric_cricket.h
:
a child class.main2.C
:
a pointer and a reference to an objectc++ main2.C
celsius == 22.2222 fahrenheit == 72 fahrenheit == 72 fahrenheit == 72What statements no longer compile if we change the
public
inheritance to
private
inheritance in
metric_cricket.h
?