问题描述
#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(尽管它确实有效),因为它对于我需要它做的事情来说太慢了。
这似乎很难相信。