3

Why did the creator of C++ decide to use constructor initializer list to initialize base classes? Why didn't he instead choose to use syntax like the second comment line in the following code?

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

class B : A{
public:
  B() : A() { }   // Why decide to use this one, using constructor initializer, to initialize base class?
  B() { A(); }    // Why not choose this one? It's easy for me to understand if it was possible.
};

int main(int argc, char *argv[]){
  /* do nothing */
}
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
Kei Minagawa
  • 4,395
  • 3
  • 25
  • 43

4 Answers4

5

The advantage of using an initializer list is, that you have a completely initialized object in the body of the constructor.

class A {
public:
    A(const int val) : val(val) { }
private:
    const int val;
};

class B : A{
public:
  B() : A(123) {
      // here A and B are already initialized
  }
};

So you have a guarantee that all members, even those of the parent, are not in an undefined state.

Edit

In the example of the question it is not necessary to call the base class in the initializer list, this is done automatically. So I slightly modified the example.

Christophe Weis
  • 2,518
  • 4
  • 28
  • 32
1

I can see a few possible alternatives to the current C++ rule that base classes and data members of a class type are initialised in the mem-initialiser list of the class type's constructor(s). They all come with their set of issues, which I will discuss below.

But first, note that you cannot simply write a derived constructor like this:

struct Derived : Base
{
  Derived()
  {
    Base(42);
  }
};

and expect the line Base(42); to call the base class constructor. Everywhere else in C++, such a statement creates a temporary object of type Base initialised with 42. Changing its meaning inside a constructor (or just inside its first line?) would be a syntax nightmare.

Which means that new syntax would need to be introduced for this. In the rest of this answer, I will use a hypothetical construct __super<Base> for this.

Now on to discuss possible approaches which would be closer to your desired syntax, and present their problems.

Variant A

Base classes are initialised in the constructor body, while data members are still initialised in the mem-initialiser list. (This follows the letter of your question the closest).

The would have the immediate problem of stuff executing in different order than it's written. For very good reasons, the rule in C++ is that base class subobjects are initialised before any data members of the derived class. Imagine this use case:

struct Base
{
  int m;

  Base(int a) : m(a) {}
};

struct Derived
{
  int x;

  Derived() :
    x(42)
  {
    __super<Base>(x);
  }
};

Written like this, you could easily assume x would be initialised first and then Base would be initialised with 42. However, that would not be the case and instead reading x would be undefined.

Variant B

Mem-initialiser lists are removed altogether, base classes are initialised using __super, and data members are simply assigned in the constructor body (pretty much the way Java does it).

This cannot work in C++ because initialisation and assignment are fundamentally different concepts. There are types where the two operations do vastly different things (e.g. references), and types which are not assignable at all (e.g. std::mutex).

How would this approach deal with a situtation like this?

struct Base
{
  int m;

  Base(int a) : { m = a; }
};

struct Derived : Base
{
  double &r;

  Derived(int x, double *pd)
  {
    __super<Base>(x);  // This one's OK
    r = *pd;  // PROBLEM
  }
};

Consider the line marked // PROBLEM. Either it means what it normally does in C++ (in which case it assigns a double into a "random place" which the uninitialised reference r references), or we change its semantics in constructors (or just in initial parts of a constructor?) to do initialisation instead of assignment. The former gives us a buggy program while the latter introduces totally chaotic syntax and unreadable code.

Variant C

Like B above, but introduce special syntax for initialising a data member in the constructor body (like we did with __super). Something like __init_mem:

struct Base
{
  int m;

  Base(int a) : { __init_mem(m, a); }
};

struct Derived : Base
{
  double &r;

  Derived(int x, double *pd)
  {
    __super<Base>(x);
    __init_mem(r, *pd);
  }
};

Now, the question is, what have we achieved? Previously, we had the mem-initialiser list, a special syntax to initialise bases and members. It had the advantage that it made clear these things happen first, before the constructor body starts. Now, we have a special syntax to initialise bases and members, and we need to force the programmer to put it at the start of the constructor.


Note that Java can get away with not having a mem-initialiser list for several reasons which don't apply to C++:

  • The syntax for creating an object is always new Type(args) in Java, whereas Type(args) can be used in C++ to construct objects by value.
  • Java only uses pointers, where initialisation and assignment are equivalent. For many C++ types, there operations are distinct.
  • Java classes can only have one base class, so using just super is enough. C++ would need to differentiate which base class you're referring to.
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
0
B() : A() { }   

This will initialize the base class in user defined way.

B() { A(); }  

This will NOT initialize the base class in user defined way. This will create an object inside of constructor i.e B(){}

sameerkn
  • 2,209
  • 1
  • 12
  • 13
  • 1
    The question ia about C++ as it could have been. Your answer assumes C++ as it was standardized. – MSalters Mar 24 '17 at 09:01
  • @MSalters: +1. Now got what's the question actually was. So, it boils down to how to naturally visualize the pre and post process of a derived object creation. – sameerkn Mar 24 '17 at 09:21
-1

I think first initialization has better readability compared to the second and you can also deduce class hierarchy.

vivekn
  • 35
  • 6
  • The second one is not legal so readability isn't even a consideration. – user4581301 Mar 24 '17 at 06:17
  • Got it. My bad. http://stackoverflow.com/questions/120876/what-are-the-rules-for-calling-the-superclass-constructor – vivekn Mar 24 '17 at 06:19
  • The second one is legal but all it does is construct an anonymous temporary. It's also a stone's throw from the most vexing parse (Google that.) – Bathsheba Mar 24 '17 at 06:33
  • @Bathsheba, yes the second one is legal - tried using MSVC 2015. – vivekn Mar 24 '17 at 06:35
  • Pretty sure @Bathsheba 's comment was directed at me. What was really going on clicked in a minute or so after making my comment. – user4581301 Mar 24 '17 at 06:41