当前课程知识点:C++语言程序设计进阶 > 第七章 继承与派生 > 基类与派生类类型转换 > 基类与派生类类型转换
公有派生类对象可以被当作基类的对象使用,反之则不可。
派生类的对象可以隐含转换为基类对象;
派生类的对象可以初始化基类的引用;
派生类的指针可以隐含转换为基类的指针。
通过基类对象名、指针只能使用从基类继承的成员。
#include <iostream> using namespace std; class Base1 { //基类Base1定义 public: void display() const { cout << "Base1::display()" << endl; } }; class Base2: public Base1 { //公有派生类Base2定义 public: void display() const { cout << "Base2::display()" << endl; } }; class Derived: public Base2 { //公有派生类Derived定义 public: void display() const { cout << "Derived::display()" << endl; } };
void fun(Base1 *ptr) { //参数为指向基类对象的指针 ptr->display(); //"对象指针->成员名" } int main() { //主函数 Base1 base1; //声明Base1类对象 Base2 base2; //声明Base2类对象 Derived derived; //声明Derived类对象 fun(&base1); //用Base1对象的指针调用fun函数 fun(&base2); //用Base2对象的指针调用fun函数 fun(&derived); //用Derived对象的指针调用fun函数 return 0; }
大家好
欢迎回来继续学习
C++语言程序设计
这一节呢
我们来学习类型转换
现在我们要学的类型转换呢
是基类与派生类之间的
那么我们来看看
基类与派生类
这两个有密切关联的类之间
有没有一些
必然的可转换规律呢
当然是有的
首先
我们本能地想到
既然派生类
全盘照收了基类的内容
全都继承过来了
那是不是一个派生类的对象
就可以当基类对象用呢
公有继承的情况下
确定是这样的
公有派生类的对象
它是可以被当做基类对象使用的
但是反过来是不行的
为什么呢
因为经过派生以后
派生类的东西
比基类里面的东西多
但是基类里面原有的内容
经过公有派生
就都拿到派生类中来了
而且公有派生
使得基类的对外访问接口
是保持不变的
所以呢
派生类对象
它可以隐含转换为基类的对象
派生类的对象
可以初始化基类的引用
派生类的指针
可以隐含转换为基类的指针
那
但是呢
这样经过转换以后
通过基类的对象名 指针
就只能使用从基类
继承的成员
派生类新增的成员
那就不能使用了
现在我们来看一个例子
这个例子很有意思
我们想利用这个类型转换规则
编写一个非常通用的显示函数
那么我们这个希望
它特别通用的这种
这种这种很好的愿望
能实现吗
拭目以待
在这里我们定义了Base1
作为基类
然后定义Base2
以公有方式继承了Base1
然后Derived再次继承Base2
也是公有方式
这样呢
就是Base1派生出Base2
Base2又派生出Derived
每个类都有一个
同名的display函数
但是呢
函数体执行的功能各不相同
我们用输出一行
不同的字符串来模拟
它的功能各不相同
接下来呢我们希望
能够写一个通用的显示函数fun
我们知道
如果这个参数类型是基类的指针
那么实参就可以是什么
是派生类的地址
我们知道用派生类的地址
是可以初始化
公有基类的指针的
所以我们期望用这个指针
在运行时
根据需要指向不同的派生对象
然后调用display函数
希望它能够调用到
各个派生类对象
自己的display函数
看看事实是不是这样呢
现在我们通过单步运行
看一下 观察一下这个情况
现在我们通过调试方式
单步跟踪运行进来
在主函数中呢
看到我们构造了这个
Base1的对象
Base2的对象
和Derived对象
然后第一次调用fun函数的时候
将Base1的地址传过去
那进到这里面了
我们这个ptr
指向的是Base1的对象
然后看它调用哪个函数去了
进到display函数里面
再进到
Base1的display函数里面了
好 执行完了回来
接下来呢
给它再传一个Base2的对象
地址
Base2对象地址
现在把它传进去
现在我们知道
这个指针
虽然它是Base1类型的指针
但是它指向的对象
是Base2类型的了
调用谁的display函数呢
跟踪进去
它还是调用了
Base1的display函数
这个没有达到
我们希望的这个理想状态
再观察一次
当将Derived地址给它的时候
走到哪儿去了
进到fun函数里面
这个ptr呢得到了Derived地址
指向的是Derived的对象
结果呢
它还是进到
Base1的display函数里面了
所以完全没有达到
我们期望的这个效果
没有达到这个期望的效果
那么看一下运行结果呢
确实都是调用的
Base1的display函数
运行结果
刚才看的是这个样子
所以没有达到这个效果
但是呢
我们看到这个程序以后
我们会期望这个效果
这种直接的期望
跟得到的结果不一样
使得程序的可读性很不好
所以在这儿建议大家
不要重新定义
继承而来的非虚函数
什么是非虚函数呢
虚函数我们还没有学
第八章讲虚函数
那当然现在的函数
这些display都不是虚函数
那么我们必要重新
用同样的名字去定义
因为我们这样的话就期望
它指针指向各自对象的时候
表现的不一样
但是达不到这个期望
那是不是我们这个期望
就一定达不到呢
是达得到的
只要我们用虚函数
就可以解决这个问题了
这个问题呢
我们先留着
第八章就会解决的
-导学
--导学
-继承的基本概念和语法
-第七章 继承与派生--继承的基本概念和语法习题
-继承方式
-第七章 继承与派生--继承方式
-基类与派生类类型转换
-第七章 继承与派生--基类与派生类类型转换
-派生类的构造和析构
--派生类的构造函数
--派生类的析构函数
--第七章 继承与派生--派生类的构造和析构
-派生类成员的标识与访问
--虚基类
-第七章 继承与派生--派生类成员的标识与访问
-小结
--小结
-综合实例
--第七章综合实例
-实验七
--实验七
-导学
--导学
-第八章 多态性--导学
-运算符重载
--运算符重载的规则
-第八章 多态性--运算符重载
-虚函数
--虚函数
--虚析构函数
--虚表与动态绑定
-第八章 多态性--虚函数
-抽象类
--抽象类
--第八章 多态性--抽象类
-override与final
-第八章 多态性--override与final
-小结
--第八章小结
-综合实例
--第八章综合实例
-实验八
--实验八
- 第八章讲义
-导学
--导学
-模板
--函数模板
--类模板
-第九章 模板与群体数据--模板
-线性群体
--线性群体的概念
-第九章 模板与群体数据--线性群体
-数组
--数组类模板
-链表
--链表类模板
-第九章 模板与群体数据--链表
-栈
--栈类模板
--栈类模板课后习题
--例9-9 栈的应用课后习题
-队列
--队列类模板
-第九章 模板与群体数据--队列
-排序
--排序概述
--插入排序
--选择排序
--交换排序
-第九章 模板与群体数据--排序
-查找
--查找
--查找课后习题
-小结
--小结
-综合实例
--综合实例
-实验九
--实验九
- 第九章讲义
-导学
--导学
-泛型程序设计及STL的结构
--STL简介
-第十章 泛型程序设计与C++标准模板库--泛型程序设计及STL的结构
-迭代器
--迭代器
-第十章 泛型程序设计与C++标准模板库--迭代器
-容器的基本功能与分类
-第十章 泛型程序设计与C++标准模板库--容器的基本功能与分类
-顺序容器
--顺序容器的特征
--第十章 泛型程序设计与C++标准模板库--顺序容器
-关联容器
--集合
--映射
-第十章 泛型程序设计与C++标准模板库--关联容器
-函数对象
--函数对象
--函数适配器
-算法
--算法
-小结
--第十章小结
-综合实例
--综合实例
-实验十
--实验十
- 第十章讲义
-导学
--导学
-I/O流的概念及流类库结构
-第十一章 流类库与输入/输出--I/O流的概念及流类库结构
-输出流
--输出流概述
--向文本文件输出
--向二进制文件输出
--向字符串输出
-第十一章 流类库与输入/输出--输出流
-输入流
--输入流概述
--输入流应用举例
--从字符串输入
-第十一章 流类库与输入/输出--输入流
-输入/输出流
--输入/输出流
-第十一章 流类库与输入/输出--输入/输出流
-小结
--小结
-综合实例
--综合实例
-实验十一
--实验十一
- 第十一章讲义
-导学
--第12章导学
-异常处理的思想与程序实现
-第十二章 异常处理--异常处理的思想与程序实现
-异常处理中的构造与析构
-第十二章 异常处理--异常处理中的构造与析构
-标准程序库异常处理
-第十二章 异常处理--标准程序库异常处理
-小结
--第12章小结
-综合实例
--综合实例
-实验十二
--实验十二
- 第十二章讲义