Мегаобучалка Главная | О нас | Обратная связь


В конструкторах предпочитайте инициализацию присваиванию



2016-01-26 370 Обсуждений (0)
В конструкторах предпочитайте инициализацию присваиванию 0.00 из 5.00 0 оценок




 

 

Резюме

 

В конструкторах использование инициализации вместо присваивания для установки значений переменных-членов предохраняет от ненужной работы времени выполнения при том же объеме вводимого исходного текста.

 

Обсуждение

 

Конструкторы генерируют скрытый код инициализации. Рассмотрим следующий код:

class A {

string s1_, s2_;

public:

A() { s1_ = "Hello, "; s2_ = "world"; }

};

В действительности сгенерированный код конструктора выглядит так, как если бы вы написали:

А() : s1_(), s2_() { s1_ = "Hello, "; s2_ = "world"; }

To есть объекты, не инициализированные вами явно, автоматически инициализируются с использованием их конструкторов по умолчанию, после чего выполняется присваивание значений с использованием их операторов присваивания. Чаще всего операторы присваивания нетривиальных объектов выполняют немного больше работы, чем конструкторы, поскольку работают с уже созданными объектами.

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

А() : s1_("Hello, "), s2_("world ") { }

Эта методика не является преждевременной оптимизацией; это — избежание преждевременной пессимизации (см. рекомендацию 9).

 

Исключения

 

Всегда выполняйте захват неуправляемого ресурса (например, выделение памяти оператором new, результат которого не передается немедленно конструктору интеллектуального указателя) в теле конструктора, а не в списке инициализации (см. [Sutter02]). Конечно, лучше всего вообще не использовать таких небезопасных и не имеющих владельца ресурсов (см. рекомендацию 13).

 

Ссылки

 

[Dewhurst03] §51, §59 • [Keffer95] pp.13-14 • [Meyers97] §12 • [Murray93] §2.1.31 • [Sutter00] §8, §47 • [Sutter02] §18

 

Избегайте вызовов виртуальных функций в конструкторах и деструкторах

 

 

Резюме

 

Внутри конструкторов и деструкторов виртуальные функции теряют виртуальность. Хуже того — все прямые или косвенные вызовы нереализованных чисто виртуальных функций из конструктора или деструктора приводят к неопределенному поведению. Если ваш дизайн требует виртуальной передачи в производный класс из конструктора или деструктора базового класса, следует воспользоваться иной методикой, например, постконструкторами.

 

Обсуждение

 

В C++ полный объект конструируется по одному базовому классу за раз.

Пусть у нас есть базовый класс В и класс D, производный от B. При создании объекта D, когда выполняется конструктор В, динамическим типом создаваемого объекта является B. В частности, вызов виртуальной функции B::Fun приведет к выполнению функции Fun, определенной в классе В, независимо от того, перекрывает ее класс D или нет. И это хорошо, поскольку вызов функции-члена D в тот момент, когда члены объекта D еще не инициализированы, может привести к хаосу. Только после завершения выполнения конструктора В выполняется тело конструктора D и объект приобретает тип D. В качестве эмпирического правила следует помнить, что в процессе конструирования В нет никакого способа определить, является ли В отдельным объектом или базовой частью некоторого иного производного объекта.

Кроме того, следует помнить, что вызов из конструктора чисто виртуальной функции, не имеющей определения, приводит к неопределенному поведению.

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

Перекладывание ответственности. Можно просто документировать необходимость вызова в пользовательском коде постконструктора после создания объекта.

Отложенная постинициализация. Можно выполнять постинициализацию при первом вызове функции-члена, для чего использовать в базовом классе флаг типа bool, который показывает, был уже вызван постконструктор или нет.

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

Использование функции-фабрики. Таким способом вы можете легко обеспечить принудительный вызов функции-постконструктора (см. примеры к данной рекомендации).

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

 

Примеры

 

Пример. Использование функции-фабрики для принудительного вызова постконструктора. Рассмотрим следующий код:

class B { // Корень иерархии

protected:

В() {/*...*/ }

virtual void PostInitialize() // Вызывается сразу

{/*...*/} // после конструирования

 

publiс:

template<class T>

static shared_ptr<T> Create() // Интерфейс для

{ // создания объектов

shared_ptr<T> p(new T);

p->PostInitialize();

return p;

}

};

 

class D : public B { /* ... */ }; // Некоторый производный

// класс

 

shared_ptr<D> p = D::Create<D>(); // Создание объекта D

Этот не вполне надежный дизайн основан на ряде компромиссов.

• Производные классы, такие как D, не должны иметь открытых конструкторов. В противном случае пользователи D смогут создавать объекты D, для которых не будет вызываться функция PostInitialize.

• Создание объекта использует оператор new, который, однако, может быть перекрыт классом В (см. рекомендации 45 и 46).

• Класс D обязательно должен определить конструктор с теми же параметрами, что и у конструктора класса B. Смягчить проблему может наличие нескольких перегрузок функции Create; эти перегрузки могут даже быть шаблонами.

• Если перечисленные требования удовлетворены, данный дизайн гарантирует, что функция PostInitialize будет вызвана для любого полностью сконструированного объекта класса, производного от B. Функция PostInitialize не обязательно должна быть виртуальной; однако она может свободно вызывать виртуальные функции.

 

Ссылки

 

[Alexandrescu01] §3 • [Boost] • [Dewhurst03] §75 • [Meyers97] §46 • [Stroustrup00] §15.4.3 • [Taligent94]

 



2016-01-26 370 Обсуждений (0)
В конструкторах предпочитайте инициализацию присваиванию 0.00 из 5.00 0 оценок









Обсуждение в статье: В конструкторах предпочитайте инициализацию присваиванию

Обсуждений еще не было, будьте первым... ↓↓↓

Отправить сообщение

Популярное:
Как распознать напряжение: Говоря о мышечном напряжении, мы в первую очередь имеем в виду мускулы, прикрепленные к костям ...
Почему люди поддаются рекламе?: Только не надо искать ответы в качестве или количестве рекламы...
Как выбрать специалиста по управлению гостиницей: Понятно, что управление гостиницей невозможно без специальных знаний. Соответственно, важна квалификация...



©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (370)

Почему 1285321 студент выбрали МегаОбучалку...

Система поиска информации

Мобильная версия сайта

Удобная навигация

Нет шокирующей рекламы



(0.009 сек.)