(tested on msvc2017)
struct AAA
{
virtual float run(int arg)
{
return 5.5f;
}
};
struct BBB : AAA
{
virtual bool run(double arg)
{
return false;
}
};
struct CCC : BBB
{
virtual float run(int arg)
{
return 7.7f;
}
virtual bool run(double arg)
{
return true;
}
};
CCC c;
BBB* pb = &c;
pb->run(5); // call CCC::run(double arg), WHY??
pb->run((int)5); // call CCC::run(double arg), WHY??
Why does pb->run(5)
call only CCC::run(double arg)
, but not CCC::run(int arg)
?
Does the virtual method of a child class with a different signature overlap the interface of the base class?
All is simple.
The class BBB has in fact two virtual functions. One is declared in its base class AAA
struct AAA
{
virtual float run(int arg)
{
return 5.5f;
}
};
And other is declared in the class BBB itself.
struct BBB : AAA
{
virtual bool run(double arg)
{
return false;
}
};
The function declared in the class BBB hides the function declared in the class AAA. (Any name declared in a derived class hides an entity with the same name declared in the base class of the derived class)
In the class CCC the both functions are overriden.
These function calls
pb->run(5); // call CCC::run(double arg), WHY??
pb->run((int)5); // call CCC::run(double arg), WHY??
do not differ because their arguments have the type int
.
The static type of the pointer pb
is BBB *
. So the compiler searches the name run in the class BBB.
Within the class only one function with this name is visible. It is the function declared in the class
virtual bool run(double arg)
{
return false;
}
So the compiler runs this virtual function with this signature but invokes it using the table of virtual function pointers defined for the class CCC because the dynamic type of the pointer pb
is CCC *
.
You could make the function declared in the class AAA visible within the class BBB by means of the using
declaration. For example
struct BBB : AAA
{
using AAA:: run;
virtual bool run(double arg)
{
return false;
}
};
In this case the declaration of the function (declared in the class AAA) would be also a member declaration inside the class BBB. That is the class BBB will have declarations of two overloaded distinct virtual functions.
Here is a demonstrative program
#include <iostream>
struct AAA
{
virtual float run(int arg)
{
return 5.5f;
}
};
struct BBB : AAA
{
using AAA:: run;
virtual bool run(double arg)
{
return false;
}
};
struct CCC : BBB
{
virtual float run(int arg)
{
return 7.7f;
}
virtual bool run(double arg)
{
return true;
}
};
int main()
{
CCC c;
BBB* pb = &c;
std::cout << pb->run(5) << '\n';
std::cout << pb->run(5.6 ) << '\n';
return 0;
}
Its output is
7.7
1
To make the situation with the member declarations in a derived class and in its base class more clear consider a similar situation with block scopes.
Here is a demonstrative program
#include <iostream>
void f( int ) { std::cout << "void f( int )\n"; }
void f( double ) { std::cout << "void f( double )\n"; }
int main()
{
void f( double );
f( 5 );
f( 5.5 );
return 0;
}
The inner declaration of the function f
in the block scope of the function main
hides the other declaration of the function in the global scope.
The program output is
void f( double )
void f( double )
When you do
struct BBB : AAA
{
virtual bool run(double arg)
{
return false;
}
};
run
has a different signature from run
in AAA
. This means that BBB::run(double)
will hide AAA::run(int)
. Since it does, the only run
that you can call from BBB
is bool run(double arg)
. When you do
pb->run(5);
it finds bool BBB::run(double arg)
as that is the only function you can call statically from a BBB
and then virtual dispatch kicks in calling CCC::run(double)
In order to get the int
version of the function to be called, you need to bring the int
version into BBB
. You can do this by writing one, or you could use using AAA::run;
to import it in. Doing either of those will make pb->run(5);
call the int
version of run
from CCC
.
Don't forget, when playing with polymorphism you should declare the top level destructor (AAA
's in this case) to be virtual. This allows you to delete objects correctly when using dynamic allocation. For full details see: When to use virtual destructors?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With