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

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.

Related

Inherited class data members

I am a bit confused, i was reading this C++ Constructor/Destructor inheritance, and so it says constructors and destructors are not inherited from the base class to the derived class but the constructors and destructors are called when i create a derived object. So is the constructors and destructors of the base class data members of the inherited classes?
Constructors and destructors are very special animals; in fact, the Standard identifies them as "Special Member Functions."
All the things that are weird and unique about constructors and destructors (for example, the fact that they don't have "names" and the fact that they aren't "inherited") is really quite irrelevant to almost all programming and programmers. Here is what you need to know:
Constructors and destructors are not member variables (obviously) -- they are (special) member functions.
When you derive from a base class, some constructor of the base class is going to be called. Which constructor depends on how you've written your code. If you don't explicitly specify which constructor to call, the default constructor will be called. This happens in your derived object's initialization list, even if you haven't written one.
You may specify another constructor through an initialization list, only:
class Bar : public Foo
{
public:
Bar()
:
Foo (1, 2, 3)
{
}
};
Here, Bar's initialization list specifies a constructor on Foo which takes 3 parameters convertible from integrals. One such constructor might be:
class Foo
{
public:
Foo (int a, long b, unsigned c)
:
mA (a),
mB (b),
mC (c)
{
}
private:
int mA;
long mB;
unsigned mC;
};
Back to the Bar example above. By the time the initialization list is done executing, and before the body of Bar's constructor starts, all of Bar's base classes and member variables have already been instantiated and initialized. This is why the only way to specify some constructor for Foo other than the default constructor is through an initialization list -- which, in turn, is why you must have an initialization list if the base class has no default constructor available.
Question: If constructors and destructors are not inherited, then why are they called when instantiating a derived type?
Answer: Because constructors are what initialize objects, and a derived type has an IS-A relationship with the base type.
Consider Foo and Bar above again. Bar derives from Foo, so in a sense Bar IS A Bar. It's more than just a Foo, it's got stuff that Foo doesn't have; but it has all the Foo-ness. Since a Bar object is in part a Foo, that Foo-ness has to be initialized. Since all initialization ob objects is done through a constructor, Foo's constructor must be called.
So, constructors and destructors aren't inherited in the sense of overloading -- but the constructors of the base class will be called. They have to be. Otherwise the Fooness of a Bar object could never come to be.
When you are deriving from a base class, something that you will often want to do is pass around a pointer to the base class. In fact, you may not need a pointer to the derived type at all in many cases. This is especially true when designing abstract interfaces.
There is a nasty problem that can come up when you do this and haven't taken all the needed preperations. Consider:
class Foo
{
public:
std::string mS;
};
class Bar : public Foo
{
public:
long mArray[0xFFFF]; // an array of 65K longs
};
int main()
{
Foo* thingy = new Bar; // Totally OK
// ... do stuff...
delete thingy; // WHOOPS! Memory leak!
}
Above in the delete call we're deleting through a base class pointer. The Foo destructor (which is implicit) is properly called, but the Bar destructor is not -- leaving that huge array of 65K longs leaked.
To fix this, we need to give Foo a virtual destructor:
class Foo
{
public:
std::string mS;
virtual ~Foo() {}
};
This doesn't do much, but it does one critical thing: it sets up polymorphism so that when we call the destructor (implicitly), the virtual override will get called.
This is a critical step when designing a class hierarchy. If you think there is any chance that people will pass around pointers to the base class, you should have a virtual destructor in the base class. In fact, I would suggest that in almost every case you should have a virtual destructor even if you don't think people will use it this way.
No. As you said, constructors and destructors are not inherited (by the way, the assignment operator isn't either). It would be logically flawed if they were inherited, right? Constructors (and destructors) are specific for that exact class; and since the derived class usually has something specific and new, the base constructor is not enough to initialize the inherited class objects.
So why is the base constructor called when creating an object of the child class?
Well, every derived object has a sub-object - which is the actual object of the parent class (I hope this sentence wasn't difficult to understand).
The compiler will:
1) locate the constructor of the derived class whose list of initializers best fits the arguments passed, but not execute it;
2) execute the constructor of the base class to create the sub-object;
3) execute the constructor of the derived class, to create the actual object.
I hope this is clear; you can find a much more detailed explanation in the answer above :)

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.

inheriting constructors of class virtually derived.

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.

Why does a purely virtual/abstract class require a constructor, in particular for protected const member variables?

I have a purely virtual class defined as such:
class BaseClass {
protected:
const int var;
public:
void somefun() = 0; // what I mean by a purely virtual class
// stuff...
};
If I don't add a constructor defined as such:
BaseClass(const int & VAR) : var(VAR) {};
that I would have to subsequently use in ever derived class, my derived class can't initialize the const variable var to whichever value it wants to. Now I actually understand what's going on here. Before constructing a derived class, a constructor of the base class is called, at which point const member variables must be initialized. My question is not a "how do I make my code work" kind of question, that's already been done. My question is about why the compiler thinks it's necessary. For a purely virtual class, shouldn't I be allowed to write something like:
class DerivedClass : BaseClass {
public:
DerivedClass() : var(SOME_VALUE) {};
}
If the compiler knows that a call to a BaseClass constructor will necessarily be followed by a call to some derived class constructror (since an object of abstract type can never be instantiated) shouldn't it give us a bit more leeway?
Is this all a consequence of how C++ chooses to get around the Diamond problem? Even if that was the case, shouldn't the compiler at least somehow allow for the possibility that const member variable of purely virtual functions will be defined in derived classes? Is that too complicated or does that mess with the C++ solution to the Diamond problem?
Thanks for the help everyone.
It's not "purely virtual" (whatever you mean by that) - it contains a data member.
Class members can only be initialised by the initialiser list of a constructor of that class, not of a derived class. That's how object initialisation is specified: all members that are initialised, are initialised before the constructor body begins.
Constant objects must be initialised, since they can't be assigned a value later.
Therefore, a class with a constant data member must initialise it in each constructor.
For a purely virtual class, shouldn't I be allowed to write something
like
No, but you can(and in this case should) write something like this:
class DerivedClass : BaseClass {
public:
DerivedClass() : BaseClass(SOME_VALUE) {};
};
The construction of an object occurs in a specific order. The base class must be fully constructed before the constructor of a derived class is run, so that the derived constructor is working with a fully formed and valid base object. If the initialization of base member variables were put off until the construction of the derived class, this invariant would be broken.

When I derive a class in C++, does it create an object of base class and store it as my member variable in derived class?

Say i create a derived class as below,
class CHIProjectData : public QObject
{
CHIProjectData(QMap<QString,QString> aProjectData,
CHIMetaData* apMetaData = 0,
QObject* parent = 0);
private:
QMap<QString,QString> m_strProjectData;
CHIAkmMetaData* m_pMetaData;
};
and i implement like,
CHIProjectData::CHIProjectData(QMap<QString,QString> aProjectData,
CHIMetaData* apMetaData,
QObject* aParent)
:m_strProjectData(aProjectData),
m_pMetaData(apMetaData),
QObject(aParent)
{
}
i know i initiate the member variables m_strProjectData, m_pMetaData in the constructor. but what does the last part "QObject(aParent)" do? does it create an object of base class and consider that as a member variable?
QObject(aParent) calls QObject's constructor with the aParent parameter. QObject is not a member variable in this case. It may seem like a subtle point, but its an important one because the way you access the properties and methods of a subobject requires different syntax than as for a member variable.
Here's an analogy to try to understand the difference between a subobject and a member variable.
In the movie "Batman: The Dark Night" there is a scene where Batman is pursuing the bad guy in his car. But the car becomes damaged and unusable, and he has to escape. At that point Batman pushes a button and part of the car detatches from the rest, becoming a motorcycle. This is kind of like a subobject. The car is a motorcycle.
Now consider the case of an RV towing a smaller vehicle, the likes of which are frequently seen on the highways of America. In this case, the RV has a vehicle. The vehicle is a member variable of the RV.
Essentially, that is what is happening under the hood. The base class parts of your object, like its data members, are called subobjects.
The notion of initializing a base as in QObject(aParent) is similar to initializing a member, but bases are always initialized first. Therefore, it would be clearer to list QObject before the members, so the list of initializers is in chronological order.
The order of initialization always follows the order the bases are named after class and the order the members are declared, no matter how the initializer sequence is written.
A class instance that is a base of a derived class is sometimes called a "base class subobject", so in some sense a base class is a distinct 'part' of your derived class.
In your constructor's initializer list, the QObject(aParent) is choosing how the base class is constructed. In this case a single parameter constructor is being used. If the base class were omitted from the initializer list of your derived class' constructor its default constructor would be used.
It's not strictly a member variable, although like a member variable it's a constituent part of your derived class along with any other base class subobjects and other members.
Not quite. It tells the base class's constructor what to do. Imagine this class:
class A
{
public:
A(int val)
: value(val)
{
}
protected:
int value;
};
To construct A, you have to pass an int. This is always true, even if you derive from it.
Say you are class B, which derives from A. You are an A, but you still have to tell the A part of your class how to construct itself:
class B : public A
{
public:
B()
: A(5)
{
}
int GetValue()
{
return value;
}
};
The members of A become your members, though, because you are an A.
In machine memory, the scenario is kind of how you described, but only in simple cases. It becomes more complicated with virtual functions, multiple inheritance, and virtual inheritance. If you stick to the is-a and has-a relationships, then you may avoid some headaches :)