Есть ли способ поздней инициализации переменной-члена (класса) в 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 вариантами?

  1. Предоставить конструктор без параметров.
  2. Инициализируйте его некоторым значением по умолчанию, а затем заново назначьте требуемое значение.

Я не могу инициализировать first в списке инициализаторов с правильным значением, так как значение получается после некоторой операции. Итак, фактическое требуемое значение для first доступно в Second() только конструктор.

7

Решение

МОЕ предложение: используйте функцию:

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.
6

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

инициализировать first в списке инициализатора члена.

Это может помочь выполнить ваши вычисления во вспомогательной функции и использовать конструктор пересылки:

class Second {
public:
Second() : Second(helper_function()) {}

private:
Second(int calc): first(calc) {}
static int helper_function() { return ...; }

First first;
};
4

Вы можете просто сделать то, что вы сказали в комментариях, или вы можете сначала сделать указатель на First и дать ему память в любое время, хотя я не рекомендую этот способ

2

Один из способов выделить время жизни объекта — использовать кучу, сделать first указатель и инициализируйте его в любое время:

class Second {
public:
First* first;
Second()  {
first = new First(123);

}
};

Конечно, вы, вероятно, захотите использовать какой-то умный указатель, а не сырье указатель.

1

Это предложение является ядром проблемы:

Я не могу инициализировать сначала в списке инициализаторов с правильным значением,
так как значение получается после некоторой операции.

Вы должны знать, что вы хотите сделать здесь не идеальный стиль программирования в Java, либо. Если оставить поле с некоторым значением по умолчанию, а затем присвоить его чуть позже, после того, как некоторые вычисления будут выполнены, то оно не будет finalи, следовательно, класс от того, чтобы быть неизменным.

В любом случае ваша цель должна заключаться в том, чтобы подтолкнуть эти вычисления непосредственно к инициализации члена, используя частные вспомогательные функции (который может быть статичным):

class Second {
private:
First first;

static int getInitializationData()
{
// complicated calculations go here...
return result_of_calculations;
}
public:
Second() : first(getInitializationData()) {}
};

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

1

Если вы не пишете код для явной инициализации переменной-члена, для ее инициализации используется инициализатор по умолчанию.

В проекте стандарта C ++ содержится следующее об инициализации базовых классов и переменных-членов:

12.6 Инициализация [class.init]

1 Если инициализатор не указан для объекта (возможно, cv-квалифицированного) типа класса (или его массива) или инициализатор имеет форму (), объект инициализируется, как указано в 8.5.

А также

12.6.1 Явная инициализация [class.expl.init]

1 Объект класса можно инициализировать с помощью списка выражений в скобках, где список выражений рассматривается как список аргументов для конструктора, который вызывается для инициализации объекта. В качестве альтернативы, одно выражение-присваивание может быть указано в качестве инициализатора с использованием формы инициализации =. Применяется либо семантика прямой инициализации, либо семантика копирования-инициализации; см. 8.5.

0
По вопросам рекламы [email protected]