I l@ve RuBoard |
21.2 |
Class type | Member function type |
|
---|---|---|
Derived | Normal | Derived, then base |
Base | Normal | Base |
Base | Virtual | Derived, then base |
Example 21-2 illustrates the use of virtual functions.
Example 21-2. virt/virt.cpp
// Illustrates the use of virtual functions
#include <iostream>
class base {
public:
void a( ) { std::cout << "base::a called\n"; }
virtual void b( ) { std::cout << "base::b called\n"; }
virtual void c( ) { std::cout << "base::c called\n"; }
};
class derived: public base {
public:
void a( ) { std::cout << "derived::a called\n"; }
void b( ) { std::cout << "derived::b called\n"; }
};
void do_base(base& a_base)
{
std::cout << "Call functions in the base class\n";
a_base.a( );
a_base.b( );
a_base.c( );
}
int main( )
{
derived a_derived;
std::cout << "Calling functions in the derived class\n";
a_derived.a( );
a_derived.b( );
a_derived.c( );
do_base(a_derived);
return (0);
}
The derived class contains three member functions. Two are
self-defined: a and b. The
third, c, is inherited from the base class. When
we call a, C++ looks at the derived class to see
whether that class defines the function. In this case it does, so the
line:
a_derived.a( );
outputs:
derived::a called
When b is called the same thing happens, and we
get:
derived::b called
It doesn't matter whether the base class defines
a and b or not. C++ calls the
derived class and goes no further.
However, the derived class doesn't contain a member
function named c. So when we reach the line:
a_derived.c( );
C++ tries to find c in the derived class and
fails. Then it tries to find the member function in the base class.
In this case it succeeds and we get:
base::c called
Now let's move on to the function
do_base. Because it takes a base class as its
argument, C++ restricts its search for member functions to the base
class. So the line:
a_base.a( );
outputs:
base::a called
But what happens when the member function b is
called? This is a virtual function.
That tells C++ that the search rules are changed. C++ first checks
whether there is a b member function in the
derived class; then C++ checks the base class. In the case of
b, there is a b in the derived
class, so the line:
a_base.b( );
outputs:
derived::b called
The member function c is also a virtual function. Therefore, C++ starts by
looking for the function in the derived class. In this case, the
function is not defined there, so C++ then looks in the base class.
The function is defined there, so we get:
base::c called
Now getting back to our mail. We need a simple base class that
describes the basic mailing functions for each different type of
service:
class mail {
public:
address sender; // Who is sending the mail (return address)?
address receiver; // Who is getting the mail?
// Send the letter
virtual void send_it( ) {
std::cout <<
"Error: send_it not defined in derived class.\n"
exit (8);
};
// Cost of sending a letter in pennies
virtual int cost( ) {
std::cout << "Error: cost not defined in derived class.\n"
exit (8);
};
};
We can define a derived class for each different type of service. For
example:
class post_office: public mail {
public:
void send_it( ) {
put_letter_in_box( );
}
int cost( ) {
return (29);
}
};
Now we can write a routine to send a letter and not have to worry
about the details. All we have to do is call
send_it and let the virtual function do the work.
The mail class is an abstraction that describes a
generalized mailer. To associate a real mailing service, we need to
use it as the base for a derived class. But what happens if the
programmer forgets to put the right member functions in the derived
class? For example:
class federal_express: public mail {
public:
void send_it( ) {
put_letter_in_box( );
}
// Something is missing
};
When we try to find the cost of sending a letter via Federal Express,
C++ will notice that there's no
cost function in
federal_express and call the one in
mail. The cost function in
mail knows that it should never be called, so it
spits out an error message and aborts the program. Getting an error
message is nice, but getting it at compilation rather than during the
run would be better.
C++ allows you to specify virtual
functions that must be overridden in
a derived class. For this example, the
new, improved, abstract mailer is:
class mail {
public:
address sender; // Who is sending the mail (return address)?
address receiver; // Who is getting the mail?
// Send the letter
virtual void send_it( ) = 0;
// Cost of sending a letter in pennies
virtual int cost( ) = 0;
};
The = 0 tells C++ that these member functions are
pure virtual
functions. That is, they can never be called
directly. Any class containing one or more pure virtual functions is
called an abstract class.
If you tried to use an abstract
class as an
ordinary type, such as:
void send_package( ) {
mail a_mailer; // Attempt to use an abstract class
you would get a compile-time error.
I l@ve RuBoard |
No comments:
Post a Comment