c++ 虚函数,纯虚函数的本质区别
转载博客:https://mp.weixin.qq.com/s?__biz=MzAxNzYzMTU0Ng==&mid=2651289202&idx=1&sn=431ffd1fae4823366a50b68aed2838d4&chksm=80114627b766cf31f72018ef5f1fe29591e9f6f4bd72018e7aea849342ca6f0a271fb38465ae#rd
学习C++的多态性,你必然听过虚函数的概念,你必然知道有关她的种种语法,但你未必了解她为什么要那样做,未必了解她种种行为背后的所思所想。深知你不想在流于表面语法上的蜻蜓点水似是而非,今天我们就一起来揭开挡在你和虚函数(女神)之间的这一层窗户纸。
首先,我们要搞清楚女神的所作所为,即语法规范。然后再去探究她背后的逻辑道理。她的语法说来也不复杂,概括起来就这么几条:
在类成员方法的声明(不是定义)语句前面加个单词:virtual,她就会摇身一变成为虚函数。
在虚函数的声明语句末尾中加个 =0 ,她就会摇身一变成为纯虚函数。
子类可以重新定义基类的虚函数,我们把这个行为称之为复写(override)。
不管是虚函数还是纯虚函数,基类都可以为提供他们的实现(implementation),如果有的话子类可以调用基类的这些实现。
子类可自主选择是否要提供一份属于自己的个性化虚函数实现。
子类必须提供一份属于自己的个性化纯虚函数实现。
语法都列出来了,背后的逻辑含义是什么呢?我们用一个生动的例子来说明,虚函数是如何实现多态性的。
假设我们要设计关于飞行器的类,并且提供类似加油、飞行的实现代码,考虑具体情况,飞行器多种多样,有民航客机、歼击机、轰炸机、直升机、热气球、火箭甚至窜天猴、孔明灯、纸飞机!
假设我们有一位牛得一比的飞行员,他能给各式各样的飞行器加充不同的燃料,也能驾驶各式各样的飞行器。下面我们来看看这些类可以怎么设计。
首先,飞行器。由于我们假设所有的飞行器都有两种行为:加油和飞行。因此我们可以将这两种行为抽象到一个基类中,并由它来派生具体的某款飞行器。
这是一个描述飞行器的基类,提供了两个基本的功能:加油和飞行
class aircraft
{
void refuel(); // 加燃油,普通虚函数
void fly()=0; // 飞行,纯虚函数
};
这是一个普通虚函数,意味着基类希望子类提供自己的个性化实现代码,但基类同时也提供一个缺省的虚函数实现版本,在子类不复写该虚函数的情况下作为备选方案
void aircraft::refuel()
{
// 加充通用型燃油
}
这是一个纯虚函数,意味着基类强制子类必须提供自己的个性化版本,否则编译将失败。但让人惊奇的是,C++仍然保留了基类提供该纯虚函数代码实现的权利,这也许是给千变万化的实际情况留下后路
void aircraft::fly()
{
// 一种不应该被使用的缺省飞行方案
}
有了基类aircraft,我们就可以潇洒地派生出各式各样的飞行器了,比如轰炸机和直升机:
轰炸机类定义,复写了加油和飞行
class bomber : public aircraft
{
void refuel(){} // 加充轰炸机的特殊燃油!
void fly(){} // 轰炸机实弹飞行!
};
直升机类定义,复写了飞行代码,但没有复写加油
class copter: public aircraft
{
void fly(){} // 直升机盘旋!
};
以上代码可以看到,直升机类(copter)没有自己的加油方式,直接使用了基类提供的缺省加油的方式。此时我们来定义一个能驾驭多机型的王牌飞行员类:
一个能王牌飞行员
class pilot
{
void refuelPlane(aircraft *p);
void dirvePlane(aircraft *p);
};
给我什么飞机我就加什么油
void pilot::refuelPlane(aircraft *p)
{
p->refuel();
}
给我什么飞机我就怎么飞
void pilot::dirvePlane(aircraft *p)
{
p->fly();
}
很明显,我们接下来要给这位很浪的飞行员表演一下操纵各种飞行器的机会,我们来定义各种飞机然后丢给他去处理
定义两架飞机,一架轰6K,一架武直10
aircraft *H6K = new bomber;
aircraft *WZ10 = new copter;
来一个王牌飞行员,给H6K加油(加的是轰炸机特殊燃油),并且按照H6K的特点飞行
pilot Jack;
Jack.refuelPlane(H6K); // 加充轰炸机燃油
Jack.flyPlane(H6K); // 轰炸机实弹飞行
给WZ10加油(加的是基类提供的通用燃油),按照WZ10的特点飞行
Jack.refuelPlane(WZ10); // 加充通用型燃油
Jack.flyPlane(WZ10); // 直升机盘旋
上述代码体现了最经典的所谓多态的场景,给Jack不同的飞机,就能表现不同的结果。虚函数和纯虚函数都能做到这一点,区别是,子类如果不提供虚函数的实现,那就会自动调用基类的缺省方案。而子类如果不提供纯虚函数的实现,则编译将会失败。基类提供的纯虚函数实现版本,无法通过指向子类对象的基类类型指针或引用来调用,因此不能作为子类相应虚函数的备选方案。下面给出总结。
第一,当基类的某个成员方法,在大多数情形下都应该由子类提供个性化实现,但基类也可以提供一个备选方案的时候,请将其设计为虚函数。例如飞行器的加油动作,每种不同的飞行器原则上都应该有自己的个性化的加充然后的方式,但也不免可以有一种通用的然后和加充方式。
第二,当基类的某个成员方法,必须由子类提供个性化实现的时候,请将其设计为纯虚函数。例如飞行器的飞行动作,逻辑上每种飞行器都必须提供为其特殊设计的个性化飞行行为,而不应该有任何一种“通用的飞行方式”。
第三,使用一个基类类型的指针或者引用,来指向子类对象,进而调用经由子类复写了的个性化的虚函数,这是C++实现多态性的一个最经典的场景。
第四,基类提供的纯虚函数的实现版本,并非为了多态性考虑,因为指向子类对象的基类指针和引用无法调用该版本。纯虚函数在基类中的实现跟多态性无关,它只是提供了一种语法上的便利,在变化多端的应用场景中留有后路。
第五,虚函数和普通的函数实际上是存储在不同的区域的,虚函数所在的区域是可被覆盖(也称复写override)的,每当子类定义相同名称的虚函数时就将原来基类的版本给覆盖了,另一侧面也说明了为什么基类中声明的虚函数在后代类中不需要另加声明一律自动为虚函数,因为它所存储的位置不会发生改变。而普通函数的存储区域不会覆盖,每个类都有自己独立的区域互不相干。
最后附一幅草图以供参考
c++ 虚函数,纯虚函数的本质区别的更多相关文章
- 虚函数&纯虚函数&抽象类&虚继承
C++ 虚函数&纯虚函数&抽象类&接口&虚基类 1. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过 ...
- 【转】C++ 虚函数&纯虚函数&抽象类&接口&虚基类
1. 动态多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过父类的指针调用实际子类的成员函数. 多态性就是允许将子类类型的指针赋值给父类类型 ...
- C++ 虚函数&纯虚函数&抽象类&接口&虚基类(转)
http://www.cnblogs.com/fly1988happy/archive/2012/09/25/2701237.html 1. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多 ...
- C++学习基础十二——纯虚函数与抽象类
一.C++中纯虚函数与抽象类: 1.含有一个或多个纯虚函数的类成为抽象类,注意此处是纯虚函数,而不是虚函数. 2.如果一个子类继承抽象类,则必须实现父类中的纯虚函数,否则该类也为抽象类. 3.如果一个 ...
- C++学习笔记(十二):类继承、虚函数、纯虚函数、抽象类和嵌套类
类继承 在C++类继承中,一个派生类可以从一个基类派生,也可以从多个基类派生. 从一个基类派生的继承称为单继承:从多个基类派生的继承称为多继承. //单继承的定义 class B:public A { ...
- c++,纯虚函数与抽象类
1.纯虚函数的定义: (1)虚函数被“初始化”为0的函数.声明纯虚函数的一般形式是virtual 函数类型 函数名(参数表列) =0;(2)纯虚函数没有函数体:(3)最后面的“=0”并不表示函数返回值 ...
- C++多态、虚函数、纯虚函数、抽象类、虚基类
一.C++多态 C++的多态包括静态多态和动态多态.静态多态包括函数重载和泛型编程,动态多态包括虚函数.静态多态是指在编译期间就可以确定,动态多态是指在程序运行时才能确定. 二.虚函数 1.虚函数为类 ...
- C++基础知识 基类指针、虚函数、多态性、纯虚函数、虚析构
一.基类指针.派生类指针 父类指针可以new一个子类对象 二.虚函数 有没有一个解决方法,使我们只定义一个对象指针,就可以调用父类,以及各个子类的同名函数? 有解决方案,这个对象指针必须是一个父类类型 ...
- C++多态、虚函数、纯虚函数、抽象类
多态 同一函数调用形式(调用形式形同)可以实现不同的操作(执行路径不同),就叫多态. 两种多态: (1)静态多态:分为函数重载和运算符重载,编译时系统就能决定调用哪个函数. (2)动态多态(简称多态) ...
- C++ 虚函数、纯虚函数、虚继承
1)C++利用虚函数来实现多态. 程序执行时的多态性通过虚函数体现,实现运行时多态性的机制称爲动态绑定:与编译时的多态性(通过函数重载.运算符重载体现,称爲静态绑定)相对应. 在成员函数的声明前加上v ...
随机推荐
- postgres主从配置
运维开发技术交流群欢迎大家加入一起学习(QQ:722381733) 开始部署postgres主从(如果没不会安装postgres的请去上一个博文中查看) 这里我使用了两台服务器部署 主:192.168 ...
- python爬虫14 | 就这么说吧,如果你不懂python多线程和线程池,那就去河边摸鱼!
你知道吗? 在我的心里 你是多么的重要 就像 恩 请允许我来一段 freestyle 你们准备好了妹油 你看 这个碗 它又大又圆 就像 这条面 它又长又宽 你们 在这里 看文章 觉得 很开心 就像 我 ...
- 【5】Django项目配置settings.py详解
夫唯不争,故天下莫能与之争 --老子<道德经> 本节内容 1.项目配置文件settings.py介绍 2.数据库配置[MySQL] 3.创建模型对象并和数据库同步 4.python官方提供 ...
- 【Codeforces 1114C】Trailing Loves (or L'oeufs?)
[链接] 我是链接,点我呀:) [题意] 问你n!的b进制下末尾的0的个数 [题解] 证明:https://blog.csdn.net/qq_40679299/article/details/8116 ...
- hdu_1008_Elevator_201308191629
ElevatorTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Sub ...
- mysql实例的连接数max_user_connections 和max_connections 配置的那些事
今天在查线上问题时,通过phpMyAdmin来进行DML操作,发现比平时慢多了,就各种进原因. 项目的场景是一个mysql实例中创建了多个数据库,猜想可能是相互影响所致. 然后,查询线上Mysql数据 ...
- Codeforces Round #305 (Div. 2) D题 (线段树+RMQ)
D. Mike and Feet time limit per test 1 second memory limit per test 256 megabytes input standard inp ...
- pascal+sublime搭建Pascal学习环境
一.fpc安装 1. 下载:http://www.freepascal.org/down/i386/win32.var(或者:http://download.csdn.net/detail/wenph ...
- Spring发送邮件_javax.mail.AuthenticationFailedException异常已解决
在Spring项目中须要增加监控功能.监控过程中发现异常时.须要邮件报警.最初选择用javamail发送,代码量比較大(相对于spring发送).终于选择Spring邮件发送~ 以下贴一下实现的代码以 ...
- AFNetworking 3.0携带參数上传文件Demo
一.服务端代码: 服务端是java用国产nutz搞的,实际mvc框架都大同小异.就是提交文件的同一时候还带了个表单參数 @AdaptBy(type=UploadAdaptor.class, args= ...