Рефакторинг C ++: порядок инициализации в конструкторах - PullRequest
1 голос
/ 20 октября 2011

Предположим, мне нужно вызвать бесплатный GlobalInitializer (), прежде чем мой конструктор инициализирует какие-либо переменные-члены. Например:

class Foo {
  public:
    Foo() : bar_()
    {
      // calling GlobalInitializer() here is too late
    }
  Bar bar_;
};

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

class MyInitializer {
  protected:
    MyInitializer() {
      GlobalInitializer();
    }
};
class UglyFoo : public MyInitializer
{
  public:
    UglyFoo() : bar_()
    { }
  Bar bar_;
};

UglyFoo выполняет свою работу, но для этого требуется этот уродливый класс MyInitializer. Есть ли более чистый шаблон дизайна или рефакторинг, который даст тот же результат?

Дополнительное примечание : GlobalInitializer () - это дорогостоящий вызов, которого я хочу избежать, если пользователь не создаст экземпляр Foo (). Внутри GlobalInitializer () есть защита от нескольких вызовов. Кроме того, могут быть другие классы, скажем, FooBar, которые также должны вызывать GlobalInitializer (), но в одном процессе GlobalInitializer () будет фактически работать один раз (если создается экземпляр Foo или FooBar) или даже не один раз (если нет экземпляры Foo или FooBar).

Ответы [ 5 ]

4 голосов
/ 20 октября 2011
class Foo {
private:
    struct Initializer {
        Initializer() { GlobalInitializer(); }
    };
    Initializer initializer__;  // declare before bar__ to ensure it is constructed first

public:
    Foo() : bar_()
    {
    }

    Bar bar_;
};
1 голос
/ 20 октября 2011

Вы, вероятно, должны переосмыслить свой дизайн.

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

Если вам это нужно, вам, вероятно, следует вызвать GlobalInitializer внутри конструктора bar_, но лучший способ сделать это - переосмыслить ваш дизайн.

0 голосов
/ 18 ноября 2011

наткнулся на меня во сне: поменяй bar_ на указатель:

class Foo {
  public:
    Foo()
    {
      GlobalInitializer();
      // now we can call GlobalInitializer() in time
      bar_ = new Bar; 
    }
    ~Foo()
    {
      delete bar_;
    }

  private:
    Bar* bar_;
};
0 голосов
/ 20 октября 2011

Предположительно, вы можете выполнить рефакторинг того, что инициализируется GlobalInitializer, чтобы больше не быть глобальным.Тогда ваши варианты откроют, как предоставить вашему классу эти данные.Вы знаете, потому что глобалы плохие и все.

0 голосов
/ 20 октября 2011

То, что вы делаете, кажется, нарушает объектно-ориентированные значения, так как ваши классы необоснованно зависят от чего-то вне их;Чтобы избежать этого, я бы порекомендовал изменить дизайн ваших классов.

При этом, вариант, который подходит вашей модели проектирования, не заставляя каждый класс наследовать другой, состоит в том, чтобы создать класс MyInitializer как объект singleton и добавить MyInitializerкаждый класс, который зависит от этой инициализации.Синглтон выполнит свою инициализацию только при первом его создании.

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