Aussie AI
Avoid unnecessary virtual function calls
-
Book Excerpt from "Generative AI in C++"
-
by David Spuler, Ph.D.
Avoid unnecessary virtual function calls
The use of virtual
functions, when they are not needed, is obviously inefficient.
virtual
functions are needed only when dealing with pointers or references to objects
of unknown type. If the program never uses pointers or references to objects, or if it does
not have any derived classes, no function needs to be virtual
and the use of virtual
wastes space. In addition, because virtual
functions relate only to the use of derived
classes, declaring any functions as virtual
in a class that has no derived classes is also
unnecessarily inefficient.
One common situation where virtual
may appear necessary, but need not be,
occurs with redefining a member function in a derived class. This does not necessarily
mean that the function must be defined as virtual
in the base class (nor in the derived
class — the virtual
keyword is never needed in the derived class). Of course, if the
program starts using pointers or references to these classes, the functions may need to be
virtual
, in which case it may be better style to declare the member function as
virtual
.
A call to a virtual
function need not always be a “real” virtual
call. For
example, passing an object by reference (either as a reference or as a pointer type) can
occur when changing functions to pass-by-reference for efficiency improvement. Any
calls to virtual
functions inside that (not necessarily virtual
) function will be such
that the compiler cannot know that an ordinary function call to the member function
would suffice. It does not perform any global analysis to determine that all arguments to
the function are base objects, and not derived objects. For example, in the following
code, it isn’t clear that the call to the (virtual
) print
function could be replaced by
an ordinary call:
void print_base_object( Base & object) { object.print(); }
The overhead of virtual function calls can be removed whenever the programmer can
be sure that only one type of pointer/reference to an object is being used. In particular,
whenever a programmer can be sure that a pointer/reference to a base class object points
to a particular object, the qualified member function name can be used. For example,
the virtual
call uses:
p->print();
And the more efficient code that avoids a virtual
function call is:
p->Base::print();
An example of extra information making this change possible occurs when a program
uses a number of different (homogeneous) linked lists, with each linked list containing
the same type of object (one with base objects, one with derived objects). When implementing a print_list
function to print out a linked list, you can write it generally to
call a virtual
-declared print_object
function:
void LinkedList::print_list() { for (Base *temp = head; temp != NULL; temp = temp->next()) temp->print_object(); }
This means that each call to print_object
has the run-time overhead of a virtual
function call. A more efficient alternative is to make use of the knowledge that each list
must contain the same type of object, and have two different print_list
functions
(i.e. use a virtual
function to do the dirty work of printing the objects).
void Base::print_list_hidden() { for (Base *temp = this; temp != NULL; temp = temp->next()) temp->Base::print_object(); } void Derived::print_list_hidden() { for (Derived *temp = this; temp != NULL; temp = (Derived*)temp->next()) temp->Derived::print_object(); } void LinkedList::print_list() { if (head != NULL) head->print_list_hidden(); // call virtual function }
With this approach, all of the lower-level calls to print_object
can be bound at compile-time and the
only virtual
call is the call to print_list_hidden
at the very top. Hence, by using our knowledge
about the linked lists, we have reduced the number of run-time virtual
function
calls.
• Next: • Up: Table of Contents |
The new AI programming book by Aussie AI co-founders:
Get your copy from Amazon: Generative AI in C++ |