C++派生类的构造函数

1、单继承时,派生类构造函数总是先调用基类构造函数再执行其他代码

1.1 派生类调用基类的构造函数

  类的构造函数不能被继承,构造函数不能被继承是有道理的,因为即使继承了,它的名字和派生类的名字也不一样,不能成为派生类的构造函数,当然更不能成为普通的成员函数。
  在设计派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数完成,但是大部分基类都有 private 属性的成员变量,它们在派生类中无法访问,更不能使用派生类的构造函数来初始化。
  这种矛盾在C++继承中是普遍存在的,解决这个问题的思路是:在派生类的构造函数中调用基类的构造函数

#include<iostream>
using namespace std;

//基类People
class People{
protected:
    char *m_name;
    int m_age;
public:
    People(char*, int);
};
People::People(char *name, int age): m_name(name), m_age(age){}

//派生类Student
class Student: public People{
private:
    float m_score;
public:
    Student(char *name, int age, float score);
    void display();
};
//People(name, age)就是调用基类的构造函数
Student::Student(char *name, int age, float score): People(name, age), m_score(score){ }
void Student::display(){
    cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<"。"<<endl;
}

int main(){
    Student stu("小明", 16, 90.5);
    stu.display();

    return 0;
}

请注意第 23 行代码:

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

People(name, age)就是调用基类的构造函数,并将 name 和 age 作为实参传递给它,m_score(score)是派生类的参数初始化表,它们之间以逗号,隔开。

也可以将基类构造函数的调用放在参数初始化表后面:

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

但是不管它们的顺序如何,派生类构造函数总是先调用基类构造函数再执行其他代码(包括参数初始化表以及函数体中的代码)

这一点总结起来就是:基类构造函数总是被优先调用,这说明创建派生类对象时,会先调用基类构造函数,再调用派生类构造函数,如果继承关系有好几层的话,例如:
A –> B –> C

那么创建 C 类对象时构造函数的执行顺序为:
A类构造函数 –> B类构造函数 –> C类构造函数

构造函数的调用顺序是按照继承的层次自顶向下、从基类再到派生类的。

1.2 派生类的构造函数只能调用直接基类的构造函数

还有一点要注意,派生类构造函数中只能调用直接基类的构造函数,不能调用间接基类的。以上面的 A、B、C 类为例,C 是最终的派生类,B 就是 C 的直接基类,A 就是 C 的间接基类。

C++ 这样规定是有道理的,因为我们在 C 中调用了 B 的构造函数,B 又调用了 A 的构造函数,相当于 C 间接地(或者说隐式地)调用了 A 的构造函数,如果再在 C 中显式地调用 A 的构造函数,那么 A 的构造函数就被调用了两次,相应地,初始化工作也做了两次,这不仅是多余的,还会浪费CPU时间以及内存,毫无益处,所以 C++ 禁止在 C 中显式地调用 A 的构造函数。
参考:http://c.biancheng.net/cpp/biancheng/view/231.html

2、多继承下的构造函数

2.1 多继承的声明

多继承的语法也很简单,将多个基类用逗号隔开即可。例如已声明了类A、类B和类C,那么可以这样来声明派生类D:

class D: public A, private B, protected C{
    //类D新增加的成员
}

2.2 多继承的构造函数的调用顺序

D 是多继承形式的派生类,它以公有的方式继承 A 类,以私有的方式继承 B 类,以保护的方式继承 C 类。D 根据不同的继承方式获取 A、B、C 中的成员,确定它们在派生类中的访问权限。
多继承形式下的构造函数和单继承形式基本相同,只是要在派生类的构造函数中调用多个基类的构造函数。以上面的 A、B、C、D 类为例,D 类构造函数的写法为:

D(形参列表): A(实参列表), B(实参列表), C(实参列表){
    //其他操作
}

基类构造函数的调用顺序和和它们在派生类构造函数中出现的顺序无关,而是和声明派生类时基类出现的顺序相同。仍然以上面的 A、B、C、D 类为例,即使将 D 类构造函数写作下面的形式:

D(形参列表): B(实参列表), C(实参列表), A(实参列表){
    //其他操作
}

那么也是先调用 A 类的构造函数,再调用 B 类构造函数,最后调用 C 类构造函数。
参考:http://c.biancheng.net/cpp/biancheng/view/236.html

3、虚继承时的构造函数

3.1 将A声明为虚基类的方法

class A//声明基类A
{…};
class B :virtual public A//声明类B是类A的公用派生类,A是B的虚基类
{…};
class C :virtual public A//声明类C是类A的公用派生类,A是C的虚基类
{…};

3.2(派生虚基类的)派生类的构造函数

class A//定义基类A
{
   A(int i){ } //基类构造函数,有一个参数};
class B : virtual public A //A作为B的虚基类
{
   B(int n):A(n){ } //B类构造函数,在初始化表中对虚基类初始化
};
class C : virtual public A //A作为C的虚基类
{
   C(int n):A(n){ } //C类构造函数,在初始化表中对虚基类初始化
};
class D :public B,public C 
{
   D(int n):A(n),B(n),C(n){ } //类D的构造函数,在初始化表中对所有基类初始化
};

注意:
在定义类D的构造函数时,与以往使用的方法有所不同。规定:
在最后的派生类中不仅要负责对其直接基类进行初始化,还要负责对虚基类初始化。C++编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略虚基类的其他派生类(如类B和类C)
对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化。
参考:https://www.cnblogs.com/yiranlaobaitu/p/3764422.html

猜你喜欢

转载自blog.csdn.net/baidu_35679960/article/details/80713457