Re[5]: Внешний конструктор - пример
От: jazzer Россия Skype: enerjazzer
Дата: 20.08.15 17:13
Оценка: 1 (1)
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, jazzer, Вы писали:


J>>В общем, стандартные прелести двухфазки


U>на самом деле это отличается от двухфазовой инициализации, т.к. к моменту завершения работы конструктора объект полностью инициализирован и находится в консистентном состоянии. готов к использованию.


Это несущественно, так как ничем не отличается от производного класса, который в своем конструкторе делает все те же самые присваивания. А насчет консистентности состояния — несколько странно оставлять консистентность на откуп внешней функции, не находишь? Ведь функция может ничего и не делать, сразу return... Так что консистентность должна быть сразу, и без этой функции.

У меня в проекте похожий подход используется, тоже последним параметром в конструктор базового класса передается std::function) — и делается это просто для того, чтоб прогнать какой-то код (какой- определяется наследником) между конструктором базы и конструктором наследника. Но по сути это все равно двухфазка, просто вторая фаза срабатывает между конструкторами.
Код примерно такой (пример умозрительный, реально там образуется куча подключений, memory-mapped файлов и всего такого прочего, плюс кое-какие данные заполняются в самом Base определенным образом, как нужно наследнику, зовутся всякие веселые инициализаторы, написанные на Питоне, и вот в таком роде все веселье):
struct Base {
  string path_;

  Base( string app_name, std::function<void()> post_init = Base::default_init )
    : path_(private_common_data_path + app_name)
  {
    post_init();
  }
};

struct Derived : Base {
  ostream logger_;
  ComponentX x_;
  Derived(string app_name)
    : Base( app_name, [this]{ mkdir(path_); } )
    , logger_( path_ + "/log.txt" )
    , x_( logger_ )
  {}
};

тут у нас есть компонент x_ (один из многих), который использует логгер (он отдается ему в конструктор и должен быть уже полностью рабочим к этому моменту).
Логгер — это член logger_, который открывает файл на запись — но чтобы открыть файл, нам нужно сначала создать директорию, где этот файл будет лежать.
Класс Base ничего ни о каких директориях не знает, он просто генерит и хранит путь.
Получается, директорию надо создать после того, как отработал конструктор Base (потому что нам нужен уже готовый path_), но до того, как начнет работать конструктор Derived, инициализирующий все его компоненты (т.е. тело конструктора Derived отпадает).
Именно это и делает post_init() выше (на самом деле, там после post_init в конструкторе Base еще кое-что зовется, типа проверок консистентности всех сгенеренных/измененных данных и т.д., плюс между Base и Derived есть еще пара классов, так что совсем вынести всё в промежуточного наследника легко не удастся). При этом по умолчанию Base кое-какие действия тоже предпринимает (Base::default_init), но это можно выключить и предоставить свои (как замену или как дополнение — никто не мешает позвать Base::default_init внутри твоего производного post_init).
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[3]: Внешний конструктор
От: omgOnoz  
Дата: 20.08.15 20:41
Оценка:
Здравствуйте, enji, Вы писали:

E>Это ж кучу вспомогательного кода надо написать.


Кодогенераторы в помощь
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.