Reputation: 81
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;
}
Upvotes: 5
Views: 115
Reputation: 56477
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.
Upvotes: 2
Reputation: 2850
Your example invokes undefined behavior by invalid pointer cast.
A a;
B *pb = (B*)&a;
As you are using a C-style cast, it's hard to see which cast is exactly used, that's why C++ introduced specific casts. static_cast
and reinterpret_cast
both result in UB after any usage of pb
, as variable a
is NOT an instance of B
. Anything that happens from here on now is compilers choice, it can crash, it can work, etc.
As your A
class has a virtual
method, you can use dynamic_cast
, to check at runtime if your pointer points to an instance of B
.
if(B* ptr = dynamic_cast<B*>(&a){
// Use ptr, we know it's not null here
}
dynamic_cast
will return a nullptr
if the cast fails.
Upvotes: 1
Reputation: 301
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
).
Upvotes: 3