A constructor initializes an object when it's created; a destructor cleans up when it's destroyed — together, they're the foundation of C++'s most important idiom, RAII.
class Car {
private:
std::string brand;
int speed;
public:
Car() : brand("Unknown"), speed(0) {} // default constructor
Car(std::string b, int s) : brand(b), speed(s) {} // parameterized constructor
// (using a member initializer list)
~Car() { // destructor
std::cout << brand << " is being destroyed\n";
}
};
int main() {
Car c1; // calls the default constructor
Car c2("Honda", 60); // calls the parameterized constructor
return 0;
} // both destructors run automatically here, in reverse order of creation
The member initializer list (: brand(b), speed(s)) initializes members directly as the object is constructed, rather than default-constructing them first and then assigning inside the constructor body — for simple types the difference is negligible, but for members that are themselves objects (like another class, or a const member, or a reference member), the initializer list is required or noticeably more efficient, so it's the idiomatic default in modern C++.
Destructors run automatically when an object goes out of scope — no explicit call needed, unlike free() in C. This automatic, deterministic cleanup is the basis of RAII (Resource Acquisition Is Initialization): a class can acquire a resource (memory, a file handle, a lock) in its constructor and release it in its destructor, guaranteeing the resource is released exactly once, even if an exception is thrown partway through a function — this pattern is explored fully in the Memory Management chapter.
If a class has any pointer members that own heap memory, forgetting to write a proper destructor (and copy constructor — see the "Rule of Three," covered later) is one of the most common sources of memory leaks and crashes in beginner C++ code.