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

Problem with abstract class inheritance and constructor at C++

本文关键字:构造函数 问题 继承 抽象类 C++      更新时间:2023-10-16
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;
}

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

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

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

如何更正我的代码以查看我想要的功能?

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

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

  3. 为成员初始器列表中的Base子对象提供初始化器。如果不向基提供初始化器,它将默认初始化。

为什么这行不通?

由于构造方式,此设计将不起作用。

当你构造一个Derived时,首先发生的是Base对象是用Base构造函数构造的。目前没有Derived对象,因此,如果要在Base构造函数中调用虚拟函数,则在离开Base构造函数的主体之前,对Base类有效的将是虚拟函数。

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

[base.class.init]/16:可以为正在构造的对象调用成员函数(包括虚拟成员函数(。(...)然而 如果这些操作是在 CTOR 初始值设定项(或 函数直接或间接从 ctor-初始值设定项调用(之前 基类的所有 MEM 初始值设定项都已完成,程序 具有未定义的行为。

这些限制不适用于从构造函数主体调用的虚函数,因为主体在所有初始值设定项之后执行。

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

[class.abstract]/6:成员函数可以从抽象类的构造函数(或析构函数(调用;使 直接对纯虚函数进行虚拟调用,或 间接地为从这样的对象创建(或销毁( 构造函数(或析构函数(未定义

什么是替代方案

不幸的是,除了使用两步初始化之外,没有其他真正的替代方法,在两步初始化中,您首先构造对象,然后在使用对象之前调用初始化函数。

此方法的唯一问题是忘记调用初始化函数的风险。 您可以保护您免受以下影响:

  • 使用标志来指示是否已发生初始化,并在所有成员函数中检查此标志(是的,它有点开销(。
  • 如果你有一个抽象类,你可以使用工厂模式(工厂方法或抽象工厂(。然后,您可以让工厂进行呼叫。
  • 您可以使用构建器
  • 模式,并确保构造函数仅对不会忘记初始化的构建器可见。

代码的其他问题

您必须小心如何从派生构造函数"调用"基构造函数:

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 {} !