当前课程知识点:C++语言程序设计进阶 > 第七章 继承与派生 > 派生类的构造和析构 > 派生类的构造函数
默认情况
基类的构造函数不被继承;
派生类需要定义自己的构造函数。
C++11规定
using B::B;
派生类新增成员可以通过类内初始值进行初始化。
可用using语句继承基类构造函数。
但是只能初始化从基类继承的成员。
语法形式:
如果派生类有自己新增的成员,且需要通过构造函数初始化,则派生类要自定义构造函数。
派生类新增成员:派生类定义构造函数初始化;
继承来的成员:自动调用基类构造函数进行初始化;
派生类的构造函数需要给基类的构造函数传递参数。
派生类只有一个直接基类的情况,是单继承。单继承时,派生类的构造函数只需要给一个直接基类构造函数传递参数。
派生类名::派生类名(基类所需的形参,本类成员所需的形参): 基类名(参数表), 本类成员初始化列表 { //其他初始化; };
#include<iostream> using namespace std; class B { public: B(); B(int i); ~B(); void print() const; private: int b; }; B::B() { b=0; cout << "B's default constructor called." << endl; } B::B(int i) { b=i; cout << "B's constructor called." << endl; } B::~B() { cout << "B's destructor called." << endl; } void B::print() const { cout << b << endl; } class C: public B { public: C(); C(int i, int j); ~C(); void print() const; private: int c; }; C::C() { c = 0; cout << "C's default constructor called." << endl; } C::C(int i,int j): B(i), c(j){ cout << "C's constructor called." << endl; } C::~C() { cout << "C's destructor called." << endl; } void C::print() const { B::print(); cout << c << endl; } int main() { C obj(5, 6); obj.print(); return 0; }
多继承时,有多个直接基类,如果不继承基类的构造函数,派生类构造函数需要给所有基类构造函数传递参数。我们来看一下语法规定
派生类名::派生类名(参数表) : 基类名1(基类1初始化参数表), 基类名2(基类2初始化参数表), ... 基类名n(基类n初始化参数表), 本类成员初始化列表 { //其他初始化; };
当基类有默认构造函数时
派生类构造函数可以不向基类构造函数传递参数。
构造派生类的对象时,基类的默认构造函数将被调用。
如需执行基类中带参数的构造函数
派生类构造函数应为基类构造函数提供参数。
派生类名::派生类名(形参表): 基类名1(参数), 基类名2(参数), ..., 基类名n(参数), 本类成员(含对象成员)初始化列表 { //其他初始化 };
调用基类构造函数。
顺序按照它们被继承时声明的顺序(从左向右)。
对初始化列表中的成员进行初始化。
顺序按照它们在类中定义的顺序。
对象成员初始化时自动调用其所属类的构造函数。由初始化列表提供参数。
执行派生类的构造函数体中的内容。
大家好
欢迎回来继续学习
C++语言程序设计
这一节呢
我们来学习
怎么写派生类的构造函数
大家知道
每个类的构造函数
是负责自己
这个类的对象的初始化的
那么派生类
把基类的所有数据成员
都继承过来了
构造派生类对象的时候
这些继承过来的成员
可怎么初始化呀
它应该不归
派生类的构造函数管吧
好象也管不了吧
那么这个问题怎么解决呢
实际上呢
在默认的情况下
确实这个基类的构造函数
它是不被继承的
派生类呢
需要编写自己的构造函数
而且
派生类自己的构造函数
还需要负责去
向基类的初始化功能传递参数
就是把基类
那些继承过来的成员
初始化需要的参数
传给基类的构造函数
由基类的构造函数
去完成那些初始化工作
由派生类构造函数的函数体
和它的初始化列表
来完成本类新增成员的构造
但是呢
C++11呢
在语法上有了
很好用的一个新规定
就是可以用using语句
继承基类的构造函数
也就是说
我们连基类的构造函数
都可以继承过来
但是
这个并不是总适用的
它一般来讲
只适用于派生类
很少增加新的数据成员
或者不增加新的数据成员的情况
因为呢
这样继承过来的构造函数
它只能初始化
从基类继承的成员
派生类新增的成员
就完全不能够通过构造函数
初始化了
那如果说
这个时候派生类新增成员
它定义了内类的初始值
那就可以用内类的初始值
去初始化它
如果没有定义内类的初始值
那就只能按默认的方式
初始化了
对于基本类型的成员来说
默认的方式初始化
就是不进行初始化
里面会是垃圾数据的
那么使用这个using关键字
它的语法形式在这儿列出来
using后面呢
我们列出基类名
还有作用域分辨符
还有它的基类构造函数名
这样写就可以了
虽然我们用using
可以继承基类的构造函数
貌似比较方便
但是我们也看到了
对于派生类新增的成员
它是无能为力的
所以建议呢
如果你的派生类
需要对自己新增的成员
进行可定制的初始化
也就是说
可以用构造函数的参数
去指定的这种初始化
那么最好还是不用
继承基类的构造函数
自己来写构造函数
但是这种情况下
那些从基类继承过来的成员
到底怎么去初始化呢
我们来看看
派生类的构造函数
有哪些必须要完成的任务
如果我们不继承基类的构造函数
那么对于派生类
新增的成员来说
当然我们用派生类
定义的构造函数进行初始化
毫无问题
那么从基类继承过来的成员呢
大家也不用担心
这个时候呢
C++的机制会自动调用
基类的构造函数进行初始化
但问题来了
调用基类的构造函数进行初始化
这件事是自动的
可是基类构造函数
需要的参数从哪儿来呢
当然没有别的地方可以来
只能由派生类的构造函数
去传递给基类的构造函数
所以派生类的构造函数
还有一个重要任务
就是需要给基类的构造函数
传递参数
如果派生类只有一个
直接基类的情况
那就是单继承的情况
在单继承的情况下呢
派生类的构造函数
只要给一个直接基类传递参数
就可以了
那么这个就是单继承情况下的
派生类构造函数的定义的语法
我们看呢
这个时候
这个派生类
它的构造函数的参数表里头
可能就需要多写点参数了
除了本类成员初始化
所需要的形参以外
那么要初始化那些从基类
继承过来的成员
要调用基类的构造函数
那么也需要参数啊
这一些参数从哪儿获得呢
一块都得列在
派生类构造函数的参数列表里面
然后在这个
派生类的构造函数的函数头之后
大家看有一个冒号
冒号之后呢
应该是初始化列表的位置 对吧
那么在初始化列表的最开头位置
就要首先列出基类名
和基类构造函数
所需要的参数表
然后再列出
本类成员的初始化列表
那类体中呢
当然是其他与初始化
相关的语句
下面这个例题呢
演示了单继承情况下
派生类的构造函数
以及构造函数的调用过程
在这里呢
我们定义了类b作为基类
这个类自己定义了默认构造函数
和有一个参数的构造函数
另外呢
还定义了析构函数
以及一个print函数
这个print函数
由于它是不改变对象状态的
所以我们把它定义成const函数
然后再看这个C类
以公有方式继承了B类
它自己也定义了
无参数的默认构造函数
以及有两个参数的构造函数
析构函数
和它自己的print函数
是const类型的
好
那我们在主函数中呢
构造一个C类的对象
看看这个构造过程中
构造函数
到底是按照什么顺序调用的
现在我们用单步执行跟踪进来
按f11
进到C类的构造函数里面
在这儿
由于它继承了B类
所以呢
它需要给B类的构造函数
传递参数
那么这里初始化列表列出来了
那么由于这个初始化列表里面
有了参数
所以B类的
要一个参数的构造函数
就应该被调用
应该进到
B类的这个构造函数中去
我们单步执行一次看看
是不是这样
按f11
果然进到了
B类的这个构造函数里面来
执行完了这个构造函数以后
给B类
继承过来的对象B初始化以后
回到C类构造函数这儿
这个时候呢
就该对C类自己定义的成员
进行初始化了
我们看目前C类自己的成员这儿
还是垃圾数据
还没有被初始化
我们执行一步以后看
这个已经初始化了
回到主函数
下面呢
我们来看一下
在多继承的情况下
派生类的构造函数该怎么写
在多继承的情况下呢
派生类构造函数
它的初始化列表中呢
就需要列出所有的基类
以及基类构造函数
它需要的初始化参数
现在我们来考虑一下呢
如果派生类
没有写构造函数会怎么样
如果一个没有继承关系的类
不写构造函数
那么我们知道没有关系
会有隐含生成的默认构造函数
只要你构造这个
类的对象的时候
你不需要初始化
那就没有问题
那么当有继承的情况下
情况就要复杂一些
当基类有默认构造函数的时候
派生类的构造函数
就可以不向基类的构造函数
传递参数
那么你不传递参数
构造对象的时候
就会自动调用基类那个
无参的默认构造函数
构造派生类对象的时候呢
如果需要执行
基类中带参数的构造函数
那么这个时候
派生类的构造函数
就要为基类的构造函数
去传递参数
在第四章呢
我们学了类的组合
现在我们又学了类的继承
如果我们写一个类
既有组合 又有继承的时候
那么这个类的构造函数
该怎么写
现在我们来看一下
那有多继承
又有对象成员的时候
也就是又有继承
又有组合的时候
那么派生类构造函数
该怎么写呢
也很简单
把我们前面学过的东西
全部加到一起就行了
那看看这个语法
就是所有的基类
以及基类的构造函数
需要的参数都列在这儿
所有的本类的对象成员
以及它们初始化需要的参数
也都列在这儿
本类的成员初始化列表里面
当然它就包含了对象成员
也包含了基本类型的数据成员
所以就是那句话
把我们前面学过的所有东西
都加在一起就行了
我们知道了构造函数怎么写
那么这些构造函数的调用次序
又是什么样的呢
特别是既有继承
而且是多继承
还要有组合的情况下
到底哪些构造函数先调用
哪个构造函数后调用
它按什么样的次序来调用呢
我们来看一下这个次序问题
那实际上呢
这个次序大家想象也会想象的到
从哪儿继承了
先把继承的问题解决 对吧
用什么部件构造的
你先把部件解决
然后你才能够得到最终的成品
是不是呢
道理就是这个
所以第一步呢
就是调用所有的基类的构造函数
要有多个基类怎么调用呢
它调用顺序
就按照它们被继承的时候
声明的顺序从左向右调用
那 接下来呢
就该对初始化列表里面的成员
进行初始化了
那么这个初始化列表里面的成员
就会包含着对象成员
和基本类型的成员
顺序是按什么呢
按照这些成员在类中定义的顺序
那这里面的对象成员
要进行初始化的时候呢
又会自动调用
它所属类的构造函数
那么这个由初始化列表
为它提供参数
这些都做完了
最后才执行派生类构造函数
也是组合类构造函数
它的函数体里面的内容
-导学
--导学
-继承的基本概念和语法
-第七章 继承与派生--继承的基本概念和语法习题
-继承方式
-第七章 继承与派生--继承方式
-基类与派生类类型转换
-第七章 继承与派生--基类与派生类类型转换
-派生类的构造和析构
--派生类的构造函数
--派生类的析构函数
--第七章 继承与派生--派生类的构造和析构
-派生类成员的标识与访问
--虚基类
-第七章 继承与派生--派生类成员的标识与访问
-小结
--小结
-综合实例
--第七章综合实例
-实验七
--实验七
-导学
--导学
-第八章 多态性--导学
-运算符重载
--运算符重载的规则
-第八章 多态性--运算符重载
-虚函数
--虚函数
--虚析构函数
--虚表与动态绑定
-第八章 多态性--虚函数
-抽象类
--抽象类
--第八章 多态性--抽象类
-override与final
-第八章 多态性--override与final
-小结
--第八章小结
-综合实例
--第八章综合实例
-实验八
--实验八
- 第八章讲义
-导学
--导学
-模板
--函数模板
--类模板
-第九章 模板与群体数据--模板
-线性群体
--线性群体的概念
-第九章 模板与群体数据--线性群体
-数组
--数组类模板
-链表
--链表类模板
-第九章 模板与群体数据--链表
-栈
--栈类模板
--栈类模板课后习题
--例9-9 栈的应用课后习题
-队列
--队列类模板
-第九章 模板与群体数据--队列
-排序
--排序概述
--插入排序
--选择排序
--交换排序
-第九章 模板与群体数据--排序
-查找
--查找
--查找课后习题
-小结
--小结
-综合实例
--综合实例
-实验九
--实验九
- 第九章讲义
-导学
--导学
-泛型程序设计及STL的结构
--STL简介
-第十章 泛型程序设计与C++标准模板库--泛型程序设计及STL的结构
-迭代器
--迭代器
-第十章 泛型程序设计与C++标准模板库--迭代器
-容器的基本功能与分类
-第十章 泛型程序设计与C++标准模板库--容器的基本功能与分类
-顺序容器
--顺序容器的特征
--第十章 泛型程序设计与C++标准模板库--顺序容器
-关联容器
--集合
--映射
-第十章 泛型程序设计与C++标准模板库--关联容器
-函数对象
--函数对象
--函数适配器
-算法
--算法
-小结
--第十章小结
-综合实例
--综合实例
-实验十
--实验十
- 第十章讲义
-导学
--导学
-I/O流的概念及流类库结构
-第十一章 流类库与输入/输出--I/O流的概念及流类库结构
-输出流
--输出流概述
--向文本文件输出
--向二进制文件输出
--向字符串输出
-第十一章 流类库与输入/输出--输出流
-输入流
--输入流概述
--输入流应用举例
--从字符串输入
-第十一章 流类库与输入/输出--输入流
-输入/输出流
--输入/输出流
-第十一章 流类库与输入/输出--输入/输出流
-小结
--小结
-综合实例
--综合实例
-实验十一
--实验十一
- 第十一章讲义
-导学
--第12章导学
-异常处理的思想与程序实现
-第十二章 异常处理--异常处理的思想与程序实现
-异常处理中的构造与析构
-第十二章 异常处理--异常处理中的构造与析构
-标准程序库异常处理
-第十二章 异常处理--标准程序库异常处理
-小结
--第12章小结
-综合实例
--综合实例
-实验十二
--实验十二
- 第十二章讲义