Preferred way to initialize a class member?

class A { public: int x[100]; }; 

Declaration A a will not initialize the object (this can be seen from the garbage values ​​in the x field). The following initiates initialization: A a{} or auto a = A() or auto a = A{} .

Should any of the three be preferable?

Next, make it a member of another class:

 class B { public: A a; }; 

The default constructor B seems to take care of initializing a . However, if you are using a custom constructor, I have to take care of this. The following two options are:

 class B { public: A a; B() : a() { } }; 

or

 class B { public: A a{}; B() { } }; 

Should any one of the two be preferable?

+5
source share
1 answer

Initialization

 class A { public: int x[100]; }; 

Declaration A a will not initialize the object (for garbage to view the value in the x field).

Correction A a defined without an initializer and does not meet any of the default initialization requirements.


1) The following initiates initialization:

 A a{}; 

Yes


2) The following actions initiate initialization:

 auto a = A(); 

Yes;

  • This is copy initialization , where the temporary prvalue value is built with direct initialization () , which
  • The temporary value prvalue is then used to direct-initialize the object.
  • Copy elision can be and is usually used to optimize copy and build A in place.
    • Side effects of skipped copy / move constructors are allowed.
  • The constructor cannot be removed. for example, A(A&&) = delete;
  • If the copy constructor is deleted, then the move constructor must be present. for example A(const A&) = delete; A(A&&) = default; A(const A&) = delete; A(A&&) = default;
  • Will not warn about narrowing the conversion.

3) The following actions initiate initialization:

 auto a = A{} 

Yes

  • This is copy initialization , where the temporary prvalue value is built with initialization of the list {} , which
  • Copy elision can be and is usually used to optimize copy and build A in place.
    • Side effects of skipped copy / move constructors are allowed.
  • The constructor cannot be removed. for example, A(A&&) = delete;
  • If the copy constructor is deleted, then the move constructor must be present. for example A(const A&) = delete; A(A&&) = default; A(const A&) = delete; A(A&&) = default;
  • Will warn of a narrowing of the conversion.
  • It works even if the default constructor is removed. for example A() = delete; (If "A" is still considered a collection)

Should any of the three be preferable?

Clearly, you should choose A a{} .


Member Initialization

Next, make it a member of another class:

 class B { public: A a; }; 

The default constructor of B seems to take care of initializing of A

No, this is not true.

  • the implicit default constructor "B" will call the default constructor A , but will not initialize the members. Direct or list initialization will not be initiated. Expression B b; for this example, it will call the default constructor, but leaves the undefined values ​​of array A

1) However, if I use a custom constructor, I have to take care of this. The following two options:

 class B { public: A a; B() : a() { } }; 

This will work;

2) or:

 class B { public: A a{}; B() { } }; 

This will work;

Clearly, you should prefer the second option.


Personally, I prefer to use curly braces everywhere, some exceptions for auto and cases where the constructor might mistake it for std::initializer_list :

 class B { public: A a{}; }; 
Constructor

A std::vector will behave differently for std::vector<int> v1(5,10) and std::vector<int> v1{5,10} . with (5,10) you get 5 elements with a value of 10 each, but with {5,10} you get two elements containing 5 and 10 respectively, because std::initializer_list is very preferable if you use curly braces. This is very well explained in Scott Meyers article 7, Effective Modern C ++.

In particular, for the list of initiators of participants , two formats can be considered:

Fortunately, there is no risk of the most unpleasant analysis on the member initializer lists. Outside the initializer list, as a stand-alone instruction, A a() would declare a function vs. A a{} , which would be clear. In addition, list initialization has the advantage of preventing narrowing conversions.

So, in general, the answer to this question is that it depends on what you want to be sure, and which will determine the form you choose. For empty initializers, the rules are simpler.

+3
source

Source: https://habr.com/ru/post/1245937/


All Articles