Search code examples
c++pointersinheritancevirtual

Why doesn't a pointer to derived and a pointer to base point to the same address if abstract classes are involved?


I came across a problem which can be reduced to the following example:

#include "iostream"

struct A {
    char a;
};
struct B : A {
    virtual void f() = 0;
};
struct C : B {
    void f() override {}
};

void f(A* fpa) {
    std::cout << fpa << '\n' << reinterpret_cast<C*>(fpa) << std::endl;
}

int main() {
    C c {};
    A* pa {&c};
    std::cout << &c << '\n' << pa << '\n' << reinterpret_cast<C*>(pa) << std::endl;
    f(&c);
}

Neither pa nor fpa keep pointing to the address of c, although both are being initialized with &c. All addresses printed after that of &c directly are offset by +8 (tested with g++ and clang++). Removing either A::a or B::f() and C::f() or initializing pa and fpa with reinterpret_cast<A*>(&c) instead of just &c fixes the addresses.

But why do I have to do that? Shouldn't any A* be able to hold the address to any A, B, or C in this case since all inheritance is public? Why does the value change implicitly? And are there warning flags I can pass to g++ or clang++ that warn about this kind of behavior?


Solution

  • or initializing pa and fpa with reinterpret_cast<A*>(&c) instead of just &c fixes the addresses.

    That doesn't "fix" the address. That breaks the address. It yields an invalid pointer.

    But why do I have to do that?

    You don't have to do that. The offset address is the correct address of the base sub object.

    Why doesn't a pointer to derived and a pointer to base point to the same address if abstract classes are involved?

    Because there is something stored in the object before the base sub object.

    Shouldn't any A* be able to hold the address to any A, B, or C

    No. The address of a valid pointer to A is always the address of an A object. If the dynamic type is derived, then that A object is a base sub object. The base can be stored at an offset from the beginning of the derived class.

    since all inheritance is public

    Accessibility of the Inheritance is irrelevant in this regard.

    And are there warning flags I can pass to g++ or clang++ that warn about this kind of behavior?

    I highly doubt that there would be. I also don't see why you'd want a warning in such case.