When creating a derived class, there are new steps that must be taken then with just a normal class.
First, before the derived class's constructor is called, the parent's class's constructor is executed (along with the initializer list). Then, the derived class's constructor is called, the initializer list is executed, and the body of the constructor executes.
However, lets say that you want to initialize the members of the parent class when creating an instance of the derived class. To do this, I was informed that you call the constructor in the derived class's initialization list. This is what I do not understand. If in the process already, the parent class's constructor is called, then why do you need to call it in the child class's initialization list?
First of all members of the parent class are initialized by parent class constructor. However if you do not specify in the initialized list a base class constructor with arguments then the default base class constructor will be called. So you can not call a base class constructor with parameters except throug the initialized list of the derieved class constructor.
For example
1 2 3 4 5 6 7 8 9 10 11 12
struct A
{
A() {}
A( int i ) : a( i ) {}
int a;
};
struct B: A
{
B() {} // A::A() will be called
B( int i ) :A( i ) {} // A( i ) will be called
};
Hallo,
You need to explicitly call the parent class's constructor in the derived class's initialization list in order to pass arguments to it, (from then, possibly, the base constructor initializes base members).
Only without these explicit call the compiler will generate a call to a default parent class's constructor (that is, without arguments) (and you get and compiler error in case the parent class have no default constructor)
?
Is the "parent class constructor" or the "default base class constructor" different then just the regular constructor?
@qPCR4vir,
Hmm.. okay. But in the creation of the child class without explicitly calling the parent class constructor, wouldn't you need to call the parent class constructor implicitly to create that class's variables?
You only need to explicitly invoke the base constructor if you need to pass arguments to it, or if you invoke the copy-constructor of the derived class. For example:
This will not invoke the copy-constructor of the base-class:
So if I do not explicitly invoke the base constructor (which means no arguments), the base constructor will only initialize the variables, but will not be accessible in the child constructor/initializer list?
P.S. The copy constructor is the constructor being called in this process?
During the copy-construction of Child, if you do not explicitly invoke the copy-constructor of the base-class, the compiler assumes the default constructor, which default-initialises the base-class.
Flurite wrote:
"the base constructor will only initialize the variables, but will not be accessible in the child constructor/initializer list?"
The base-class is constructed first. By the time the derived class begins construction, all base-classes would've been fully constructed and initialised. This makes it safe to access the base-class members within both the initialiser list of the derived class and the constructor body of the derived class. For instance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
struct Base
{
Base() : BaseMember(0) { }
Base(Base const &B) : BaseMember(B.BaseMember) { }
int BaseMember;
};
struct Child : public Base
{
Child() : ChildMember(this->BaseMember) { }
Child(Child const &C) : Base(C), ChildMember(this->BaseMember) { }
int ChildMember;
};
Note the use of the base-class' data members in the initialiser list of the derived class' constructor initialiser list.
class X1
{
X1(int arg_value1) : value1(arg_value1) {}
int value1;
};
class X2 : public X1
{
X2(int arg_value1, int arg_value2) : value1(arg_value1), value2(arg_value2) {}
int value2;
};
Notice how in X2, I try to initialize value1, but the compiler will complain that it doesn't exist.
Here, you're not initialising the data members of the base-class. Instead, you're attempting to re-initialise an already initialised data member. By the time the derived class beings construction, all data members of the base-class would've been fully initialised. A constructor's initialisation list is supposed to initialise its own members and shouldn't concern itself with the data members of its base-class.
Yes, correct. However, the initialization list is to initialize variables with a value. In a child class, you cannot directly initialize members inherited from the parent class. You have to call the constructor in the initializer list to do so. My question is that whether C++ checks the initializer list FIRST, and if there is a call to a parent constructor, it will initialize the parent class variables with those values passed as arguments.
The constructor of the base-class is invoked first, so there's no need to invoke the constructor again. However, if you manually invoke the base-class' default constructor, the compiler will not invoke it again. Like I said before, the only time you need to invoke a base-class constructor is when:
- You need to pass arguments the the base-class constructor
- Or you need to invoke the base-class copy-constructor
So does C++ check the child class's initialization list first to see if there is a manual call to the base class constructor before actually constructing the base class? Also, what is the "default base constructor"? From my research, the constructor is simply the regular constructor that is called.
The compiler will check the initialiser list of the derived class' constructor. If a call to the base-class is made, it'll use the constructor you specified to build the base-class. If no call is made, the compiler will choose the default base-class constructor. If the compiler automatically chose the default constructor, the derived-class would not be able to pass arguments to the base-class.
Flurite wrote:
"Also, what is the "default base constructor"?"
It's the default constructor (the constructor that takes either no arguments are has all default arguments) of the base-class.
First part is making a lot more sense now, thanks.
As for the default constructor, it seems that it is just the regular constructor. Here is some sample code, and you can read the output, which signifies it is calling the constructor, which has an argument, but no default value for that argument.
Since you've declared a constructor, the compiler will not generate a default one, but it will generate a copy-constructor and an assignment operator. If you specify default arguments for all parameters in a constructor, the constructor will act as a default constructor. So:
1 2 3 4 5
class X1
{
X1(int arg_value1 = 0) : value1(arg_value1) {}
int value1;
};
...will do the trick; that is, X1::X1() is now a default constructor. Why? Since you've specified default arguments, the compiler doesn't need the calling function to pass arguments to the constructor in order for it to construct the object.
The constructor of the base-class needs to be public, too. Didn't I say this once? Since you've overloaded the default constructor, the compiler will not generate one.