- Working Example: cnstrs.cpp - Practical implementation with deep copy and Rule of Three
- Initialization List Deep Dive - Why it's more efficient
- Explicit Keyword Explained - Prevent implicit conversions
- What is a Constructor?
- Default Constructor
- File:
01_basic_constructor.cpp
- Passing arguments to constructors
- Multiple constructors (Function Overloading)
- File:
02_parameterized_constructor.cpp
- What is a Copy Constructor?
- Shallow vs Deep Copy
- When is it called?
- File:
03_copy_constructor.cpp
- Multiple constructors with different signatures
- File:
04_constructor_overloading.cpp
- Member initialization list syntax
- Why use it? (Performance & Efficiency)
- When is it mandatory? (const, reference, objects without default constructor)
- Initialization vs Assignment
- Initialization order (declaration order matters!)
- File:
05_initialization_list.cpp
Deep Dive Explanation: See detailed explanation below
- What is a Destructor?
- When is it called?
- Resource cleanup
- File:
06_destructor_basics.cpp
- Order of execution in inheritance
- Virtual destructors importance
- File:
07_constructor_destructor_order.cpp
- Explicit keyword (prevent implicit conversions)
- Private Constructors (Singleton, Factory patterns)
- Delegating Constructors (C++11)
- Delete/Default keywords (C++11)
- File:
08_special_cases.cpp
Deep Dive Explanation: See explicit keyword explanation below
- Dynamic memory allocation
- RAII (Resource Acquisition Is Initialization)
- File:
09_realworld_examples.cpp
- Common interview questions and answers
- File:
10_interview_questions.cpp
- Same name as class, no return type
- Called automatically when object is created
- Can be overloaded
- Types: Default, Parameterized, Copy
- Initialization list vs assignment in constructor body
- Cannot be virtual
- Name: ~ClassName()
- No parameters, no return type
- Only one destructor per class
- Called when object goes out of scope
- Should be virtual in base class if inheritance is used
- Used for cleanup (deallocate memory, close files, etc.)
- Part 1: Basic Constructor
- Part 2: Parameterized Constructor
- Part 3: Copy Constructor
- Part 4: Constructor Overloading
- Part 5: Initialization List
- Part 6: Destructors
- Part 7: Constructor/Destructor Order
- Part 8: Special Cases ✅ COMPLETED!
- Part 9: Real-World Examples (Optional)
- Part 10: Interview Questions (Optional)
Without Initialization List (Assignment in Body):
class Student {
string name;
int age;
public:
Student(string n, int a) {
name = n; // Assignment
age = a;
}
};What happens behind the scenes:
Step 1: Default constructor called AUTOMATICALLY
name = string(); // Empty string created
age = int(); // Default int
Step 2: Assignment in constructor body
name = n; // Destroy old, assign new
age = a;
Step 3: Cleanup (for complex types)
Total operations for 'name': 3 steps
Time: ███
With Initialization List:
class Student {
string name;
int age;
public:
Student(string n, int a) : name(n), age(a) {
// Members ALREADY initialized!
}
};What happens behind the scenes:
Step 1: Direct construction with value
name = string(n); // Construct directly
age = a; // Direct initialization
Total operations for 'name': 1 step
Time: █
| Method | Operations | Time Complexity |
|---|---|---|
| Assignment | Default construct → Assign → Cleanup | O(3) |
| Initialization List | Direct construction | O(1) |
Impact: For complex types (string, vector, objects), initialization list is significantly faster!
Class = Blueprint (No memory allocated)
class Student {
string name; // Just a declaration, no memory yet!
int age; // Just a declaration, no memory yet!
};
// At this point: NO OBJECT EXISTSObject Creation Process:
Step 1: Memory Allocation
┌─────────────────┐
│ Memory for obj │ ← Space allocated
│ - name: ??? │
│ - age: ??? │
└─────────────────┘
Step 2: Initialization List Executes
┌─────────────────┐
│ Memory for obj │
│ - name: value │ ← Initialized directly
│ - age: value │ ← Initialized directly
└─────────────────┘
Step 3: Constructor Body Executes
(Additional logic if any)
Step 4: Object Ready!
Variables now exist and are usable
📋 Class Definition = Blueprint
- Room 1: Living room
- Room 2: Bedroom
(No actual house exists yet!)
🏗️ Object Creation = Construction Process:
Step 1: Allocate land (memory allocation)
Step 2: Build rooms (initialization list)
Step 3: Interior decoration (constructor body)
🏠 Object Complete
- Rooms exist with furniture
- House is ready to use
// Before: No object, no variables exist
Student s1("John", 20); // Object creation starts!
// During creation:
// 1. Memory allocated for s1
// 2. Initialization list: name="John", age=20
// 3. Constructor body runs
// 4. Object ready
// After: s1 exists, name and age exist in memoryQ: Why is initialization list more efficient?
It directly constructs members with given values in one step, whereas assignment first default-constructs members, then assigns new values (two steps). This matters especially for complex types like strings, vectors, and objects.
Q: When do member variables come into existence?
Member variables come into existence when an object is created. Memory is allocated, then the initialization list runs to initialize members, then the constructor body executes. Before object creation, the class is just a blueprint.
Q: When MUST you use initialization list?
- Const member variables (cannot be assigned after creation)
- Reference member variables (must be initialized)
- Member objects without default constructors
- Calling base class constructors (inheritance)
Without explicit keyword:
class Distance {
int meters;
public:
Distance(int m) : meters(m) { } // Single-parameter constructor
};
// This WORKS but might be unintended!
Distance d = 100; // int implicitly converted to DistanceWhat C++ compiler does automatically:
Step 1: Sees you want Distance object
Step 2: Sees you provided int (100)
Step 3: Finds constructor Distance(int)
Step 4: Automatically calls Distance(100)
Step 5: Distance d = 100; becomes Distance d = Distance(100);
This is called IMPLICIT TYPE CONVERSION - compiler does it automatically without asking you!
Real-world example:
class BankAccount {
double balance;
public:
BankAccount(double bal) : balance(bal) { }
};
void withdraw(BankAccount acc) {
// Process withdrawal from account
}
// Later in code:
withdraw(5000); // 🚨 DANGER! What does this mean?The problem:
- Did you mean account ID
5000? - Or create NEW account with balance ₹5000?
- Code compiles but does WRONG thing!
- C++ silently creates temporary
BankAccount(5000)- BUG!
class Distance {
int meters;
public:
explicit Distance(int m) : meters(m) { }
};
// Now:
Distance d = 100; // ❌ ERROR! Implicit conversion blocked
Distance d(100); // ✓ OK - Direct initialization
Distance d = Distance(100); // ✓ OK - Explicit conversionWhat explicit does:
- Tells compiler: "Don't automatically convert for me!"
- Forces programmer: "Be explicit about what you want!"
- Prevents bugs: No accidental conversions
WITHOUT explicit:
==================
int (100) ──→ [Automatic Magic ✨] ──→ Distance object
(Compiler does this silently!)
WITH explicit:
==================
int (100) ──X──→ Distance object (BLOCKED!)
↓
Must explicitly say:
Distance(100) ✓
Distance d = Distance(100); ✓
Modern C++ Best Practice (Scott Meyers - Effective C++):
"Make constructors
explicitby default. Only remove it if you have a good reason."
// ✅ DEFAULT: Always start with explicit
class MyClass {
public:
explicit MyClass(int value) { }
};
// ❌ RARE: Only remove if you specifically want implicit conversion
class MyClass {
public:
MyClass(int value) { } // After careful consideration only!
};Some standard library types intentionally allow implicit conversion:
// std::string allows implicit conversion - by design
std::string s = "hello"; // char* → string (convenient!)
// std::complex allows implicit conversion
std::complex<double> c = 3.14; // double → complex (makes sense!)But these are carefully designed! For your classes: use explicit by default.
Q: What is the explicit keyword and when should you use it?
A: The explicit keyword prevents single-parameter constructors from being used for implicit type conversions.
- Without
explicit:Distance d = 100;works (compiler silently converts int → Distance) - With
explicit:Distance d = 100;gives compile error, must useDistance d(100);
Why use it? Prevents accidental bugs from unintended type conversions. Makes code intentions clear and compiler catches mistakes at compile-time.
Best practice: Make ALL single-parameter constructors explicit unless you specifically want implicit conversion. It's the safe default in modern C++.
Think of explicit as a safety belt - always wear it! 🔒