C++ Constructors and Object Initialization: Techniques, Order and a Worked Trace
Learn how C++ chooses default, parameterised, delegating and copy constructors, then trace member states and initialization order through complete code.
KnowledgeGate Team
Exam prep & CS education

StudentRecord a;, StudentRecord b{17, "Asha", 84};, and StudentRecord c = b; create objects in three different-looking ways, while a later b.setMarks(91) changes an object that already exists. Mixing initialization with assignment makes constructor-selection and output questions harder than they are; ask whether a new lifetime is starting or an existing state is changing. One trace shows how default, parameterised, delegating, and copy constructors establish a, b, and c. If you need the wider language sequence first, begin with the C++ Tutorial: Complete Learning Path in 12 Weeks.
What a constructor establishes when an object begins its lifetime
A constructor is a special class member that establishes an object's initial state. It has the class name, has no return type, and runs during object creation. It can validate inputs or acquire resources, but the StudentRecord trace follows predictable field values through each initialization path.
The sequence matters: storage is obtained, base classes and data members are initialized, and then the constructor body runs. Later member calls and assignments act on an object whose lifetime has already begun. The ISO C++ FAQ on constructors explains why member-initialiser lists are preferred and why declaration order controls member initialization.
A default constructor is any constructor callable with no arguments, not merely one written with zero parameters. Constructor overloading supplies several parameter lists, and overload resolution selects a viable constructor at each creation expression. For the wider connection among classes, objects, and encapsulation, see Object Oriented Technology Explained.
Main object-initialization forms
Form | Constructor selected | What it means |
|---|---|---|
| Default | Default-initializes |
| Default | Direct-list-initializes |
| Three-argument | Initializes all three fields |
| Copy | Initializes new object |
Despite the =, the last row is initialization because c is being declared. By contrast, b.setMarks(91); is later mutation. A later c = b; would request copy assignment, but this exact class rejects it because const int roll_ makes the implicitly generated copy-assignment operator deleted.
Braces also expose narrowing. StudentRecord bad{17, "Asha", 84.5}; is rejected because double would narrow to int. StudentRecord bad(17, "Asha", 84.5); permits the standard conversion and discards the fractional part. This topic also fits the broader Coding & DSA Courses for Placements.
Worked example: default, parameterised, delegating and copy construction
#include <iostream>
#include <string>
class StudentRecord {
private:
const int roll_;
std::string name_;
int marks_;
public:
StudentRecord() : StudentRecord{0, "Unassigned", 40} {
std::cout << "default\n";
}
StudentRecord(int roll, const std::string& name, int marks)
: roll_{roll}, name_{name}, marks_{marks} {
std::cout << "parameterized\n";
}
StudentRecord(const StudentRecord& other)
: roll_{other.roll_}, name_{other.name_}, marks_{other.marks_} {
std::cout << "copy\n";
}
void setMarks(int marks) { marks_ = marks; }
void print() const {
std::cout << roll_ << ' ' << name_ << ' ' << marks_ << '\n';
}
};
int main() {
StudentRecord a;
StudentRecord b{17, "Asha", 84};
StudentRecord c = b;
b.setMarks(91);
a.print();
b.print();
c.print();
}Creating a first enters the target three-argument constructor with 0, "Unassigned", and 40, so it prints parameterized. Only after that target finishes does the delegating default-constructor body print default. Creating b initializes 17, "Asha", and 84, then prints parameterized. Creating c copies those current values from lvalue b, then prints copy.
Next, b.setMarks(91) changes only b.marks_, from 84 to 91. The separate c.marks_ remains 84. The exact output is:
parameterized
default
parameterized
copy
0 Unassigned 40
17 Asha 91
17 Asha 84
Member-initialiser lists initialize; constructor-body statements come later
In the three-argument constructor, : roll_{roll}, name_{name}, marks_{marks} is the member-initialiser list. All three members hold their initialized values before the std::cout statement in the body runs.
Writing roll_ = roll; in the body cannot initialize the const member. If name_ were omitted from the list, it would first be default-constructed, and name_ = name; in the body would be assignment to an existing string.
class SafePair {
int base_;
int doubled_;
public:
explicit SafePair(int n) : base_{n}, doubled_{base_ * 2} {}
};For SafePair p{10}, declaration order gives base_ = 10, followed by doubled_ = 10 * 2 = 20. If declarations are reversed but the list stays base_{n}, doubled_{base_ * 2}, doubled_ still initializes first and reads base_ too early. There is no valid portable numeric result. Declare dependent members in dependency order, then list initializers in that same order.

Combine in-class defaults, delegation and explicit construction deliberately
StudentRecord() delegates by naming exactly one target constructor in its initializer. Writing StudentRecord(0, "Unassigned", 40); as a body statement would instead create a separate temporary, not initialize the current object.
class AttemptPolicy {
int attempts_ = 3;
bool negativeMarking_ = false;
public:
AttemptPolicy() = default;
explicit AttemptPolicy(int attempts) : attempts_{attempts} {}
};
AttemptPolicy standard{}; // attempts_=3, negativeMarking_=false
AttemptPolicy custom{5}; // attempts_=5, negativeMarking_=falseA constructor initializer overrides the corresponding in-class default; an omitted member keeps its default. Thus custom has attempts_ = 5 and negativeMarking_ = false. AttemptPolicy implicit = 5; is rejected because the one-argument constructor is explicit, while AttemptPolicy direct{5}; is valid. = default requests normal compiler behaviour. = delete can deliberately forbid an operation, such as no-argument construction.
Common constructor traps and exact repairs
int StudentRecord(...)-> declares something other than a constructor -> remove the return type.StudentRecord x();-> declares a function namedx-> writeStudentRecord x{};.Calling another constructor in the body -> constructs a temporary -> delegate in the initializer list.
Assigning
const roll_in the body -> compilation fails -> initialize it in the list.Trusting initializer-list order -> may read a later-declared member too early -> follow declaration order.
Treating
StudentRecord c = bas assignment -> names the wrong operation -> identify copy construction.Assuming the copy shares
marks_-> predictscchanges withb-> remember that each object owns its ownint.
There is another trap: declaring only StudentRecord(int, const std::string&, int) does not also provide a usable no-argument constructor. Add a real default constructor, or justified = default, only when a no-argument state is meaningful. Use braces when you want their narrowing check, and mark a converting one-argument constructor explicit unless implicit conversion is intentional. explicit does not change three-argument construction.
How exam-style and interview questions probe constructor semantics
Constructor questions typically test overload selection, declaration order, and final object state:
First two lines for
StudentRecord a?parameterized, thendefault.After
b.setMarks(91), what print?b:17 Asha 91;c:17 Asha 84.What does
StudentRecord x();declare? A function.What controls member initialization order? Declaration order in the class.
Is
StudentRecord c = bconstruction or assignment? Copy construction.
List initialization rejects narrowing, and const roll_ prevents implicit copy assignment in this class. StudentRecord bad{17, "Asha", 84.5}; is rejected for list narrowing. A later c = b; is rejected for this class because const roll_ prevents implicit copy assignment. This is not a rule that every class with a copy constructor rejects assignment.
Practise constructor selection, declaration order, narrowing, copy construction, and object state until each trace is mechanical. For wider placement-oriented C++ practice, use Coding For Placements.
C++ constructors: the short version and the next step
Constructors establish initial state.
The member-initialiser list runs before the body.
The creation expression selects default, parameterised, or copy construction.
Delegation initializes the current object through one target constructor.
Members initialize in declaration order.
The trace is a -> 0, Unassigned, 40; b -> 17, Asha, 84 then 91; c copies b at 84 and remains 84. Now add StudentRecord d{28, "Kabir", 73}; StudentRecord e = d; d.setMarks(79);. Predict before running: d prints 28 Kabir 79; e prints 28 Kabir 73.
Continue with the C++ Programming course for a structured language sequence. Coding for Placements adds wider multi-language practice. Keep initialization and later assignment separate, and constructor traces become mechanical.
Keep learning
Templates in C++: Reusable Parameterized Code with Worked Examples
Learn how one C++ template produces type-specific functions and classes. Follow exact calls, calculations, deduction failures, and practical corrections.
Friend Functions in C++: Controlled Class Access with a Worked Example
See how a non-member friend function can validate and update two Wallet objects while private balances stay hidden from ordinary callers. Includes runnable C++ and an exact trace.
Data Types in C++: Fundamental and Derived Types with Worked Examples
Learn how C++ fundamental types differ from arrays, pointers, references, and function types. Trace one complete program, then test the conversion traps.
Virtual Functions MCQs: 11 Solved C++ Runtime Polymorphism Questions
Test C++ virtual dispatch with 11 solved exam MCQs, worked explanations and three code traces covering final overriders and safe base-class destruction.





