I define 2 classes
class BaseA {
public:
virtual void methodA() = 0;
};
class BaseB {
public:
virtual void methodB(int val) = 0;
};
Child inherits 2 Base Class
class Child : public BaseA, public BaseB {
public:
void methodA() override {
printf("Child A\n");
}
void methodB(int val) override {
printf("Child B %d\n", val);
}
};
Then I write following code.
void callBaseB(void *p) {
BaseB *b = (BaseB *) p;
b->methodB(0);
}
int main() {
auto child = new Child;
callBaseB(child);
return 0;
}
console print
Child A
Why this happened? Why not call method B?
(This is what happend when a Java engineer try to write C code)
CodePudding user response:
You should just do this: void callBaseB(BaseB *p) {p->methodB(0);}
.
If you want to keep void *p
as a parameter, you need to cast it to exactly Child *
first. Either:
BaseB *b = (Child *)p;
b->methodB(0);
Or:
Child *b = (Child *)p;
b->methodB(0);
Alternatively, cast to BaseB *
before converting to void *
. Then casting from void *
back to Base *
will work.
What happens here is that the BaseB
subobject is at non-zero offset inside of Child
.
When you convert Child *
to BaseB *
(either explicitly with a cast, or implicitly, either by assigning pointers or by calling a method of BaseB
on the pointer), the compiler automatically offsets the pointer by the required amount, to point to the BaseB
subobject.
The offset is determined entirely at compile-time (unless virtual inheritance is involved), based on the two types.
When you obscure the source type using void *
, the compiler has no way to determine the correct offset, and doesn't even try to apply any offset, so you get weird behavior, because the resulting BaseB *
doesn't point to a BaseB
instance, but rather to some junk inside of Child
.
Even with virtual inheritance, despite the offset being determined at runtime, the calculation depends on the source pointer type being known.
CodePudding user response:
Casting a void*
to a BaseB*
cannot be done (as @PaulSanders said), but you can definitely cast a Child*
to a BaseB*
as follows:
void callBaseB(Child* p)
{
BaseB* b = p;
b->methodB(0);
}
The above code should successfully call methodB()
.
But if you really need to use void*
, you can so something like this:
void callBaseB(void* p)
{
Child* b = (Child*)p;
b->methodB(0);
}