Search

12.2 — Virtual functions and polymorphism

In the previous lesson on pointers and references to the base class of derived objects, we took a look at a number of examples where using pointers or references to a base class had the potential to simplify code. However, in every case, we ran up against the problem that the base pointer or reference was only able to call the base version of a function, not a derived version.

Here’s a simple example of this behavior:

This example prints the result:

rBase is a Base

Because rBase is a Base reference, it calls Base::getName(), even though it’s actually referencing the Base portion of a Derived object.

In this lesson, we will show how to address this issue using virtual functions.

Virtual functions and polymorphism

A virtual function is a special type of function that, when called, resolves to the most-derived version of the function that exists between the base and derived class. This capability is known as polymorphism. A derived function is considered match if it has the same signature (name, parameter types, and whether it is const) and return type as the base version of the function. Such functions are called overrides.

To make a function virtual, simply place the “virtual” keyword before the function declaration.

Here’s the above example with a virtual function:

This example prints the result:

rBase is a Derived

Because rBase is a reference to the Base portion of a Derived object, when rBase.getName() is evaluated, it would normally resolve to Base::getName(). However, Base::getName() is virtual, which tells the program to go look and see if there are any more-derived versions of the function available between Base and Derived. In this case, it will resolve to Derived::getName()!

Let’s take a look at a slightly more complex example:

What do you think this program will output?

Let’s look at how this works. First, we instantiate a C class object. rBase is an A reference, which we set to reference the A portion of the C object. Finally, we call rBase.getName(). rBase.GetName() evaluates to A::getName(). However, A::getName() is virtual, so the compiler will call the most-derived match between A and C. In this case, that is C::getName(). Note that it will not call D::getName(), because our original object was a C, not a D, so only functions between A and C are considered.

As a result, our program outputs:

rBase is a C

A more complex example

Let’s take another look at the Animal example we were working with in the previous lesson. Here’s the original class, along with some test code:

This prints:

Fred says ???
Garbo says ???

Here’s the equivalent class with the speak() function made virtual:

This program produces the result:

Fred says Meow
Garbo says Woof

It works!

When animal.speak() is evaluated, the program notes that Animal::speak() is a virtual function. In the case where animal is referencing the Animal portion of a Cat object, the program looks at all the classes between Animal and Cat to see if it can find a more derived function. In that case, it finds Cat::speak(). In the case where animal references the Animal portion of a Dog object, the program resolves the function call to Dog::speak().

Note that we didn’t make Animal::GetName() virtual. This is because GetName() is never overridden in any of the derived classes, therefore there is no need.

Similarly, the following array example now works as expected:

Which produces the result:

Fred says Meow
Garbo says Woof
Pooky says Woof
Truffle says Woof
Tyson says Meow
Zeke says Meow

Even though these two examples only use Cat and Dog, any other classes we derive from Animal would also work with our report() function and animal array without further modification! This is perhaps the biggest benefit of virtual functions -- the ability to structure your code in such a way that newly derived classes will automatically work with the old code without modification!

A word of warning: the signature of the derived class function must exactly match the signature of the base class virtual function in order for the derived class function to be used. If the derived class function has different parameter types, the program will likely still compile fine, but the virtual function will not resolve as intended.

Use of the virtual keyword

Only the most base class function needs to be tagged as virtual for all of the derived functions to work virtually. However, having the keyword virtual on the derived functions does not hurt, and it serves as a useful reminder that the function is a virtual function rather than a normal one. Consequently, it’s generally a good idea to use the virtual keyword for virtualized functions in derived classes even though it’s not strictly necessary.

Return types of virtual functions

Under normal circumstances, the return type of a virtual function and its override must match. Consider the following example:

In this case, Derived::getValue() is not considered a matching override for Base::getValue() (it is considered a completely separate function).

Do not call virtual functions from constructors or destructors

Here’s another gotcha that often catches unsuspecting new programmers. You should not call virtual functions from constructors or destructors. Why?

Remember that when a Derived class is created, the Base portion is constructed first. If you were to call a virtual function from the Base constructor, and Derived portion of the class hadn’t even been created yet, it would be unable to call the Derived version of the function because there’s no Derived object for the Derived function to work on. In C++, it will call the Base version instead.

A similar issue exists for destructors. If you call a virtual function in a Base class destructor, it will always resolve to the Base class version of the function, because the Derived portion of the class will already have been destroyed.

Rule: Never call virtual functions from constructors or destructors

The downside of virtual functions

Since most of the time you’ll want your functions to be virtual, why not just make all functions virtual? The answer is because it’s inefficient -- resolving a virtual function call takes longer than a resolving a regular one. Furthermore, the compiler also has to allocate an extra pointer for each class object that has one or more virtual functions. We’ll talk about this more in future lessons in this chapter.

Quiz time

1) What do the following programs print? This exercise is meant to be done by inspection, not by compiling the examples with your compiler.

1a)

Show Solution

1b)

Show Solution

1c)

Show Solution

1d)

Show Solution

1e)

Show Solution

1f)

Show Solution

12.2a -- The override and final specifiers, and covariant return types
Index
12.1 -- Pointers and references to the base class of derived objects

39 comments to 12.2 — Virtual functions and polymorphism

  • Ben

    Hi Alex,
    in your all your classes, whether they are derived, or base classes, you put the virtual keyword in front of the function definition of every function which shall be overwritten in the derived class, even though this is not necessary in the derived class - as long there is no class, which inherits from that, which shall overwrite the function again. It would be clearing, to point out, that it is not necessary.
    Ben

  • manju23reddy

    hi the below code gets core dump y and how to solve it so that i should set base object with derived pointer in get function only and use rBase in main to call a function of those classes.

    • ray

  • Hallo all!

    Urm, this is going to sound really stupid, so I’m sorry for my ignorance.

    But why don’t we just use a pointer type of the derived class? It seems that the main reason for virtual functions is that we are using a pointer from the base class instead of the derived.

    EDIT;

    Ah! wait a min!! So say you have a pointer of the animal base type, you might want to point that to the derived classes of say Dolphin, Elephant, Human etc…. so rather than creating a pointer for each derived you just create one base class pointer and use that with virtual functions. right?

    • Alex

      Two primary reasons:
      1) You can write functions that take a pointer (or reference) to the base class object type and then pass in ANY type of object derived from that type, and as long as the functions are properly virtualized, they will resolve correctly. This means we can have write one function that works with multiple classes instead of one function for each derived class type.
      2) We can have arrays of pointers (or references) to the base class object type and put ANY type of object derived from that type in the array. Same thing as above here: one array that can hold any type of derived object rather than having to have multiple different arrays, one for each type.

  • Lavanya

    hi all,

    I was looking for a solution to the below problem. Can someone please help?

    I’m given this statement Base * b = new Derived;
    just this line.
    I know that Both base and derived classes have a virtual (overidden in derived) function called Fun().

    Given only the above line, how do i invoke base’s Fun(), instead of Derived’s Fun().

    Would appreciate any responses.

  • Rami

    is there a performance penalty by using virtual functions? If not then all functions should be virtual functions, no harm right?! =D

    • Alex

      Yes! There is a penalty for using virtual functions. I talk about this more in future lessons, but in short:
      * Any class that has a virtual function has an extra (hidden) pointer per object, pointing to the virtual table
      * All function calls to virtual functions get routed through the virtual table, which adds a level of indirection. This makes a call to a virtual function slightly slower than a normal function, although this is probably insignificant unless you’re calling many virtual functions in performance sensitive code.

  • Steve

    I’m sorry but you lost me here.

    Why do u p.e. use “virtual const char* GetName() { return “A”; }”?

    Why are u using a pointer to a const … as return value? I have run the example with a normal pointer as return, and it ran the same?

    Thx

    • Steve

      Never mind, I figured it out.

      The solution was if there was no pointer to a const (const char*), the user would be able to do this:

      rBase.GetName()[0] = ‘a’;

      which would cause an access violation exception at runtime.

  • Mystic

    Use of the virtual keyword

    Technically, the virtual keyword is not needed in derived class. For example:

    prints

    rBase is a Derived

    What would happen if it was changed to include something like:

    ? Then wouldn’t it say “rDerived is a ‘Derived'” instead of “rDerived is a ‘DerivedTwice”? That’s not what you’d want… You might want to say something to that effect (if that actually happens, I haven’t checked my code).

    • D.M. Ryan

      The odd thing is, when I tried your code I got “rDerived is a DerivedTwice”. [I compiled it using Visual Studio 2005.] I thought a second virtual keyword would be needed to get that output, but it isn’t. For VS2005, at least, it’s only necessary for the virtual keyword to be at the base; virtual carries through all the way down the inheritance chin.

      To be specific, this code:

      outputs the “rDerived is a DerivedTwice” being looked for, when compiled with VS’05. Other compilers might require the second keyword.

  • Ravi Gautam

    Hi,

    why are we using &rBase instead of rBase?

    I have read the chapter on reference but still can’t figure it out.

    Please do reply.

  • Reem Mahmoud

    simply, this is a great tutorial (y)

  • Sri

    Superb and in-depth tutorial for C++ beginners. Thanks mate!

  • cesare

    Hi Alex,
    just to be sure I understood what ‘virtual’ do in context of virtual functions, is it right if I say:

    "When designing a class that is supposed to be used as a base for other classes, ‘virtual’ keyword is used to specify which
    function should be overridden to customize the behavior of derived classes".

  • Kiran C K

    Also, can’t we use something like the one below:

    I hope this is an alternate solution for virtual pointers. Are there any downsides for such statements?

  • Pisces

    Hello,

    Can you briefly explain why you return const char* in GetName()? It this the most efficient return? Can you return this name as a char[] or std::string also?

    • Alex

      Since the string literal we’re returning has type const char*, it makes sense for the function to return an object of type const char*.

      char[] won’t work as a return type (only as a function parameter). You could return a std::string instead, and C++ would convert the C-style string literal to a std::string before returning it, but this is a needless conversion. It’s better to return the literal as the type it is and let the caller of the function convert it to a std::string if that’s what they desire.

  • Suresh

    Hi Alex, what if A is the base class, B is the class derived from A, and C is the class derived from B. what if base class [A]’s function is not defined as a virtual function, but B’s function[with same signature] is derived as virtual function, same C’s function[with same signature] is derived as virtual funtcion. then using base class pointer[A*] if we call the function, what will happen?

  • Naveen

    Above code gives error. what is the problem with string n("")
    if changed to string n="", error is resolved.
    Thank you

  • Pip

    Alex, I have a quick question about the use of an array of pointers. For an example above you employed:

    Am I correct in thinking that, while the pointers (of the array) themselves are held sequentially in memory, the objects they point to are not. So while (apcAnimals+1) = &cGarbo, &cFred and &cGarbo are not x bytes apart in memory where x is the size of the class Cat? (My thoughts come from re-reading 6.8a — Pointer arithmetic and array indexing).

    The Last few chapters have been extremely interesting.

    • Alex

      Yes, you are correct. You can assume the elements of apcAnimals are in sequential memory (arrays guarantee this), but you can not assume anything else. The layout of variables in stack memory is not guaranteed.

  • Anonymous

    Hi,

    I have two questions. This first one goes like so …

    I wrote this sample code, according to my understanding, since the function "fn" in "Class A" is private, it won’t be inherited (into Class B and Class C), and the non-virtual function "fn" in Class B will be inherited by Class C, which overloads "fn".

    When I call "fn" (from main) using the reference or pointer of "Class B", (based on my understanding) I should have got the output as "B" (as fn in Class B is not virtual, it should call function "fn" of the type variable "p" belongs to).

    But the output is "C".

    May I know why or how I’m getting the output as "C"?

    ………………………………….

    Now for my next question…

    In the above snippet both the statements gives me an error. May I know why?

    PS: Please excuse me if there are any grammatical (or other) mistakes.

    Thanks in advance.

    • Alex

      1) A function that overrides a virtual function is considered virtual whether it’s declared to be so or not. The access specifiers don’t matter, they just restrict who can call what.
      2a) A &p = c is an error because B inherits A privately. If you change B so it inherits A publicly, you will see that the line compiles.
      2b) p.fn() can’t be executed because fn() is private in A.

  • G Nabi

    interesting topic.

  • Gabriel

    Hello Alex. Before anything, thanks for this wonderful c++ course.

    In exercise 1e, you say: "Because B::getName() and c::getName() are not const, they are not considered overrides…". But they ARE const. Am I getting something wrong?

    Again, thanks for your time to take care of this course.

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter