正在调用基方法,而不是从构造函数派生方法

Base method is being called instead of derive method from constructor

本文关键字:方法 构造函数 派生 调用      更新时间:2023-10-16

我原以为派生方法get()将从A的构造函数调用。想知道为什么没有发生这种情况?

方法get()在基类中是虚拟的,因此派生类B将覆盖此方法。

#include <iostream>
using namespace std;
class A {
protected:
virtual int get() {
cout<<" in A::get "<<endl;
return 0;
}
public:
A() {
get();
}
};
class B : public A {
public:
B():A() {}
protected:  
int get() override{
cout<<"in B:get() "<<endl;
return 0;
}
};

int main() {
A *a;  a = new B();
return 0;
}

虽然这是可能的,有时也是一个不错的选择,但大多数时候在构造函数中调用虚拟函数是个坏主意。只有当你真正理解这意味着什么的时候,才能这样做。这同样适用于在析构函数中调用虚拟函数。

当构造类B的实例时,它首先构造一个A。在此阶段,对象仅被标识为A的实例,而不是B的实例。所以在那个时候,对象有一个类为A的虚拟函数表。这意味着每个虚拟函数调用都将解析为类A(或超类)的函数;应用相同的规则,就好像只构造CCD_ 10的实例一样。

要获得完整的解释,请阅读当我的基类的构造函数在其this对象上调用虚拟函数时,为什么我的派生类对该虚拟函数的覆盖不被调用?

即使可以在A的构造函数内调用函数B::get()(例如:http://ideone.com/sF3411),这将是未定义的行为,因为当执行A的构造函数时,this指针还没有指向B的实例,如果您愿意,它只是"准备中"。链接的代码可能会起作用,但这是一次黑客攻击,暴露了未定义的行为。

除了来自leemes的C++FAQ链接,C++标准阻止它被调用,我将引用该标准。

下面说,虚拟成员函数可以在构造函数/析构函数中调用,但它们的作用是"非虚拟的",这在C++常见问题解答中已经知道了。

可以调用成员函数,包括虚拟函数(10.3)在建造或破坏期间(12.6.2)。当虚拟功能直接或间接从构造函数或从销毁器,包括在建造或销毁类的非静态数据成员,以及调用的对象applies是正在构建或销毁的对象(称之为x),调用的函数是构造函数或中的最后一个重写器析构函数的类,而不是在更派生的类中重写它。如果虚拟函数调用使用显式类成员访问(5.2.5),对象表达式是指x的完整对象或该对象的基类子对象之一,但不是x或其基类子对象,行为是未定义的。

它实际上还说,当从一个指针(指向它自己)调用虚拟函数时,该指针的类型不是它自己的直接基类(在多重继承中),行为是未定义的。

示例(来自标准)

struct V {
virtual void f();
virtual void g();
};
struct A : virtual V {
virtual void f();
};
struct B : virtual V {
virtual void g();
B(V*, A*);
};
struct D : A, B {
virtual void f();
virtual void g();
D() : B((A*)this, this) { }
};
B::B(V* v, A* a) {
f(); // calls V::f, not A::f
g(); // calls B::g, not D::g
v->g(); // v is base of B, the call is well-defined, calls B::g
a->f(); // undefined behavior, a’s type not a base of B
}

上述规则适用于其他动态绑定内容,包括typeid,这意味着您不能在基构造函数中使用typeid来区分派生类类型。

typeid运算符(5.2.8)可以在构造或destruction(12.6.2)。当在构造函数中使用typeid时(包括非静态的mem初始值设定项或大括号或等号初始值设定值数据成员)或在析构函数中,或在调用(直接或间接)来自构造函数或析构函数,如果typeid的操作数指的是正在构造或销毁时,typeid产生std::typeinfo对象,表示构造函数或析构函数的类。如果typeid的操作数引用正在建造或毁坏的物体以及操作数既不是构造函数或析构函数的类,也不是在其基中,typeid的结果是未定义的。