如果涉及抽象 类,为什么指向派生的指针和指向基的指针不指向同一地址?

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

我遇到了一个可以简化为以下示例的问题:

#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);
}

pafpa 都没有继续指向 c 的地址,尽管它们都被 &c 初始化。在 &c 之后打印的所有地址都直接偏移 +8(用 g++ 和 clang++ 测试)。删除 A::a 或 B::f() 和 C::f() 或使用 reinterpret_cast<A*>(&c) 而不是 &c 初始化 pafpa修复地址。

但为什么我必须这样做?在这种情况下,任何 A* 不应该能够保存任何 ABC 的地址,因为所有继承都是 public 吗?为什么值会隐式改变?是否有我可以传递给 g++ 或 clang++ 的警告标志来警告这种行为?

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

那不是 "fix" 地址。 破坏了地址。它产生一个无效的指针。

But why do I have to do that?

您不必那样做。偏移地址是基础子对象的正确地址。

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

因为在基础子对象之前的对象中存储了一些东西。

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

没有。指向 A 的有效指针的地址始终是 A 对象的地址。如果动态类型是派生的,则该 A 对象是基础子对象。基数可以存储在距派生 class.

开头的偏移处

since all inheritance is public

继承的可访问性与此无关。

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

我非常怀疑会有。我也不明白你为什么要在这种情况下发出警告。