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?
source to share
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 classBase
and in a classDerived
derived directly or indirectly fromBase
, a member functionvf
with the same name, parameter-list (8.3.5), cv-qualification, and refqualifier (or lack of the same) as andBase::vf
declared then isDerived::vf
also virtual (regardless of declared) and it overridesBase::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) {
^
source to share
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.
source to share
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.
source to share