HellasT:
Is it the same just written in more lines of code or is it that member initializers serve other purposes also ?
Initializer lists can initialize anything. The approach you used (with the assignment in the constructor) can only be used for types that have a default constructor.
You can see the difference here: Compiler Explorer
The code has a class "S" that prints when an object is created, when it is copied, and when it is destroyed. Each instance of the class "S" has a unique ID.
Then there are two classes, Good and Bad, that have a member of type S. One uses an initializer list, the other does not.
In this example, "Good" and "Bad" are like "blinker" in your code, and their member "S s" is like the member "byte LedPin" in your "blinker" class.
The rest is some boilerplate for printing (std::cout, puts) and keeping the unique ID.
#include <iostream>
// Ignore this class for now, it's just a class that prints its unique ID when it
// is created, copied or destroyed.
class S {
public:
S() : index(++instance_ctr) { std::cout << "Default constructor: "<< index << std::endl; }
S(const char *name) : index(++instance_ctr), name(name) { std::cout << "Constructor with name: " << index << " (" << name << ")" << std::endl; }
S &operator=(const S &other) {
this->name = other.name;
std::cout << "Copy assignment: " << this->index << " â " << other.index << " (" << name << ")" << std::endl;
return *this;
}
~S() { std::cout << "Destructor: " << index << " (" << name << ")" << std::endl; }
private:
int index = 0;
const char *name = nullptr;
static int instance_ctr;
};
int S::instance_ctr = 0;
// With an initializer list
class Good {
public:
Good(const char *name) : s(name) {}
private:
S s;
};
// Without an initializer list
class Bad {
public:
Bad(const char *name) {
s = name;
}
private:
S s;
};
int main() {
puts("");
puts("Creating 'Good'");
{ Good g = "Good"; }
puts("");
puts("Creating 'Bad'");
{ Bad b = "Bad"; }
}
The output:
Creating 'Good'
Constructor with name: 1 (Good)
Destructor: 1 (Good)
Creating 'Bad'
Default constructor: 2
Constructor with name: 3 (Bad)
Copy assignment: 2 â 3 (Bad)
Destructor: 3 (Bad)
Destructor: 2 (Bad)
The class "Good" that uses the initializer list initializes its member "s" directly, you see one call to the constructor of "S". Only one "S" object is created (with index 1).
The class "Bad" on the other hand first initializes its member "s" with the default constructor (without a name). This member "s" without a name has index 2.
Then it uses the name to creates a temporary "S" object (with index 3), and then copies the temporary (index 3) to its member "s" (index 2).
That's the difference: initializer lists initialize the members directly, your version first constructs a default-initialized variable, and then copies the actual value into it.