Skip to content

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 constructor

2. 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.