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

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.