Как я могу инициализировать переменные-члены объекта С++ в конструкторе?

У меня есть класс, который имеет несколько объектов в качестве переменных-членов. Я не хочу, чтобы конструкторы для этих членов вызывались при объявлении, поэтому я пытаюсь явно указать указатель на объект. Я не имею понятия что я делаю. о_О

В StackOverflow я, кажется, смог найти другие примеры переменных-членов объекта, но обычно конструктор вызывается немедленно, например так:

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

Но я хочу, чтобы конструктор MyClass AnotherClass конструктор 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

Я использую правильный подход, но неправильный синтаксис? Или я должен прийти к этому с другой стороны?

Ответ 1

Вы можете указать, как инициализировать элементы в списке инициализаторов членов:

BigMommaClass {
    BigMommaClass(int, int);

private:
    ThingOne thingOne;
    ThingTwo thingTwo;
};

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

Ответ 2

Вы пытаетесь создать ThingOne с помощью operator=, который не будет работать (неправильный синтаксис). Кроме того, вы используете имя класса как имя переменной, то есть ThingOne* ThingOne. Во-первых, пусть исправить имена переменных:

private:
    ThingOne* t1;
    ThingTwo* t2;

Так как это указатели, они должны указать на что-то. Если объект еще не сконструирован, вам нужно сделать это явно с помощью нового в конструкторе BigMommaClass:

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

Ответ 3

Этот вопрос немного устарел, но здесь в С++ 11 "сделать больше работы" в конструкторе перед инициализацией переменных-членов:

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

Вышеприведенная лямбда-функция будет вызвана, а результат передан конструктору thingOnes. Конечно, вы можете сделать лямбду настолько сложной, насколько захотите.

Ответ 4

Я знаю, что это 5 лет спустя, но в приведенных выше ответах не говорится о том, что не так с вашим программным обеспечением. (Ну, Юуши это делает, но я не понял, пока не набрал это - дох!). Они отвечают на вопрос в заголовке Как я могу инициализировать переменные-члены объекта C++ в конструкторе? Это о других вопросах: я использую правильный подход, но неправильный синтаксис? Или я должен прийти к этому с другой стороны?

Стиль программирования в значительной степени зависит от мнений, но альтернативный подход к тому, чтобы делать как можно больше в конструкторе, - сводить конструкторы к минимуму, часто имея отдельную функцию инициализации. Нет необходимости пытаться втиснуть всю инициализацию в конструктор, не говоря уже о том, чтобы время от времени пытаться принудительно вводить вещи в список инициализации конструкторов.

Итак, что же случилось с вашим программным обеспечением?

private:
    ThingOne* ThingOne;
    ThingTwo* ThingTwo;

Обратите внимание, что после этих строк ThingOneThingTwo) теперь имеют два значения в зависимости от контекста.

За пределами 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 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 предназначен для обозначения класса. Так что другим способом исправить ваши проблемы было бы написать

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

или просто

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

используя :: для изменения интерпретатора идентификатора компилятором.

Ответ 5

Я (также, как и другие упоминали) осознаю тот факт, что этот вопрос старый, но я хотел бы кое-что указать относительно первого (и замечательного) ответа от @chris, который предложил решение ситуации, в которой содержатся ученики. как "настоящие составные" члены (т.е. НЕ как указатели, НИ ссылки). Примечание немного большое, поэтому я продемонстрирую его здесь с примером кода.

Когда вы решили держать членов, как я уже упоминал, вы должны иметь в виду и эти две вещи:

1) Для каждого "составного объекта", который НЕ имеет ctor по умолчанию - вы ДОЛЖНЫ инициализировать его в списке инициализации ВСЕХ ctor класса "папа" (то есть - BigMommaClass или MyClass в исходных примерах и MyClass в коде ниже), если их несколько (см. InnerClass1 в примере ниже). Это означает, что вы можете "закомментировать" m_innerClass1(a) и m_innerClass1(15) ТОЛЬКО, если вы включите InnerClass1 умолчанию в InnerClass1.

2) Для каждого " в составе объекта", который действительно имеет 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;
}