class A { public: A() { v.resize(a); } A(int a, int b): a(a), b(b), A() {} // ! private: std::vector<int> v; int a, b; }; 

Is the order of calling a, b, A() guaranteed here, or can it happen that A() called first, and then a, b ? If not, how to fix it to get the desired behavior?

    3 answers 3

    The order of the call corresponds to the order of declaring members whatever you write in the constructor, v will be initialized first, then a and b .

    • even if you do not explicitly call, for example a , but call only b , then first there will be default initialization for a , and then value initialization for b ? - nikita
    • Yes, for example, in class A { .. int a; int b; A(int x):b(x),a(b){}}; class A { .. int a; int b; A(int x):b(x),a(b){}}; value a not defined. - Harry
    • I'll know, thank you - nikita
    • in your example, UB will be, right? because a assigned an undefined value - nikita
    • There is in the standard a very subtle difference between UB and UV (undefined value), in which I swim, here it is necessary that the experts say exactly ... - Harry

    Let's start with the fact that this class declaration

     class A { public: A() { v.resize(a); } A(int a, int b): a(a), b(b), A() {} // ! private: std::vector<int> v; int a, b; }; 

    is incorrect. If a delegating constructor is used, that is, a constructor that delegates initialization to another constructor, as in this declaration

     A(int a, int b): a(a), b(b), A() {} // ! ^^^ 

    then the initialization list should contain only a call to the constructor to which initialization is delegated.

    From standard C ++ (12.6.2 Initializing bases and members)

    6 A mem-initializer-list can be delegated to another constructor of the constructor's class. If a mem-initializer-id designates the constructor's class, mem-initializer ; The constructor is a delegating constructor. This is the first constructor of the object (it’s not the target constructor for that object’s construction). The target constructor is selected by overload resolution. Once the target constructor returns, the body of the delegating constructor is executed. If a constructor is delegated to directly or indirectly, the program is ill-formed; no diagnostic is required

    It would be correct to define your class, for example, as follows

     class A { public: A() : A( 0, 0 ) {} A( int a, int b ): a( a ), b (b ) { v.resize( a ); } private: std::vector<int> v; int a, b; }; 

    That is, the delegating constructor is the default constructor.

    As for the order of initialization, it is described in the following quotation from the C ++ standard (12.6.2 Initializing bases and members)

    13 In a non-delegating constructor, initialization proceeds in the following order:

    This is where the derived ac 1.8 1.8 1.8 ac ac where where where where where where where where where where “To-right” is the base class specifier-list.

    - Then there are no rules (no matter the order of the mem-initializers).

    - Then they were subject to the order of the mem-initializers.

    - Finally, the body is executed.

    That is, the constructors of the virtual base classes are first called. Then the constructors of the base classes are called directly in the order they are listed when declaring the derived class. Then the members of the derived class are initialized in the order in which they are declared, and finally the body of the constructor of the derived class itself is executed.

    Notice that each initialization expression is a so-called full expression. This means that side effects are applied after calculating initialization expressions and are visible at the next initialization.

    From standard C ++ (12.6.2 Initializing bases and members)

    1. ... the memorized constitutions of a full-expression. This is what you are doing. This is not the most derived class.

    Consider the following example.

     #include <iostream> struct A { A(int i) : a(i) {} int a; }; struct B : virtual A { B(int i) : A(i++), b(i++) {} int b; }; struct C : virtual A { C(int i) : A(i++), c( i++ ) {} int c; }; struct D : B, C { D(int i = 0) : d(i++), C(i++), B(i++), A(i++) {} int d; }; int main() { D d; std::cout << "a = " << da << ", b = " << db << ", c = " << dc << ", d = " << dd << std::endl; } 

    In the constructor of class D , initialization elements are listed in random order. How will initialization be performed?

    According to the citation from the standard, the constructor of the virtual class A first called. Therefore, the variable a will get the value 0. Then the constructor of the immediate base class is called, which is listed first in the list of the declaration of the derived class D This class is class B In the initialization list of the constructor of this class there is a call of its own base class A However, such a call is ignored. The constructor of class A has already been called from class D Therefore, the only thing that the class B constructor does is initialize the variable b , which will get the value 1. Then the class C constructor is called. Its own constructor also calls the constructor of class A But this call will be ignored for the same reason as for class B The variable c will get the value 2. And finally, the variable d will be initialized, which is declared in the class D

    The output of the program to the console confirms the validity of what has been said.

     a = 0, b = 1, c = 2, d = 3 

      You can rewrite your class to define a different initialization order:

       class A { ... private: int a, b; std::vector<int> v; }; 

      Then the constructor can be defined as follows:

       A() : a(0), b(0), v(a) { } 

      Tip: better for a variable storing (accepting) size to use the size_t type.

      Solution using delegating constructor:

       class A { public: A(int a, int b) : a(a), b(b) { v.resize(a); } A() : A(0, 0) { } private: int a, b; std::vector<int> v; }; 

      The solution with the allocation of a common initialization code in a separate function init() :

       class A { public: A() : a(0), b(0) { init(); } A(int a, int b) : a(a), b(b) { init(); } private: void init() { v.resize(a); } private: int a, b; std::vector<int> v; }; 
      • init for modern C ++ is not very relevant, it is better to use a delegating constructor. - ixSci
      • @ixSci, added code using delegating constructor. Thanks for the advice! - aleks.andr