Reputation: 671
#include <iostream>
using namespace std;
class dissection {
int x;
public:
void test() {
cout<<"Test base";
}
void caller(){ test(); }
};
class dissectionDerived: public dissection {
int x;
public:
void test(){
cout<< "Test derived";
}
};
int main( int argc, char ** argv ) {
dissectionDerived derived;
derived.caller();
return 0;
}
In the code sample above, the ouput is "Test base". The way I was thought about it was, since derived doesn’t have a function named caller it could invoke the base class function, however since the actual type of the object is dissectionDerived it would be able to call test function of dissectionDerived class. Is this because overload resolution stops after it finds the closest test function in the base class scope?
If the caller function can be invoked from the derived function, why cant it also be a part of the overload resolution in the derived class ?
I have used -cg g++ compiler flag and then did an obj dump of the objectfile, the output is shown below:
SYMBOL TABLE:
0000000000000000 l d .text._ZN10dissection4testEv 0000000000000000 .text._ZN10dissection4testEv
0000000000000000 l d .text._ZN10dissection6callerEv 0000000000000000 .text._ZN10dissection6callerEv
0000000000000000 w F .text._ZN10dissection4testEv 000000000000001d _ZN10dissection4testEv
0000000000000000 *UND* 0000000000000000 __gxx_personality_v0
0000000000000000 *UND* 0000000000000000 _ZSt4cout
0000000000000000 *UND* 0000000000000000 _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
0000000000000000 w F .text._ZN10dissection6callerEv 000000000000001a _ZN10dissection6callerEv
c++filt _ZN10dissection4testEv gives dissection::test()
Thanks!
Upvotes: 1
Views: 108
Reputation: 114481
For historical reasons the default dispatch in C++ is logically wrong i.e. depends on the static type of the reference/pointer and not on the actual type of the object.
With "static type" in C++ what is meant is the type of the reference or of the pointer... i.e. in
const BaseObj& x;
the static type ofx
isBaseObj
.With "dynamic type" it's meant instead the real type of the object instance beging pointed to or referenced and can be either the same as the static type or a type derived from it. For example in
BaseObj *p = new DerivedObj;
the static and dyamic types are different.With non-virtual methods (unfortunately the default) the code called depends on the static type and not on the real type of the instance.
The official reason for this logically wrong default is efficiency so the address to call can be computed at link time and is fixed.
To have the dispatch depending on the type of the object you need to declare the member function to be virtual
.
Note that the virtual
keyword is needed per method but only in the base class as in that case it's assumed also in all derived classes... it's anyway IMO good documentation to repeat it also in derived classes.
Also remember that in C++ it's very important to declare the destructor virtual for classes that are meant to be derived and destroyed polymorphically because even for it otherwise you're going to run into trouble (undefined behavior) if deleting a derived instance using a delete
on a pointer to a base.
Normally in a C++ program either a class is not meant to be derived (then the virtual
keyword is not present and you can shave off a few bytes from each instance) or it's meant to be derived and it's better to declare the destructor and other methods to be virtual
. Having a non-virtual method in a class meant to be derived is sort of an exception and IMO should happen only for proven good reasons.
In the words of C++ original author the non-virtual dispatch by default was just because of classes not meant to be used as bases and because of memory optimization and byte level compatibility with C and Fortran structures.
Upvotes: 1
Reputation: 31647
To print "Test derived", declare dissection::test()
as virtual
:
virtual void test(){ cout<<"Test base"; }
Upvotes: 0
Reputation: 129344
When you want "which actual class is involved" to determine which function gets called, that function needs to be marked virtual
.
This makes the compiler put a "virtual function table" as part of that and any derived class.
In other words, make virtual void test();
in base
. The virtual
attribute will "inherit", so your derived class will also be virtual
- for "safety" you can mark it override
(e.g. void test() override { ... }
, to ensure that the compiler gives an error if you try to "override" something that isn't part of the base-class.
Upvotes: 2