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

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

В 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

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

54

Решение

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

BigMommaClass {
BigMommaClass(int, int);

private:
ThingOne thingOne;
ThingTwo thingTwo;
};

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

Другие решения

Вы пытаетесь создать 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))
{ }
23

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

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

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

8

Я знаю, что это 5 лет спустя, но в приведенных выше ответах не говорится о том, что не так с вашим программным обеспечением. (Ну, Юши делает, но я не понял, пока не набрал это — дох!). Они отвечают на вопрос в заголовке Как я могу инициализировать переменные-члены объекта 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 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);

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

1

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

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

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

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