Как оператор 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 идиома.