Зачем нужен конструктор и деструктор

Обновлено: 20.05.2024

Конструктор - блок инструкций, вызываемый при создании объекта класса.

Деструктор - блок инструкций, вызываемый при уничтожении объекта класса.

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

Назначение конструктора: присвоение каких-то значений полям, выделение памяти, открытие файлов и установление сетевых соединений.

Назначение деструктора: соответственно, наоборот очистка памяти и сохранение файлов.

Параметры конструктора. Конструктор может принимать какие-то значения в качестве аргументов - например, для установки значений полям. Также возможно создание нескольких конструкторов с разными параметрами. Деструктор всегда один и без параметров.

Оформление конструктора (внутри класса):

Оформление деструктора (внутри класса):

Пример вызова конструктора:

Чтобы исключить вызов конструктора через присваивание, как показано в последнем примере, к конструктору дописывается ключевое слово explicit (перед конструктором).

Для конструктора, конструкторов копирования и перемещения, а также деструктора справедливо следующее: если программист не написал их сам, то компилятор допишет их за него. Такие конструкторы(деструкторы) называются неявными. Также важно понимать, что конструктор, декструтор, к.копирования и к.перемещения должны иметь доступ модификатор доступа public.

Это конструктор вида (для случая внутри класса):

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

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

Благодаря этому конструктору, мы можем, например, писать вот так:

В данном примере для a2 и a3 будет вызван конструктор копирования, который скопирует каждое поле a1.

Самое главное про конструктор копирования: если вы оставили для полей-указателей простое копирование, то, когда в первом объекте (который мы копировали) они будут почищены (например, деструктором), во втором они также почистятся, а этого вы, скорее всего, не задумываете.

Если мы захотим сломать конструктор копирования, указав явно только один его вариант, который принимает не только ссылку, но и другие аргументы, то программа сама додумает за нас самый просто конструктор копирования с единственным аргументом (ссылкой). И пример, приведённый выше, всё ещё будет работать. А вот если мы сами напишем конструктор копирования с одним аргументом (ссылкой) и добавим к нему слово explicit, то пример работать перестанет, так как из-за третьей строчки программа не скомпилируется.

Также обратите внимание, что переопределив конструктор копирования вы никак не затронете обычные операции присваивания.

Если мы переопределили конструктор копирования, чтобы он, например, менял местами поля и дописывал всякие нехорошие слова к полям-строкам, а затем запустили пример, приведённый выше, то эти действия произведутся только для a1, а объект a2 будет идентичен объекту b.

Это конструктор вида (для случая внутри класса):

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

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

Конструктор (от construct – создавать) – это особый метод класса, который выполняется автоматически в момент создания объекта класса. То есть, если мы пропишем в нем, какими значениями надо инициализировать поля во время объявления объекта класса, он сработает без “особого приглашения”. Его не надо специально вызывать, как обычный метод класса.

В строках 11 – 17 определяем конструктор: имя должно быть идентично имени класса; конструктор НЕ имеет типа возвращаемого значения (void в том числе). Один объект объявляется сразу во время определения класса – строка 25. При запуске программы, конструктор этого объекта сработает даже до входа в главную функцию. Это видно на следующем снимке:

конструктор класса c++, деструктор класса c++, конструктор и деструктор класса с++

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

Деструктор (от destruct – разрушать) – так же особый метод класса, который срабатывает во время уничтожения объектов класса. Чаще всего его роль заключается в том, чтобы освободить динамическую память, которую выделял конструктор для объекта. Имя его, как и у конструктора, должно соответствовать имени класса. Только перед именем надо добавить символ ~

Добавим деструктор в предыдущий код. И создадим в классе два конструктора: один будет принимать параметры, второй – нет.

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

Если конструктор требует аргументы, их следует указать:

date today = date(6,4,2014); // полная форма
date xmas(25,12,0); // сокращенная форма
// date my_burthday; // недопустимо, опущена инициализация

Если необходимо обеспечить несколько способов инициализации объектов класса, задается несколько конструкторов:

class date <
int month, day, year;
public :
date( int , int , int ); // день месяц год
date( char *); // дата в строковом представлении
date(); // дата по умолчанию: сегодня
>;

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

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

Конструктор по умолчанию

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

class date
int month, day, year;
public :
date( int , int , int );
date( char *);
date(); // конструктор по умолчанию
>;

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

Однако имеются случаи, в которых создание объекта без вызова конструктора осуществляется неявно:

  • формальный параметр – объект, передаваемый по значению, создается в стеке в момент вызова функции и инициализируется копией фактического параметра;
  • результат функции – объект, передаваемый по значению, в момент выполнения оператора return копируется во временный объект, сохраняющий результат функции.

Во всех этих случаях транслятор не вызывает конструктора для вновь создаваемого объекта:

  • date2 в приведенном определении;
  • для создаваемого в стеке формального параметра;
  • для временного объекта, сохраняющего значение, возвращаемое функцией.

Вместо этого в них копируется содержимое объекта-источника:

  • date1 в приведенном примере;
  • фактического параметра;
  • объекта-результата в операторе return .

Конструктор копии

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

Деструкторы

Определяемый пользователем класс имеет конструктор, который обеспечивает надлежащую инициализацию. Для многих типов также требуется обратное действие. Деструктор обеспечивает соответствующую очистку объектов указанного типа. Имя деструктора представляет собой имя класса с предшествующим ему знаком «тильда» ~ . Так, для класса X деструктор будет иметь имя ~X() . Многие классы используют динамическую память, которая выделяется конструктором, а освобождается деструктором.

class date
int day, year;
char *month;
public :
date( int d, char * m, int y)
day = d;
month = new char [strlen(m)+1];
strcpy_s(month, strlen(m)+1,m);
year = y;
>
~date() < delete [] month; >// деструктор
>;

Поля, имеющие тип класса

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

Конструктор нового класса имеет пустое тело и список вызываемых конструкторов класса vect , перечисленных после двоеточия (:) через запятую (,). Они выполняются с целым аргументом i , создавая 3 объекта класса vect: a, b, c .

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

Здравствуйте, у меня такой вопрос. В коде я не нашел сам деструктор. Он создается и запускается сам после выхода из блока main ?

В этой статье мы рассмотрим, для чего в C++ нужны конструкторы и деструкторы.

Проблема с инициализацией

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

Например, в языке С мы можем создать переменную и тут же задать ей значение:

Эта строка прямого действия — как она написана, так она и выполняется.

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

То есть программисту на C++ нужно все время помнить, на каком уровне что работает:

  • или на уровне объявления класса,
  • или на уровне экземпляра класса.

Например, наследование работает на уровне объявления класса, а присваивание значений — на уровне экземпляра класса.

Как же задать значение переменной? Первое, что приходит в голову — это создать экземпляр класса и затем задать значение свойству класса. То есть написать так.

using namespace std;

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

Base b[10];

мы создадим 10 экземпляров класса и нам придется писать инициализацию для всех десяти переменных. Это при том, что у нас одна переменная, а если у нас их десяток, то присвоение начального значения становятся весьма трудоемким занятием.

Для решения этой проблемы в C++ добавлен такой инструмент как конструктор класса.

Конструктор класса

Конструктор — это функция, которая имеет то же имя, что и класс и вызывается каждый раз при создании класса. Если вы не определили ни одного конструктора, компилятор создаст конструктор по умолчанию, не имеющий параметров.

Конструктор работает на уровне экземпляра класса и предназначен для присвоения значений переменных. Конструктор не имеет типа возвращаемого значения.

using namespace std;

В этом пример мы объявили конструктор с именем Base, в котором и задали начальное значение. Хотя, конечно, по сравнению с C, где мы все это сделали одной строчкой, выглядит достаточно громоздко.

Динамические объекты в C++

Следующей важной особенностью C++ является работа с динамической памятью. В языке С работа с динамической памятью очень проста и использует всего две функции:

  • malloc — резевирует память,
  • free — освобождает память.

char *string; // задали указатель на строку

string = malloc(1000); // получили память

free( string ); // освободили память

В C++ все намного сложнее. Для работы с динамической памятью вводится понятие динамический объект .

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

Для создания нового объекта используется ключевое слово new .

int *px = new int;

В этой строке создан новый динамический объект — целое число. Возможно создать объекта любого типа данных: int, float, double, char и т. д. Создадим динамический экземпляр класса:

Base *pBase = new Base;

В этой строке мы создали динамический экземпляр класса. Только в этом случае мы должны ссылаться на свойства и методы через знак «->».

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

delete pBase;

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

Создание динамических объектов происходит в конструкторе класса. А где же проиходит их удаление? Для этого в классе есть специальная функция — деструктор .

Деструктор класса

Деструктор — это функция, которая имеет то же имя, что и класс, но со знаком «~» (тильда) в начале. В деструкторе нужно уничтожить все динамические объекты, которые были созданы в конструкторе.

При объявлении деструкторов действуют несколько правил. Деструкторы:

  • Не могут иметь аргументов.
  • Не могут иметь возвращаемого типа (включая void).
  • Не могут возвращать значение с помощью оператора return.

Теперь напишем пример, в котором используются все функции, которые мы узнали:

using namespace std;

px = new int(5);

Base *pBase = new Base;

В этом примере мы создали:

  • динамическую переменную px,
  • динамический массив pa,
  • динамический экземпляр класса pBase.

Только надо указать компилятору, что используется C++ 11, раньше нельзя было задать элементы массива при создании динамического массива.

Проблема утечки памяти

Работа с динамическими объектами в C++ породило проблему утечки памяти.

В языке Си в силу простоты вызова обычно программисты используют схему:

  • Взял память,
  • Использовал,
  • Освободил.

Ошибиться, конечно, можно и здесь, но в целом схема простая и ошибку найти не сложно.

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

С точки зрения пользователя — это выглядит так. Сначала программа работала быстро, а потом начинает замедляться и замедляться. И помогает только закрытие программы и открытие ее снова.

Проблема утечки памяти — это одна из самых распространенных ошибок в языке C++. Как выразился автор книги «Думай как программист» Антон Спрол: «Управление временем существования переменной — это бич каждого программиста С++».

Поэтому работа с динамическими объектами в C++ требует особой внимательности.

Все ссылки на статьи и ролики моего канала Old Programmer :
Программирование. Тематическое оглавление моего Zen-канала (Old Programmer) . Все ссылки на материалы об объектно-ориентированном программировании собраны в один раздел .

Сегодня продолжим тему объектно-ориентированного программирования ( ООП ), а она, ну право, необъятна. И это мы еще не дошли до Python. Но дойдем, я вам обещаю, дойдем. Сегодня разберем два специальных метода: конструктор (constructor) и деструктор (destructor). Эти методы не вызываются программно, они запускаются автоматически. Конструктор - при создании объекта, деструктор - при уничтожении объекта.

Объектно-ориентированное программирование (C++). Статья 1 (динамические и статические объекты, три кита ООП)

Конструкторы и деструкторы в ООП

Зачем нам эти методы? - спросите вы. А вот зачем. В конструктор можно поместить код, который инициализирует некоторые переменные класса, выделяет им место в памяти (если нужно), присваивает определенные значения. Типичным примером может быть объект, который описывает окно. Нужно задать размер окна, цвет, заголовок, другие его свойства. Задать элементы в окне. Метод деструктор служит для того, чтобы, например, освободить память, если она выделялась в процессе выполнения конструктора или других методов, ну и, возможно, произвести другие изменения, которые важно сделать именно после уничтожения объекта. Конструктор и деструктор делают объект самодостаточной единицей программирования.

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

Пример использования конструктора и деструктора в языке C++

Программа ниже как раз и демонстрирует работу конструктора A(int, int) и деструктора ~A() . Мы видим, что при создании объекта динамически создаются две переменные типа int и им присваиваются значения, переданные через параметры конструктора. При уничтожении объекта ( delete ) автоматически выполняется деструктор, где освобождается память, которая была выделена в конструкторе. В примере ниже у объекта есть также метод mul , который возвращает произведение двух переменных класса, значения которых задаются при создании объекта.

Вот, на сегодня и все, я надеюсь вы продвигаетесь в своем стремлении стать программистами. Читайте мои статьи на канале Old Programmer и ставьте лайки. Подписывайтесь на мой канал. Пока!

Читайте также: