Skip to content
C++ Better Explained
Go back
C++ Constructor Initializer Lists Explained for Beginners
Edit page

C++ Constructor Initializer Lists Explained for Beginners

When you write a constructor in C++, you have two places to set up member variables: the initializer list (before the body) or assignment inside the body. Most beginners use the body because it looks familiar. But C++ has a better way — and in some cases, a required way.


The Syntax

An initializer list comes after the constructor’s parameter list, separated by a colon. Each member is listed with its initial value in parentheses:

#include <iostream>
#include <string>

class Rectangle {
public:
    int width;
    int height;

    // Initializer list — width and height are initialized directly
    Rectangle(int w, int h) : width(w), height(h) {}

    int area() const { return width * height; }
};

int main() {
    Rectangle r(5, 3);
    std::cout << "Area: " << r.area() << "\n";  // 15
    return 0;
}

The : width(w), height(h) part is the initializer list. Members are initialized in the order they’re declared in the class, not in the order they appear in the initializer list.


Initializer List vs Assignment in the Body

Here’s what happens without an initializer list:

class Player {
public:
    std::string name;
    int score;

    // WITHOUT initializer list
    Player(std::string n, int s) {
        // name is default-constructed (empty string), THEN assigned
        name = n;
        // score is default-initialized (undefined), THEN assigned
        score = s;
    }
};

And with an initializer list:

class Player {
public:
    std::string name;
    int score;

    // WITH initializer list
    Player(std::string n, int s) : name(n), score(s) {}
    // name is constructed directly with n — no default then assign
};

For int and double, the difference is negligible. For std::string and other class types, the initializer list avoids a wasted default construction plus an assignment — it constructs the object once, correctly, from the start.


When You MUST Use an Initializer List

Some members cannot be assigned in the constructor body — they can only be initialized. The initializer list is the only option in these three cases.

1. const Member Variables

#include <iostream>

class Circle {
public:
    const double PI;
    double radius;

    Circle(double r) : PI(3.14159), radius(r) {}
    // You CANNOT write: PI = 3.14159; inside the body — it won't compile
    
    double area() const { return PI * radius * radius; }
};

int main() {
    Circle c(5.0);
    std::cout << "Area: " << c.area() << "\n";
    return 0;
}

2. Reference Member Variables

#include <iostream>

class Logger {
public:
    std::ostream& output;  // reference member

    Logger(std::ostream& out) : output(out) {}
    // References must be bound at initialization — you can't assign them later

    void log(const std::string& msg) {
        output << "[LOG] " << msg << "\n";
    }
};

int main() {
    Logger logger(std::cout);
    logger.log("Application started");
    return 0;
}
If you're looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you're a complete beginner or looking to solidify your understanding. Just $19.

3. Member Objects With No Default Constructor

#include <iostream>

class Engine {
public:
    int horsepower;
    Engine(int hp) : horsepower(hp) {}
    // No default constructor — Engine() would fail to compile
};

class Car {
public:
    Engine engine;  // Engine has no default constructor
    std::string model;

    Car(std::string m, int hp) : model(m), engine(hp) {}
    // Without the initializer list, the compiler tries Engine() — which doesn't exist
};

int main() {
    Car myCar("Mustang", 450);
    std::cout << myCar.model << "" << myCar.engine.horsepower << " hp\n";
    return 0;
}

Initialization Order

Members are always initialized in the order they’re declared in the class, regardless of the order in the initializer list. This can cause subtle bugs if one member depends on another:

class Box {
public:
    int width;
    int area;  // declared second

    // BUG: area is initialized before width (declaration order),
    // so width is uninitialized when area uses it!
    Box(int w) : area(w * width), width(w) {}
};

The fix: either reorder the class declarations, or compute area in the constructor body after width is definitely initialized.


Summary

Use an initializer list whenever you can — it’s more efficient and it’s the only option for const members, references, and objects with no default constructor. The syntax takes a minute to get used to, but once it clicks, you’ll use it in every class you write.



Take Your C++ Further

If you’re looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you’re a complete beginner or looking to solidify your understanding. Just $19.

👉 Get the C++ Better Explained Ebook — $19

📋

Free Download: The 10 Mistakes Every C++ Beginner Makes

A free 1-page checklist that shows the exact traps that slow down every C++ beginner — so you can avoid them from day one.

🔒 No spam. Unsubscribe anytime.


Edit page
Share this post on:

Previous Post
C++ Copy Constructor: Deep Copy vs Shallow Copy Explained
Next Post
C++ Scope Resolution Operator (::) Explained for Beginners