You need to call the destructor if there are other things you need to do other than just allocate memory allocation.
In addition to simple classes, there are usually.
Things like closing file descriptors or closing database connections, deleting other objects pointed to by member data in your object, etc.
A classic example is the implementation of the stack:
class myStack { private: int *stackData; int topOfStack; public: void myStack () { topOfStack = 0; stackData = new int[100]; } void ~myStack () { delete [] stackData; }
Now think about what happens if the destructor is not called every time one of your stacks is deleted. In this case, there is no automatic garbage collection in C ++, so the stackData
memory will leak and you will end up.
This requirement of the destructor to remove all its resources moves down the tree in relation to the main types. For example, you might have a database connection pool with an array of database connections. The destructor for this will be delete
for each individual database connection.
A single database connection can highlight many things, such as data buffers, caches, compiled SQL queries, etc. Thus, the database connection destructor would also need to delete
about all of these things.
In other words, you have something like:
+-------------------------------------+ | DB connection pool | | | | +-------------------------+---+---+ | | | Array of DB connections | | | | | +-------------------------+---+---+ | | | | | +-----------------------------|---|---+ | | +---------+ | +-> | DB Conn | +---------+ | +---------+ | DB Conn | <----+ / | \ +---------+ buffers | queries / | \ caches buffers | queries caches
Releasing the memory of the database connection pool will not affect the existence of a separate database connection or other objects to which they point.
That's why I mentioned that only simple classes can leave without a destructor, and these are the classes that usually appear at the bottom of this tree above.
Class, for example:
class intWrapper { private: int value; public: intWrapper () { value = 0; } ~intWrapper() {} void setValue (int newval) { value = newval; } int getValue (void) { return value; } }
doesn't have a real need for a destructor, since freeing memory is all you need to do.
The bottom line is that new
and delete
are opposite ends of the same pole. The new
call first allocates memory, and then calls the appropriate constructor code to get your object in a healthy state.
Then, when you are finished, delete
calls the destructor to "tear down" your object, restores the allocated memory for this object.