Я иду из фона Java. У меня есть следующая программа.
#include <string>
#include <iostream>
class First {
public:
First(int someVal): a(someVal) {
}
int a;
};
class Second {
public:
First first;
Second() { // The other option would be to add default value as ": first(0)"first = First(123);
}
};
int main()
{
Second second;
std::cout << "hello" << second.first.a << std::endl;
}
В классе Second
Я хотел переменную first
оставаться неинициализированным, пока я специально не инициализирую его в Second()'s constructor
, Есть ли способ сделать это? Или я просто остался с 2 вариантами?
Я не могу инициализировать first
в списке инициализаторов с правильным значением, так как значение получается после некоторой операции. Итак, фактическое требуемое значение для first
доступно в Second()
только конструктор.
МОЕ предложение: используйте функцию:
private: static int calculate_first(int input) {return input*5;}
explicit Second(int input) : first(calculate_first(input)) {}
Базовые классы будут инициализированы в том порядке, в котором они объявлены в списке наследования классов, а затем члены будут инициализированы в том порядке, в котором они перечислены в классе, поэтому расчет Можно зависит от нестатических переменных-членов и базовых классов если они уже были инициализированы.
В качестве альтернативы:
Конструктор по умолчанию, затем переназначить:
explicit Second(int input) { first = input*5; }
Фиктивное значение, затем переназначение:
explicit Second(int input) : first(0) { first = input*5; }
Используйте boost :: необязательно:
boost::optional<First> first;
explicit Second(int input) { first = input*5; }
Используйте кучу:
std::unique_ptr<First> first;
explicit Second(int input) { first.reset(new First(input*5));}
Second(const Second& r) first(new First(*(r->first))) {}
Second& operator=(const Second& r) {first.reset(new First(*(r->first)));}
Размещение нового:
This is tricky and not suggested
and worse in every way than boost::optional
So sample deliberately missing.
But it is an option.
инициализировать first
в списке инициализатора члена.
Это может помочь выполнить ваши вычисления во вспомогательной функции и использовать конструктор пересылки:
class Second {
public:
Second() : Second(helper_function()) {}
private:
Second(int calc): first(calc) {}
static int helper_function() { return ...; }
First first;
};
Вы можете просто сделать то, что вы сказали в комментариях, или вы можете сначала сделать указатель на First и дать ему память в любое время, хотя я не рекомендую этот способ
Один из способов выделить время жизни объекта — использовать кучу, сделать first
указатель и инициализируйте его в любое время:
class Second {
public:
First* first;
Second() {
first = new First(123);
}
};
Конечно, вы, вероятно, захотите использовать какой-то умный указатель, а не сырье указатель.
Это предложение является ядром проблемы:
Я не могу инициализировать сначала в списке инициализаторов с правильным значением,
так как значение получается после некоторой операции.
Вы должны знать, что вы хотите сделать здесь не идеальный стиль программирования в Java, либо. Если оставить поле с некоторым значением по умолчанию, а затем присвоить его чуть позже, после того, как некоторые вычисления будут выполнены, то оно не будет final
и, следовательно, класс от того, чтобы быть неизменным.
В любом случае ваша цель должна заключаться в том, чтобы подтолкнуть эти вычисления непосредственно к инициализации члена, используя частные вспомогательные функции (который может быть статичным):
class Second {
private:
First first;
static int getInitializationData()
{
// complicated calculations go here...
return result_of_calculations;
}
public:
Second() : first(getInitializationData()) {}
};
На мой взгляд, все остальное — просто обходной путь, который в долгосрочной перспективе усложнит вашу жизнь.
Если вы не пишете код для явной инициализации переменной-члена, для ее инициализации используется инициализатор по умолчанию.
В проекте стандарта C ++ содержится следующее об инициализации базовых классов и переменных-членов:
12.6 Инициализация [class.init]
1 Если инициализатор не указан для объекта (возможно, cv-квалифицированного) типа класса (или его массива) или инициализатор имеет форму (), объект инициализируется, как указано в 8.5.
А также
12.6.1 Явная инициализация [class.expl.init]
1 Объект класса можно инициализировать с помощью списка выражений в скобках, где список выражений рассматривается как список аргументов для конструктора, который вызывается для инициализации объекта. В качестве альтернативы, одно выражение-присваивание может быть указано в качестве инициализатора с использованием формы инициализации =. Применяется либо семантика прямой инициализации, либо семантика копирования-инициализации; см. 8.5.