如何在构造函数中初始化 C++ 对象成员变量?

Posted

技术标签:

【中文标题】如何在构造函数中初始化 C++ 对象成员变量?【英文标题】:How can I initialize C++ object member variables in the constructor? 【发布时间】:2012-10-17 04:33:36 【问题描述】:

我有一个有几个对象作为成员变量的类。我不希望在声明时调用这些成员的构造函数,所以我试图明确地挂在指向对象的指针上。我不知道我在做什么。

我想也许我可以做以下事情,在初始化对象成员变量时立即调用构造函数:

class MyClass 
    public:
        MyClass(int n);
    private:
        AnotherClass another(100); // Construct AnotherClass right away!
;

但我希望MyClass 构造函数调用AnotherClass 构造函数。这是我的代码的样子:

文件 BigMommaClass.h

#include "ThingOne.h"
#include "ThingTwo.h"

class BigMommaClass 

        public:
                BigMommaClass(int numba1, int numba2);

        private:
                ThingOne* ThingOne;
                ThingTwo* ThingTwo;
;

文件 BigMommaClass.cpp

#include "BigMommaClass.h"

BigMommaClass::BigMommaClass(int numba1, int numba2) 
        this->ThingOne = ThingOne(100);
        this->ThingTwo = ThingTwo(numba1, numba2);

这是我尝试编译时遇到的错误:

g++ -Wall -c -Iclasses -o objects/BigMommaClass.o classes/BigMommaClass.cpp
In file included from classes/BigMommaClass.cpp:1:0:
classes/BigMommaClass.h:12:8: error: declaration of âThingTwo* BigMommaClass::ThingTwoâ
classes/ThingTwo.h:1:11: error: changes meaning of âThingTwoâ from âclass ThingTwoâ
classes/BigMommaClass.cpp: In constructor âBigMommaClass::BigMommaClass(int, int)â:
classes/BigMommaClass.cpp:4:30: error: cannot convert âThingOneâ to âThingOne*â in assignment
classes/BigMommaClass.cpp:5:37: error: â((BigMommaClass*)this)->BigMommaClass::ThingTwoâ cannot be used as a function
make: *** [BigMommaClass.o] Error 1

我是否使用了正确的方法,但语法错误?还是我应该从不同的方向来?

【问题讨论】:

您只是想从那里调用它以便使用参数吗? @chris,现在,我想这就是我要做的一切。但我也想知道如何做到这一点,假设我需要在传递参数之前做一些事情:比如添加“numba1”和“numba2”并将总和传递给成员变量构造函数。 好吧,您的直接错误是您正在将一个对象分配给一个指针(您需要一个new,但无论如何还有更好的选择)。然而,手头的问题可以通过成员初始化器来解决。 私人:AnotherClass another(100); // 这立即构造了另一个类!可以?我的编译器确实不接受这一点,除非声明在函数内部。此行被解释为函数声明,编译器需要一个参数列表,而不是常量或变量。 【参考方案1】:

您可以在成员初始化器列表中指定如何初始化成员:

BigMommaClass 
    BigMommaClass(int, int);

private:
    ThingOne thingOne;
    ThingTwo thingTwo;
;

BigMommaClass::BigMommaClass(int numba1, int numba2)
    : thingOne(numba1 + numba2), thingTwo(numba1, numba2) 

【讨论】:

仍然困扰着我,使用这种语法,我无法在构造函数中做任何真正的工作来传递给其他构造函数。构造函数可能应该是轻量级的,所以我在想出一个真实的例子时遇到了麻烦,但我发现自己希望语法看起来更像this.thingOne = new ThingOne(100);,因为这样可以提供更多的灵活性。但我离题了。 thingOne 和 thingTwo 必须 在 BigMommaClass 的构造函数中初始化吗?还是可以在其他调用的函数中完成? @nobism,对于 initializaion 而不是赋值,它必须在构造函数中完成。您可以执行: thingOne(calculateThing1(numba1, numba2)) 之类的操作。在极少数情况下,您可能需要初始化,但无法在初始化列表中进行。在这种情况下,可以使用std::optionalts::deferred_construction 之类的东西。但是,做出这个决定时必须考虑很多。【参考方案2】:

您正在尝试使用 operator= 创建一个 ThingOne,但它不起作用(语法不正确)。此外,您使用类名作为变量名,即ThingOne* ThingOne。首先,让我们修复变量名:

private:
    ThingOne* t1;
    ThingTwo* t2;

由于这些是指针,它们必须指向某些东西。如果尚未构造对象,则需要在 BigMommaClass 构造函数中使用 new 显式执行此操作:

BigMommaClass::BigMommaClass(int n1, int n2)

    t1 = new ThingOne(100);
    t2 = new ThingTwo(n1, n2);

然而,通常初始化列表是构造的首选,所以它看起来像:

BigMommaClass::BigMommaClass(int n1, int n2)
    : t1(new ThingOne(100)), t2(new ThingTwo(n1, n2))
 

【讨论】:

谢谢。那么我应该使用ThingOne* t1 还是应该使用ThingOne t1 @DavidEnglund,使用后者。不必要的动态分配是不好的。如果您真的需要它,智能指针将是您的最佳选择。 @chris,谢谢。我正在使用指针来防止立即调用构造函数。不过,我认为现在已经解决了。 @DavidEnglund,我只想指出,在类的声明中指定参数只是 C++11,但即使它也不会立即调用它。初始化该对象时,如果您不覆盖它,那将只是默认设置。查找类内成员初始化以获取更多信息。我认为你也必须使用统一初始化来做到这一点,如果它不仅仅是一个带有一个参数的隐式构造函数,你可以通过ThingOne thingOne = 100; 获得。 @DavidEnglund 正如其他人所说,在这种情况下动态分配是不必要的。然而,它有一些用例(pimpl idiom,多态类,只需要前向声明来帮助减少编译时间等)。【参考方案3】:

这个问题有点老了,但这是 C++11 中在初始化成员变量之前在构造函数中“做更多工作”的另一种方式:

BigMommaClass::BigMommaClass(int numba1, int numba2)
    : thingOne([](int n1, int n2)return n1+n2;(numba1,numba2)),
      thingTwo(numba1, numba2) 

上面的 lambda 函数将被调用并将结果传递给 thingOnes 构造函数。你当然可以让 lambda 变得像你喜欢的那样复杂。

【讨论】:

括号不平衡。【参考方案4】:

我知道这是 5 年后的事了,但上面的回复并未说明您的软件出了什么问题。 (嗯,Yuushi 有,但直到我输入了这个我才意识到 - 哦!)。他们回答了标题中的问题如何在构造函数中初始化 C++ 对象成员变量? 这是关于其他问题的:我是否使用了正确的方法但语法错误?还是我应该从不同的方向来?

编程风格在很大程度上是一个见仁见智的问题,但在构造函数中尽可能多地做的另一种观点是将构造函数保持在最低限度,通常具有单独的初始化函数。无需尝试将所有初始化都塞入构造函数中,更不用说有时尝试将事物强制放入构造函数初始化列表中。

那么,直截了当,您的软件出了什么问题?

private:
    ThingOne* ThingOne;
    ThingTwo* ThingTwo;

请注意,在这些行之后,ThingOne(和ThingTwo)现在有两个含义,具体取决于上下文。

在 BigMommaClass 之外,ThingOne 是您使用 #include "ThingOne.h" 创建的类

在 BigMommaClass 中,ThingOne 是一个指针。

这是假设编译器甚至可以理解这些行并且不会陷入循环认为 ThingOne 是指向本身就是指向的指针的指针指向...的指针

以后写的时候

this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);

请记住,在 BigMommaClass 内部,您的 ThingOne 是一个指针。

如果您更改指针的声明以包含前缀 (p)

private:
    ThingOne* pThingOne;
    ThingTwo* pThingTwo;

那么ThingOne 将始终引用类,pThingOne 将始终引用指针。

然后可以重写

this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);

作为

pThingOne = new ThingOne(100);
pThingTwo = new ThingTwo(numba1, numba2);

它纠正了两个问题:双重含义问题和缺失的new。 (喜欢的可以留下this->!)

有了这些,我可以将以下几行添加到我的 C++ 程序中,它可以很好地编译。

class ThingOnepublic:ThingOne(int n);;
class ThingTwopublic:ThingTwo(int x, int y);;

class BigMommaClass 

    public:
            BigMommaClass(int numba1, int numba2);

    private:
            ThingOne* pThingOne;
            ThingTwo* pThingTwo;
;

BigMommaClass::BigMommaClass(int numba1, int numba2)

    pThingOne = new ThingOne(numba1 + numba2);
    pThingTwo = new ThingTwo(numba1, numba2);
;

你写的时候

this->ThingOne = ThingOne(100);
this->ThingTwo = ThingTwo(numba1, numba2);

this-> 的使用告诉编译器左侧的ThingOne 旨在表示指针。但是我们当时在BigMommaClass 里面,没有必要。

问题在于等号的右侧,其中ThingOne 旨在表示类。所以另一种纠正你的问题的方法是写

this->ThingOne = new ::ThingOne(100);
this->ThingTwo = new ::ThingTwo(numba1, numba2);

或者干脆

ThingOne = new ::ThingOne(100);
ThingTwo = new ::ThingTwo(numba1, numba2);

使用:: 更改编译器对标识符的解释。

【讨论】:

【参考方案5】:

关于the first (and great) answer from chris谁提出了解决类成员作为“真正的复合”成员(即-不是作为指针的情况 也不 参考文献):

注释有点大,这里用一些示例代码来演示一下。

当你选择持有我提到的成员时,你还必须记住这两件事:

    对于没有默认构造函数的每个“组合对象” - 您必须所有构造函数的初始化列表中对其进行初始化的“父亲”类(即原始示例中的BigMommaClassMyClass 和下面代码中的MyClass),以防有多个(参见下面示例中的InnerClass1)。这意味着,您可以“注释掉”m_innerClass1(a)m_innerClass1(15) ,前提是您启用了 InnerClass1 默认构造函数。

    对于每个确实有默认构造函数的“组合对象” - 您可以在初始化列表中对其进行初始化,但如果您选择不这样做(请参阅下面示例中的InnerClass2)。

查看示例代码(在Ubuntu 18.04 (Bionic Beaver) 下编译,g++ 版本 7.3.0):

#include <iostream>

using namespace std;

class InnerClass1

    public:
        InnerClass1(int a) : m_a(a)
        
            cout << "InnerClass1::InnerClass1 - set m_a:" << m_a << endl;
        

        /* No default constructor
        InnerClass1() : m_a(15)
        
            cout << "InnerClass1::InnerClass1() - set m_a:" << m_a << endl;
        
        */

        ~InnerClass1()
        
            cout << "InnerClass1::~InnerClass1" << endl;
        

    private:
        int m_a;
;

class InnerClass2

    public:
        InnerClass2(int a) : m_a(a)
        
            cout << "InnerClass2::InnerClass2 - set m_a:" << m_a << endl;
        

        InnerClass2() : m_a(15)
        
            cout << "InnerClass2::InnerClass2() - set m_a:" << m_a << endl;
        

        ~InnerClass2()
        
            cout << "InnerClass2::~InnerClass2" << endl;
        

    private:
        int m_a;
;

class MyClass

    public:
        MyClass(int a, int b) : m_innerClass1(a), /* m_innerClass2(a),*/ m_b(b)
        
            cout << "MyClass::MyClass(int b) - set m_b to:" << m_b << endl;
        

         MyClass() : m_innerClass1(15), /*m_innerClass2(15),*/ m_b(17)
        
            cout << "MyClass::MyClass() - m_b:" << m_b << endl;
        

        ~MyClass()
        
            cout << "MyClass::~MyClass" << endl;
        

    private:
        InnerClass1 m_innerClass1;
        InnerClass2 m_innerClass2;
        int m_b;
;

int main(int argc, char** argv)

    cout << "main - start" << endl;

    MyClass obj;

    cout << "main - end" << endl;
    return 0;

【讨论】:

以上是关于如何在构造函数中初始化 C++ 对象成员变量?的主要内容,如果未能解决你的问题,请参考以下文章

C++类和对象下

如何在 C++ 子类的构造函数中初始化超类的 const 成员变量?

C++中如何在子类的构造函数中调用基类的构造函数来初始化基类成员变量

C++:首先调用/初始化哪个?其成员变量的类构造函数或构造函数?

C++构造函数的初始化列表

C++构造函数-对象的初始化