Destructor and the rule of zero
A constructor runs when an object is created. It has a counterpart: a method that runs when the object stops existing.
Picture a stack of plates: the plate put on last is taken off first. The objects of one block live the same way. The order in which they are created and destroyed becomes visible when both methods print the object's name:
#include <iostream>
struct Tracer {
char name;
Tracer(char n) : name(n) { std::cout << '+' << name << ' '; }
~Tracer() { std::cout << '-' << name << ' '; }
};
int main() {
Tracer a('a');
{
Tracer b('b');
}
std::cout << "end ";
} // +a +b -b end -aThe destructor
A destructor is a method named after its class with a leading tilde, such as ~Tracer, with no parameters or return type. It runs on its own when the object's lifetime ends.
For a local variable, that is the exit from the block where it is declared. Let's walk through the output step by step:
+a: the objectais created.+b: the objectbis created in the inner block.-b: at the inner block's closing brace,bis destroyed.end:mainprints the word.-a: at the end ofmain,ais destroyed.
Objects of one block are destroyed in reverse order, like plates off a stack: the last one created goes first. Interviewers like to ask about the order of destructor calls, and the answer is always the same: the reverse of the order of creation.
A destructor is needed when an object itself owns something that must be returned to the system, such as allocated memory. The object gets it in the constructor and returns it in the destructor, so returning it can no longer be forgotten. It is like a library with a turnstile: you get a book on the way in, and the turnstile takes it back by itself on the way out.
The rule of zero
Most classes need no destructor of their own. std::string and std::vector free their own memory, and the destructors of the fields run automatically when the object they belong to is destroyed:
#include <string>
#include <vector>
struct Contact {
std::string name;
std::vector<std::string> phones;
};Contact declares no destructor: the compiler creates one by itself, and when a contact is destroyed, that destructor calls the destructors of name and phones. Copying works too: a copy of a contact gets its own string and vector.
This is the rule of zero: a class whose fields free their own memory declares no destructor of its own, and copying works by itself. Build classes from such fields, and you will almost never have to think about a destructor.
Key points
- A destructor
~Truns on its own when the object's lifetime ends. - A local object is destroyed at the closing brace of its block.
- Objects of one block are destroyed in the reverse order of creation.
- A class made of fields like
std::stringandstd::vectordeclares no destructor of its own: that is the rule of zero.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.