C++虚函数,纯虚函数,抽象类以及虚基类的区别

Part1.C++中的虚函数

  • 什么是虚函数:

    • 直观表达就是,如果一个函数的声明中有 virtual 关键字,那么这个函数就是虚函数。

       
  • 虚函数的作用:
    • 虚函数的最大作用就是实现面向对象程序设计的一大特点,多态性,多态性表达的是一种动态的概念,是在函数调用期间,进行动态绑定,以达到什么样的对象就实现什么样的功能的效果。

       
  • 虚函数的一般声明语法:
    • virtual 函数类型 函数名 (形参表)

       
  • 注意:
    • 虚函数的声明只能出现在类的定义中,不能出现在成员函数实现的时候
    • 虚函数一般不声明为内联函数,但是声明为内联函数也不会引起错误

       
  • 在运行过程中要实现多态的三个条件:
    • 类之间满足赋值兼容关系(也就是类之间有继承关系)
    • 要声明为虚函数
    • 调用虚函数时,要由成员函数或者是指针和引用来访问

 

  • 代码举例
	#include <iostream>
using namespace std; class Base1 {
public:
public:
virtual void play();
}; void Base1::play()
{
cout << "Base1::play()" << endl;
} class Base2:
public Base1
{
virtual void play();
}; void Base2::play() {
cout << "Base2::play()" << endl;
} class Derived :
public Base2
{
virtual void play();
}; void Derived::play() {
cout << "Derived:: play()" << endl;
} void fun(Base1* ba) { //声明一个基类的指针
ba->play();
} int main()
{
Base1 ba1;
Base2 ba2;
Derived de;
//分别用不同的对象指针来调用 fun 函数
fun(&ba1);
fun(&ba2);
fun(&de);
return 0;
}

这代码含义就充分体现了虚函数作为实现多态条件的原因,由于 Base1 是 Base2 和 Derived 的父类,所以,Base1 是可以兼容 Base2 和 Derived 的,所以在 fun 函数这里是用的 Base1 的指针来作为形参,不同的是当我传入参数不同时,fun 函数执行的是不同的结果,这就体现了多态的效果,我需要那个类型的实例,他就执行那个实例对应的方法。

需要注意的是:

基类的指针可以指向派生类的对象,基类的引用可以作为派生类的别名,但是基类的对象不能表示派生类的对象,所以在 fun 函数中我使用的是 Base1 的指针(换成引用也可以),而不是 Base1 的对象。

Part2.纯虚函数和抽象类

纯虚函数一般是和抽象类一起使用的,因为抽象类的定义就是:拥有纯虚函数的类是抽象类,所以需要先了解一下纯虚函数。

  • 什么是纯虚函数

    • 在虚函数的声明基础上,在函数声明后面加上 =0,的函数为纯虚函数

       
  • 一般定义语法:
    • virtual 函数类型 函数名(参数表)=0;

       
  • 什么时候使用纯虚函数:
    • 当一个方法不用实例化
    • 方法的实现必须在派生类中

       
  • 注意:
    • 纯虚函数不用被实现
    • 区分纯虚函数和函数体为空的函数,这是两中不同的函数

抽象类:

  • 抽象类的主要作用:

    • 为一个类族建立起一个公共的接口,使他们能够完整发挥多态的特性

       
  • 注意:
    • 抽象类不能实例化

       

代码举例

	 #include <iostream>
using namespace std; class Base1 {
public:
public:
virtual void play()=0;
}; void Base1::play()
{
cout << "Base1::play()" << endl;
} class Base2:
public Base1
{
virtual void play();
}; void Base2::play() {
cout << "Base2::play()" << endl;
} class Derived :
public Base2
{
virtual void play();
}; void Derived::play() {
cout << "Derived:: play()" << endl;
} void fun(Base1* ba) { //声明一个基类的指针
ba->play();
} int main()
{
Base2 ba2;
Derived de;
//分别用不同的对象指针来调用 fun 函数
fun(&ba2);
fun(&de);
return 0;
}

这段代码和上面的代码区别不大,唯一的区别在于,将 Base1 的 play方法声明为了纯虚函数,所以 base1 成了一个抽象类,就不能在直接声明一个 Base1 类型的对象,如果在声明一个纯虚函数,编译器就会报错,因为抽象类不能被实例化。

Part3.虚基类

之所以把虚基类放到这里为了做一个比较,因为学习了虚函数,抽象类后容易把这几个概念弄混,所以在这里对比记忆。

  • 为什么要使用虚基类
	 #include <iostream>
using namespace std; class A {
public :
int varA;
}; class B:
public A
{
public:
int varB;
}; class C:
public A
{
public:
int varC;
}; class D :
public C, public B
{
public:
int varC;
}; int main()
{
D d;
d.varA;
return 0;
}

解析上面的代码,由于对象 D 是继承与 B 和 C的,然而 B 和 C 又共同继承与 A,所以 B 和 C都具有从 A 中继承下来的属性 varA,所以我可以用 d 对象来调用 varA ,但是正是因为 B 和 C 中都具有 varA,所以我用

d 对象调用 varA 时,编译器会产生疑问,到底是调用的 B 的 varA 还是 C 的 varA,这样就会产生错误,当然我们可以使用作用域分辨符来区分到底是谁的 varA,但是这样就不能很好的体现继承的概念了,所以这里使用虚基类来解决这个问题。

  • 虚基类的作用

    • 当一个派生类中有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接基类的数据成员的多份同名成员,就会造成二义性,将这个共同基类设置为虚基类时,同名的数据成员就会只存在一个副本,同一个函数名只有一个映射,就解决了二义性。

也就是说,当声明为虚基类时,在程序中只保留了一个共同基类的数据副本,对象 B 和对象 C 的 varA 都指向同一个 varA(本来也应该是这样),这样再调用 varA 时,在程序中只有一个 varA,编译器就不会产生疑问了。

  • 基本语法
   class A {
public :
int varA;
}; class B:
virtual public A
{
public:
int varB;
}; class C:
virtual public A
{
public:
int varC;
}; class D :
public A, public B
{
public:
int varC;
};

Part4.总结

虚函数,是在类中中的概念,他的重要作用就是实现面向对象的多态的特性,

抽象类,是类的概念,他必须和纯虚函数函数搭配使用

虚基类,是继承中的概念,是继承过程中用来解决多个间接基类同一个副本产生的二义性的方法。

c++第五次博客作业的更多相关文章

  1. 第五周博客作业 <西北师范大学| 周安伟>

    第五周博客作业 一,助教博客链接https://home.cnblogs.com/u/zaw-315/ 二,本周工作查阅项目汇报,班级微信群.对同学们的中期项目汇报进行查看,解决上周留言问题,对及时出 ...

  2. 第十五周博客作业 <西北师范大学| 周安伟>

    第十五周作业 助教博客链接https://home.cnblogs.com/u/zaw-315/ 作业要求链接https://www.cnblogs.com/nwnu-daizh/p/10946673 ...

  3. 第五周博客作业<西北师范大学|李晓婷>

    1.助教博客链接:https://home.cnblogs.com/u/lxt-/ 2.作业要求链接:https://www.cnblogs.com/nwnu-daizh/p/10527959.htm ...

  4. 第五次博客作业——Alpha2项目的测试

    格式描述: 这个作业属于哪个课程 <课程的链接> 这个作业要求在哪里 <作业要求的链接> 团队名称 你的代码我的发 这个作业的目标 选取非自己所在团队的3个项目进行测试,并写出 ...

  5. C语言第五次博客作业--函数

    一.PTA实验作业 题目1:使用函数判断完全平方数 1. 本题PTA提交列表 2. 设计思路 3.本题调试过程碰到问题及PTA提交列表情况说明. 部分正确 :将else的情况放入for循环内,导致循环 ...

  6. C语言第五次博客作业

    一.PTA实验作业 题目1:6-6 使用函数输出水仙花数 1. 本题PTA提交列表 2. 设计思路 (1) 首先先定义narcissistic函数. (2)定义四个整形变量n,a,d,cnt,sum, ...

  7. java课程设计——博客作业教学数据分析系统(201521123083 戴志斌)

    目录 一.团队课程设计博客链接 二.个人负责模块或任务说明 三.自己的代码提交记录截图 四.自己负责模块或任务详细说明 五.课程设计感想 (题外话,终于可以用markdown建目录) 一.团队课程设计 ...

  8. Java课程设计——博客作业教学数据分析系统(201521123082 黄华林)

    Java课程设计--博客作业教学数据分析系统(201521123082 黄华林) 一.团队课程设计博客链接 博客作业教学数据分析系统(From:网络五条狗) 二.个人负责模块或任务说明 1.网络爬虫 ...

  9. OO第一次博客作业

    OO第一次博客作业 一.三次作业的bug反省 1.自己发现别人的问题 (1)输入处理的问题,比如第一次作业,主要就是处理输入的字符串,然后有同学的正则表达式有问题,则对于一些错误输入就不能正确判断. ...

随机推荐

  1. 【Leetcode_easy】965. Univalued Binary Tree

    problem 965. Univalued Binary Tree 参考 1. Leetcode_easy_965. Univalued Binary Tree; 完

  2. CSS3 《3D骰子 压大小》

    游戏在线预览地址:http://dtdxrk.github.io/game/3d-dice/index.html js判断一个随机数大小的游戏. 本来想用canvas做的,平面的生产一个点数,感觉没啥 ...

  3. Sequelize模型定义

    定义 定义模型与表之间的映射,使用 define 方法. Sequelize 会自动增加 createdAt 和 updated 属性,这样能知道一个实例的创建时间和最终修改时间.如果不想自动生成,可 ...

  4. css 颜色混合模式 mix-blend-mode

    CSS3 新增了一个很有意思的属性 -- mix-blend-mode ,其中 mix 和 blend 的中文意译均为混合,那么这个属性的作用直译过来就是混合混合模式,当然,我们我们通常称之为混合模式 ...

  5. git的使用学习(二)git的操作使用

    1.创建版本库 什么是版本库呢?版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改.删除,Git都能跟踪,以便任何时刻都 ...

  6. Anaconda是什么?Anconda下载安装教程(1)

    一.如果从事Python开发,配置环境需要安装两个包: 第一个安装开发工具,俗称IDE : 推荐使用 Pycharm 第二个安装开发工具包: Anaconda ps:Windows 下 Pycharm ...

  7. dotnet core use RabbitMQ

    安装RabbitMQ 自从使用了Docker之后,发现Docker真的是比虚拟机好用啊, 因此我这里在Docker里面创建一个RabbitMQ容器. 这里注意获取镜像的时候要获取management版 ...

  8. numpy 数组集合运算及下标操作

    1. 数组的集合运算 1.1. 并集 np.union1d(a,b)计算数组的并集: In [1]: import numpy as np In [2]: a = np.array([1,2,3]) ...

  9. SSM基本案例

    1.搭建环境,导入maven依赖 <properties> <project.build.sourceEncoding>UTF-8</project.build.sour ...

  10. STL源码剖析——iterators与trait编程#2 Traits编程技法

    在算法中运用迭代器时,很可能用到其相应类型.什么是相应类型?迭代器所指对象的类型便是其中一个.我曾有一个错误的理解,那就是认为相应类型就是迭代器所指对象的类型,其实不然,相应类型是一个大的类别,迭代器 ...