Virtual functions with different types of arguments

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

I wrote this little program:

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!
five
First!
3

However, if I change int

in the function Second::f()

to a different type, for example:

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 get this as a result:

First!
five
First!
3

Can someone explain to me why this is happening?

+3


source to share


3 answers


When using virtual function dispatch, a so-called "end interceptor" is called. For a function to even override an inherited virtual function, it must meet some criteria:

If a virtual member function is vf

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-list (8.3.5), cv-qualification, and refqualifier (or lack of the same) as and Base::vf

declared then is Derived::vf

also virtual (regardless of declared) and it overrides Base::vf

.

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

Simply put, in the second example, the parameter list for Second::f(double)

is different from the list First::f(int)

, so Second::f(double)

it is not (automatically) virtual and does not override First::f(int)

.



The C ++ 11 keyword override

declares its intent that a method overrides an inherited virtual method so the compiler can tell you when it doesn't. For example, if you did this:

void f(double a) override {

      

The compiler would give you this diagnostic to tell you that it doesn't actually override anything, and even tells you why it doesn't ("type mismatch in the first parameter (" int "or" 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) {
             ^

      

+8


source


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

In your second example, the derived class declares a new non-virtual function (since there is no virtual specification specifier) ​​with the same name as the name of the virtual function in the base class. The new declared function hides the virtual function declaration in the base class.

In this piece of code

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

      



the static type of the pointer p

is First

. Therefore, the compiler looks at the table of virtual functions declared in the First class and finds a pointer to the function declared in the First class. This pointer is not overwritten by the address of the virtual function in the derived class, because the derived class does not override the base class function.

If you write for example

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

      

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

+2


source


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

Now, since all calls are made from First (or the object pointed to by First), it works like this: When you call from the first object, it is obvious that the function in First will be called. Now when you call from the second object pointed to by the pointer to First, then since f () is virtual, the "compiler" [see p. Comment below] looks for it in the actual class (which is the second one), but since there is no function here that matches void f (int) and then takes the value First.

+1


source







All Articles