tags:
- Cpp
Member Initializer List in C++ (ENG)
There isn't much talk about this member initializer list really. We only use initializer list to initialize a new class object. You may see the member initializer list after a function definition of any kind of constructor, syntax with a colon character :
. For example, consider the following:
class MyClass{
int x, y, z;
public:
// Most commonly used and seen
MyClass(int _x, int _y, int _z) : x(_x), y(_y), z(_z) {}
// Brace-enclosed initializer list semantics after C++11
MyClass(int _x, int _y, int _z) : x{_x}, y{_y}, z{_z} {}
};
But we have to following some rules here to order:
class MyClass{
const int& ref;
MyClass() : ref(10) {} // This would cause an error
};
class MyClass {
int x, y, z;
public:
// x is still initialized before y and z
MyClass(int _x, int _y, int _z) : z(_z), y(_y), x(_x) {}
};
x
is initialized after y
or z
, or we say y
is initialized after z
, shit happens:class MyClass {
int x, y, z;
public:
// x is initialized using the uninitialized y
MyClass(int _x, int _y, int _z) : x(y), y(_y), z(_z) {}
};
So, why are you recommended to use {}
to initialize an object even though we have ()
? Because they have different semantics. An exact example is how they are used in STL container.
std::vector<int> v(100, 1); // Initializes a vector containing 100 items of value 1
std::vector<int> v{100, 1}; // Initializes a vector containing 2 items: 100 and 1
int i(3.14); // Okay, i will be assigned the value 3 (narrowing conversion)
int i{3.14}; // Error, the assigned value must be type-specific (no narrowing)
Another great thing with curly braces {}
is automatically initial everything to zero for basic data types. For example:
int a{}; // a is initialized to 0
void* ptr{}; // ptr is initialized to nullptr
class MyClass {
public:
int a;
void* ptr;
MyClass() = default;
};
MyClass obj{}; // All members are initialized to 0 (pointer to nullptr)
But note that if you have a user-defined constructor like this, it can cause undefined behavior:
class MyClass {
public:
int a;
void* ptr;
MyClass() {} // Would cause undefined behavior
};
MyClass obj{}; // Not all members are initialized
std::initializer_list
Use initializer list for copy avoidance purposes.
#include <iostream>
class MyClass {
public:
MyClass(int x, int y) {
std::cout << "Regular constructor called with " << x << " and " << y << std::endl;
}
MyClass(std::initializer_list<int> list) {
std::cout << "Initializer list constructor called with ";
for (auto elem : list) {
std::cout << elem << " ";
}
std::cout << std::endl;
}
};
int main() {
MyClass obj1(100, 200); // Calls the regular constructor
MyClass obj2{100, 200}; // Calls the initializer list constructor
// MyClass obj3{100.5, 200.5}; // No narrowing conversion
return 0;
}