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

Главная Форумы Программирование Программирование на С++ Учебные материалы по С++ Конструкторы и дестркутор по умолчанию в С++

В этой теме 2 ответа, 2 участника, последнее обновление  Васильев Владимир Сергеевич 1 год, 7 мес. назад.

  • Автор
    Сообщения
  • #2711

    questioner
    Участник

    Здравствуйте. Говорят, что в С++ компилятор автоматически генерирует конструктор без параметров, конструктор копирования и дестркутор. Зачем тогда их иногда пишут вручную?

  • #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 вместо массива символов.

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

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

  • #2717

    Запустим наши примеры.

    int main() {
      LeakClass a, b; 
    }

    Программа отрабатывает без ошибок, но как только добавим вызов конструктора копирования (который создан неявно) – получим ошибку во время выполнения (память по указателю очищается дважды):
    LeakClass c(a);

Для ответа в этой теме необходимо авторизоваться.