Evgeny
Evgeny

Reputation: 85

Virtual functions with different argument types

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?

Upvotes: 8

Views: 11683

Answers (3)

cdhowie
cdhowie

Reputation: 168988

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) {
             ^

Upvotes: 13

RoaaGharra
RoaaGharra

Reputation: 710

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.

Upvotes: 1

Vlad from Moscow
Vlad from Moscow

Reputation: 310930

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.

Upvotes: 3

Related Questions