web-dev-qa-db-de.com

Wie kann ich C++ - Objektmitgliedsvariablen im Konstruktor initialisieren?

Ich habe eine Klasse mit einigen Objekten als Elementvariablen. Ich möchte nicht, dass die Konstruktoren für diese Member bei der Deklaration aufgerufen werden. Daher versuche ich, explizit einen Zeiger auf das Objekt zu halten. Ich habe keine Ahnung was ich tue. o_O

In StackOverflow scheint es mir möglich zu sein, andere Beispiele für Objektmitgliedsvariablen zu finden, aber normalerweise wird der Konstruktor sofort aufgerufen:

class MyClass {
    public:
        MyClass(int n);
    private:
        AnotherClass another(100); // this constructs AnotherClass right away!
};

Aber ich möchte, dass der Konstruktor MyClass den Konstruktor AnotherClass aufruft. So sieht mein Code aus:

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

Hier ist der Fehler, den ich bekomme, wenn ich versuche zu kompilieren:

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

Benutze ich den richtigen Ansatz, aber die falsche Syntax? Oder sollte ich aus einer anderen Richtung dazu kommen?

54
Logical Fallacy

Sie können angeben, wie Mitglieder in der Mitgliederinitialisierungsliste initialisiert werden:

BigMommaClass {
    BigMommaClass(int, int);

private:
    ThingOne thingOne;
    ThingTwo thingTwo;
};

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

Sie versuchen, eine ThingOne mit operator= zu erstellen, die nicht funktioniert (falsche Syntax). Außerdem verwenden Sie einen Klassennamen als Variablennamen, dh ThingOne* ThingOne. Zuerst müssen wir die Variablennamen korrigieren:

private:
    ThingOne* t1;
    ThingTwo* t2;

Da es sich um Zeiger handelt, müssen sie auf etwas zeigen. Wenn das Objekt noch nicht erstellt wurde, müssen Sie dies explizit mit new in Ihrem BigMommaClass-Konstruktor tun:

BigMommaClass::BigMommaClass(int n1, int n2)
{
    t1 = new ThingOne(100);
    t2 = new ThingTwo(n1, n2);
}

Im Allgemeinen werden Initialisierungslisten für die Konstruktion bevorzugt, daher sieht es folgendermaßen aus:

BigMommaClass::BigMommaClass(int n1, int n2)
    : t1(new ThingOne(100)), t2(new ThingTwo(n1, n2))
{ }
23
Yuushi

Diese Frage ist ein bisschen alt, aber in C++ 11 gibt es noch eine andere Möglichkeit, im Konstruktor "mehr Arbeit zu erledigen", bevor Sie Ihre Mitgliedsvariablen initialisieren:

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

Die obige Lambda-Funktion wird aufgerufen und das Ergebnis an den thingOnes-Konstruktor übergeben. Sie können das Lambda natürlich so komplex machen, wie Sie möchten.

8
patmanpato

Ich weiß, dass dies fünf Jahre später ist, aber die obigen Antworten beziehen sich nicht auf das, was mit Ihrer Software falsch war. (Nun, Yuushi's tut es, aber ich merkte nicht, bis ich das geschrieben hatte - doh!). Sie beantworten die Frage im Titel Wie kann ich C++ - Objektmitgliedsvariablen im Konstruktor initialisieren? Hier geht es um die anderen Fragen: Benutze ich den richtigen Ansatz, aber die falsche Syntax? Oder sollte ich aus einer anderen Richtung dazu kommen?

Der Programmierstil ist weitgehend eine Ansichtssache, aber als Alternative dazu, so viel wie möglich in einem Konstruktor zu tun, ist es, Konstruktoren auf ein Minimum zu beschränken, oft mit einer separaten Initialisierungsfunktion. Es ist nicht notwendig, die gesamte Initialisierung in einen Konstruktor zu stopfen, und es ist zu beachten, dass Sie manchmal versuchen, Dinge in die Initialisierungsliste des Konstruktors zu zwingen.

Also, was war los mit Ihrer Software?

private:
    ThingOne* ThingOne;
    ThingTwo* ThingTwo;

Beachten Sie, dass nach diesen Zeilen ThingOne (und ThingTwo) jetzt je nach Kontext zwei Bedeutungen haben. 

Außerhalb von BigMommaClass ist ThingOne die Klasse, die Sie mit #include "ThingOne.h" erstellt haben.

In BigMommaClass ist ThingOne ein Zeiger.

Das heißt, der Compiler kann sogar die Zeilen verstehen und bleibt nicht in einer Schleife stecken, in der Annahme, dass ThingOne ein Zeiger auf etwas ist, das selbst ein Zeiger auf etwas ist, das ein Zeiger auf ...

Später, wenn Sie schreiben

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

denken Sie daran, dass Ihr BigMommaClass in ThingOne ein Zeiger ist.

Wenn Sie die Deklarationen der Zeiger ändern, um ein Präfix (p) aufzunehmen 

private:
    ThingOne* pThingOne;
    ThingTwo* pThingTwo;

Dann wird ThingOne immer auf die Klasse und pThingOne auf den Zeiger verweisen.

Es ist dann möglich, umzuschreiben

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

wie

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

das behebt zwei Probleme: das Doppelbedeutungsproblem und das fehlende new. (Sie können this-> verlassen, wenn Sie möchten!) Damit kann ich die folgenden Zeilen zu einem C++ - Programm von mir hinzufügen und es lässt sich gut kompilieren.

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

Wenn du geschrieben hast

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

die Verwendung von this-> teilt dem Compiler mit, dass die linke Seite ThingOne den Zeiger bedeuten soll. Wir befinden uns zu diesem Zeitpunkt jedoch in BigMommaClass und es ist nicht notwendig . Das Problem liegt auf der rechten Seite der Gleichen, wobei ThingOne die Klasse bedeuten soll. Eine andere Möglichkeit, Ihre Probleme zu beheben, wäre das Schreiben gewesen

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

oder einfach

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

verwenden Sie ::, um die Interpretation des Bezeichners durch den Compiler zu ändern.

1
user3070485

Ich bin (auch, wie von anderen erwähnt wurde) sich der Tatsache bewusst, dass diese Frage alt ist, aber ich wollte etwas auf die erste (und tolle) Antwort von @chris) hinweisen., der eine Lösung für die Situation vorgeschlagen hat, in der die Klassenmitglieder als "true composite" -Mitglieder gehalten werden (dh NICHT as Zeiger NOR Referenzen) ..__ Die Notiz ist etwas groß, ich werde sie hier mit Beispielcode demonstrieren.

Wenn Sie sich dafür entschieden haben, die Mitglieder wie oben erwähnt zu halten, müssen Sie auch diese beiden Dinge beachten:

1) Für jedes "zusammengesetzte Objekt" hat DOES NOT einen voreingestellten ctor - Sie MUST initialisieren es in der Initialisierungsliste von ALL des Ctors der "vater" -Klasse (dh BigMommaClass oder MyClass in den ursprünglichen Beispielen und MyClass im Code unten), falls mehrere vorhanden sind (siehe InnerClass1 im Beispiel unten). Das heißt, Sie können die Funktion m_innerClass1(a) und m_innerClass1(15) NUR "auskommentieren", wenn Sie den Standardcode InnerClass1 aktivieren. 

2) Für jedes "zusammengesetzte Objekt", das DOES haben einen Standard - ctor - Sie MAY initialisiert es in der Initialisierungsliste, funktioniert jedoch auch, wenn Sie dies nicht tun (siehe InnerClass2 im Beispiel unten).

Siehe Beispielcode (unter Ubuntu 18.04 mit g++ Version 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