在设计派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数完成,但是大部分基类都有 private 属性的成员变量,它们在派生类中无法访问,更不能使用派生类的构造函数来初始化。

解决办法:在派生类的构造函数中调用基类的构造函数。

在派生类的构造函数中调用基类的构造函数:

#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;
}

运行结果为:

小明的年龄是16,成绩是90.5。

请注意第 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){ }

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

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

当然这段代码只是为了方便大家理解,实际上这样写是错误的,因为基类构造函数不会被继承,不能当做普通的成员函数来调用。换句话说,只能将基类构造函数的调用放在函数头部,不能放在函数体中

另外,函数头部是对基类构造函数的调用,而不是声明,所以括号里的参数是实参,它们不但可以是派生类构造函数参数列表中的参数,还可以是局部变量、常量等,例如:

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

构造函数的调用顺序

从上面的分析中可以看出,基类构造函数总是被优先调用,这说明创建派生类对象时,会先调用基类构造函数,再调用派生类构造函数,如果继承关系有好几层的话,例如:

A --> B --> C

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

A类构造函数 --> B类构造函数 --> C类构造函数

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

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

基类构造函数调用规则

通过派生类创建对象时必须要调用基类的构造函数,这是语法规定。换句话说,定义派生类构造函数时最好指明基类构造函数;如果不指明,就调用基类的默认构造函数(不带参数的构造函数);如果没有默认构造函数,那么编译失败。请看下面的例子:

#include <iostream>
using namespace std;//基类People
class People{public:People();  //基类默认构造函数People(char *name, int age);
protected:char *m_name;int m_age;
};
People::People(): m_name("xxx"), m_age(0){ }
People::People(char *name, int age): m_name(name), m_age(age){}//派生类Student
class Student: public People{public:Student();Student(char*, int, float);
public:void display();
private:float m_score;
};
Student::Student(): m_score(0.0){ }  //派生类默认构造函数
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 stu1;stu1.display();Student stu2("小明", 16, 90.5);stu2.display();return 0;
}

运行结果:

xxx的年龄是0,成绩是0。
小明的年龄是16,成绩是90.5。

创建对象 stu1 时,执行派生类的构造函数Student::Student(),它并没有指明要调用基类的哪一个构造函数,从运行结果可以很明显地看出来,系统默认调用了不带参数的构造函数,也就是People::People()。

创建对象 stu2 时,执行派生类的构造函数Student::Student(char *name, int age, float score),它指明了基类的构造函数。

在第 27 行代码中,如果将People(name, age)去掉,也会调用默认构造函数,第 37 行的输出结果将变为:

xxx的年龄是0,成绩是90.5。

如果将基类 People 中不带参数的构造函数删除,那么会发生编译错误,因为创建对象 stu1 时需要调用 People 类的默认构造函数, 而 People 类中已经显式定义了构造函数,编译器不会再生成默认的构造函数。

C++ 基类和派生类的构造函数相关推荐

  1. C++中基类与派生类的构造函数和析构函数

    1.Cpp中的基类与派生类的构造函数 基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承.构造函数不能被继承是有道理的,因为即使继承了,它的名字和 ...

  2. 构造函数怎么在主函数调用_C++ 虚基类及其派生类构造函数(学习笔记:第7章 12)...

    虚基类及其派生类构造函数[1] 建立对象时所指定的类称为最远派生类. 虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的. 在整个继承结构中,直接或间接继承虚基类的所有派生类,都 ...

  3. 派生类到基类的转换 和基类到派生类的转换

    一. 基类与派生类的转换     3种继承方式(公用.保护.私有继承)中,公用派生类才是基类真正的子类型,它完整地继承了基类的功能.     不同类型数据之间在一定条件下可以进行类型的转换.基类与派生 ...

  4. C++:基类和派生类

    4.1 派生类的声明 继承实例如下: class Person{ //声明基类Person public:void print(){cout<<"name:"<& ...

  5. 基类与派生类之间的转换关系

    一. 派生类到基类的转换: 1.可以使用派生类指针初始化基类指针,同样也可以使用派生类的引用或对象初始化基类的引用,注意这里提到的是对象的指针和引用而不是对象本身: (但是,在使用基类指针(或引用)指 ...

  6. c++中基类与派生类中隐含的this指针的分析

    先不要看结果,看一下你是否真正了解了this指针? 1 #include<iostream> 2 using namespace std; 3 4 class Parent{ 5 publ ...

  7. C++学习 十五、类继承(1)基类,派生类,访问权限,protected

    C++学习 十五.类继承(1)基类,派生类 前言 类继承 类的关系与继承 基类, 派生类 基类 派生类 构造函数,析构函数 文件位置 访问权限 protected 后记 前言 本篇开始学习C++类的继 ...

  8. python根据一个基类生成派生类_python中的封装继承多态

    1.封装 类的封装可以隐藏类的实现细节,迫使用户只能通过方法去访问数据,这样就可以增强程序的安全性.接下来演示未使用封装可能出现的问题,如例所示. 在例中,运行结果输出的成绩为-68,在程序中不会有任 ...

  9. 14.11 基类与派生类关系的详细再探讨

    一:派生类对象模型简述 Men mymen:子类(派生类对象),包含多个组成部分(也就是多个子对象); <1>一个是含有派生类自己定义的成员变量,成员函数的子对象: <2>一个 ...

  10. C++ 抽象基类与派生类

    抽象基类与派生类:构造方法的注意事项 1.抽象基类的构造方法不要用纯虚函数 2.子类的属性名如果和抽象基类的属性名相同的情况下 (1).使用子类对象调用此属性时会优先调用子类的属性 (2).在构造方法 ...

最新文章

  1. centos7 yum 安装 openssl 1.1.1k
  2. 当人工智能掌管城市,会带来怎样的巨变?
  3. python爬虫——利用BeautifulSoup4爬取糗事百科的段子
  4. 进程间通信(5) 命名管道
  5. IO - 同步,异步,阻塞,非阻塞
  6. 与Maven和Docker的集成测试
  7. 2场直播,本周二四丨Oracle drop table MySQL HeatWave
  8. CI框架下nginx重写规则
  9. 各种对话框 Dialog
  10. Xtrabackup远程备份+限速
  11. 计算机关键性检测的部件是,试论计算机硬件关键技术的若干问题分析
  12. 20155307《网络对抗》信息搜集与漏洞扫描
  13. postgre sql安装时忘记之前密码时如何处理。
  14. 2021年Java大厂面试必备面试题
  15. 嵌入式基础面八股文——进程与线程的基本概念(1)
  16. Python基础--03
  17. c语言编程 进制转换,c语言中的进制转换
  18. 前端跨域解决方案总结
  19. SCM供应链管理系统对更多行业未来
  20. Linux 通配符 与 正则表达式 的区别与详解

热门文章

  1. mobi格式电子书_进阶能力 | 了解常见的电子书格式
  2. 如何在Win11上快速加密硬盘 Win11上快速加密硬盘方法步骤
  3. 小鱼一键重装系统win7教程
  4. 如何实现RTSP/RTMP流接入到RTSP网关
  5. 前端给后端传递数据的时候,有些后端自己可以获取到的值应该由前端传递吗?
  6. 关联规则算法c语言样例及分析_推荐系统总结系列-关联规则算法(四)
  7. python使用xlrd读取xlsx文件_$ 用python处理Excel文档(1)——用xlrd模块读取xls/xlsx文档...
  8. elasticsearch scroll 一页最大数据量_elasticsearch 百亿级数据检索案例与原理
  9. hbase可视化工具_做数据可视化,三大热门BI工具试用总结
  10. java kafka 分区_Java kafka如何实现自定义分区类和拦截器