Destructor, rule of zero, const methods
A constructor runs when an object is created. It has a counterpart: a method that runs when the object stops existing. The order of these calls becomes visible when both 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. A local variable is destroyed when its block ends: b at the inner closing brace, a at the end of main. Objects of one block are destroyed in reverse order: the last one created goes first.
A destructor is needed when an object owns a resource that must be returned to the system, such as allocated memory. The resource is acquired in the constructor and returned in the destructor, so returning it can no longer be forgotten.
The rule of zero
Most classes need no destructor of their own. std::string and std::vector free their own memory. 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, but when a contact is destroyed, the compiler 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 manage their resources declares neither a destructor nor its own copy operations.
const methods
An object declared const cannot be changed. The compiler decides whether a method may be called on such an object by the method's declaration, not its body. So on a const object, as through a const T& reference, only const methods can be called. These are methods with the word const after the parameter list:
class Temperature {
public:
Temperature(int celsius) : celsius_(celsius) {}
int celsius() const { return celsius_; }
void warm() { ++celsius_; }
private:
int celsius_;
};For const Temperature t(20);, the call t.celsius() compiles, while t.warm() does not. Inside a const method the object is const too: a field cannot be assigned there unless it is declared mutable. That is why a method that only reads fields is declared const: it can be called on any object, const or not.
Common mistake
A method only reads a field but is declared without const:
class Temperature {
public:
Temperature(int celsius) : celsius_(celsius) {}
int celsius() { return celsius_; }
private:
int celsius_;
};
int twice(const Temperature& t) {
return t.celsius() * 2; // ✗
}error: 'this' argument to member function 'celsius' has type 'const Temperature', but function is not marked constThe parameter t is a const reference, and celsius() does not promise to keep it unchanged, so the call does not compile. Add const after the parameter list: int celsius() const.