In depth parsing of the constructors of derived classes in C++

  • 2020-05-05 11:35:35
  • OfStack

Base class constructors cannot be inherited, and when a derived class is declared, the constructor of the derived class initializes the inherited member variables. So when designing a constructor for a derived class, you should consider not only the new member variables of the derived class, but also the member variables of the base class, so that they are all initialized.

The idea is to call the constructor of the base class when the constructor of the derived class is executed.

The following example shows how to call the constructor of a base class in the constructor of a derived class.


#include<iostream>
using namespace std;
// The base class 
class People{
protected:
  char *name;
  int age;
public:
  People(char*, int);
};
People::People(char *name, int age): name(name), age(age){}
// Derived classes 
class Student: public People{
private:
  float score;
public:
  Student(char*, int, float);
  void display();
};
// The constructor of the base class is called 
Student::Student(char *name, int age, float score): People(name, age){
  this->score = score;
}
void Student::display(){
  cout<<name<<" The age is "<<age<<" , the result is "<<score<<endl;
}
int main(){
  Student stu(" Xiao Ming ", 16, 90.5);
  stu.display();
  return 0;
}

The result is:
Xiao Ming's age is 16 and his score is 90.5

Note line 23:


Student::Student(char *name, int age, float score): People(name, age)


This is how the constructor for the derived class Student is written. The colon is preceded by the header of the derived class constructor, which takes the same form as the constructor we introduced before, except that its list of parameters includes the data needed to initialize the member variables of the base class and the derived class. The colon is followed by a call to the base class constructor, which is very similar to the argument initialization table for a normal constructor.

In fact, you can put the call to the base class constructor and the argument initialization table together, as shown below:


Student::Student(char *name, int age, float score): People(name, age), score(score){}


The base class constructor is separated from the initialization table by a comma.

It is important to note that the colon is followed by a call to the base class constructor, not a declaration, so the arguments in parentheses are arguments that can be not only arguments in the derived class constructor's total reference list, but also local variables, constants, and so on. As shown below:


Student::Student(char *name, int age, float score): People(" Li lei ", 20)


The base class constructor calls the rule

In fact, the constructor of the base class must be called when an object is created from a derived class. That is, it is best to specify the base class constructor when defining the derived class constructor. If not specified, the default constructor of the base class is called (the constructor with no arguments). If there is no default constructor, the compilation fails.

See the following example:


#include<iostream>
using namespace std;
// The base class 
class People{
protected:
  char *name;
  int age;
public:
  People();
  People(char*, int);
};
People::People(){
  this->name = "xxx";
  this->age = 0;
}
People::People(char *name, int age): name(name), age(age){}
// Derived classes 
class Student: public People{
private:
  float score;
public:
  Student();
  Student(char*, int, float);
  void display();
};
Student::Student(){
  this->score = 0.0;
}
Student::Student(char *name, int age, float score): People(name, age){
  this->score = score;
}
void Student::display(){
  cout<<name<<" The age is "<<age<<" , the result is "<<score<<endl;
}
int main(){
  Student stu1;
  stu1.display();
  Student stu2(" Xiao Ming ", 16, 90.5);
  stu2.display();
  return 0;
}

Results:


xxx The age is 0 , the result is 0
 Xiao Ming's age is 16 , the result is 90.5


 

When the object stu1 is created, the constructor Student::Student() of the derived class is executed. It does not specify which constructor of the base class to call.

When the object stu2 is created, the constructor Student::Student(char *name, int age, float score) of the derived class is executed, which specifies the constructor of the base class.

In line 31, if you remove People(name, age), the default constructor is also called, and the output of stu2.display () will be:
xxx has an age of 0 and a score of 90.5

If you remove the no-argument constructor from the base class People, a compilation error occurs because the base class constructor was not called when the object stu1 was created.

Summary: if the base class has a default constructor, the derived class constructor can not be specified, the system will be called by default; If not, you must specify that otherwise the system does not know how to call the constructor of the base class.
Constructor call order

To figure this out, let's start with an example:


#include<iostream>
using namespace std;
// The base class 
class People{
protected:
  char *name;
  int age;
public:
  People();
  People(char*, int);
};
People::People(): name("xxx"), age(0){
  cout<<"PeoPle::People()"<<endl;
}
People::People(char *name, int age): name(name), age(age){
  cout<<"PeoPle::People(char *, int)"<<endl;
}
// Derived classes 
class Student: public People{
private:
  float score;
public:
  Student();
  Student(char*, int, float);
};
Student::Student(): score(0.0){
  cout<<"Student::Student()"<<endl;
}
Student::Student(char *name, int age, float score): People(name, age), score(score){
  cout<<"Student::Student(char*, int, float)"<<endl;
}
int main(){
  Student stu1;
  cout<<"--------------------"<<endl;
  Student stu2(" Xiao Ming ", 16, 90.5);
  return 0;
}

Result:


PeoPle::People()
Student::Student()
--------------------
PeoPle::People(char *, int)
Student::Student(char*, int, float)

From the results of the run, it is clear that when a derived class object is created, the base class constructor is called before the derived class constructor is called. If the inheritance relationship has several layers, for example,
A -- > B -- > C
When an C class object is created, the constructor's execution order is:
A class constructor --> B class constructor --> C class constructor
The constructor is called top-down, from the base class to the derived class, at the level of inheritance.

C++ constructor
for derived classes with child objects The data members of a class can not only be standard (int, char) or system-supplied (string), but can also contain class objects, such as
, when a class is declared


  Student s1; //Student Is the declared class name, s1 is Student The object of the class 


At this point, s1 is an embedded object in a class object called a child object (subobject), which is an object within an object.


So how do you initialize a child object when you initialize a data member? Take a close look at the following program, paying particular attention to how the derived class constructor is written.

Contains the constructor for the derived class of the child object. To simplify the program and make it easier to read, let's say that the base class Student has only two data members, num and name.


#include <iostream>
#include <string>
using namespace std;
class Student// Statement of the base class 
{
public: // Common parts 
  Student(int n, string nam ) // Base class constructor, with example 11.5 The same 
  {
   num=n;
   name=nam;
  }
  void display( ) // Member functions that output data members of the base class 
  {
   cout<<"num:"<<num<<endl<<"name:"<<name<<endl;
  }
protected: // Protect part 
  int num;
  string name;
};
class Student1: public Student // Declare a common derived class Student1
{
public:
  Student1(int n, string nam,int n1, string nam1,int a, string ad):Student(n,nam),monitor(n1,nam1) // Derived class constructor 
  {
   age=a;
   addr=ad;
  }
  void show( )
  {
   cout<<"This student is:"<<endl;
   display(); // The output num and name
   cout<<"age: "<<age<<endl; // The output age
   cout<<"address: "<<addr<<endl<<endl; // The output addr
  }
  void show_monitor( ) // A member function that outputs a child object 
  {
   cout<<endl<<"Class monitor is:"<<endl;
   monitor.display( ); // Calls the base class member function 
  }
private: // Private data of a derived class 
  Student monitor; // Define child objects ( Monitor of the class )
  int age;
  string addr;
};
int main( )
{
  Student1 stud1(10010,"Wang-li",10001,"Li-sun",19,"115 Beijing Road,Shanghai");
  stud1.show( ); // Output student data 
  stud1.show_monitor(); // Output data for child objects 
  return 0;
}

The run-time output is as follows:


Student::Student(char *name, int age, float score): People(name, age)
0

Notice that there is one data member in the derived class Student1:


Student::Student(char *name, int age, float score): People(name, age)
1

The monitor's type is not a simple type (int, char, float, etc.), it is an object of the Student class. We know that the data members of an object should be initialized when it is created. So how do you initialize a child object? Obviously you cannot initialize a derived class when it is declared (e.g. Student monitor(10001, "Li-fun ");) Because the class is an abstract type, it is just a model, it cannot have specific data, and the child object of each derived class object is generally not the same (for example, the monitor of students A, B, C is A, and the monitor of students D, E, F is F). So the initialization of the child object is done by calling the derived class constructor when the derived class is created.

The task of the derived class constructor should include three parts :

  initializes data members of the base class; Initialize the data member of the child object; Initializes the derived class data member.

The header of the derived class constructor in the program looks like this:


Student::Student(char *name, int age, float score): People(name, age)
2


There are six parameters in the constructor above, the first two as parameters to the base class constructor, the third and fourth as parameters to the child object constructor, and the fifth and sixth as parameters to the derived class data member.

In summary, the general form of defining a derived class constructor is: derived class constructor name (list of total parameters): base class constructor name (list of parameters), child object name (list of parameters)
{
New number of member data initializer statement
in derived class }

The order in which the derived class constructor is executed is
Call the base class constructor to initialize the base class data member;
Call the subobject constructor and initialize the data member of the subobject.
The derived class constructor itself is then executed to initialize the derived class data member.

The parameters in the column of the total parameter table of the derived class constructor should include the parameters in the column of the parameter table of the base class constructor and the child object. The order of the base class constructor and child objects can be arbitrary, and the derived class constructor header above can be written as


  Student1(int n, string nam,int n1, string nam1,int a, string ad): monitor(n1,nam1),Student(n,nam)


The compilation system establishes their pass-through relationship based on the same parameter names, not on their order. But it's customary to write the base class constructor first.

If there are more than one child, the derived class constructor is written in such a way that the name of each child and its argument table column are listed.


Related articles: