Constructors, Destructors & RAII
Constructor, destructor ও RAII
1. Constructors — How Objects Begin Life
A constructor sets up an object. It runs once, when the object is created.
#include <iostream>
#include <string>
class Box {
int w_, h_;
public:
Box() : w_(1), h_(1) { std::cout << "default\n"; }
Box(int w, int h) : w_(w), h_(h) { std::cout << "sized\n"; }
int area() const { return w_ * h_; }
};
int main() {
Box a; // default
Box b{3, 4}; // parameterized
std::cout << a.area() << " " << b.area() << "\n";
}
2. The Six Special Member Functions
| Member | Signature | Purpose |
|---|---|---|
| Default ctor | T() | Construct with no args |
| Destructor | ~T() | Clean up resources |
| Copy ctor | T(const T&) | Construct from another |
| Copy assign | operator=(const T&) | Assign from another |
| Move ctor | T(T&&) | Steal from temporary |
| Move assign | operator=(T&&) | Move-assign from temp |
3. Destructor & RAII
The destructor runs automatically when the object's lifetime ends. This is RAII's hammer:
#include <iostream>
#include <fstream>
class File {
std::FILE* f_ = nullptr;
public:
File(const char* path, const char* mode) {
f_ = std::fopen(path, mode);
if (!f_) throw std::runtime_error{"open failed"};
}
~File() { if (f_) std::fclose(f_); } // auto cleanup!
File(const File&) = delete; // non-copyable
File& operator=(const File&) = delete;
};
4. Member Initialization List
Always prefer the member init list over assignment in the constructor body:
// Good — init list
Person(std::string n, int a) : name_(std::move(n)), age_(a) {}
// Bad — body assignment (members default-constructed first, then overwritten)
Person(std::string n, int a) {
name_ = n;
age_ = a;
}
For const and reference members, the init list is the only option.
5. Object Lifecycle Demo
#include <iostream>
struct Tracer {
Tracer() { std::cout << "ctor\n"; }
Tracer(const Tracer&) { std::cout << "copy\n"; }
Tracer(Tracer&&) noexcept { std::cout << "move\n"; }
~Tracer() { std::cout << "dtor\n"; }
};
Tracer make() { return Tracer{}; }
int main() {
Tracer a;
Tracer b = make();
Tracer c = a;
}
6. Practice Problems
- Write a default constructor for a class.
✨ Show Answer
MyClass() {} // or = default; - When does the destructor run?
✨ Show Answer
(1) Local objects: when their scope ends. (2) Heap objects: on
delete. (3) Members: when their containing object is destroyed. (4) Temporaries: at the end of the full expression. - Why use a member init list?
✨ Show Answer
More efficient (no default-construct-then-assign), and required for const/reference members.
- Define a class that opens a socket in ctor and closes in dtor.
✨ Show Answer
See File example above. Same pattern: acquire in ctor, release in dtor, delete copies.
- What does
= defaultdo?✨ Show Answer
Asks the compiler to generate the default implementation of a special member function. Useful when you've declared others and lost the implicit defaults.
- What does
= deletedo?✨ Show Answer
Forbids that operation.
T(const T&) = delete;makes the class non-copyable. - When is the move ctor called?
✨ Show Answer
When constructing from a rvalue (temporary or
std::move(x)). Steals resources instead of copying. - Why must destructors typically not throw?
✨ Show Answer
If a destructor throws during stack unwinding (caused by another exception),
std::terminateis called. Mark destructorsnoexcept(the default) and don't throw from them. - Add a logging line in the constructor of class X.
✨ Show Answer
X() { std::cout << "X created\n"; } - Difference between
T x;andT x{};?✨ Show Answer
T x;default-initializes (built-ins are uninitialized).T x{};value-initializes (built-ins zero-out). - What's a delegating constructor?
✨ Show Answer
class X { int a, b; public: X() : X(0, 0) {} // delegates to next X(int a, int b) : a(a), b(b) {} }; - Why is the destructor called for an object thrown out of scope by an exception?
✨ Show Answer
Stack unwinding. When an exception propagates, all local objects in scope have their destructors called. RAII relies on this.
- What's an explicit constructor?
✨ Show Answer
class X { public: explicit X(int); // no implicit conversion }; // X x = 5; // ERROR // X x{5}; // OK - When is the implicit copy constructor generated?
✨ Show Answer
When you don't declare any of: copy ctor, move ctor, copy assign, move assign, destructor. If you declare any, the auto-generation rules get trickier — see Rule of Five (Module 24).
- Construct an object inside a vector with
emplace_back.✨ Show Answer
std::vector<Person> v; v.emplace_back("Sara", 21); // constructs in-place - Why prefer
emplace_backoverpush_back?✨ Show Answer
emplace_backforwards arguments to construct the element in-place.push_backrequires a constructed object first, then copies/moves it. - Show that destructors run in reverse order of construction.
✨ Show Answer
{ Tracer a; Tracer b; Tracer c; } // dtors run: c, b, a - When NOT to write a destructor?
✨ Show Answer
When all members already manage their own resources (smart pointers, vectors, strings). The compiler-generated destructor calls each member's destructor automatically. This is the "Rule of Zero" — the cleanest design.
Summary
Constructors initialize, destructors clean up. RAII ties resources to lifetimes, making cleanup automatic and exception-safe. Always use member init lists. Aim for the Rule of Zero — let your members manage their own resources, and you write nothing special.