Appearance
Constructor Initialization Lists
Video: Member Initializer Lists in C++ (Constructor Initializer List)
Constructor initialization lists are a crucial feature in C++ that allows you to initialize member variables directly during object construction. Understanding when and why to use them is essential for writing efficient, correct C++ code.
What are Constructor Initialization Lists?
Constructor initialization lists appear after the constructor's parameter list and before the constructor body. They use a special syntax to initialize member variables directly:
cpp
class Example {
private:
int data;
std::string name;
const double PI;
public:
// Constructor with initialization list
Example(int value, const std::string& str)
: data(value), name(str), PI(3.14159) {
// Constructor body - members are already initialized!
}
};Key Points:
- Syntax:
: member1(value1), member2(value2), ... - Timing: Members are initialized BEFORE the constructor body executes
- Purpose: Direct initialization instead of default construction + assignment
Why Use Initialization Lists?
1. Performance: Avoid Double Initialization
Without initialization lists, member objects go through unnecessary steps:
cpp
class Widget {
private:
std::string name;
std::vector<int> data;
public:
// WRONG: Double initialization
Widget(const std::string& n, const std::vector<int>& d) {
// Step 1: Default construction (already happened)
// Step 2: Assignment (what we're doing here)
name = n; // std::string::operator=()
data = d; // std::vector::operator=()
}
// CORRECT: Direct initialization
Widget(const std::string& n, const std::vector<int>& d)
: name(n), data(d) {
// Members are already initialized with correct values
}
};Performance Impact:
cpp
// Without initialization list:
// 1. std::string name; // Default constructor
// 2. std::vector<int> data; // Default constructor
// 3. name = n; // Assignment operator
// 4. data = d; // Assignment operator
// With initialization list:
// 1. std::string name(n); // Copy constructor
// 2. std::vector<int> data(d); // Copy constructor2. Required for const and Reference Members
Some member types must be initialized in the initialization list:
cpp
class Circle {
private:
const double PI; // Must be initialized
double& radius; // Must be initialized
double area; // Can be initialized or assigned
public:
// WRONG: Won't compile
Circle(double r, double& rRef) {
PI = 3.14159; // ERROR: const cannot be assigned
radius = rRef; // ERROR: reference cannot be assigned
area = PI * r * r; // OK: non-const member
}
// CORRECT: Must use initialization list
Circle(double r, double& rRef)
: PI(3.14159), radius(rRef), area(3.14159 * r * r) {
// All members properly initialized
}
};Why This Matters:
- const members: Cannot be modified after initialization
- Reference members: Must refer to an object from the start
- Objects without default constructors: Cannot be default-constructed
3. Initialization Order Control
Member variables are initialized in the order they're declared in the class, not in the initialization list:
cpp
class OrderExample {
private:
int first; // Declared first
int second; // Declared second
int third; // Declared third
public:
// Initialization list order doesn't matter
OrderExample(int a, int b, int c)
: third(c), first(a), second(b) {
// But members are initialized in declaration order:
// 1. first = a
// 2. second = b
// 3. third = c
}
};Important Rule:
cpp
class DependencyExample {
private:
int nameLength; // Declared first - but depends on name!
std::string name; // Declared second!
public:
// WRONG: nameLength depends on name, but name isn't initialized yet
DependencyExample(const std::string& n)
: name(n), nameLength(name.size()) {
// nameLength will be garbage because name isn't initialized yet!
}
// CORRECT: Declare members in dependency order
DependencyExample(const std::string& n)
: nameLength(n.size()), name(n) {
// name is initialized first, then nameLength
}
};Best Practices
Always Use Initialization Lists For:
cpp
class BestPractices {
private:
// 1. const members
const int MAX_SIZE;
// 2. Reference members
std::string& nameRef;
// 3. Objects without default constructors
std::unique_ptr<Resource> resource;
// 4. Objects with expensive constructors
std::vector<std::string> data;
// 5. Base classes (in inheritance)
// BaseClass base;
public:
BestPractices(int maxSize, std::string& ref, std::unique_ptr<Resource> res)
: MAX_SIZE(maxSize), // const member
nameRef(ref), // reference member
resource(std::move(res)), // unique_ptr
data() { // empty vector (efficient)
// Constructor body
}
};Consider Assignment When:
cpp
class AssignmentCases {
private:
int simpleInt;
double simpleDouble;
public:
AssignmentCases(int value) {
// Simple primitive types - minimal performance impact
simpleInt = value;
simpleDouble = value * 1.5;
// Complex logic that can't be done in initialization list
if (value < 0) {
simpleInt = 0;
simpleDouble = 0.0;
}
}
};Do Use Initialization Lists For:
- All member variables when possible
- const and reference members (required)
- Objects without default constructors
- Objects with expensive constructors
- Base class initialization (in inheritance)
Consider Assignment When:
- Simple primitive types with minimal performance impact
- Complex logic that can't be expressed in initialization
- Conditional initialization based on runtime values
Always Remember:
- Member initialization order follows class declaration order
- const and reference members must use initialization lists
- Performance impact can be significant for objects
- Initialization lists are executed before constructor body
Questions
Q: What is the main advantage of using constructor initialization lists over assignment in the constructor body?
All of the above are correct! Initialization lists avoid double initialization (default construction + assignment), allow initialization of const and reference members, and often result in more readable code. This is especially important for performance-critical applications.
Q: What happens if you don't use an initialization list for a const member variable?
const member variables must be initialized in the initialization list. If you try to assign to them in the constructor body, the code won't compile because const objects cannot be modified after initialization.
Q: In what order are member variables initialized when using an initialization list?
Member variables are always initialized in the order they are declared in the class, regardless of the order in the initialization list. This is why it's important to declare members in the order you want them initialized.
Q: When should you use initialization lists instead of assignment in the constructor body?
You should use initialization lists for all member variables, especially objects and references. This avoids unnecessary default construction and assignment, making your code more efficient and allowing initialization of const and reference members.
Q: What is the performance impact of not using initialization lists for objects?
Not using initialization lists causes double initialization: first default construction, then assignment. This can have significant performance impact, especially for objects with expensive constructors or when creating many instances of the class.