This page was last modified 07:32, 14 July 2007.
Двухфазное конструирование
From Forum Nokia Wiki
Перевод с английского, оригинальная статья находится здесь
Contents |
Краткое описание
В C++ программировании под Symbian ОС концепция двухфазного конструирования используется для того чтобы гарантировано избежать сброса(leave) при создании и инициализации объектов перед тем, как поместить их в стек очистки (cleanup stack).
Причины возникновения проблемы
В C++ программировании под Symbian ОС указатели на объекты в динамической памяти сохраняются в стеке очистки. Это позволяет автоматически уничтожать объекты в случае возникновения сброса(leave). Вновь созданный объект помещается в стек очистки сразу же после создания и инициализации его с помощью конструктора. Согласно правилу, принятому в C++, после выделения памяти для объекта и до завершения выполнения конструктора объект не может быть сохранен в стеке очистки - вот почему возникновение сброса(leave) в это время недопустимо.
Решение проблемы и его последствия
Для того чтобы предотвратить возникновение исключительной ситуации до того, как объект помещен в стек очистки, нужно предотвратить возникновение исключения в конструкторе. Этого можно достигнуть, если реализовать двухфазный конструктор. Согласно концепции двухфазного конструирования, весь код, который может привести к сбросу, а так же все вызовы функций, который могут выбросить исключения выносят из конструктора в отдельный метод ConstructL() - он и является второй фазой конструирования. (Если предок содержит свой собственный метод ConstructL() - он должен быть явно вызван во время второй фазы конструирования объекта.) Вызов ConstructL() производится после создания объекта и помещения его в стек очистки. Для часто используемых классов вызов обычного конструктора и метода ConstructL() объединяют в одну специальную статическую функцию NewL(). Соответственно, для создания объекта нужно пользоваться этой функцией-фабрикой. Алгоритм работы этой функции следующий:
- выделить память под объект
- поместить его в стек очистки
- выполнить вторую фазу конструирования (вызвать ConstructL())
- извлечь созданный и проинициализированный объект из стека очистки
Если подразумевается дальнейшее использование вновь созданного объекта как локального (то есть он должен находится в стеке очистки все свое время жизни) реализуется дополнительная статическая функция NewLC() - алгоритм ее работы такой же как и у NewL() за одним исключением - вновь созданный объект не извлекается из стека очистки. Пример реализации ConstructL(), NewL(), NewLC():
// Фаза #1 CMyClass::CMyClass() { } // Фаза #2 void CMyClass::ConstructL() { // Инициализация членов-данных } // Обе фазы создания вместе ... CMyClass * CMyClass::NewL() { CMyClass * self = new (ELeave) CMyClass(); CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(self); return self; } CMyClass * CMyClass::NewLC() { CMyClass * self = new (ELeave) CMyClass(); CleanupStack::PushL(self); self->ConstructL(); return self; }
В будущем...
Строго говоря, нет необходимости поддерживать двухфазное конструирование и стек очистки после того, как в Symbian появилась поддержка стандартных возможностей C++ и реализация сбросов(leaves) была переделана на обычные исключения. Exception/Leave - защищенный код гораздо проще реализовать в стандартном C++, однако Symbian продолжает поддерживать старый вариант реализации в целях совместимости. Когда-нибудь в будущем (будем надеется недалеком) мы сможем забыть эту изжившую себя концепцию и использовать стандартный C++ для разработки ПО под Symbian ОС.
