Урок 120. Деструкторы

   | 

   | 

 Обновлено 1 Мар 2018  | 

 2278

Деструктор — это еще один специальный тип функции-члена класса, который выполняется при удалении объекта класса. В то время как конструкторы предназначены для инициализации класса, деструкторы предназначены для его очистки.

Когда объект автоматически выходит из области видимости или динамически выделенный объект явно удаляется с помощью ключевого слова delete, вызывается деструктор класса (если он существует) для выполнения необходимой очистки до того, как объект будет удален из памяти. Для простых классов (тех, которые только инициализируют значения обычных переменных-членов) деструктор не нужен, так как C++ автоматически очистит память сам.

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

Имена деструкторов

Так же, как и конструкторы, деструкторы имеют определенные правила, которые касаются их названий:

  Деструктор должен иметь то же имя, что и класс, со знаком тильда (~) в самом начале.

  Деструктор не может принимать аргументы.

  Деструктор не имеет типа возврата.

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



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

Пример деструктора

Рассмотрим простой класс с деструктором:

Результат выполнения программы выше:

The value of element 7 is 8

В первой строке функции main() мы создаем новый объект класса Massiv с именем arr и передаем длину (length) 15. Это приводит к вызову конструктора, который динамически выделяет память для массива-члена класса (m_array). Мы должны здесь использовать динамическое выделение, поскольку на момент компиляции мы не знаем длину массива (это значение нам передает caller).

В конце main() объект arr выходит из области видимости. Это приводит к вызову деструктора ~Massiv() и удалению массива, который мы выделили в конструкторе!

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

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

Результат:

Constructing Another 1
1
Constructing Another 2
2
Destructing Another 2
Destructing Another 1

Обратите внимание, «Another 1» уничтожается после «Another 2», так как мы удалили pObject до завершения функции main(), тогда как объект object не был удален до конца main().

Идиома программирования RAII

RAIIResource Acquisition Is Initialization» переводится как «Получение ресурсов есть инициализация») — это идиома объектно-ориентированного программирования, при которой использование ресурсов привязывается к времени жизни объектов с автоматической продолжительностью (например, не динамически выделенные объекты). В C++ RAII реализуется через классы с конструкторами и деструкторами. Ресурс (например, память, файл или база данных) обычно приобретается в конструкторе объекта (хотя этот ресурс может быть получен и после создания объекта, если в этом есть смысл). Затем этот ресурс можно использовать, пока объект жив. Ресурс освобождается в деструкторе при уничтожении объекта. Основным преимуществом RAII является то, что это помогает предотвратить утечку ресурсов (например, память, которая не была освобождена), так как все объекты, содержащие ресурсы, автоматически очищаются.

В рамках идиомы программирования RAII объекты, располагающие ресурсами, не должны быть динамически выделенными, так как деструкторы вызываются только при уничтожении объектов. Для объектов, выделенных в Стеке, это происходит автоматически, когда объект выходит из области видимости, поэтому нет необходимости беспокоиться о том, что ресурс в конечном итоге не будет очищен. Однако с динамически выделенными объектами, которые выделяются из Кучи, уже пользователь несет ответственность за очистку — если он забыл её выполнить, деструктор вызываться не будет, и память как для объекта класса, так и для управляемого ресурса будет потеряна – произойдет утечка памяти!

Класс IntArray в начале этого урока является примером класса, который реализует принцип RAII — выделение в конструкторе, освобождение в деструкторе. std::string и std::vector – это примеры классов в стандартной библиотеке, которые следуют принципам RAII — динамическая память приобретается при инициализации и автоматически очищается при уничтожении.

Правило: Используйте идиому программирования RAII и не выделяйте объекты вашего класса динамически.

Предупреждение о функции exit()

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

Итого

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

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (21 оценок, среднее: 5,00 из 5)
Загрузка...
Подписаться на обновления:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

ПОДПИСЫВАЙТЕСЬ

НА КАНАЛ RAVESLI В TELEGRAM

@ravesli

ПОДПИСАТЬСЯ БЕСПЛАТНО