正向声明要在基类中使用的派生类会导致缺少默认构造函数错误

forward declaring a derived class to be used in a base class causes a missing default constructor error

本文关键字:错误 构造函数 默认 派生 声明 基类      更新时间:2023-10-16

有一个复杂的程序,不断抛出丢失的默认构造函数错误,经过多次修补,我发现了完全相同的场景,也出现了相同的错误。这个怎么了?

class B;
class A
{
public:
    A() {instance = new B;}
    virtual ~A() {delete instance;}
private:
    A*instance;
};
class B : public A
{
public:
    B(){}
}

不能正向声明要在基类中使用的派生类吗?

如果编译器对类B一无所知,new B如何成功?如果将A类的成员函数实现移到B类定义之下,它应该可以工作:

class A
{
public:
    A();
    virtual ~A();
private:
    A * instance;
};
class B : public A
{
public:
    B(){}
};
A::A()
{
    instance = new B;
}
A::~A()
{
    delete instance;
}

A和B到底是什么?让基类实例化派生类确实有点不寻常。

您需要将A的构造函数的定义(但不是声明)放在A之外,放在B的定义之后。在A中使用B是可以的,只要您不需要它是完整的(完全定义的)-new B肯定需要。

c++关于编译器向前看的规则并不明显(例如,在方法中使用instance是可以的,即使稍后在类中定义了成员,但在同一源文件中使用稍后定义的类是不可以的)。

在这种情况下,问题(正如另一个报告的那样)是,当编译new B时,编译器必须了解更多关于B的信息,而不仅仅是它是一个类,并且它不会一直阅读A类来了解B的真正含义

一种可能的解决方案是稍后放置A构造函数和析构函数的定义(仍然使它们内联):

class B;
class A
{
public:
    A();
    virtual ~A();
private:
    A *instance;
};
class B : public A
{
public:
    B() {}
};
inline A::A() { instance = new B; }
inline A::~A() { delete instance; }

这会编译,但不会正确运行,因为您在这里尝试做的事情确实很混乱。

要创建A的实例,您需要创建B的实例,但BA的特殊化,因此当您创建B的实例时,您还将创建A的实例(BA基本子对象)。

这意味着要创建A的实例,您需要间接创建A的实例。

听起来很古怪,不是吗?

使用此代码实例化A(或B)的实例将导致无限递归(即,在大多数实现中会出现奇怪的崩溃)。

否。因为派生类可能包含其他元素。所以您不能使用它来用派生类对象初始化基类对象。