Деструктор на языке Си

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

Допустим, деструктор String обозначается так — ~String(). Если он имеет неопределенное значение, его будет давать компилятор. Для большинства классов этого вполне достаточно. Имеется необходимость в определении деструктора, если класс содержит дескрипторы для системных ресурсов.

Особенности

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

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

При объявлении деструкторы иногда представляются как virtual. При применении виртуальных деструкторов появляется возможность ликвидировать данные без знания их вида. При этом неправильный деструктор находится за счет функционала виртуальных опций. Стоит учитывать, что для абстрактных классов деструкторы иногда используются в качестве виртуальных функций. Схема в С++ выглядит так:

#includе <iоstream>
using namespace std;

class NameOfClass
{
privаte:
int a;
public:
NameOfClass(int m);
~NameOfClass();
};

NameOfClass::~NameОfClass()
{
cоut << this->a << endl;
}

NameOfClass::NameОfClass(int m)
{
а = m;
}

Применение

Вызов деструкторов происходит в следующих случаях:

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

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

Удаление

При выхождении объекта за пределы области, процесс его удаления выглядит так:

  1. Первым делом находится деструктор, а затем проводится тело его функции.
  2. Вызов деструкторов для объектов нестатических элементов происходит в таком порядке, при котором все действия приравниваются в обратному процессу появления в объявлении класса. В подобных ситуациях необязательный перечень идентификации данных не оказывает влияния на системные процессы.
  3. Деструкторы для невиртуальных классов стоит вызывать в обратную последовательность объявления.

Деструкторы для виртуальных базовых классов необходимо находить в той последовательности, которая будет соответствовать обратному порядку объявления.

Виртуальные базовые классы

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

  1. Посмотреть на левую часть графа, процесс начинается с самого глубокого его значения.
  2. Посмотреть на граф в направлении справа налево до периода, пока не будут проанализированы все узлы. Название текущего узла потребуется запомнить.
  3. Посмотреть на предыдущий узел по направлению снизу направо. Это необходимо для того, чтобы определить, можно ли назвать исследуемый узел виртуальным базовым классом.
  4. Если он таковым является, то необходимо посмотреть на список, был ли этот узел введен ранее. Если он не будет назван виртуальным базовым классом, его потребуется игнорировать.
  5. Если исследуемого узла нет в списке, его следует в него добавить.
  6. Далее необходимо просмотреть граф сверху и вдоль.
  7. Повторить действие из шага 2.
  8. Если путь вверх был исчерпан, пользователю необходимо записать название текущего узла.
  9. Повторить шаг из пункта 3.
  10. Повторять действия до тех пор, пока нижний узел не станет вновь текущим узлом.

В течение этого процесса будет составлен перечень уникальных записей. В такой ситуации имя классов не будет указываться два раза подряд. Далее список будет просмотрен в обратном порядке. Затем произойдет вызов деструктора для всех классов в перечне от крайнего к первому. Схема виртуального деструктора:

сlass Father
{
publiс:
Father() {}
~Fathеr() {}
};

class Son : public Father
{
publiс:
int* buffer;
Sоn() : Father() { buffеr = new int[1024]; }
~Son() { delеte[] buffеr; }
};

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

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

Явный вызов деструктор

Нужда в явном вызове случается крайне редко. Иногда может быть необходимо удаление объектов, которые размещены по абсолютным адресам. Чаще всего они обозначаются при помощи new оператора, который принимает аргумент размещения. Delete оператор не имеет возможности освобождать эту память из-за того, что она не выделяется из бесплатного хранилища. Вызов деструктора позволяет произвести соответствующую очистку.

s.String::~String(); // non-virtual call
ps->String::~String(); // non-virtual call

s.~String(); // Virtual call
ps->~String(); // Virtual call

Отказоустойчивость

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

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

void copy_strings()
{
String str1(«I have a sense of impending disaster…»);
String str2 = str1; // str1.text and str2.text now refer to the same object
} // delete[] _text; deallocates the same memory twice
// undefined behavior

Особенности Идиомы программирования RAII

Идиома RAII — элемент объектно-ориентированного программирования, которая позволяет ресурсам иметь отношение к времени жизни объектов с автоматической продолжительностью жизни. В языке программирования C++ ее реализация происходит через классы, имеющие конструкторы и деструкторы. Приобретение ресурса чаще всего происходит в конструкторе. Ресурсом может выступать память или, например, база данных. В некоторых случаях ресурс может получаться и после нахождения объекта.

Далее ресурс применяется до тех пор, пока объект жив. Ресурс удаляется в деструкторе после уничтожения объекта. Одно из главных достоинств RAII — предотвращение потери данных (допустим, памяти, которая не освободилась). Это происходит за счет того, что все объекты, содержащие ресурсы, проходят автоматическую очистку.

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

Важно учитывать, что очистку динамически обозначенных объектов пользователь ответственности не несет. Если по каким-то причинам он забыл ее провести, вызов деструктора не произойдет. В итоге это приведет потери памяти.

Класс Massiv считается одним из примеров класса, функционирующего на основе идиомы RAII. Обозначение здесь происходит в конструкторе, а освобождение в деструкторе. Std::string и std::vector — классы языка программирования C++, которые также соответствуют стандартам инструмента RAII.

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

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

Особенности композиции объектов

Композиция нужна для создания новых объектов. В этом случае типом отношений будет «имеет». Но важно учитывать, что композицию объектов можно назвать одним из двух главных методов, предоставляющих возможность пользователю получить сложные классы в языке программирования C++. Второй вариант — это заимствование, способное моделировать вид отношения «является» между объектами.

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

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

Техника (ПК, телефоны) также наследует функционал от своих технологических предшественников. При этом в ней появляется что-то свое: функции и опции. Она сохраняет обратную совместимость. Например, процессор Intel Pentium перенял многие функциональные свойства от процессора Intel 486, который, в свою очередь, получил свои особенности от более ранних устройств. Язык C++ многое получил от ЯП Си, на котором он и был создан. ЯП Cи получил свои характеристики и функции от других языков, которые были востребованы до него.

Здесь можно рассмотреть пример с яблоками и бананами. Хотя яблоко и банан — это непохожие изделия, они имеют разную форму, цвет, вкус, состав. Но у них есть общее качество: они оба они относятся к категории фруктов. Раз яблоки и бананы — это фрукты, то, по правилам, всё, что верно для фруктов, подходит и для яблок с бананами.

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

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

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