inheriting constructors of class virtually derived. - c++

I came across this question which asks its output.
#include<iostream>
using namespace std;
class A{
public:
int i;
A(int j=3):i(j){}
};
class B:virtual public A{
public:
B(int j=2):A(j){}
};
class C:virtual public A{
public:
C(int j=1):A(j){}
};
class D:public B, public C {
public:
D(int j=0):A(j), B(j+1), C(j+2){}
};
int main()
{
D d;
cout<<d.i;
return 0;
}
There are few things which I did not understand.
Please clarify these doubts.I could not google it as I did not know what to search for.
Q1. As in the code a parameterised constructor is used.Just after the colon(:) we write the constructor of the parent class.How
A(int j=3):i(j){}
is used? As i is not a class.
Q2. In the class D, the constructor for the class is using the constructors for initialising the base classes.But as it can be seen that all the constructors modify variable i of the class A only.
Then what is the sequence of the constructor calling here.
I know when we do not call the constructor of the parent class, it is explicitly called and the order is well known, but what when we implicitly call the constructor like here.
Q3. Inspite of the parameters being initialized, the value which we send in the constructor seems to make a difference.Why is it so ?

A1. :i(j) in A(int j=3):i(j){} is an initializer list. Initializer lists can specify how parent classes and member variables are initialized. (j) is the initializer for i and behaves similarly to initialization for a local variable: int i(j);. (you may be more familiar with the initialization syntax int i = j; which is similar. You can't use the = syntax in initializer lists.)
A2. Virtual base classes are always initialized exclusively by the most derived class's constructor. So D's constructor initializes its A base class and when D's constructor calls the constructors for B and C those constructors do not reinitialize A.
A3. The syntax D(int j=0) does not initialize the parameter. Instead it declares a default value for the parameter which is ignored whenever you explicitly pass a value for that parameter.

The ctor-initializer-list contains the initializers for all subobjects. That means base class subobjects and member subobjects.
Subobjects are initialized in the order in which they appear in the class definition, always. it doesn't matter what order you put them in the ctor-initializer-list (although putting them in any other order is confusing when the order is ignored).
Inherited base subobject constructors are called by the constructor of the class which is directly derived... except for virtual base subobjects, which are called directly from the constructor for the most-derived object. Virtual base subobjects are constructed before anything else.
There's no such thing as "parameters being initialized". Those are default arguments, and they are ignored when an actual argument is provided.

1) The colon : can be used in a constructor to call the constructors of member variables with the given arguments:
public:
int i;
A(int j=3):i(j){}
means that A's member i will call its constructor with j as the argument.
2) Subobjects will be initialized in the order they appear in the class definition
class D:public B, public C
3) They weren't initialized, they were given default arguments.

Q1:
That code in the constructor between the signature of the function and before the opening bracket of the function body is called an initializer list.
The syntax of an initializer list is a listing of member variables of the class with corresponding initial values. The initial values are in parenthesis. The member variables are separate by comma's. The first variable comes after a colon.
An initializer list can also include a constructor to base class, which is what your B and C class do. Your 'A' class simply uses the form that initializes a member variable.
Q2:
To find the order that the constructors are executed in, put in some print statements. It will help you understand the order of construction. Also put in a destructor and put print statements in those too. I can't do all of your homework for you on this question. :)
Q3:
I guess by parameters being initialized you mean the default arguments? Default arguments are always always overridden if a value is actually passed into the function.

Related

Why C++ doesn't allow derived class to use base class member in initialization list?

I've got following program:
#include<iostream>
using namespace std;
struct Base01{
int m;
Base01():m(2){}
void p(){cout<<m<<endl;}
};
struct Derived01:public Base01{
Derived01():m(3){}
};
struct Derived02:virtual public Base01{
Derived01():m(4){}
};
struct my: Derived01,Derived02{
my():m(5){}
};
int main(){
return 0;
}
Both gcc/clang reports compilation error.
I just wish to know what's the language design consideration here, why derived class can only call base class ctor in initialization list, but cannot use base class members directly?
What you do in the constructor initializer list is initialization. It is something that has to be done only once in the lifetime of the object. In general case, that's what starts the objects lifetime.
Base class's constructor (which finished working before your derived class's constructor-proper became active) has already initialized all direct subobjects of the base class. It has already started their lifetimes. If you attempt to reach-in and initialize a direct subobject of base class from the derived class's constructor, that will obviously be the second initialization of the same object. This is completely unacceptable in C++. The language design generally does not allow you to initialize something the second time.
In your case the subobject in question has fundamental type int, so it is hard to see the harm in such "re-initialization". But consider something less trivial, like an std::string object. How do you suggest the derived class should "undo and redo" the initialization already performed by the base class? And while formally it is possible to do it properly, constructor initializer lists are not intended for that purpose.
In general case doing something like that would require a language feature that would allow user to tell base class's constructor something along the lines of "please, leave this subobject of yours uninitialized, I will reach-in and initialize it later from the derived class". However, C++ does not provide users with such capability. A vaguely similar feature exists in virtual base class initialization, but it serves a very specific (and different) purpose.
The proper way to do this in C++ is to pass that value to the base class constructor. Your Base01 class needs an additional constructor that takes the desired value for m. Something like this:
struct Base01{
int m;
Base01():m(2){}
// Added this:
Base01(int mVal) : m(mVal) {}
void p(){cout<<m<<endl;}
};
struct Derived01:public Base01{
Derived01() : Base01(3) {} // Calling base constructor rather than
// initializing base member
};
struct Derived02:virtual public Base01{
Derived01() : Base01(4){} // Same here
};
struct my: Derived01,Derived02{
my(): Base01(5){} // And here.
};
As AnT said, you can't initialize twice--but you can set it up so that things are initialized the way you want in the first place by doing as above.
You certainly can use a base class member in the ctor-initializer list:
struct Base
{
int x;
Base(int x) : x(x) {}
};
struct Derived
{
int y;
Derived() : Base(7), y(x) {}
}
Here, the base member x appears in the initializer for the derived member y; its value will be used.
AnT has done a very nice job explaining why the ctor-initializer list can't be used to (re-)initialize members of base subobjects.
The fundamental language design considerations in this are separation of concerns (avoiding making a base class depend on its derived classes), and that a base class is responsible for initialising its own members (and any bases it has).
A related consideration is that members of the base class don't exist - as far as the derived class constructor is concerned - before the base class constructor completes. If an initialiser list of a derived class was able to reach in and initialise a base class member, then there are two possible consequences
If the base class constructor has not been invoked, its members will not exist when the derived class tries to initialise them.
If the base class constructor has been invoked, the member has been initialised. Initialisation (as distinct from assignment to reinitialise) happens once in the lifetime of an object, so it does not make sense to initialise it again.
Neither of these possibilities really make sense in practice, unless the base class is poorly designed (e.g. its constructors do not properly initialise its members). The sort of machinery needed so they might make sense (e.g. changing order of construction of base classes in a hierarchy, dependent on what member a derived class is trying to initialise) would make compiler machinery more complicated (e.g. being able to both control and track the order of construction of base class members, in case a derived class should choose to reach in), and also mean that the order of construction of classes would depend on derived classs). This would introduce a dependency of the base class behaviour (the means by which it is initialised) on derived classes.
The simpler means is for the base class to provide a constructor that properly initialised the member in question, and for the derived class constructor to invoke that base class constructor in its initialiser list. All of the above (hypothetical) considerations then go away.

C++: how is construction by initialization list done?

C++ allows instantiating a class, setting the public members' value via an initialization list, as the example below shows for b1:
class B {
public:
int i;
std::string str;
};
B b1{ 42,"foo" };
B b2();
; however, if I provide a constructor
B(int k) { }
, it won't compile.
So, what's going on behind the hood?
Is it that when no constructor is provide, the compiler will provide one? But how could it provide one with initialization list? I thought it will just provide a "blank" constructor taking no input, as the example shows for b2. Or does it provide both?
But how could it provide one with initialization list?
No, it won't.
Note that for list initialization B b1{ 42,"foo" };, aggregate initialization is performed.
If T is an aggregate type, aggregate initialization is performed.
And B is an aggregate type,
An aggregate is one of the following types:
array type
class type (typically, struct or union), that has
no private or protected non-static data members
no user-provided, inherited, or explicit constructors (explicitly defaulted or deleted constructors are allowed)
no virtual, private, or protected base classes
no virtual member functions
That's why B b1{ 42,"foo" }; works well.
And If you provide a user-defined constructor, B becomes non-aggregate type, then aggregate initialization won't work again. In this case, you can only initialize B like B b1{42}; or B b2(42);, which will call the appropriate constructor.
BTW: After providing a user-defined constructor (taking one parameter), the implicitly-declared default constructor won't be declared by the compiler again. It means B b2; or B b2{}; won't work again.
BTW2: B b2(); might be a function declaration. See Most vexing parse.

order of calling constructor in inheritance

I am new to C++ programming language, i have a confusion about order of calling the constructor in inheritance. my question is even though the constructor and destructor are not inherited by derived class why the base class constructor will call when i create a derived class object.
The purpose of a constructor is to define how the data members should be initialised. Since a derived class inherits the data members from the base class, any constructor of the derived class must define not only how to initialise the data members that are specific to the derived class, but also those coming from the base class.
The natural way to do this (and required by the C++ Standard) is by calling a base class constructor. If you don't include a specific constructor call in the initialization list for your derived-class constructor, the default constructor of the base class will be used to initialise the base-class members.
struct base
{
int _i; // a data member
base():_i(0) {} // default constructor
base(int i):_i(i) {} // special constructor
};
struct derived : base
{
int _j; // a data member specific to the derived class
derived(int i, int j):_j(j) {} // user-defined constructor for the derived class
};
The example above illustrates how the derived-class constructor can initialise its member _j, but the member _i, coming from the base class, must be initialised using a base-class constructor.
The way it's written above, the default constructor base::base() will be automatically called by the compiler, i.e. _i will be initialised to 0.
But you can change that behaviour by including a call to a specific constructor:
struct derived : base
{
int _j;
derived(int i, int j):base(i),_j(j) {} // user-defined constructor for the derived class
};
The order of the constructor calls is: First constructor calls of the base class, then initializers for the derived-class specific members. This is only natural because the derived class, in a sense, is an extension of the base class, and it makes sense to regard the base-class part of the derived-class object as being there first.

The call sequence of the Constructors between Derived Class and base class

I've known that if B is derived from A, then when I create a new object of B, for example b, the constructor of A will call first. When I destroy the object b, The destructor of B will call first. Then I have a question here, if there're more than one constructor in the Base class, which constructor will call ? and why?
I've write one test program below, I guess it will call the default constructor in the Base class, But I'm not sure if it is just a coincidence?
#include <iostream>
using namespace std;
class A{
public:
A(int i){cout<<"A con with param"<<endl;}
A(){cout<<"A con"<<endl;}
~A(){}
};
class B : public A
{
public:
B(int i){cout<<"B con with param"<<endl;}
B(){cout<<"B con"<<endl;}
~B(){}
};
int main()
{
B b(5);
return 0;
}
I wonder if any boss can tell me the reason or any advise to figure out this problem?
If you write:
B(int i) { cout<<"B con with param"<<endl; }
then constructor A() (without arguments) will be called.
If you write:
B(int i): A(i) { cout<<"B con with param"<<endl; }
then constructor A(int) will be called.
Nothing is by coincidence in programming.
The default constructor is called because, you did not pass int argument explicitly to base class.
For other constructor of base class to be called, the derived class constructor needs to pass the parameters to base class constructor.
So for A(int) to be called, you need to have B():A(some_int) or B(int):A(some_int)
The empty constructor of the parent class will be called unless you call some other constructor explicitly in the initializer list.
EDIT: here is how you call constructor explicitly:
B(int i) : A(i) {
... do stuff ...
}
The order of initialization is a bit more convoluted than that. Technically it starts in the initialization list of the most derived object. That constructor is selected with regular overload resolution from the location where the object is being created. The initialization list of the most derived type constructor lists (either explicitly or implicitly) the constructors of the bases, and again the constructor of the bases will be selected using overload resolution on the call in the initialization list. If the base is not explicitly stated in the initialization list, the compiler will inject a call to the default constructor.
The exact order of construction is also a bit more complex in that the first subobjects that are initialized are the virtual bases, in a particular order (depth-first, left-to-right where left-to-right is the order of declaration in the base classes declaration). Once all virtual bases are initialized, then the direct non-virtual bases of the most derived type are initialized, again in the order of declaration (left-to-right). Once all bases are initialized, the members are then initialized in the order of declaration in the class.
Note that in all cases, the code can explicitly list the subobject in the initialization list, and the constructor listed there will be used, or the default constructor if the entity is not listed.

Order of calling base class constructor from derived class initialization list

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.
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.
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.
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.