Each derived class object consists of a (non-static) member defined in the derived class, plus one or more base class child objects, so that when the derived type object is constructed, copied, assigned, and revoked, the base class child objects are also constructed, copied, assigned, and revoked.
Constructors and replication control members cannot inherit, and each class defines its own constructors and replication control members. Like any class, a composite version is used if the class does not define its own default constructor and copy control members.
1: Constructors and inheritance
The constructors of a derived class are affected by an inheritance relationship, and each derived class constructor initializes the base class in addition to initializing its own data members.
The synthetic default constructor of a derived class: In addition to initializing the data member of a derived class, it initializes the base class portion of the derived class object. The base class part is initialized by the default constructor of the base class:
Class Father
{public
:
int publ_i;
Father (int a=1, int b=2, int c=3):p ubl_i (a), priv_i (b), prot_i (c)
{
cout << "Father constructor" << en DL;
}
virtual void display ()
{
cout << "[Father]publ_i is" << publ_i << Endl;
cout << "[Father]priv_i is" << priv_i << Endl;
cout << "[Father]prot_i is" << prot_i << Endl;
}
Private:
int priv_i;
Protected:
int prot_i;
};
Class Son:public father
{public
:
void display ()
{
cout << ' [Son]publ_i is ' << Publ_i << Endl;
cout << "[Son]prot_i is" << prot_i << Endl;
}
;
int main ()
{
son ss1;
Ss1.display ();
}
Execute "son ss1;" Statement, the default constructor of the derived class son is invoked, which first invokes the default constructor of the base class. The result of this code is:
Father Constructor
[son]publ_i is 1
[Son]prot_i is 3
If a derived class itself defines a constructor, the constructor implicitly calls the default constructor of the base class:
Class Son:public father
{public
:
son (int a = 2): Mypriv_i (a)
{
cout << "son constructor" < < Endl;
}
void display ()
{
cout << "[Son]publ_i is" << publ_i << Endl;
cout << "[Son]prot_i is" << prot_i << Endl;
cout << "[Son]mypriv_i is" << mypriv_i << Endl;
}
Private:
int mypriv_i;
};
int main ()
{
son ss1;
Ss1.display ();
}
Execute "son ss1;" statement, you invoke the default constructor of the derived class son, which first calls the default constructor of the base class to initialize the base class part, and then initializes the son::mypriv_i with the initialization list, and finally, the function body that executes the son constructor. The result of this code is:
Father Constructor
son constructor
[son]publ_i is 1
[son]prot_i are 3
[Son]mypriv_i is 2
The derived class constructor can also explicitly invoke the constructor of the base class in the initialization list to indirectly initialize the base class members. Note that in a list of derived classes, you cannot initialize a base class member directly:
Class Son:public father
{public
:
son (int a = 2): Mypriv_i (a), Father (MB)
{
cout << "son Constructor "<< Endl;
}
void display ()
{
cout << "[Son]publ_i is" << publ_i << Endl;
cout << "[Son]prot_i is" << prot_i << Endl;
cout << "[Son]mypriv_i is" << mypriv_i << Endl;
}
Private:
int mypriv_i;
};
int main ()
{
son ss1;
Ss1.display ();
}
The derived class constructors, regardless of the order in the initialization list, first call the base class constructor to initialize the base class members, and then initialize according to the declaration order of the derived class members.
If you attempt to initialize a base class member directly in the initialization list, a compilation error occurs:
Son (int a = 2): Mypriv_i (a), Publ_i (m)
{
cout << "son constructor" << Endl;
}
The error is as follows:
Test2.cpp:In constructor ' Son::son (int) ':
test2.cpp:29:29:error:class ' son ' does ' not have ' No field named ' Publ_i ' C1/>son (int a = 2): Mypriv_i (a), publ_i (MB)
^
A derived class constructor cannot initialize a member of a base class and should not assign a value to a base class member. If those members are public or protected, the derived constructor can assign values to the base class members in the constructor function body, but doing so violates the interface of the base class. Derived classes should respect the initialization intent of a base class by using a base class constructor, rather than assigning values to those members in a derived class constructor function body.
Note that a class can only initialize its own direct base class.
2: Copy Control and inheritance
A copy control function synthesized by a derived class that copies, assigns, or revokes the base class part of an object along with the members of the derived part, automatically calls the copy constructor, assignment operator, or destructor of the base class to copy, assign, or undo the base class part:
Class Father {Public:int publ_i; Father (int a=1, int b=2, int c=3):p ubl_i (a), priv_i (b), prot_i (c) {cout << "father Constructor" << ;
Endl } father (const father &SRC):p ubl_i (src.publ_i), Priv_i (src.priv_i), prot_i (src.prot_i) {cout <<
"Father copy constructor" << Endl;
} father& operator= (const father& src) {publ_i = src.publ_i;
Priv_i = src.priv_i;
Prot_i = src.prot_i;
cout << "Father operator =" << Endl;
return *this;
} ~father () {cout << "Father destructor" << Endl;
virtual void display () {cout << "[Father]publ_i is" << publ_i << Endl;
cout << "[Father]priv_i is" << priv_i << Endl;
cout << "[Father]prot_i is" << prot_i << Endl;
} Private:int priv_i;
Protected:int prot_i;
}; Class Son:Public Father {Public:son (int a = 2): Mypriv_i (a) {cout << "son constructor" << Endl;
} void Display () {cout << "[Son]publ_i is" << publ_i << Endl;
cout << "[Son]prot_i is" << prot_i << Endl;
cout << "[Son]mypriv_i is" << mypriv_i << Endl;
} Private:int mypriv_i;
};
int main () {son ss1 (3);
Son Ss2 (SS1);
Ss2.display ();
Son Ss3;
SS3 = SS1;
Ss3.display (); }
"Son Ss2 (SS1);" statement, a synthesized copy constructor of the derived class son is invoked, which will automatically call the copy constructor of the base class; "SS3 = SS1;" statement, the synthesized assignment operator function of the derived class son is invoked. The function automatically calls the assignment operator function of the base class; Finally, the three objects of the derived class son are refactored before the program exits, thus automatically calling the base class's destructor; the results of the preceding code are as follows:
Father Constructor
son constructor
father copy constructor
[son]publ_i is 1
[son]prot_i is 3
[son] Mypriv_i is 3
father Constructor
son constructor father operator
=
[son]publ_i are 1
[Son]prot_i is 3
[Son]mypriv_i is 3
father destructor
father destructor
father destructor
If the derived class itself defines a copy constructor or assignment operator, then the copy constructor or assignment operator of the calling base class should be displayed:
Class Father {Public:int publ_i; Father (int a=1, int b=2, int c=3):p ubl_i (a), priv_i (b), prot_i (c) {cout << "father Constructor" << ;
Endl } father (const father &SRC):p ubl_i (src.publ_i), Priv_i (src.priv_i), prot_i (src.prot_i) {cout <<
"Father copy constructor" << Endl;
} father& operator= (const father& src) {publ_i = src.publ_i;
Priv_i = src.priv_i;
Prot_i = src.prot_i;
cout << "Father operator =" << Endl;
return *this;
} ~father () {cout << "Father destructor" << Endl;
virtual void display () {cout << "[Father]publ_i is" << publ_i << Endl;
cout << "[Father]priv_i is" << priv_i << Endl;
cout << "[Father]prot_i is" << prot_i << Endl;
} Private:int priv_i;
Protected:int prot_i;
}; Class Son:Public Father {Public:son (int a = 2): Mypriv_i (a), Father (MB) {cout << "son constructor" <<
Endl } son (const son& src): mypriv_i (Src.mypriv_i), Father (src) {cout << "son copy constructor" <&l T
Endl
} son& operator= (const son& src) {father::operator= (SRC);
Mypriv_i = src.mypriv_i;
cout << "son operator =" << Endl;
return *this;
} void Display () {cout << "[Son]publ_i is" << publ_i << Endl;
cout << "[Son]prot_i is" << prot_i << Endl;
cout << "[Son]mypriv_i is" << mypriv_i << Endl;
} Private:int mypriv_i;
};
int main () {son ss1 (3);
Son Ss2 (SS1);
Ss2.display ();
Son Ss3;
SS3 = SS1;
Ss3.display (); }
In the copy constructor of the derived class son, the initialization list shows the calling base class copy constructor to initialize the base class section with SRC. Here, if the call to the base class copy constructor is omitted, the compiler automatically calls the default constructor of the base class to initialize the base class part, which causes the newly constructed object to have a strange configuration, with its base class partially holding the default value, while his derived class part is a copy of SRC;
In the assignment operator function of the derived class son, the assignment operator function that called the base class is displayed. If this call is omitted, the assignment operation simply makes the mypriv_i of the object the same as the mypriv_i of SRC;
The results of the preceding code are as follows:
Father Constructor
son constructor
father copy constructor
son copy constructor
[Son]publ_i is 100
[Son]prot_i is 3
[Son]mypriv_i is 3
father constructor son constructor father operator = son operator
=
[Son]publ_ I is
[son]prot_i are 3
[son]mypriv_i is 3
father destructor
father
destructor father destructor
If you define a destructor for a derived class, it is different from the copy constructor and assignment operator: The compiler automatically calls the base class's destructor, so in the destructor of the derived class, you are only responsible for clearing your own members:
~son ()
{
cout << "son destructor" << Endl;
}
After defining the son destructor, the result of the preceding code is:
...
Son destructor
father destructor
son destructor
father
destructor
son destructor father destructor
objects are reversed in reverse order: The derived destructor is run first, and then the base class destructor is called up, sequentially, in the inheritance hierarchy.
In addition, if the base class pointer points to a derived class object, when the base class pointer is deleted, the destructor of the base class is called only if it is not a virtual function, and the derived class destructor is not invoked. For example, the above father and son two classes, run the following code:
Father *fp = new Son;
Delete FP;
The result is:
Father Constructor
son constructor
father destructor
To avoid this, the destructor of the base class should be defined as a virtual function, so that derived class destructors are virtual functions, regardless of whether the derived class explicitly defines a destructor or uses a synthetic destructor. So that we can get the right results.
Therefore, even if the destructor has no work to do, the root class of the inheritance hierarchy should also define a virtual destructor.
3: Calling a virtual function in a constructor or destructor
When you run a constructor or destructor, the object is incomplete. To accommodate this incomplete, the compiler treats the type of the object as having changed during construction or destructor. In a base class constructor or destructor, treat a derived class object as a base class type object.
Therefore, if you call a virtual function in a constructor or destructor, you are running a version that is defined for the constructor or destructor's own type. If a virtual function is invoked in a base class constructor, the virtual function is actually the version of the base class that is invoked when the derived class is constructed:
class father {Public:int publ_i; Father (int a=1, int b=2, int c=3):p ubl_i (a), priv_i (b), prot_i (c) {cout << "father Constructor" << ;
Endl
Display ();
virtual void display () {cout << "[Father]publ_i is" << publ_i << Endl;
cout << "[Father]priv_i is" << priv_i << Endl;
cout << "[Father]prot_i is" << prot_i << Endl;
} Private:int priv_i;
Protected:int prot_i;
};
Class Son:public father {Public:son () {cout << "son constructor" << Endl;
} void Display () {cout << "[Son]publ_i is" << publ_i << Endl;
cout << "[Son]prot_i is" << prot_i << Endl;
}
}; int main () {father *fp = new Son;}
When constructing the Son object, you need to call the Father constructor and call the virtual function display in the Father constructor. At this point, although the derived class object is constructed, it still calls the display function of the base class. Therefore, the result of the preceding code is:
Father Constructor
[father]publ_i is 1
[father]priv_i are 2
[father]prot_i is 3
son constructor
This binding is applied whether a virtual function is called directly by a constructor (or a destructor), or if a virtual function is called indirectly from a function called by a constructor (or destructor).
To understand this behavior, consider what happens if you call a derived class version of a virtual function from a base class constructor (or destructor). A derived class version of a virtual function is likely to access a member of a derived class object, but the members of the derived part of the object are not initialized during the runtime of the base class constructor, and in fact, if such access is allowed, the program is likely to crash.