Как я могу инициализировать переменные-члены объекта С++ в конструкторе?
У меня есть класс, который имеет несколько объектов в качестве переменных-членов. Я не хочу, чтобы конструкторы для этих членов вызывались при объявлении, поэтому я пытаюсь явно указать указатель на объект. Я не имею понятия что я делаю. о_О
В 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;
Обратите внимание, что после этих строк 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);
используя ::
для изменения интерпретатора идентификатора компилятором.
Ответ 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;
}