Ответ в теме: Конструкторы и дестркутор по умолчанию в С++

      Комментарии к записи Ответ в теме: Конструкторы и дестркутор по умолчанию в С++ отключены
#2716

Компилятор автоматически генерирует:

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

Понятно, что если ваш класс не наследует класс с виртуальным деструктором, но при этом вы допускаете его полиморфное использование — вам надо явно (руками) описывать этот деструктор. Кроме того, надеяться на деструктор, созданный неявно нельзя если объект внутри использует динамическое выделение памяти:

class LeakClass {
  char *s;
public:
  LeakClass() {
    s = new char[255];
  }
};

Деструктор освободит память, занимаемую указателем (пусть 4 байта), но не вызовет оператор delete[] для строки из 255 байт.

Аналогичная проблема возникнет конструкторе копирования и операторе присваивания:

class LeakClass {
  char *s;
public:
  LeakClass() {
    s = new char[255];
  }

  ~LeakClass() {
    delete[] s;
  }
};

// ...
{
  LeakClass a, b; // вызов конструктора
  LeakClass c(a); // вызов конструктора копирования

  b = a; // вызов оператора присваивания
} // объекты выходят из области видимости, вызываются деструкторы

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

  1. внутри у нескольких объектов находится указатель на одну и ту же область памяти, которая освобождается в деструкторе. Если один из объектов уничтожается, то у остальных оказывается указатель на уже освобожденную память. Найти ошибку иногда очень сложно, т.к. при выполнении delete[] содержимое памяти не изменится, она просто будет помечена как свободная — проблемы начнутся только когда другой объект получит эту же область памяти и перезапишет ее;
  2. объекты будут освобождать область памяти, которая уже была освобождена;
  3. при выполнении присваивания у объекта, стоящего слева от оператора будет происходить утечка памяти, т.к. указатель получит новое значение, но по старому указателю в конструкторе была выделена память.

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

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

Кроме того, если вы не напишите в своем коде вообще никакого конструктора — компилятор создаст конструктор по умолчанию (работающий без параметров).