考虑这个简单的情况:
A.h
class A {
public:
virtual void a() = 0;
};
B.h
#include <iostream>
class B {
public:
virtual void b() {std::cout << "b()." << std::endl;};
};
C.h
#include "A.h"
#include "B.h"
class C : public B, public A {
public:
void a() {std::cout << "a() in C." << std::endl;};
};
int main() {
B* b = new C();
((A*) b)->a(); // Output: b().
A* a = new C();
a->a(); // Output:: a() in C.
return 0;
}
换句话说:
- A是一个纯虚类。
- B 是一个没有父类(super class)和一个非纯虚函数的类。
- C 是 A 和 B 的子类,并且覆盖了 A 的纯虚函数。
让我吃惊的是第一个输出即
((A*) b)->a(); // Output: b().
虽然我在代码中调用了a(),但是调用了b()。我的猜测是,这与变量 b 是指向类 B 的指针这一事实有关,它不是类 A 的子类。但运行时类型仍然是指向 C 实例的指针。
从 Java 的角度来看,解释这个奇怪行为的确切 C++ 规则是什么?
最佳答案
您正在无条件转换 b
到A*
使用 C 风格的转换。编译器不会阻止你这样做;你说这是一个A*
所以这是一个 A*
.所以它将它指向的内存视为 A
的一个实例。 .自 a()
是 A
中列出的第一个方法的 vtable 和 b()
是 B
中列出的第一个方法的 vtable,当你调用 a()
时在一个真正是 B
的对象上, 你得到 b()
.
你很幸运,对象布局是相似的。不能保证一定是这种情况。
首先,您不应该使用 C 风格的转换。你应该使用 C++ casting operators哪个更安全(尽管您仍然可以搬起石头砸自己的脚,所以请仔细阅读文档)。
其次,你不应该依赖这种行为,除非你使用 dynamic_cast<>
.
关于子类中未调用 C++ 虚函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2105282/