ClassName (int x, int y, int z) : x(x), y(y), z(z) {}
versus
ClassName (int x, int y, int z) {
this->x = x;
this->y = y;
this->z = z;
}
The benefit of using the first version (member initializer list) is primarily related to performance and initialization behavior. In the initializer list, member variables are initialized directly, while in the second version they are first default-constructed and then assigned values. If they were not int but complex classes where you need to pass some parameters, that could also be a challenge (complex objects or types that don't have default constructors)
it should as this is the best way to write an initializer. for example const and reference members must be initialized during construction and cannot be assigned later. The initializer list is the only way to initialize them
class A {
private:
const int constant;
public:
A(const int constantValue) : constant(constantValue) {}
// A(const int constantValue) {constant = constantValue;} // error: assignment of read-only member
void print() {
Serial.print("constant = ");
Serial.println(constant);
}
};
// various ways of writing code to instantiate an A
A a1(42); // Direct initialization
A a2{42}; // prevents certain implicit type conversions and helps avoid narrowing conversions
A a3 = 42; // not recommended — the process involves creating a temporary object and copying it to a3
void setup() {
Serial.begin(115200);
a1.print();
a2.print();
a3.print();
}
void loop() {}
the constructor with the initializer list works whereas you'll get an "error: assignment of read-only member" if you go for the assignment in the constructor's body.
an additional example to build onto the previous one.
As we have seen you can't instantiate A without a parameter
Imagine you have a class B and you want B to have an instance variable of type A.
➜ you can't write the constructor of B without an initializer list.
class A {
private:
const int constant;
public:
A(const int constantValue) : constant(constantValue) {}
// A(const int constantValue) {constant = constantValue;} // error: assignment of read-only member
void print() {
Serial.print("constant = ");
Serial.println(constant);
}
};
class B {
private:
A a;
public:
B(const int aConstant) : a(aConstant) {}
// B(const int aConstant) {a(aConstant);} // error: no matching function for call to 'A::A()'
void print() {
Serial.print("from B, ");
a.print();
}
};
A a(42);
B b(84);
void setup() {
Serial.begin(115200);
a.print();
b.print();
}
void loop() {}
the error you'd get if you try to compile with the commented out constructor would be error: no matching function for call to 'A::A()' because the class tries to build its instance variables and so tries to first call a default constructor for a and there is none.
i provide examples using C which i believe most people on this forum struggle with and can learn from, if not immediately can become aware of
initializer list seems to be a more advanced feature of C++, a more esoteric feature as @J-M-L has now described. While they may be using libraries based on clases, it seems that C++ is beyond what most people, the neophytes on this forum are writing code for
This discussion primarily revolves around object-oriented programming, making the mention of initializer lists quite relevant. By consistently applying best practices for constructors, newcomers can gain valuable insights, while seasoned C programmers may be inspired to explore new concepts.
Adhering strictly to C, despite the platform being compiled with a C++ compiler and the common use of class instances through various libraries (often without a full understanding, I agree) limits perspective significantly.
Challenging one's mind is always beneficial, especially as we grow older...
So much so that the Rust folks have excluded the feature until the problems can be resolved despite the convenience.
...versus...
The question to ask when deciding what arguments should be included for constructors... Is the {whatever} a valid {whatever} without that value?
In this case, can a Train be nameless? If "no" then just the second constructor is the correct choice. Name is required so it should be impossible to create a Train without a name.
If "yes" then the first constructor is the correct choice with a setName method. You may want to also include the second constructor as a convenience. But, you may want to instead consider a TrainBuilder class for constructing Trains or you may want to support chained setters.
In other words, choose constructor parameters wisely and cautiously.
but some suggest that even using features of C (e.g. structs, enums, dare i say arrays, or pointers) is too confusing for the typical Arduino newbie.
i think, but have been chastised, that showing how some feature of the language someone appears unfamilar with can make their program simpler, is a good thiing, at the very least making them aware of it, even if not understandable at the time.
some newbie code is so convoluted that i think it makes sense to just demonstrate a different approach.
I don't see the harm, the OP is free to ignore the post. is there just one acceptable approach for responding to a post? is there just one type of newbie?