Как оператор delete освобождает память, зачем нужен деструктор?

в C++ часто задаваемые вопросы: http://www.parashift.com/c++-часто задаваемые вопросы-лайт/dtors.сообщение: чаво-11.9

помните: delete p делает две вещи: он вызывает деструктор и освобождает память.

Если delete освобождает память, то зачем нужен деструктор здесь?

9 ответов


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

кроме очень простых классов, обычно есть.

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

классическим примером является реализация стека:

class myStack {
    private:
        int *stackData;
        int topOfStack;

    public:
        void myStack () {
            topOfStack = 0;
            stackData = new int[100];
        }

        void ~myStack () {
            delete [] stackData;
        }

        // Other stuff here like pop(), push() and so on.
}

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


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

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

другими словами, у вас есть что-то вроде:

+-------------------------------------+
| DB connection pool                  |
|                                     |
| +-------------------------+---+---+ |
| | Array of DB connections |   |   | |
| +-------------------------+---+---+ |
|                             |   |   |
+-----------------------------|---|---+
                              |   |   +---------+
                              |   +-> | DB Conn |
             +---------+      |       +---------+
             | DB Conn | <----+         /  |  \
             +---------+         buffers   |   queries
               /  |  \                  caches
        buffers   |   queries
               caches

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

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

класс:

class intWrapper {
    private:
        int value;
    public:
        intWrapper () { value = 0; }
        ~intWrapper() {}
        void setValue (int newval) { value = newval; }
        int getValue (void) { return value; }
}

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


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

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


если delete освобождает память, то в чем необходимость деструктор здесь?

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

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

Предположим, у вас есть класс, который динамически выделяет память:

class something {
public:
    something() {
        p = new int;
    }

    ~something() {
        delete p;
    }

    int *p;
};

теперь давайте динамически выделить


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


это освобождает память, занятую что


Если вы объявляете класс normal (не указатель), он автоматически вызывает конструктор и вызывает деструктор автоматически при закрытии программы. Если вы объявляете как указатель, он вызывает конструктор при инициализации с помощью new и не вызывает деструктор автоматически, пока вы не вызовете удалить этот указатель с помощью delete


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


деструктор не было бы обязательной функцией. Такие языки, как C, Java, C#, не имеют деструкторов. C++ также может жить без нее.

деструктор-это специальное учреждение предоставлено C++ (так же, как конструктор). Это вызывается, когда объект "уничтожен".

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

A* foo ()
{
  static A obj;  // 'A' is some class
  A *p = &obj;
  return p;
}

в вышеприведенном коде obj это static данные, созданные типа A; foo() возвращает ссылку на этот obj это нормально, потому что obj.~A() пока не называется. Предположим obj нестатично. Код будет компилировать,, A* возвращено foo() теперь указывает на место памяти, которое больше не является доступные одном месте.


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