具有抽象类继承和C ++构造函数的问题

问题描述 投票:0回答:2
class Base {
public:
    Base(int a) : a_(a) {
        //do something
        someMethod();
        //do something else
    };
protected:
    int a_;
    virtual void someMethod() = 0 {};
};

class Derived : Base {
public:
    Derived() {
        Base::Base(42);
    }
protected:
    void someMethod() override {
        //realisation
    }
};

int main() {
    Derived *obj = new Derived();
    delete obj;
}

此代码由于两个错误而无法正常工作:需要基类的默认构造函数,并且由于使用抽象方法而无法调用带参数的基类的构造函数

我的问题是,当我创建someMethod()的对象时,根本不会调用在class Derived中实现的class Derived。我也不想使用class Base的默认构造函数,但是编译器发誓。

如何纠正我的代码以查看所需的功能?

c++ inheritance constructor virtual abstract
2个回答
0
投票

如何纠正我的代码以查看所需的功能?

  1. Base的构造函数中删除对纯虚函数的调用。

  2. 在替代它的派生类的构造函数中调用someMethod

  3. 在成员启动程序列表中为Base子对象提供初始化程序。如果您不向基础提供初始化程序,它将被默认初始化。


0
投票

为什么这不起作用?

由于对象的构建方式,该设计将无法正常工作。

[构造一个Derived时,第一件事是使用Base构造函数构造了一个Base对象。目前没有Derived对象,因此,如果您要在Base构造函数中调用虚拟函数,则对于Base类而言,该虚拟函数将是有效的,直到您离开Base构造函数的身体。

这是标准允许的,但有限制:

[base.class.init] / 16:可以为正在构造的对象调用成员函数(包括虚拟成员函数)。 (...)但是如果这些操作是在ctor初始化程序(或在ctor初始化器中直接或间接调用的函数)所有用于基类的mem初始化程序都已完成,该程序具有未定义的行为。

这些限制不适用于从构造函数主体调用的虚函数,因为该主体在所有初始化程序之后执行。

但是在您的情况下,对于Base,虚函数是纯虚函数。因此,根据以下条款,它是UB:

[class.abstract] / 6:成员函数可以从抽象类的构造函数(或析构函数)中调用;制作的效果直接调用纯虚拟函数或间接地针对从这样的对象中创建(或销毁)的对象构造函数(或析构函数)为undefined

替代品

不幸的是,除了使用two step initialization之外,没有其他真正的选择,在该方法中,您首先构造对象,然后在使用对象之前调用初始化函数。

这种方法的唯一问题是忘记调用初始化函数的风险。您可以防止这种情况:

  • 使用一个标志来指示是否已初始化,并在所有成员函数中检查该标志(是的,这会增加一些开销)。
  • 如果有抽象类,则可能使用工厂模式(工厂方法或抽象工厂)。然后,您可以让工厂拨打电话。
  • 您可以使用构建器模式,并确保构造器仅对不会忘记初始化的构建器可见。

您的代码的其他问题

您必须小心如何从派生的构造函数中“调用”基本构造函数:

class Derived : Base {
public:
    Derived() : Base(42) // this is the correct place !
    {  
    //Base::Base(42);  //<====  OUCH !!! NO !! This creates a temporary base object !!
    }
    ...
};

您还需要注意纯虚拟(我不知道这是拼写错误还是编译器可以编译您的代码):

virtual void someMethod() = 0;  // if it's abstract, no pending {} !
© www.soinside.com 2019 - 2024. All rights reserved.