C++ Constructors and Destructors
A constructor is a special method that runs automatically when an object is created. It gives the object its first values, so an object is never in a half-made state. A destructor runs when the object is destroyed.
A constructor
A constructor has the same name as the class and no return type. The member initializer list, after the colon, sets the members before the body runs; it is the preferred way to initialise them.
#include <iostream>
#include <string>
class Student {
std::string name;
int marks;
public:
Student(const std::string& n, int m) : name(n), marks(m) {}
void show() const { std::cout << name << ": " << marks << "\n"; }
};
int main() {
Student a("Asha", 84);
Student b{"Ravi", 77}; // braces work too
a.show();
b.show();
return 0;
}Asha: 84 Ravi: 77
Once a class has a constructor with parameters, Student c; without arguments no longer compiles: every student must get a name and marks.
Several constructors
Like functions, constructors can be overloaded. A constructor without parameters is the default constructor. Default arguments can merge several constructors into one.
#include <iostream>
class Point {
int x, y;
public:
Point() : x(0), y(0) {} // default constructor
Point(int x, int y) : x(x), y(y) {} // members and parameters may share names here
void show() const { std::cout << "(" << x << ", " << y << ")\n"; }
};
int main() {
Point origin;
Point p(3, 4);
origin.show();
p.show();
return 0;
}(0, 0) (3, 4)
explicit
A constructor with one parameter also acts as an automatic conversion: with Circle(double r), a function taking a Circle would silently accept 5.0. Marking the constructor explicit turns that off, which avoids surprises. Make one-parameter constructors explicit unless the conversion is really wanted.
Destructors
A destructor has the class name with a tilde: ~Logger(). It runs when the object goes out of scope (or is deleted), and releases what the object holds: memory, files, connections. Objects are destroyed in the reverse order of their creation.
#include <iostream>
#include <string>
class Logger {
std::string name;
public:
explicit Logger(const std::string& n) : name(n) { std::cout << "open " << name << "\n"; }
~Logger() { std::cout << "close " << name << "\n"; }
};
int main() {
Logger a("first");
{
Logger b("second");
std::cout << "working\n";
} // b is destroyed here
std::cout << "end of main\n";
return 0;
} // a is destroyed hereopen first open second working close second end of main close first
This pattern, where an object takes a resource in its constructor and releases it in its destructor, is called RAII. It is how std::string, std::vector, file streams and smart pointers clean up after themselves without you writing any cleanup code.
Practice exercises
- Give
Rectanglea constructor that takes the width and height and refuses negative values (use 0 instead). - Add a default constructor that makes a 1 by 1 rectangle.
- Add a destructor that prints a message, create rectangles in a block and watch the order of the messages.