web-dev-qa-db-ja.com

コンストラクターでC ++オブジェクトメンバー変数を初期化するにはどうすればよいですか?

メンバー変数としていくつかのオブジェクトを持つクラスがあります。これらのメンバーのコンストラクターが宣言されたときに呼び出されたくないので、明示的にオブジェクトへのポインターに固執しようとしています。何をしているかが自分もわからない。 o_O

StackOverflowでは、オブジェクトメンバー変数の他の例を見つけることができるようですが、通常、コンストラクタは次のようにすぐに呼び出されます。

class MyClass {
    public:
        MyClass(int n);
    private:
        AnotherClass another(100); // this constructs 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

私は正しいアプローチを使用していますが、間違った構文を使用していますか?または、これに別の方向から来るべきですか?

63
Logical Fallacy

メンバー初期化子リストでメンバーを初期化する方法を指定できます。

BigMommaClass {
    BigMommaClass(int, int);

private:
    ThingOne thingOne;
    ThingTwo thingTwo;
};

BigMommaClass::BigMommaClass(int numba1, int numba2)
    : thingOne(numba1 + numba2), thingTwo(numba1, numba2) {}
82
chris

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))
{ }
27
Yuushi

この質問は少し古いですが、メンバー変数を初期化する前にコンストラクタで「もっと作業を行う」というc ++ 11の別の方法があります。

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

上記のラムダ関数が呼び出され、結果がthingOnesコンストラクターに渡されます。もちろん、ラムダを好きなだけ複雑にすることができます。

8
patmanpato

私はこれが5年後であることを知っていますが、上記の回答はあなたのソフトウェアのどこが悪いのかを述べていません。 (まあゆうしはそうですが、これをタイプするまで気がつきませんでした!彼らはタイトルの質問に答えますコンストラクターでC++オブジェクトメンバー変数を初期化するにはどうすればよいですか?これは他の質問についてです:私は正しいアプローチを使用していますが間違った構文?または、これに別の方向から来る必要がありますか?

プログラミングスタイルは主に意見の問題ですが、コンストラクターでできる限り行うことの代替ビューは、コンストラクターを最小限に抑え、多くの場合別個の初期化関数を使用することです。すべての初期化をコンストラクターに詰め込むことを試みる必要はありません。時々、コンストラクター初期化リストに物事を強制しようとすることを気にしないでください。

それで、ポイントまで、あなたのソフトウェアで何が悪かったのですか?

private:
    ThingOne* ThingOne;
    ThingTwo* ThingTwo;

これらの行の後、ThingOne(およびThingTwo)には、コンテキストに応じて2つの意味があることに注意してください。

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);

これは2つの問題を修正します。二重の意味の問題と、欠落しているnew。 (必要に応じてthis->を残すことができます!)これを使用して、次の行を私のc ++プログラムに追加して、うまくコンパイルできます。

class ThingOne{public:ThingOne(int n){};};
class ThingTwo{public: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がクラスを意味することを意図しているequalsの右側にあります。したがって、問題を修正する別の方法は、

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

または単に

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

::を使用して、コンパイラの識別子の解釈を変更します。

2
Ivan

私は(他の人が言ったように)この質問が古いという事実を知っていますが、/からの最初の(and great)答えに関して何かを指摘したかったです@ chrisクラスメンバーが「true composite "メンバーとして保持される状況の解決策を提案した人( ie-NOTaspointersNORreferences)。メモは少し大きいので、ここでサンプルコードを使用して説明します。

私が述べたようにメンバーを保持することを選んだとき、あなたはこれらの2つのことも念頭に置いておく必要があります:

1)DOES NOTがデフォルトのctorを持つすべての「構成されたオブジェクト」に対して、を初期化する必要がありますALLの初期化リストで、「父」クラスの俳優(つまり、元の例ではBigMommaClassまたはMyClass、以下のコードではMyClass)いくつかあります(以下の例のInnerClass1を参照)。つまり、InnerClass1デフォルトのctorを有効にした場合にのみ、m_innerClass1(a)およびm_innerClass1(15)を「コメントアウト」できます。

2)DOESが持っているすべての「構成されたオブジェクト」についてデフォルトのctor-あなたで初期化できます初期化リスト。ただし、選択しない場合も機能します(以下の例のInnerClass2を参照)。

サンプルコードを参照してください(Ubuntu 18.04で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 cotr
        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;
}
1
Guy Avraham