14.3.1. Явный вызов деструктора
Иногда вызывать деструктор для некоторого объекта приходится явно. Особенно часто такая необходимость возникает в связи с оператором new (см. раздел 8.4). Рассмотрим пример. Когда мы пишем:
char *arena = new char[ sizeof Image ];
то из хипа выделяется память, размер которой равен размеру объекта типа Image, она не инициализирована и заполнена случайными битами. Если же написать:
Image *ptr = new (arena) Image( «Quasimodo»);
то никакой новой памяти не выделяется. Вместо этого переменной ptr присваивается адрес, ассоциированный с переменной arena. Теперь память, на которую указывает ptr, интерпретируется как занимаемая объектом класса Image, и конструктор применяется к уже существующей области. Таким образом, оператор размещения new() позволяет сконструировать объект в ранее выделенной области памяти.
Закончив работать с изображением Quasimodo, мы можем произвести какие-то операции с изображением Esmerelda, размещенным по тому же адресу arena в памяти:
Image *ptr = new (arena) Image( «Esmerelda» );
Однако изображение Quasimodo при этом будет затерто, а мы его модифицировали и хотели бы записать на диск. Обычно сохранение выполняется в деструкторе класса Image, но если мы применим оператор delete:
// плохо: не только вызывает деструктор, но и освобождает память
то, помимо вызова деструктора, еще и возвратим в хип память, чего делать не следовало бы. Вместо этого можно явно вызвать деструктор класса Image:
сохранив отведенную под изображение память для последующего вызова оператора размещения new.
Отметим, что, хотя ptr и arena адресуют одну и ту же область памяти в хипе, применение оператора delete к arena
// деструктор не вызывается
не приводит к вызову деструктора класса Image, так как arena имеет тип char*, а компилятор вызывает деструктор только тогда, когда операндом в delete является указатель на объект класса, имеющего деструктор.
Читайте также
Вызов функций
Вызов функций После объявления функции ее можно вызвать из любого Web-сценария, присутствующего на этой же Web-странице. Формат вызова функции:<имя функции>([<список фактических параметров, разделенных запятыми>])Здесь указывается имя нужной функции и в круглых
Вызов приложений
Вызов приложений Открываются окна обычно вместе с запущенными в них приложениями. Способов же запуска последних в Cinnamon, как и во всех современных десктопах, несколько.Первый, наиболее универсальный – запуск из командной строки терминала путём ввода соответствующей
Вызов функций
Вызов функций После объявления функции ее можно вызвать из любого Web-сценария, присутствующего на этой же Web-странице. Формат вызова функции:<имя функции>([<список фактических параметров, разделенных запятыми>])Здесь указывается имя нужной функции и в круглых
21.1.1. Вызов gcc
21.1.1. Вызов gcc Формат вызова компилятора такой:gcc [опции] helloworld.сВы написали свою программу на С++? Нет проблем: компиляторы С и С++ являются интегрированными. Чтобы сообщить компилятору, на каком языке вы писали свою программу, нужно указать определенную опцию при вызове
Вызов
Вызов Вызвать любого человека из вашего контакт-листа можно несколькими способами. Во-первых, просто щелкнуть по нему мышкой. Или щелкнуть мышкой – но уже правой кнопкой, чтобы появилось контекстное меню – в этом случае мы можем точно выбрать вид связи, либо просто
R.5.2.2 Вызов функции
R.5.2.2 Вызов функции Вызов функции является постфиксным выражением, за которым следует, возможно пустой, список выражений в скобках, разделенных запятой. Эти выражения образуют фактические параметры функции. Постфиксное выражение должно иметь тип "функция, возвращающая T",
R.13.4.4 Вызов функции
R.13.4.4 Вызов функции Вызов функции есть конструкция вида:первичное-выражение ( список-выражений opt )Она считается бинарной операцией, в которой первичное-выражение представляет первый операнд, а список-выражений (возможно пустой), — второй операнд. Именем, задающим функцию,
7.9.3. Вызов
7.9.3. Вызов Указатель на функцию применяется для вызова функции, которую он адресует. Включать оператор разыменования при этом необязательно. И прямой вызов функции по имени, и косвенный вызов по указателю записываются одинаково:#includeint min( int*, int );int (*pf)( int*, int ) = min;const int iaSize
6.8 Вызов Функции
6.8 Вызов Функции Вызов функции, то есть запись выражение(список_выражний), можно проинтерпретировать как бинарную операцию, и операцию вызова можно перегружать так же, как и другие оперции. Список параметров функции operator() вычисляется и прверяется в соответствие с
19.11. Вызов функций
19.11. Вызов функций В завершение этой главы рассмотрим два различных способа работы с функциями: вызов функций из исходного файла и применение функций, размещенных в
Вызов компонента
Вызов компонента Отмеченная звездочками инструкцияp1.translate (4.0, -1.5)заслуживает внимательного изучения, поскольку представляет собой первый пример использования базового механизма ОО-вычислений (basic mechanism of object-oriented computation). Это обращение к компоненту или вызов компонента
Вызов функций
Вызов функций После того как мы объявили функцию, мы можем вызвать ее из любого места кода в пределах области видимости. Для этого используется следующий формат: <Имя функции>([<Список фактических параметров, разделенных запятыми>]) Здесь указывается имя нужной
Урок №120. Деструкторы
На этом уроке мы рассмотрим, что такое деструкторы в языке С++, зачем они нужны, как их использовать и нюансы, которые могут возникнуть при их использовании.
Деструкторы
Деструктор — это специальный тип метода класса, который выполняется при удалении объекта класса. В то время как конструкторы предназначены для инициализации класса, деструкторы предназначены для очистки памяти после него.
Когда объект автоматически выходит из области видимости или динамически выделенный объект явно удаляется с помощью ключевого слова delete, вызывается деструктор класса (если он существует) для выполнения необходимой очистки до того, как объект будет удален из памяти. Для простых классов (тех, которые только инициализируют значения обычных переменных-членов) деструктор не нужен, так как C++ автоматически выполнит очистку самостоятельно.
Однако, если объект класса содержит любые ресурсы (например, динамически выделенную память или файл/базу данных), или, если вам необходимо выполнить какие-либо действия до того, как объект будет уничтожен, деструктор является идеальным решением, поскольку он производит последние действия с объектом перед его окончательным уничтожением.
Имена деструкторов
Так же, как и конструкторы, деструкторы имеют свои правила, которые касаются их имен:
деструктор должен иметь то же имя, что и класс, со знаком тильда (
деструктор не может принимать аргументы;
деструктор не имеет типа возврата.
Из второго правила вытекает еще одно правило: для каждого класса может существовать только один деструктор, так как нет возможности перегрузить деструкторы, как функции, и отличаться друг от друга аргументами они не могут.
Пример использования деструктора на практике
Рассмотрим простой класс с деструктором:
Massiv() вызывается тоже здесь
Результат выполнения программы:
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
Идиома RAII (англ. «Resource Acquisition Is Initialization» = «Получение ресурсов есть инициализация») — это идиома объектно-ориентированного программирования, при которой использование ресурсов привязывается к времени жизни объектов с автоматической продолжительностью жизни. В языке C++ идиома RAII реализуется через классы с конструкторами и деструкторами. Ресурс (например, память, файл или база данных) обычно приобретается в конструкторе объекта (хотя этот ресурс может быть получен и после создания объекта, если в этом есть смысл). Затем этот ресурс можно использовать, пока объект жив. Ресурс освобождается в деструкторе при уничтожении объекта. Основным преимуществом RAII является то, что это помогает предотвратить утечку ресурсов (например, памяти, которая не была освобождена), так как все объекты, содержащие ресурсы, автоматически очищаются.
В рамках идиомы программирования RAII объекты, располагающие ресурсами, не должны быть динамически выделенными, так как деструкторы вызываются только при уничтожении объектов. Для объектов, выделенных из стека, это происходит автоматически, когда объект выходит из области видимости, поэтому нет необходимости беспокоиться о том, что ресурс в конечном итоге не будет очищен. Однако за очистку динамически выделенных объектов, которые выделяются из кучи, уже пользователь несет ответственность: если он забыл её выполнить, деструктор вызываться не будет, и память как для объекта класса, так и для управляемого ресурса будет потеряна — произойдет утечка памяти!
Класс Massiv из программы, приведенной в начале этого урока, является примером класса, который реализует принципы RAII: выделение в конструкторе, освобождение в деструкторе. std::string и std::vector — это примеры классов из Стандартной библиотеки С++, которые следуют принципам RAII: динамическая память выделяется при инициализации и автоматически освобождается при уничтожении.
Правило: Используйте идиому программирования RAII и не выделяйте объекты вашего класса динамически.
Предупреждение о функции exit()
Если вы используете функцию exit(), то ваша программа завершится, и никакие деструкторы не будут вызваны. Будьте осторожны, если в таком случае вы полагаетесь на свои деструкторы для выполнения необходимой работы по очистке (например, перед тем, как выйти, вы записываете что-нибудь в лог-файл или в базу данных).
Заключение
Используя конструкторы и деструкторы, ваши классы могут выполнять инициализацию и очистку после себя автоматически без вашего участия! Это уменьшает вероятность возникновения ошибок и упрощает процесс использования классов.
Конструктор и деструктор класса в C++
Возможно вы заметили, что определяя класс, мы не можем инициализировать его поля (члены) в самом определении. Можно присвоить им значение, написав соответствующий метод класса и вызвав его, после создания объекта вне класса. Такой способ не совсем удобен, так как объявляя, допустим, 33 объекта класса нам придется 33 раза вызывать метод, который присваивает значения полям класса. Поэтому, как правило, для инициализации полей класса, а так же для выделения динамической памяти, используется конструктор.
Конструктор (от construct – создавать) – это особый метод класса, который выполняется автоматически в момент создания объекта класса. То есть, если мы пропишем в нем, какими значениями надо инициализировать поля во время объявления объекта класса, он сработает без “особого приглашения”. Его не надо специально вызывать, как обычный метод класса.
В строках 11 – 17 определяем конструктор: имя должно быть идентично имени класса; конструктор НЕ имеет типа возвращаемого значения (void в том числе). Один объект объявляется сразу во время определения класса – строка 25. При запуске программы, конструктор этого объекта сработает даже до входа в главную функцию. Это видно на следующем снимке:

программа еще не дошла до выполнения строки 29 setlocale ( LC_ALL , «rus» ) ; , а конструктор уже “отчитался”, что сработал (кириллица отобразилась некорректно). В строке 30 – смотрим, что содержат поля класса. Второй раз конструктор сработает в строке 32, во время создания объекта obj2 .
Деструктор (от destruct – разрушать) – так же особый метод класса, который срабатывает во время уничтожения объектов класса. Чаще всего его роль заключается в том, чтобы освободить динамическую память, которую выделял конструктор для объекта. Имя его, как и у конструктора, должно соответствовать имени класса. Только перед именем надо добавить символ
Добавим деструктор в предыдущий код. И создадим в классе два конструктора: один будет принимать параметры, второй – нет.