Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ - [18]

Шрифт
Интервал

Конечно, применимость идеи функций, возвращающих ссылки, для предотвращения проблем, связанных с порядком инициализации, зависит от того, существует ли в принципе разумный порядок инициализации ваших объектов. Если вы напишете код, в котором объект A должен быть инициализирован прежде, чем объект B, и одновременно сделаете инициализацию A зависимой от инициализации B, то вас ждут проблемы – и поделом! Если, однако, вы будете избегать таких патологических ситуаций, то описанная схема сослужит вам добрую службу, по крайней мере, в однопоточных приложениях.

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

Что следует помнить

• Всегда вручную инициализировать объекты встроенных типов, поскольку C++ делает это, только не всегда.

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

• Избегайте проблем с порядком инициализации в разных единицах трансляции, заменяя нелокальные статические объекты локальными статическими объектами.

Глава 2

Конструкторы, деструкторы и операторы присваивания

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

Правило 5: Какие функции C++ создает и вызывает молча

Когда пустой класс перестает быть пустым? Когда за него берется C++. Если вы не объявите конструктор копирования, оператор присваивания или деструктор самостоятельно, то компилятор сделает это за вас. Более того, если вы не объявите вообще никакого конструктора, то компилятор автоматически создаст конструктор по умолчанию. Все эти функции будут открытыми и встроенными (см. правило 30). Например, такое объявление:


>class Empty {};


эквиваленто следующему:


>class Empty {

>public:

>Empty() {...} // конструктор по умолчанию

>Empty(const Empty& rhs) {...} // конструктор копирования

>~Empty() {...} // деструктор – см. ниже

>// о виртуальных деструкторах

>Empty& operator=(const Empty& rhs) {...} // оператор присваивания

>};


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


>Empty e1; // конструктор по умолчанию;

>// деструктор

>Empty e2(e1); // конструктор копирования

>e2 = e1; // оператор присваивания


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

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


>Template

>class NamedObject {

>public:

>NamedObject(const char *name, const T& value);

>NamedObject(const std::string& name, const T& value);

>...

>private:

>std:string nameValue;

>T objectValue;

>};


Поскольку в классе NamedObject объявлен конструктор, компилятор не станет генерировать конструктор по умолчанию. Это важно. Значит, если вы спроектировали класс так, что его конструктору обязательно должны быть переданы какие-то аргументы, то вам не нужно беспокоиться, что компилятор проигнорирует ваше решение и по собственной инициативе добавит еще и конструктор без аргументов.

В классе NamedObject нет ни конструктора копирования, ни оператора присваивания, поэтому компилятор сгенерирует их (при необходимости). Посмотрите на следующее употребление конструктора копирования:


>NamedObjectno1(“Smallest Prime Number”, 2);

>NamedObjectno2(no1); // вызывается конструктор копирования


Конструктор копирования, сгенерированный компилятором, должен инициализировать no2.nameValue и no2.objectValue, используя nol.nameValue и nol.objectValue соответственно. Член nameValue имеет тип string, а в стандартном классе string объявлен конструктор копирования, поэтому no2. nameValue будет инициализирован вызовом конструктора копирования string с аргументов nol.nameValue. С другой стороны, член NameObject::objectValue имеет тип int (поскольку T есть int в данной конкретизации шаблона), а int – встроенный тип, поэтому no2.objectValue будет инициализирован побитовым копированием nol.objectValue.


Рекомендуем почитать
Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


MFC и OpenGL

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


Симуляция частичной специализации

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


Обработка событий в С++

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


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.