如何显式初始化没有默认构造函数的成员?

时间:2019-10-19 15:41:27

标签: c++ oop

我已绘制了一个非常简单的图形,以按照下面的描述进行操作,希望对您有所帮助: enter image description here

我的问题: 我需要 classA 中的 classC 对象。问题是 classC 在构造函数中需要两个参数,这些参数只能在 classB 中创建(在 classB 中调用init方法之后,但是对于现在,假设已经足够早地调用了此init方法。此外,我有一个 classD ,它继承自 classB (具有所需的两个参数)。需要 classD 中的 classA 对象。

如前所述,我需要 classA 中的 classC 对象。我不能直接在 classA 中创建它,因为我无法提供必要的参数(在 classB 中创建)。我的想法是在 classB 中创建一个 classC 对象(因为这是用参数初始化该对象的唯一方法(我想)。然后,由于我需要 classD 中的 classA 对象,并且 classD 是从 classB 继承的,所以我认为可以在 classD 中抓取 classC (在 classB 中创建)的对象(由于继承是可能的,不是吗?),将其传递给 classA 作为其构造函数的参数。 (我不希望 classA 从classD继承。)

这是个好主意吗?在我的经验不足的人看来,它看起来很混乱,直到现在,我都无法使其正常工作(也许这是不可能的,或者我只是没有正确地设置所有东西)。但是,对于如何更优雅地解决此问题的任何建议,我都会感到满意。感谢您的帮助。

编辑

感谢所有答案!我试图尽可能简化代码。我得到一个错误,即构造函数必须显式初始化classC的成员(请参阅classA.cpp和classB.cpp注释)。但是初始化classC对象的必要参数是在classB中的init函数调用期间创建的。这是什么问题,即为什么会出现这些错误?我以为,当我将一个类的对象作为另一个类的构造函数的参数传递时,我不需要再次实例化该类吗? 非常感谢您的帮助!

A级

classA.h

#include <classC.h>

classA
{
    public:
        explicit classA(classC classC_object);
        void doSomething();
}

classA.cpp

#include <classA.h>

classA::classA(classC classC_object) // Error: constructor for classC must explicitly initialize the member classC_object which does not have a default constructor
{
}

void classA::doSomething()
{
    classC_object.someMethod();
}

B类

classB.h

classB
{
    public:
        classB(int argc, char** argv);

        void init();

        classC classC_object;
}

classB.cpp

#include <classB.h>

classB::classB(int argc, char** argv) // Error: constructor for classB must explicitly initialize the member classC_object which does not have a default constructor
{
}

void classB::init()
{
    some_type_specificly_created_in_classB arg;
    classC classC_object(arg);
}

C类

classC.h

classC
{
    public:
        classC(const some_type_specificly_created_in_classB& arg);
        // some methods I want to access in classA
        void someMethod();
}

D类

classD.h

#include <classB.h>
#include <classA.h>

classD
{
    public:
        classD(int argc, char** argv);

    private:
        classB classB_object;
        classA* classA_object;
}

classD.cpp

#include <classD.h>
#include <classA.h>

classD::classD(int argc, char** argv) : classB_object(argc, argv)
{
    classA_object = new classA(classB_object.classC_object);
}

1 个答案:

答案 0 :(得分:1)

您的代码与您的描述不匹配(例如,D不继承自代码中的B)。也有许多编译器错误源于草率的语法,例如,类定义中缺少关键字class。不过,我认为这足以作为一个工作起点。

总体而言,我怀疑您的设计存在应解决的弱点,尤其是在classB::init()方面。但是,该问题没有(并且可能应该没有)足够的上下文来确定这一点。因此,我将按照对我来说有意义的顺序进行您的课程。


让我们从classC开始,对我来说,它看起来像是此设置的起点。你所拥有的看起来很合理。该类具有一个公共构造函数,该构造函数带有只能从classB获得的参数,该参数几乎与您的描述匹配。您的描述中确实提到了两个参数,而不只是一个参数,但这可能是微不足道的区别。只需考虑标记构造函数explicit


我要看的下一个课程是classA。您的描述指出需要“ classA 中的 classC 对象”,但是在您的实现中似乎忽略了这一点。另一方面,classA::doSomething引用了一个未定义的变量(classC_object)。我的结论是,classC_object应该是A的成员,类型为classC。因此,我将继续将该字段添加到您的类定义中。

class classA
{
        classC classC_object;
    public:
        explicit classA(classC classC_object) : classC_object(std::move(classC_object)) {}
        void doSomething()
        {
            classC_object.someMethod();
        }
};

您的描述和代码都说classA无法构造classC对象。不过,我要指出的是,如果给定some_type_specificly_created_in_classB对象,它就可以。自然,如果破坏封装,这将不是一个好的选择。


现在我们来到classB,在那里我们面临一个难题。您已经声明了类型为classC的成员,但是直到调用init()才能构造该成员。尚不清楚如何在没有更好的上下文的情况下解决此问题。根据您的描述,我认为构造参数将存储在classB对象中,而不是整个classC对象中。这是一种优化以避免避免多次构造classC对象吗?是否由于其他原因,每个classB对象都需要其classC对象?如果两个答案均为“否”,则您可能希望将classC classC_object替换为some_type_specificly_created_in_classB arg,并添加一个成员函数,该成员函数基于classC构造并返回一个arg对象。

如果init()有理由构造classC对象,则可以这样做。但是,首先,请删除构造名为classC_object local 对象的no-nothing行–这不会影响具有相同名称的成员,实际上,此声明使该成员成为阴影(另请参见Detecting shadowing of member variables)。完成后,您可以将数据成员更改为某种指针。在构造函数中,此指针将设置为null,这意味着您还需要确定在调用classC之前请求init()对象时该怎么做。此时,我将调用init()。 (我不知道这是否合理,因为我不知道为什么不从构造函数中调用init()。它不带参数,因此调用起来似乎很安全。)

#include <memory>

class classB
{
        std::unique_ptr<classC> classC_pointer;  // <-- pointer, and private.

    public:
        classB(int argc, char** argv) : classC_pointer()
        {
             // do something
             // ** Why is init() not part of this constructor? **
        }

        void init()
        {
            some_type_specificly_created_in_classB arg;
            std::make_unique<classC>(arg).swap(classC_pointer); // <-- construct classC object
        }

        const classC & getC()
        {
            if ( !classC_pointer ) // If the pointer is null
                init();
            return *classC_pointer;
        }
};

同样,这里还有其他选择。特别是,我将看看init()的功能是否可以在构造函数中完成。如果可以从初始值设定项列表中调用此功能,则可能可以返回到具有classC成员而不是指针的情况。指针方法可能是更复杂的可能性之一。如果发现自己需要这种指针方法,则可能需要重新评估您的设计。可能会有更好的方法来划分任务。


最后,有classD。您的描述说,这应该继承自classB,我坚持这样做,因为这样可以更轻松地确保按要求的顺序完成初始化。 (基类在成员之前进行了初始化。)除了确保将参数传递给classB外,对您的定义没有太多要做。在样式方面,我将在初始化器列表中为classA_object(更准确地称为classA_pointer)赋予一个值。

class classD : public classB
{
    public:
        classD(int argc, char** argv) : classB(argc, argv),
            classA_object{nullptr}
        {
            init();  // <-- You did say this would be called early enough!
            classA_object = new classA(getC());
        }

    private:
        classA* classA_object;
};

请注意,使classA_object成为指针的唯一原因似乎是可以及时调用init()。如果该功能可以移到classB的构造函数中,那么也许classA_object可以变成classA对象而不是classA指针?实际上,我在getC()中采用的方法无论如何都会允许这样做。

class classD : public classB
{
    public:
        classD(int argc, char** argv) : classB(argc, argv),
            classA_object(getC())
        {}

    private:
        classA classA_object; // <-- no longer a pointer
};