Home > front end >  C Why wrong override method get called
C Why wrong override method get called

Time:02-28

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);
}
  •  Tags:  
  • c
  • Related