Инициализация статических членов объекта

Меня иногда смущают статические члены. Я понимаю, как инициализировать простой встроенный тип, такой как int, чем-то вроде int myClass::statVar = 10;, который вы помещаете в файл .cpp, но у меня есть что-то вроде следующего:

class myClass
{
public:
 // Some methods...

protected:
 static RandomGenerator itsGenerator;
}

Основная идея достаточно проста: myClass требуется доступ к генератору случайных чисел для одной из его функций-членов. У меня также может быть только несколько экземпляров генератора, так как каждый объект довольно большой. Однако тип RandomGenerator должен быть, так сказать, "инициализирован" вызовом RandomGenerator::Randomize(), чего компилятор не позволит вам сделать, поскольку это не константное значение (правильно?).

Итак, как я могу заставить это работать?

Или, может быть, мне не следует использовать статическую переменную в этом случае, а сделать это как-то иначе?


person Kristian D'Amato    schedule 20.07.2010    source источник


Ответы (7)


Вы можете создать класс-оболочку, который будет содержать экземпляр RandomGenerator и вызывать RandomGenerator::Randomize в своем конструкторе.

person Kirill V. Lyadvinsky    schedule 20.07.2010
comment
Это плохо. Внешний класс не должен знать о существовании генератора. Вы изменили путь к нему только. Это обходной путь, но это не РЕШЕНИЕ. - person Gangnus; 19.12.2011
comment
@Gangnus: не обязательно. Инкапсуляция — это хорошо, но нет ничего плохого в предоставлении политик извне, особенно если вы рискуете возложить более чем одну ответственность на существующий внешний класс. - person Lightness Races in Orbit; 13.08.2013

Поместите его в приватное поле, выставьте статический аксессор. В методе доступа, если элемент еще не инициализирован, инициализируйте его.

person onof    schedule 20.07.2010
comment
Я склоняюсь к этому методу. Я думаю, что это может быть лучшим решением, хотя обертывание генератора также кажется элегантной идеей. - person Kristian D'Amato; 20.07.2010
comment
Это плохо. Внешний класс не должен знать о существовании генератора. Вы изменили путь к нему только. Это обходной путь, но это не РЕШЕНИЕ. - person Gangnus; 19.12.2011

В таких случаях синглтоны на самом деле ваши друзья, несмотря на другие их недостатки.

person Pontus Gagge    schedule 20.07.2010
comment
Да. Боюсь, в c++ это единственно правильный путь. Любая установка глубоко внутреннего приватного поля из внешнего пространства ОЧЕНЬ неверна. Внешнее пространство не должно знать о внутреннем генераторе и его инициализации. - person Gangnus; 19.12.2011
comment
@Gangus: Смотрите другие мои комментарии. Это не обязательно так. Иногда можно предоставить политику извне. См. стандартную библиотеку — вы можете указать типы распределения в качестве параметров шаблона практически для всех типов контейнеров, и это совершенно нормально. - person Lightness Races in Orbit; 13.08.2013

Если RandomGenerator можно скопировать, вы можете использовать вспомогательную функцию для инициализации:

RandomGenerator init_rg() {
    RandomGenerator rg;
    rg.Randomize();
    return rg;
}

RandomGenerator myClass::itsGenerator = init_rg();
person Georg Fritzsche    schedule 20.07.2010
comment
просто будьте осторожны при инициализации глобальных (статических) переменных, потому что порядок не гарантируется. - person doron; 20.07.2010

Просто напишите функцию, которая возвращает ссылку на правильно рандомизированный RandomGenerator и превращает itsGenerator в ссылку на генератор:

class myClass
{
public:
 // Some methods...

protected:
 // make this a reference to the real generator
 static RandomGenerator& itsGenerator;
public:
 static RandomGenerator& make_a_generator() 
 {
   RandomGenerator *g=0;
    g=new RandomGenerator();
    g->Randomize();
   return *g;
 }
}

RandomGenerator& myClass::itsGenerator=myClass::make_a_generator();
person Nordic Mainframe    schedule 20.07.2010
comment
Почему так сложно, если вы можете просто вернуть экземпляр, размещенный в стеке, и позволить NRVO позаботиться обо всем остальном или использовать static экземпляр, локальный для функции? - person Georg Fritzsche; 20.07.2010
comment
RandomGenerator нуждается в доступном конструкторе копирования даже с NRVO. Я просто хотел избежать ненужных требований. - person Nordic Mainframe; 20.07.2010

Это только одна функция, которая нуждается в RandomGenerator? Вы можете сделать это следующим образом:

int myClass::foo() { static RandomGenerator itsGenerator = RandomGenerator::Randomize() ... }

person gaspode    schedule 20.07.2010
comment
Это не сработает, потому что Randomize не возвращает генератор. Разделение строк приведет к рандомизации генератора каждый раз, когда вы вводите блок. - person Kristian D'Amato; 20.07.2010

Если только myClass нужен RandomGenerator, то:

myClass::myClass()
{
    itsGenerator.Randomize();
}

Имеет ли значение, если вы повторно рандомизируете свой генератор случайных чисел для каждого объекта? Я предполагаю, что нет ;-)

person Josh    schedule 02.03.2011