Destructors are the complement of constructors. They’re called automatically when an object is destroyed, when the object goes out of scope. Similarly to constructors, destructors have no return type and must be public. By contrast, they cannot return any type.

However, dynamically allocated memory must be explicitly freed by the developer, and isn’t freed automatically when a variable goes out of scope (we’d get a memory leak). Whenever we use dynamically allocated data in our program, we must implement our own destructor.

Student::~Student() { // no return type
	if (grades != nullptr) {
		delete [] grades;
	}
}

Note the destructor’s property of being automatically called when an object is destroyed. What this means is that we can build in a “chain reaction” deletion for linear data structures (like linked lists, stacks, and queues).

~ComplexNum() { if (next != NULL) delete next; }
// another file
ComplexNum *p = new ComplexNum;
p->next = new ComplexNum; // should use mutator but just for example
delete p;

In the snippet above, when we call delete p, we call the destructor on the first object. That then deletes the object in the next pointer. This allows us to quickly delete an entire structure if need be.