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




>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.