Читаем Программирование полностью

  Если класс обладает ресурсами, то он должен иметь деструктор. Ресурс — это то, что вы “где-то взяли” и должны вернуть, когда закончите его использовать. Очевидным примером является память, выделенная с помощью оператора new, которую вы должны освободить, используя оператор delete или delete[]. Для хранения своих элементов наш класс vector требует память, поэтому он должен ее вернуть; следовательно, он должен иметь деструктор. Другие ресурсы, которые используются в более сложных программах, — это файлы (если вы открыли файл, то должны его закрыть), блокировки (locks), дескрипторы потоков (thread handles) и двунаправленные каналы (sockets), используемые для обеспечения взаимосвязи между процессами и удаленными компьютерами.

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

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

  Если производный класс должен иметь деструктор, то базовый класс должен иметь виртуальный деструктор (см. раздел 17.5.2).

<p id="AutBody_Root327"><strong>18.3.1. Явные конструкторы</strong></p>

Конструктор, имеющий один аргумент, определяет преобразование типа этого аргумента в свой класс. Это может оказаться очень полезным. Рассмотрим пример.

class complex {

public:

  complex(double); // определяет преобразование double в complex

  complex(double,double);

  // ...

};

complex z1 = 3.18; // OK: преобразует 3.18 в (3.18,0)

complex z2 = complex(1.2, 3.4);

  Однако неявные преобразования следует применять скупо и осторожно, поскольку они могут вызвать неожиданные и нежелательные эффекты. Например, наш класс vector, определенный выше, имеет конструктор, принимающий аргумент типа int. Отсюда следует, что он определяет преобразование типа int в класс vector. Рассмотрим пример.

class vector {

  // ...

vector(int);

  // ...

};

vector v = 10;  // создаем вектор из 10 элементов типа double

v = 20;         // присваиваем вектору v новый вектор

                // из 20 элементов типа double to v

void f(const vector&);

f(10);          // Вызываем функцию f с новым вектором,

                // состоящим из 10 элементов типа double

  Кажется, мы получили больше, чем хотели. К счастью, подавить такое неявное преобразование довольно просто. Конструктор с ключевым словом explicit допускает только обычную семантику конструирования и не допускает неявные преобразования. Рассмотрим пример.

class vector {

  // ...

  explicit vector(int);

  // ...

};

vector v = 10;  // ошибка: преобразования int в vector нет

v = 20;         // ошибка: преобразования int в vector нет

vector v0(10);  // OK

void f(const vector&);

f(10);          // ошибка: преобразования int в vector нет

f(vector(10)); // OK

Для того чтобы избежать неожиданных преобразований, мы — и стандарт языка — потребовали, чтобы конструктор класса vector с одним аргументом имел спецификатор explicit. Очень жаль, что все конструкторы не имеют спецификатора explicit по умолчанию; если сомневаетесь, объявляйте конструктор, который может быть вызван с одним аргументом, используя ключевое слово explicit

<p id="AutBody_Root328"><strong>118.3.2. Отладка конструкторов и деструкторов</strong></p>

  Конструкторы и деструкторы вызываются в точно определенных и предсказуемых местах программы. Однако мы не всегда пишем явные вызовы, например vector(2); иногда мы пишем объявление объекта класса vector, передаем его как аргумент функции по значению или создаем в свободной памяти с помощью оператора new. Это может вызвать замешательство у людей, думающих в терминах синтаксиса. Не существует синтаксической конструкции, которая осуществляла бы диспетчеризацию вызовов конструкторов. О конструкторах и деструкторах проще думать следующим образом.

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже