Как лучше инициализировать учеников? - PullRequest
0 голосов
/ 28 августа 2018

Давайте предположим, что у меня есть класс А, который выглядит так:

class A {
public:
  public A(bool someFlag, Params someParams);
private:
  vector<string> texts;
}

Я хотел бы извлечь логику инициализации поля texts. Я придумал 2 идеи:

  1. Первая идея - статические, закрытые функции-члены, которые возвращают нужные векторы текстов.

    A::A(bool someFlag, Params someParams) {
      if (someFlag)
        texts = createSomeTexts(someParams);
      else
        texts = createOtherTexts(someParams);
    }
    
  2. Вторая идея - закрытые функции-члены, которые присваивают желаемые векторы текстов членам класса .

    A::A(bool someFlag, Params someParams) {
      if (someFlag)
        createAndAssignSomeTexts(someParams);
      else
        createAndAssignOtherTexts(someParams);
    }
    

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

Ответы [ 2 ]

0 голосов
/ 28 августа 2018

Я бы использовал случай 1, потому что функции createSomeTexts и createOtherTexts не изменяют никаких переменных класса. Это означает, что эти функции могут быть проверены модулем.

Лучше не использовать глобальные переменные и, если необходимо, не изменять их из глобальной области (this->), а передавать их по ссылке или в качестве указателя на вашу функцию. Таким образом, вы можете передавать заглушки в коде и писать тестовые случаи.

Кроме того, Params должен быть константной ссылкой:

class A {
  public:
    public A(const bool someFlag, const Params &someParams);
private:
  vector<string> texts;
}
0 голосов
/ 28 августа 2018

Вы должны стремиться инициализировать свой элемент данных, а не назначать их в теле конструктора. Обе версии, которые вы показывали, вызывают создание по умолчанию экземпляра std::vector<std::string> и присваиваются ему позже. Следовательно, я бы предложил это:

A::A(bool someFlag, const Params& someParams) :
    texts(someFlag ? createSomeTexts(someParams) : createOtherTexts(someParams))
{}

или, что более читабельно, пусть createSomeTexts также обрабатывает флаг:

A::A(bool someFlag, const Params& someParams) :
    texts(createSomeTexts(someFlag, someParams))
{}

Сделайте createSomeTexts функцией-членом, если ей нужен доступ к другим элементам данных (убедитесь, что они объявлены перед элементом texts и правильно инициализированы - как указывал @Scheff, это вряд ли будет хорошей идеей, хоть). В противном случае сделайте его свободной функцией (см. здесь , почему это предпочтительнее). Если createSomeTexts является свободной функцией, вы можете с таким же успехом построить объект следующим образом:

std::vector<std::string> stringsToInject = createSomeText(/* Some flags.... */);

A instance(stringsToInject); // A's ctor updated to make this work 

, который может еще больше разделить проблемы, так как конструктор A заботится об инициализации элементов данных, тогда как логика для создания данных инициализации находится где-то еще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...