We all know that the base class can't be converted to a derived class, but as the code shows, we did, and we got the result B::foo() and A::fun(), how to interpret this Situation? (Why are B::foo() and A::fun()?)
#pragma
#include "pch.h"
#include<iostream>
using namespace std;
class A
{
public:
void foo()
{
printf("A::foo()\n");
}
virtual void fun()
{
printf("A::fun()\n");
}
};
class B : public A
{
public:
void foo()
{
printf("B::foo()\n");
}
void fun()
{
printf("B::fun()\n");
}
};
int main(void)
{
A a;
B *pb = (B*)&a;
pb->foo();
pb->fun();
return 0;
}
When we create an instance of a base class, its data members are allocated in memory, but of course data members of inherited classes are not allocated. So, downcasting from a base to a derived class is not possible because data members of the inherited class are not allocated.
One reason for this could be that BaseClass is abstract (BaseClasses often are), you want a BaseClass and need a derived type to initiate an instance and the choice of which derived type should be meaningful to the type of implementation.
Explanation: A base class pointer can point to a derived class object, but we can only access base class member or virtual functions using the base class pointer because object slicing happens when a derived class object is assigned to a base class object.
Derived classes are used for augmenting the functionality of base class by adding or modifying the properties and methods to suit the requirements of the specialization necessary for derived class.
This is undefined behavior territory.
You get the same behavior by declaring the B::fun() to be virtual and removing the inheritance from A
.
#include<iostream>
using namespace std;
class A
{
public:
void foo()
{
printf("A::foo()\n");
}
virtual void fun()
{
printf("A::fun()\n");
}
};
class B
{
public:
void foo()
{
printf("B::foo()\n");
}
virtual void fun()
{
printf("B::fun()\n");
}
};
int main(void)
{
A a;
B *pb = (B*)&a;
pb->foo();
pb->fun();
return 0;
}
This is likely due to the behavior of the VTable. Since A has a virtual function, it has __vptr
storing the location of the VTable, where virtual functions can be looked up. When the void fun()
function becomes virtual in B
, it means that it follows the __vptr
to check the VTable for a pointer to the void fun()
function. Since the __vptr
in A
points to the A
VTable, it follows the the pointer to void fun()
will point to A::fun()
.
In other words, you're playing with the how the internal representation of things happen to look and getting lucky based on this internal representation. Similarly pointing a B*
and any other piece of memory could work if the memory happens to contain similar enough information to a B
that the program could work with it. It will however fail if you try to do more complex stuff (such as accessing variables defined in B
).
We all know that the base class can't be converted to a derived class
That's not 100% true. Well, it is true as stated. But then in your example you show us "pointer casts", not "type casts". And these are a bit different. This code is completely valid:
B b;
A* a = (A*)&b;
B* b2 = (B*)a;
The third line is correct, because we know that a
is actually pointing to an instance of B
. This behaviour is sometimes useful (e.g. a dispatcher).
but as the code shows, we did, and we got the result B::foo() and A::fun(), how to interpret this Situation?
Your code, on the other hand has undefined behaviour inside it. You start with object of type A
, you take A*
and you downcast it to B*
. The compiler will allow you to do that because in general, as I said earlier this actually can be a correct code under certain circumstances (and it is hard for a compiler to detect these). But not in your case, in your case the result of this operation is not defined in terms of the C++ standard.
All in all: you should interpret it as an incorrect code that does random and dangerous stuff. And it needs fixing.
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