Деструкторы

Мы познакомились с деструкторами в главе, посвященной структурам (см. раздел Конструкторы и деструкторы). Напомним вкратце, что деструктор — это метод, вызываемый в момент удаления объекта. Деструктор имеет имя, совпадающее с именем класса, но к нему спереди добавлен префикс в виде символа тильды '~'. Деструктор не возвращает значения и не имеет параметров. Деструкторов может быть не больше одного.

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

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

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

Для знатоков C++ отметим, что в MQL5 деструкторы всегда виртуальные (мы обратимся к понятию виртуальных методов в разделе Виртуальные методы (virtual и override)). На синтаксис описания этот фактор не влияет.

В примере программы рисования, в принципе, не требуется деструктор для фигур, но мы его заведем, чтобы проследить последовательность вызовов конструкторов и деструкторов. Начнем с упрощенного наброска, в котором "печатается" полное название метода:

class Shape
{
   ...
   ~Shape()
   {
      Print(__FUNCSIG__);
   }
};

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

Рассмотрим следующий пример. Пара объектов Shape описана в двух разных контекстах: глобальном (вне функций) и локальном (внутри OnStart). Конструктор глобального объекта будет вызван после загрузки скрипта и до вызова OnStart, а деструктор — перед выгрузкой скрипта. Конструктор локального объекта будет вызван в строке с определением переменной, а деструктор — при выходе из блока кода, содержащего это определение, в данном случае, из функции OnStart.

// конструктор и деструктор global связаны с загрузкой и выгрузкой скрипта 
Shape global;
 
// ссылка на объект не создает копию и не влияет на время жизни
void ProcessShape(Shape &shape)
{
   // ...
}
 
void OnStart()
{
   // ...
   Shape local// <- вызов конструктора local
   // ...
   ProcessShape(local);
   // ...
// <- вызов деструктора local

Передача объекта по ссылке в другие функции не создает его копий, не вызывает конструктор и деструктор.