第二十二篇:C++中的多态机制

前言

封装性,继承性,多态性是面向对象语言的三大特性。其中封装,继承好理解,而多态的概念让许多初学者感到困惑。本文将讲述C++中多态的概念以及多态的实现机制。

什么是多态?

多态就是多种形态,就是许多情况下可以互换地使用基类型和派生类型的多种形态。

多态的实现

依赖于动态绑定机制。

动态绑定机制相关

动态绑定是函数实际参数和形式参数绑定的一种方式,它是指我们能够在函数接口中使用继承层次中任意类型的对象,无需关心对象的具体类型。

动态执行接口函数的对象参数的哪个函数得在程序实际执行的时候才能确定

C++中默认不使用动态绑定,要触发动态绑定必须满足两个条件:

1. 接口函数的形式参数必须是引用类型或者指针类型。

2. 动态执行函数(对象参数的成员函数而非接口函数)必须是声明为虚成员函数。

代码实例一

下面代码创建基类对象a,然后创建其派生对象b,当将a,b作为参数传入函数printNum()后,函数让它们分别调用自己的函数getNum():

 1 #include <iostream>
 2
 3 using namespace std;
 4
 5 // 基类
 6 class A {
 7 public:
 8     A() {a=0;}
 9     // **需要动态执行的函数必须声明为虚函数,满足了上条件2。
10     virtual int getNum() {
11         return a;
12     }
13 private:
14     int a;
15 };
16
17 // 派生类
18 class B : public A {
19 public:
20     B() {b=1;}
21     // 基类中已经声明过为虚函数了不需要再次声明
22     int getNum() {
23         return b;
24     }
25 private:
26     int b;
27 };
28
29 // **接口函数形参声明为引用类型,满足了上条件1。
30 void printNum(A & a) {
31     // 当实参为基类对象则调用基类对象的getNum,实参为派生类对象则调用派生类对象的getNum。
32     cout << a.getNum() << endl;
33 }
34
35 int main()
36 {
37     A a;
38     B b;
39
40     printNum(a);
41     printNum(b);
42
43     return 0;
44 }

运行结果:

可以观察到顺利实现了动态绑定,a b分别执行自己的getNum() 函数。

代码实例二

  下面代码同样创建基类对象a,然后创建其派生对象b,当将a,b作为参数传入函数printNum()后,函数让它们分别调用自己的函数getNum()(但本例接口函数的形式参数改成了值类型 ):

 1 #include <iostream>
 2
 3 using namespace std;
 4
 5 // 基类
 6 class A {
 7 public:
 8     A() {a=0;}
 9     // **需要动态执行的函数必须声明为虚函数,满足了上条件2。
10     virtual int getNum() {
11         return a;
12     }
13 private:
14     int a;
15 };
16
17 // 派生类
18 class B : public A {
19 public:
20     B() {b=1;}
21     // 基类中已经声明过为虚函数了不需要再次声明
22     int getNum() {
23         return b;
24     }
25 private:
26     int b;
27 };
28
29 // **接口函数形参声明为值类型,不满足上条件1。
30 void printNum(A a) {
31     // 未有实现动态绑定,因此不论实参是何种类型,均执行基类的getNum()函数。
32     cout << a.getNum() << endl;
33 }
34
35 int main()
36 {
37     A a;
38     B b;
39
40     printNum(a);
41     printNum(b);
42
43     return 0;
44 }

运行结果:

可以观察到没有实现动态绑定,a b都执行a的getNum() 函数。这意味着,对象本身并不支持多态,它刚进入函数就被彻底地转换成了形参类型。因此,实现多态要靠的是对象的指针或者引用,而不是对象本身。这也是《C++ Primer》一书中不断强调的东西。

说明

1. 派生类和基类的虚函数类型要一致,只有一种例外 --- 返回对基类类型的引用的虚函数。派生类中的虚函数可以返回基类函数所返回类型的派生类的引用。

2. 基类和派生类的虚函数的默认实参要相同,不然会引起混淆。

小结

封装保证了类的重用( 安全方面 ),继承实现了类的重用,多态则实现了接口的重用。这三个机制体现了C++代码的可重用性,反映了C++在处理大型程序的优势。

时间: 2024-10-24 11:58:03

第二十二篇:C++中的多态机制的相关文章

Egret入门学习日记 --- 第二十二篇(书中 9.7~9.8 节 内容)

第二十二篇(书中 9.7~9.8 节 内容) 开始 9.7节 内容. 重点: 1.进度条ProgressBar的声明和使用. 操作: 1.进度条ProgressBar的声明和使用. 现在真的轻车熟路了,很简单.无非就是设置一下最大值,当前值的属性. 然后,事件监听的话,也是一样的.只不过事件名字的话,我就选书中这个事件吧. 可惜不能发动图,不然你们就可以看到这个进度条,每帧+1的速度前进. 当然,如果你想换自定义皮肤,还是老规矩,去找默认的 EXML 文件. 然后,怎么换素材,就按照自己喜欢的换

Python开发【第二十二篇】:Web框架之Django【进阶】

Python开发[第二十二篇]:Web框架之Django[进阶] 猛击这里:http://www.cnblogs.com/wupeiqi/articles/5246483.html 博客园 首页 新随笔 联系 订阅 管理 随笔-124  文章-127  评论-205 Python之路[第十七篇]:Django[进阶篇 ] Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接数据库,并编写数据访问层代码 业务逻

第二十二篇:再写Windows驱动,再玩Windbg---NET

2011年到现在,就没再怎么搞过Windows驱动了. 最近, 由于项目需要, 试着改一改一个显卡驱动(KMDOD), 从实践上证明, 我在理论上对一个驱动的架构的正确与否.(USB Display = KMDOD + AVStream). 其中, KMDOD是完成显示的部分功能, 完成其中的VidPN(Video present network), 将驱动中原来的POST物理设备转变为USB物理设备. 而AVStream之所以这样提出, 完成是由于USB Video class的启发, 要不然

第二十二篇 信念

第二十二篇  信念 "信念"能带给一个人无穷的力量,这些力量可以支撑自己走过漫长的人生.一个人如果没有信念,就很难找到自己的人生方向,所以"信念"也可以理解为希望. 信念可以给到我们希望,也可以给到我们力量,所以一个人的信念会影响到自己的整个人生.当然信念也有好坏之分,好的信念能让自己积极向上.不畏艰难:坏的信念会让我们不思进取.随波逐流.这两种不同的信念会给到我们两种完全不同的人生,就看亲人们如何作出正确的选择. 一个人活在世上,可以选择走正确的人生道路,依靠好的

Python之路【第二十二篇】:Django之Model操作

Django之Model操作 一.字段 AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bigint自增列,必须填入参数 primary_key=True 注:当model中如果没有自增列,则自动会创建一个列名为id的列 from django.db import models class UserInfo(models.Model): # 自动创建一个列名为id的且为自增的整数列 usern

第二十二篇:在SOUI中使用代码向窗口中插入子窗口

使用SOUI开发客户端UI程序,通常也推荐使用XML代码来创建窗口,这样创建的窗口使用方便,当窗口大小改变时,内部的子窗口也更容易协同变化. 但是最近不断有网友咨询如何使用代码来创建SOUI子窗口,特此在这里统一解答. 要回答这个问题,首先要了解SOUI窗口创建及布局的流程. 先从swnd.cpp里抄一段创建子窗口的代码: 1 BOOL SWindow::CreateChildren(pugi::xml_node xmlNode) 2 { 3 TestMainThread(); 4 for (p

第二十二篇 Java的一些关键字、 作用域 还有运算符的使用

大家好,我来和大家一起分享一下今天我所学习到的一些知识点,今天我学会了怎么去使用Java中的一些关键字 .作业域和运算符的使用.... 首先来介绍一下 Java中的关键字有哪些,由于数量有很多 ,我就来说一下我今天所学会的关键字 与其作用: 我学到的第一个关键字是----Scanner ,他在Java中 代表这扫描器的作用,并且要引入包,他的作用就是监听键盘的输入,其中见过次数最多的是在注册或登陆界面上,那是候会直接采用你所输入的值,并取出来使用, 第二个关键字是 是-------return,

python全栈开发基础【第二十二篇】进程池和回调函数

一.数据共享 1.进程间的通信应该尽量避免共享数据的方式 2.进程间的数据是独立的,可以借助队列或管道实现通信,二者都是基于消息传递的. 虽然进程间数据独立,但可以用过Manager实现数据共享,事实上Manager的功能远不止于此. 命令就是一个程序,按回车就会执行(这个只是在windows情况下) tasklist 查看进程 tasklist | findstr pycharm #(findstr是进行过滤的),|就是管道(tasklist执行的内容就放到管道里面了, 管道后面的findst

第二十二篇:信号的接收和处理

前言 要想掌握 Linux 系统编程,自然要好好学学其信号机制. 本文介绍一个简单的信号接收处理程序,为后面继续深入学习信号机制打下基础. 什么是信号 信号是软件中断,它提供一种处理异步事件的方法. 信号产生的条件 1. 当用户按某些终端按键时.比如:Ctrl + D / Ctrl + C 等. 2. 硬件异常.比如:除数为 0,无效内存引用等. 3. 调用 kill 函数可以将信号发送给另一个进程或者进程组. 4. 当检测到某种软件条件已经发生时.比如:alarm 到时,网络传来某些带外数据时