【问题标题】:Base method is being called instead of derive method from constructor [duplicate]正在调用基本方法而不是从构造函数派生方法[重复]
【发布时间】:2014-02-04 17:44:14
【问题描述】:

我期待派生方法 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;
}

【问题讨论】:

    标签: c++ inheritance


    【解决方案1】:

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

    下面说,可以在构造函数/析构函数中调用虚成员函数,但它们充当“非虚函数”,您已经在 C++ 常见问题解答中知道了这一点。

    可以调用成员函数,包括虚函数(10.3) 在建造或销毁期间 (12.6.2)。当一个虚函数 从构造函数或从 析构函数,包括在构造或销毁 类的非静态数据成员,以及调用的对象 apply 是正在构造或销毁的对象(称为 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) 可以在构造过程中使用或 销毁(12.6.2)。在构造函数中使用 typeid 时(包括 非静态的 mem 初始化器或大括号或相等初始化器 数据成员)或在析构函数中,或在调用的函数中使用 (直接或间接)来自构造函数或析构函数,如果 typeid 的操作数是指正在构建的对象或 破坏,typeid 产生 std::type_info 对象,表示 构造函数或析构函数的类。如果 typeid 的操作数是指 正在构造或销毁的对象和静态类型 操作数既不是构造函数或析构函数的类也不是一个 在它的基础中,typeid 的结果是未定义的。

    【讨论】:

      【解决方案2】:

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

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

      完整解释请阅读When my base class's constructor calls a virtual function on its this object, why doesn't my derived class's override of that virtual function get invoked?

      即使可以在A 的构造函数中调用函数B::get()(例如:http://ideone.com/sF3411),这也是未定义的行为,因为当A 的构造函数被执行时,@987654333 @ 指针尚未指向 B 的实例,如果您愿意,它只是“准备中”。链接的代码可能有效,但它是一种 hack,并且会暴露未定义的行为。

      【讨论】:

        猜你喜欢
        • 2011-03-05
        • 2018-11-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-26
        相关资源
        最近更新 更多