C++虚函数未在子类中调用

Mar*_*arc 7 c++ polymorphism virtual-functions

考虑这种简单的情况:

A.h

class A {
public:
    virtual void a() = 0;
};
Run Code Online (Sandbox Code Playgroud)
B.h

#include <iostream>

class B {
public:
    virtual void b() {std::cout << "b()." << std::endl;};
};
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)

换句话说:
- A是纯虚拟类.
- B是一个没有超类和一个非纯虚函数的类.
- C是A和B的子类,并覆盖A的纯虚函数.

令我惊讶的是第一个输出即

((A*) b)->a();    // Output: b().
Run Code Online (Sandbox Code Playgroud)

虽然我在代码中调用了(),但是调用了b().我的猜测是,它与变量b是指向类B的指针这一事实有关,而B类不是类A的子类.但运行时类型仍然是指向C实例的指针.

从Java的角度来看,解释这个问题的确切C++规则是什么,奇怪的行为?

i_a*_*orf 24

你是无条件地浇铸b到一个A*使用C样式转换.编译器不会阻止你这样做; 你说它是一个A*所以它是一个A*.因此它将它指向的内存视为一个实例A.因为a()Avtable中列出的第一个方法,并且b()是vtable中列出的第一个方法B,当你调用a()一个真正的对象时B,你就得到了b().

你很幸运,对象布局是相似的.这不能保证是这种情况.

首先,你不应该使用C风格的演员表.你应该使用更安全的C++强制转换操作符(虽然你仍然可以用脚射击自己,所以仔细阅读文档).

其次,除非你使用,否则你不应该依赖这种行为dynamic_cast<>.


sth*_*sth 11

在多重继承树中进行投射时,请勿使用C样式转换.如果您使用,dynamic_cast则获得预期结果:

B* b = new C();
dynamic_cast<A*>(b)->a();
Run Code Online (Sandbox Code Playgroud)

  • 它执行运行时类型检查.并且它看到`b`是`C`的一个实例,它继承自`A`. (2认同)

Mar*_*som 5

您将从B*开始并将其转换为A*.由于这两者是无关的,你正在深入研究未定义行为的范围.