Вы должны вызывать деструктор, если есть другие вещи, которые нужно сделать, кроме простого удаления памяти.
Кроме очень простых классов, обычно есть.
Такие вещи, как закрытие файловых дескрипторов или закрытие соединений с базой данных, удаление других объектов, на которые указывают данные членов в вашем объекте, и т. Д.
Классическим примером является реализация стека:
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
вызывает деструктор, чтобы «разрушить» ваш объект, освобождает память, выделенную для этого объекта.