将指针转换为不同的指针会导致调用错误的虚函数

问题描述

#include <iostream>

struct A {
    virtual void a() {
        puts("A");
    }
};

struct B {
    virtual void b() {
        puts("B");
    }
};

struct C {
    virtual void c() {
        puts("C");
    }
};

struct D : public A,public B,public C {
    virtual void c() {
        C::c();
        puts("cd");
    }
};

int main() {
    A* obj = new D;

    obj->a();

    B* b = (B*)obj;
    b->b();
    C* c = (C*)obj;
    c->c();

    return 0;
}

我有这段代码我有非虚拟多重继承。但是,当我调用函数中的函数时,它似乎调用错误的虚函数。 而不是输出

A
B
C
cd

输出

A
A
A

令我困惑的是,当我将代码更改为这样做时:

B* b = (B*)(D*)obj;
b->b();
C* c = (C*)(D*)obj;
c->c();

输出我所期望的(见上文)。 Afaik 像这样进行双指针转换不会产生任何影响,并且会被编译器优化掉。但它似乎正在改变正在调用的虚函数

有人可以解释为什么这会改变正在调用的虚函数吗?

注意事项:

我在每一步都打印了指针,它们是一样的。

我想避免使用 dynamic_cast(虽然它确实有效),因为它对于我需要它做的事情来说太慢了。

解决方法

有人可以解释为什么这会改变正在调用的虚函数吗?

通常,指针类型之间的 C 样式转换不会改变指针的值,因此不会产生任何影响。但是,有一个例外。

一个类和一个父类或子类之间的转换可以改变指针的值。例如:

class A
{ int a; };

class B
{ int b; };

class C : public A,public B
...

现在,指向类 A 实例的指针可能与指向其 a 成员的指针具有相同的值,而指向类 B 实例的指针可能具有相同的值与指向其 b 成员的指针相同的值。指向类 C 实例的指针不能与指向其 A::a 及其 B::b 成员的指针具有相同的值,因为它们是不同的对象。

期望 B* 的函数可以传递 C*,因为 C B。同样,出于同样的原因,可以向需要 A* 的函数传递 C*。但至少其中之一需要更改指针的值。

所以这些类型之间的转换改变值,其他的都是空操作。

当然,这一切都是UB。您在不相关的类型之间进行转换,然后取消引用它们。

我想避免使用 dynamic_cast(尽管它确实有效),因为它对于我需要它做的事情来说太慢了。

这似乎很难相信。