11
struct B { 
  int b1, b2;  
  B(int, int);
};

struct D : B {
  int d1, d2;
// which is technically better ?
  D (int i, int j, int k, int l) : B(i,j), d1(k), d2(l) {} // 1st Base
// or
  D (int i, int j, int k, int l) : d1(k), d2(l), B(i,j) {} // last Base
};

Above is just a pseudo code. In actual, I wanted to know that does the order of calling base constructor matter?

Are there any bad behaviors (especially corner cases) caused by any of the cases? My question is on more technical aspect and not on coding styles.

Milan
  • 1,743
  • 2
  • 13
  • 36
iammilind
  • 68,093
  • 33
  • 169
  • 336

3 Answers3

21

The order you refer in your question is not the "order of calling base constructor". In fact, you can't call a constructor. Constructors are not callable by the user. Only compiler can call constructors.

What you can do is to specify initializers. In this case (constructor initializer list) you are specifying initializers for subobjects of some larger object. The order in which you specify these initializers does not matter: the compiler will call the constructors in a very specific order defined by the language specification, regardless of the order in which you specify the initializers. The base class constructors are always called first (in the order in which the base classes are listed in class definition), then the constructors of member subobjects are called (again, in the order in which these members are listed in the class definition).

(There are some peculiarities in this rule when it comes to virtual base classes, but I decided not to include them here.)

As for the bad behaviors... Of course there is a potential for "bad behaviors" here. If you assume that the order of initialization depends on the order you used in the constructor initializer list, you will most likely eventually run into an unpleasant surprise, when you discover that the compiler completely ignores that order and uses its own order (the order of declaration) instead. For example, the author of this code

struct S {
  int b, a;
  S() : a(5), b(a) {}
};

might expect a to be initialized first, and b to receive the initial value of 5 from a, but in reality this won't happen since b is initialized before a.

Krizz
  • 11,362
  • 1
  • 30
  • 43
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 3
    Probably, also worthwhile to mention that virtual Base classes are given preference in the order of initialization for Base classes. – Alok Save Jun 06 '11 at 04:01
  • 2
    @Als: ... and that virtual base class initializers are simply ignored unless you are constructing the most-derived object. I didn't want to overload my answer with these issues. – AnT stands with Russia Jun 06 '11 at 04:06
  • 3
    "Constructors are not callable by the user. Only compiler can call constructors." Aren't construtors manually called with placement new ? Or is this considered "running" the constructor ? – Nikos Athanasiou Oct 03 '14 at 07:54
  • Worth pointing out that that since the base constructor is called first, listing it first in the initializer list makes most sense. And indeed compilers can warn about this (`-Wreorder` in gcc for example). – Zitrax Dec 28 '22 at 12:20
6

The order is well defined. It does not depend on how you specify them while initializtion.
Base class constructor B will be called first and then the member variables(d1 & d2) in the order in which they are declared.

To explain the comment in @Andrey T's answer.

class MyClass1: public MyClass2, public virtual MyClass3
{


};

The order of calling the Base class constructors is well defined by the standard and will be:

MyClass3  
MyClass2
MyClass1

The virtual Base class MyClass3 is given preference over Base Class MyClass2.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • Can you explain your `virtual` base comment in the answer ? – iammilind Jun 06 '11 at 04:05
  • @aloksave why virtual base class is given preference over the myclass2. – Krishna Oza Mar 12 '14 at 10:12
  • @darth_coder `virtual` base classes are given preference because they must be constructed before any non-`virtual` base classes that depend on them. Consider this: `struct A{}; struct B : virtual A {}; struct C: virtual A {}; struct D : A, B {};`. In this case, `D` must construct `A` before it can construct either `B` or `C`, because both `B` and `C` require that `A` be constructed before they are. Rather than allowing either `B` or `C` to construct the `A` (which could get complex in more complex situations, such as if there was also `struct E : C, B {};`, requiring additional compiler – Justin Time - Reinstate Monica Feb 19 '17 at 19:06
  • overhead and possibly introducing undefined behaviour (if a simple compiler decides that `B` always constructs `A`, then `E` would have UB, on the grounds that its `C` is constructed before its `B` can construct its `A`) or breaking expectations (if said compiler silently, and evilly, constructs `B` before `C`, ignoring `E : C, B`, then this violates the principle of least astonishment)), the standard simply specifies that the most-derived class must construct any `virtual` base classes in its inheritance hierarchy before constructing any non-`virtual` base classes, guaranteeing that – Justin Time - Reinstate Monica Feb 19 '17 at 19:10
  • `A` will always be valid when `B` and `C` are constructed, no matter which order they're constructed in, preventing any UB. (Or, to shorten that wall of text, `virtual` base classes are given preference to guarantee that they'll always be valid when the non-`virtual` base classes are constructed, preventing UB and/or keeping the compiler from violating the standard to prevent UB.) – Justin Time - Reinstate Monica Feb 19 '17 at 19:10
3

The order things appear in the initialisation list is not significant. In your case, the base object will always be initialised first, followed by d1 and d2, in that order. Initialisation is performed in order of derivation and in order members appear in the class definition.

Having said that, it is normally considered good style to write the initialisation list in the order of initialisation, and some compilers will issue a warning if you don't do this.