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
的默认构造函数,但是编译器发誓。
如何纠正我的代码以查看所需的功能?
如何纠正我的代码以查看所需的功能?
在Base
的构造函数中删除对纯虚函数的调用。
在替代它的派生类的构造函数中调用someMethod
。
在成员启动程序列表中为Base
子对象提供初始化程序。如果您不向基础提供初始化程序,它将被默认初始化。
由于对象的构建方式,该设计将无法正常工作。
[构造一个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 {} !