A learner can often write a class yet still be unsure which constructor runs, when a copy is made, or why a destructor appears to run "by itself". Tracing object values from creation to scope exit resolves that confusion. A Student trace exposes copy construction and reverse scope cleanup; a ScoreBuffer trace shows RAII releasing heap memory.
What Constructors and Destructors Actually Do
A constructor is the special member function that establishes a usable state for a new object. It has the class name and no return type. A destructor is the special member function named ~ClassName() that runs when the object's lifetime ends and performs cleanup.
Keep storage, initialisation, and lifetime separate. Storage may exist before construction completes, but the object becomes usable only after its members are initialised. For Student{17, 86}, construction produces an object whose roll_ is 17 and score_ is 86. Destruction ends that object's lifetime before its storage is reclaimed.
Property | Constructor | Destructor |
|---|---|---|
Name | Same as the class |
|
Parameters | May take parameters | Takes no parameters |
Overloading | Allowed | Not allowed |
Return type | None | None |
Order for local objects | Creation order | Reverse creation order |
To learn this inside the wider language sequence, use the C++ Programming Course - Concepts, MCQs & Coding.
Constructor Types and Why Initialiser Lists Matter
The three beginner-level constructor forms are Student(), Student(int roll, int score), and Student(const Student& other). They are the default, parameterised, and copy constructors. Move construction is another form for later study, especially when an object owns a transferable resource.
Prefer direct member initialisation:
Student(int roll, int score) : roll_(roll), score_(score) {}This initialises the members instead of default-initialising them and assigning values later. Members are always initialised in their declaration order, not the visual order in the initialiser list. A const member or reference member must be initialised this way.
Overload selection follows the expression used. Student a; selects the default constructor. Student b(17, 86); selects the two-argument constructor. Student c = b; creates c with the copy constructor, while c = b; after both objects already exist would call copy assignment.
Worked Example: Trace Three Students from Construction to Scope Exit
Before reading the output, predict the first destructor line. Will destroy: 17 91 or destroy: 17 86 appear first, and which closing brace decides it?
#include <iostream>
class Student {
int roll_;
int score_;
public:
Student() : roll_(0), score_(0) {
std::cout << "default: " << roll_ << ' ' << score_ << '\n';
}
Student(int roll, int score) : roll_(roll), score_(score) {
std::cout << "parameterised: " << roll_ << ' ' << score_ << '\n';
}
Student(const Student& other)
: roll_(other.roll_), score_(other.score_) {
std::cout << "copy: " << roll_ << ' ' << score_ << '\n';
}
void setScore(int score) { score_ = score; }
~Student() {
std::cout << "destroy: " << roll_ << ' ' << score_ << '\n';
}
};
int main() {
Student a;
Student b(17, 86);
{
Student c = b;
c.setScore(91);
}
}The exact output is:
default: 0 0
parameterised: 17 86
copy: 17 86
destroy: 17 91
destroy: 17 86
destroy: 0 0Line 1 constructs a with values 0 and 0. Line 2 constructs b with 17 and 86. Line 3 copy-constructs c, so it starts with its own copy of those values. c.setScore(91) changes only c, not b.
The inner closing brace ends c's lifetime, producing line 4 with 17 and 91. At the end of main, reverse construction order produces line 5 for b and line 6 for a. Therefore destroy: 17 91 comes first.

Destructors, Ownership, and RAII with a Three-Element Buffer
RAII ties a resource to an object's lifetime. Construction acquires the resource, and destruction releases it even when a scope exits early. Production C++ should normally prefer std::vector or smart pointers. A raw array exposes the constructor's allocation and the destructor's deallocation directly.
#include <cstddef>
#include <iostream>
class ScoreBuffer {
std::size_t size_;
int* data_;
public:
explicit ScoreBuffer(std::size_t n)
: size_(n), data_(new int[n]) {
for (std::size_t i = 0; i < size_; ++i) {
data_[i] = static_cast<int>((i + 1) * 10);
}
std::cout << "allocate " << size_ << " scores\n";
}
ScoreBuffer(const ScoreBuffer&) = delete;
ScoreBuffer& operator=(const ScoreBuffer&) = delete;
int at(std::size_t index) const { return data_[index]; }
~ScoreBuffer() {
std::cout << "release " << size_ << " scores\n";
delete[] data_;
}
};
int main() {
ScoreBuffer scores(3);
std::cout << "middle value: " << scores.at(1) << '\n';
}For indices 0, 1, and 2, (i + 1) * 10 gives 10, 20, and 30. The observable sequence is:
allocate 3 scores
middle value: 20
release 3 scoresConstruction stores size_ = 3, allocates three integers, and fills them. at(1) reads the second cell, which is 20. When scores leaves main, its destructor prints the release line and calls delete[] data_. Automatic cleanup also happens during a normal early return or stack unwinding after an exception.
![Memory diagram of ScoreBuffer scores(3): a stack object points to a heap array of 10, 20, 30 that delete[] frees at scope exit.](https://cdn.knowledgegate.ai/blog-assets/blog_asset_1784269856680_0yq65y.jpg)
When Destruction Happens and in What Order
An automatic local object is destroyed at scope exit. A dynamically allocated object is destroyed when the matching delete runs. Member objects are destroyed when their containing object is destroyed. If a program leaks a new allocation and loses its pointer, it cannot reach that object's destructor through the lost pointer.
For an order exercise, construct Logger first("A") and then Logger second("B") in one scope. Leaving that scope prints destroy B and then destroy A. If a class declares Engine engine_; before Battery battery_;, its destructor body runs first, then the members are destroyed as battery_ followed by engine_.
A base-class destructor should be virtual when an object may be deleted through a base pointer. With Base* p = new Derived; delete p;, behaviour is undefined if Base lacks a virtual destructor, so Derived cleanup is not guaranteed.
Common Constructor and Destructor Errors
Mistake | Consequence | Fix |
|---|---|---|
Giving a constructor a return type | It becomes an ordinary function or fails to declare the intended constructor | Use the class name with no return type |
Leaving primitive members uninitialised | The object starts with indeterminate values | Initialise every member, preferably in the initialiser list |
Treating copy construction as copy assignment | You reason about the wrong special member function | Check whether the destination object already exists |
Calling | Normal scope exit may destroy the same object again | Let automatic lifetime rules call the destructor |
Allocating in a constructor without safe ownership | Copies can share one raw pointer and double-delete it | Use a standard container, smart pointer, or correctly designed copy policy |
The raw-pointer trap is exact: if two objects shallow-copy data_ = 0xA0, both destructors try delete[] 0xA0. ScoreBuffer prevents that by deleting copy construction and copy assignment. If custom copying is truly required, study the Rule of Three/Five and implement ownership deliberately.
Changing the order of initialiser-list entries does not change declaration-order initialisation. Virtual calls during construction or destruction also do not dispatch as though the most-derived object were fully alive.
How Tests and Interviews Probe Object Lifetime
Three common question shapes are selecting which overloaded constructor runs, tracing exact output across nested scopes, and spotting a shallow-copy or non-virtual-destructor bug. Reuse the worked values: Student b(17, 86) selects the parameterised constructor, Student c = b copies 17 and 86, and changing c to 91 does not alter b.
Self-check:
Can a destructor be overloaded? No.
Does
Student c = b;call assignment? No, it copy-constructs.Which dies first,
bor inner-scopec?c.What does
at(1)return for[10, 20, 30]? 20.Why delete copying in
ScoreBuffer? To prevent two owners from deleting the same allocation.
KnowledgeGate currently offers 40+ constructor questions in its practice bank. For broader coding practice aimed at placement problems, continue with Coding for Placements - C, C++, Java, Python. For the next concept in reasoning about program cost, read Time Complexity: Big-O, Theta, Omega & Master Theorem.
Short Version and the Next Step
Constructors establish valid state. Initialiser lists initialise members directly. Destructors release owned resources. Scope controls cleanup in reverse construction order.
If you want a structured path through programming fundamentals, browse Coding & Skill Development Courses. Once object lifetime and basic classes feel comfortable, the next worked tutorial is Sorting Algorithms: Complexity, Stability, n log n Bound.




