8

I'm trying to understand how virtual functions work, and I am stuck at a certain part.

I have written this small programm:

class First
{
public:
    virtual void f(int a) 
    {
        cout << "First!" << endl;
        cout << a << endl;
    }
};

class Second : public First
{
public:
    void f(int a) {
        cout << "Second!" << endl;
        cout << a << endl;
    }
};

void main() {
    Second s;
    First *p = &s;
    p->f(5);
    First n;
    p = &n;
    p->f(3);
    _getch();
}

This code results in:

Second!
5
First!
3

However, if I change int in the Second::f() function to a different type, like this:

class First
{
public:
    virtual void f(int a) {
        cout << "First!" << endl;
        cout << a << endl;
    }
};

class Second : public First
{
public:
    void f(double a) { //double instead int here!
        cout << "Second!" << endl;
        cout << a << endl;
    }
};

void main() {
    Second s;
    First *p = &s;
    p->f(5);
    First n;
    p = &n;
    p->f(3);
    _getch();
}

My program never calls Second::f(), and I'm getting this as a result:

First!
5
First!
3

Can someone explain to me why this happens?

13
  • 6
    Because that breaks their "connection"? void f(int a) and void f(double a) are two distinct function declarations Commented Apr 26, 2017 at 20:46
  • 1
    You don't override base function since they differ in declaration. Commented Apr 26, 2017 at 20:47
  • 1
    Try p->f(5.0); to see the difference. Commented Apr 26, 2017 at 20:48
  • 2
    @Mudi @UnholySheep: while override is not required it was introduced to help in exactly such cases. If override was used compiler would error that void f(double) doesn't override anything pointing at the problem. I'm surprised both answers (so far) doesn't mention it Commented Apr 26, 2017 at 21:11
  • 2
    And by the way: void main is not legal C++ (it must be int main), using namespace std is a bad habit and _getch(); solves the wrong problem. Commented Apr 26, 2017 at 21:28

3 Answers 3

13

When using virtual function dispatch, the so-called "final overrider" is what gets called. For a function to even override an inherited virtual function, it must meet some criteria:

If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list (8.3.5), cv-qualification, and refqualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides Base::vf.

-- ISO/IEC 14882:2001(E) §10.3 (bold emphasis mine)

Quite simply, in your second example the parameter list for Second::f(double) differs from that of First::f(int), so Second::f(double) is not (automatically) virtual and does not override First::f(int).

The C++11 keyword override declares your intent that a method override an inherited virtual method so that the compiler can tell you when it does not. For example, had you done this instead:

void f(double a) override {

The compiler would have given you this diagnostic to inform you that it doesn't actually override anything, and it even informs you why it doesn't ("type mismatch at 1st parameter ('int' vs 'double')"):

main.cpp:15:18: error: non-virtual member function marked 'override' hides virtual member function
void f(double a) override { //double instead int here!
                 ^
main.cpp:7:14: note: hidden overloaded virtual function 'First::f' declared here: type mismatch at 1st parameter ('int' vs 'double')
virtual void f(int a) {
             ^
Sign up to request clarification or add additional context in comments.

Comments

3

in fact derived classes do not redeclare virtual functions of base classes. They redefine them that in the terms of C++ means to override definitions of virtual functions of base classes.

In your second example the derived class declares a new non-virtual function (because the function specifier virtual is absent) with the same name as the name of the virtual function in the base class. The newly declared function hides the declaration of the virtual function in the base class.

In this code snippet

Second s;
First *p = &s;
p->f(5);

the static type of the pointer p is First. So the compiler looks through the table of virtual functions declared in the class First and finds the pointer to the function declared in the class First. This pointer is not overwritten by the address of a virtual function in the derived class because the derived class did not override the base class function.

If you will write for example

Second s;
Second *p = &s;
^^^^^^
p->f(5);

then the non-virtual function declared in the derived class will be called.

Comments

1

In the second piece of code, Second inherits a virtual function called f() and has its own function called also f().

Now, since all calls are done from First (or an object pointed to by First) it works as follows: When you call from a First object, it's obvious that the function in First will be called. Now, when you call from a Second object pointed to by a pointer to First then since f() is virtual, the "compiler" [please see a comment below] searches for it in the actual class (which is Second) but since there is no function there that matches void f(int) then it takes the one from First.

7 Comments

JVM searcher?! This isn't Java.
Interpreter? C++ is a compiled language
@ChristianHackl That was a typo. Thanks.
"the compiler" is not really correct, either, because virtual functions are usually resolved at runtime (not necessary in this mini example, but in real life, the concrete subclass may not be known until runtime). The compiler inserts a mechanism by which this resolution can take place.
@UnholySheep I wasn't sure which. I was about to check. Thanks.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.