Есть ли способ поздней инициализации переменной-члена (класса) В C++?
Я исхожу из фона 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
доступна
6 ответов
мое предложение: используйте функцию:
private: static int calculate_first(int input) {return input*5;}
explicit Second(int input) : first(calculate_first(input)) {}
базовые классы будут инициализированы в том порядке, в котором они объявлены в списке наследования классов, а затем члены будут инициализированы в том порядке, в котором они перечислены в классе, поэтому вычисление can зависит от нестатических переменных-членов и базовых классов если они уже инициализированы.
Альтернативно:
конструктор по умолчанию, а затем переназначение:
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)));}
размещение new:
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;
};
вы можете просто сделать то, что вы сказали в комментариях, или, вы можете сделать первый указатель на первый и дать ему память, когда захотите, хотя я не рекомендую этот способ
один из способов разделения времени жизни объекта-использовать кучу, make first
указатель и инициализируйте его в любое время:
class Second {
public:
First* first;
Second() {
first = new First(123);
}
};
конечно, вы, вероятно, захотите использовать какой-то умный указатель, а не raw указатель.
это предложение является ядром проблемы:
Я не могу инициализировать сначала в списке инициализаторов с правильным значением, так как значение получается после некоторой операции.
вы должны знать, что то, что вы хотите сделать здесь не идеальный стиль программирования на 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 инициализации [класс.init]
1 когда инициализатор не указан для объекта (возможно, CV-квалифицированного) типа класса (или его массива), или инициализатор имеет форму (), объект инициализации, как указано в 8.5.
и
12.6.1 явная инициализация [class.expl.init]
1 объект типа class может быть инициализирован с заключенным в скобки expression-list, где expression-list интерпретируется как список аргументов для конструктора, который вызывается для инициализации объекта. Кроме того, в качестве инициализатора можно указать одно выражение-назначение, используя форму = инициализации. Применяется семантика прямой инициализации или семантика инициализации копирования; см. 8.5.