Register for EarthWeb's Million Dollar Sweepstakes!
home account info subscribe login search My ITKnowledge FAQ/help site map contact us


 
Brief Full
 Advanced
      Search
 Search Tips
To access the contents, click the chapter and section titles.

C++ in Plain English
(Publisher: IDG Books Worldwide, Inc.)
Author(s): Brian Overland
ISBN: 1558284729
Publication Date: 04/01/96

Bookmark It

Search this book:
 
Previous Table of Contents Next


Chapter 9
Virtual Functions and Why They're Good

Here's one of the best ways to understand object orientation: imagine a set of independent objects that communicate to each other by sending messages. The C++ virtual-function/ capability fits neatly into this picture. Virtual functions enable different kinds of objects to respond to the same message (actually, a call to a member function) in different ways. Different objects are like different kinds of printers. Each is sent the same message: “Print this page.” But the two printers—one a dot-matrix printer and the other a laser printer—respond with different behavior.

None of this is a dramatic break from earlier concepts in this book. Virtual functions are vaguely similar to function overloading, but there is a difference: with overloading, the exact behavior of a name or operator is determined at compile time. With virtual functions, the exact behavior is determined at run time. Overloading is a nice convenience, but virtual functions extend the flexibility of what an object can do.

When you're starting out with objects and C++ programming, it's not entirely clear what virtual function are or why you'd use them. After explaining syntax and usage, I'll spend much of the chapter on a simple menu command example. Through this example, I hope to show you how virtual functions can make your life easier by strengthening the independence and self-reliance of objects. In simple terms, it's a way of decentralizing program control.

What does the virtual in virtual function mean, anyway? In computer terms—such as virtual memory and virtual reality—virtual usually describes something that isn't quite real but works as if it were. In the case of functions, virtual describes a function call that, at run time, could refer to any number of different function calls. It is not a “real” function call in that it doesn't correspond to a specific code address. In some ways, the name is misleading, because virtual functions are entirely real. They are simply called through indirect function calls rather than standard function calls.

Applying the virtual Keyword

To make a member function into a virtual function, precede the function declaration with the virtual keyword. The keyword must appear at the beginning of the declaration, before the return-value type.

virtual return_type name(arguments);

As a result, the function is declared virtual, meaning that it is late bound. In late binding, the address of the function does not have to be determined until run time. (I devote most of the rest of the chapter to explaining when this is useful.)

Once the virtual keyword is used in the function declaration, you don't need to use it again either in the function definition, in derived class declarations, or in derived class function definitions. The function remains virtual in all derived-class declarations using the same name and type.

For example, suppose you have a class CTimePiece that has a ShowTime function. This function is declared virtual.

class CTimePiece {
public:
   virtual void ShowTime(void);
   ...
};

The function definition for ShowTime does not need to include the virtual keyword.

#include <iostream.h>

void CTimePiece::ShowTime(void) {
   cout << hours << “:” << minutes;
};

In any classes derived, directly or indirectly, from CTimePiece, the ShowTime function is virtual—including the following class, which overrides the ShowTime function.

class CClock : public CTimePiece {
public:
   void ShowTime(void); // Override function
   ...
};
void CClock::ShowTime(void) {
   paint_clock_face(hours, minutes);
}

There is no requirement that a virtual function be overridden by derived classes or that it be called through a pointer. A virtual function can be called in the same ways as any other member function is called. There is no difference in the syntax, either in declaration or in usage. (This is to say that the syntax in the C++ source code looks the same as for normal member functions, even though there are differences under the covers.)

CTimePiece time;
CClock clock;
time.ShowTime();
clock.ShowTime();

Virtual functions do have a few minor restrictions. Inline functions cannot be virtual, for example.

The effect of making a function virtual most often shows up when you access objects through base-class pointers. (You might want to review the last section in Chapter 8 “Inheritance: C++ and Good Genes” if you haven't read it.) With the ShowTime function, you could use the same line of code to call different function implementations. In the following example, assume that CClock and CDigital are both derived classes of CTimePiece.

CTimePiece *pTime;

pTime = new CClock;
pTime->ShowTime(); // Calls CClock::ShowTime

pTime = new CDigital;
pTime->ShowTime(); // Calls CDigital::ShowTime

When Should You Make a Function Virtual?

As I mentioned in the previous section, there is no difference in syntax between virtual and nonvirtual functions except for the one-time use of virtual in a declaration. This is true even though C++ handles them differently. This fact makes it easy to introduce virtual functions into existing C++ projects.

But should you make all your functions virtual just because it's easy to do so? Except for inline functions, you could make almost all your functions virtual simply by putting the keyword everywhere the functions are first declared. But not all functions should be virtual functions.

If there's no chance that a function will ever be overridden by a derived class, then it should not be declared virtual. If you write a class that you never intend to derive other classes from, there is no point in making any function virtual. Every time you call a virtual function, you pay a slight performance penalty. Each virtual function also takes up slightly more space in program memory than its nonvirtual counterpart would. The point is that there's no reason to make a function virtual unless doing so might make a difference.

But if you write a function in a base class and it is clear that the function is intended to be overridden in derived classes, then it is wise to declare the function virtual. You can override a function in a derived class even though it is not virtual, but then in certain situations the wrong function might be called.

In the example at the end of the last chapter, the following code calls the function CStr::cpy (the base-class version) even though it looks as if it should call CPoStr::cpy (the derived-class version).

CIoStr prstr;
CStr pStr = &prstr;
pStr->cpy(“Initialize me.”);

But if the cpy function were originally declared as virtual, then this code would call CIoStr::cpy as expected.

class CStr {
   ...
   virtual void cpy(char *s);
...


Previous Table of Contents Next
[an error occurred while processing this directive]

Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.