Version | S | Description | Date | By |
---|---|---|---|---|
V1 | C | First Version | 2020-12-27 | AYZP |
C―― Create,> A—— Add,> M—— Modify,> D—— Delete。
前言
C++的三大特性,多态,慢慢记。
一 简单描述下
慢慢改
多态的实现主要分为静态多态和动态多态,静态多态主要是重载,在编译的时候就已经确定;动态多态是用虚函数机制实现的,在运行期间动态绑定。举个例子:一个父类类型的指针指向一个子类对象时候,使用父类的指针去调用子类中重写了的父类中的虚函数的时候,会调用子类重写过后的函数,在父类中声明为加了virtual关键字的函数,在子类中重写时候不需要加virtual也是虚函数。
虚函数的实现:在有虚函数的类中,类的最开始部分是一个虚函数表的指针,这个指针指向一个虚函数表,表中放了虚函数的地址,实际的虚函数在代码段(.text)中。当子类继承了父类的时候也会继承其虚函数表,当子类重写父类中虚函数时候,会将其继承到的虚函数表中的地址替换为重新写的函数地址。使用了虚函数,会增加访问内存开销,降低效率。
二 基本信息
2.1 定义
指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性(静态多态),运行时多态性(动态多态)。
2.2 作用
在面向对象的程序设计中使用多态,能够增强 程序的可扩充性,即程序需要修改或增加功能 的时候,需要改动和增加的代码较少。
2.3 分类
- 静态多态
- 静态多态主要重载,编译时已经确定
- 动态多态
- 动态多态是用虚函数机制实现的,在运行期间动态绑定。
- 虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。
- 虚函数的实现:在有虚函数的类中,类的最开始部分是一个虚函数表的指针,这个指针指向一个虚函数表,表中放了虚函数的地址,实际的虚函数在代码段(.text)中。当子类继承了父类的时候也会继承其虚函数表,当子类重写父类中虚函数时候,会将其继承到的虚函数表中的地址替换为重新写的函数地址。使用了虚函数,会增加访问内存开销,降低效率。
2.4 动态多态
动态多态,主要通过虚函数机制实现;
虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。
- 一个父类指针指向了一个子类对象。
- 使用父类指针调用子类对象中重写了的父类虚函数
- 那么会调用子类中重写的虚函数(而不是父类的同名函数)
- 这就是多态
举个栗子:
class CBae {
public:
virtual void SomeVirtualFunction() {}
};
class CDerived:public CBase {
public:
virtual void SomeVirtualFunction() {}
};
int main()
{
CDerived ODerived; //子类对象
CBase * p = & ODerived; //父类指针
p->SomeVirtualFunction(); // 调用哪个虚函数取决于p指向哪种类型的对象,这里是子类
}
- C++的多态是通过覆盖实现的,即父类的函数被子类覆盖了!
- 父类的该函数为虚函数,告诉父类的指针/引用,你调用这个函数的时候必须看一看你绑定的对象到底是哪个类的对象,然后去那个类里调用该函数!
2.5 虚析构函数
为什么虚函数不能作为构造函数:
参考文献[3]很详细
①从存储空间角度:虚函数的调用通过虚函数表来实现,虚函数表存储在对象的内存空间,执行构造函数时都没建立实例,当然也就没有内存空间。
虚函数对应一个vtable(虚函数表),这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,无法找到vtable,所以构造函数不能是虚函数。
②从使用角度:构造函数本身就是建立实例,虚函数主要是调用实例的函数,实例都没有,去哪里调用。
虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。
2)析构函数是虚函数时:使用基类指针删除派生对象时,通常情况下只调用基类的析构函数,但是,删除一个派生类的对象时,应该先调用派生类的析构函数,再调用基类的析构函数,因此析构函数要是虚函数。
2.6 纯虚函数和抽象类
纯虚函数的作用就是基类不指定确定功能,具体功能由子类决定。
在基类中不能对虚函数给出有意义有实现,而把它说明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。
2.7 虚函数与虚指针到底它们分别是存放在类里还是对象里面呢
- 一个类(有虚函数)一张虚表,虚表一般在代码段中
- 类实例中存放指向虚表的指针。
- C++中虚函数表位于只读数据段(.rodata),也就是C++内存模型中的常量区;而虚函数则位于代码段(.text),也就是C++内存模型中的代码区。
三 有趣的问题
待补充
参考资料
[1] qiuri2008. C++的三大特性?C也可以做到. cnblogs. 2018.06.
https://www.cnblogs.com/jiangzhaowei/p/9129092.html
[2] 南子衿. C++之构造函数、析构函数、虚函数、多态、动态绑定. cnblogs. 2019.06.
https://www.cnblogs.com/xiaominkang/p/10979089.html
[3] cainiao000001. 在C++中为什么构造函数不能是虚函数,而析构函数可以. CSDN. 2018.08.
https://blog.csdn.net/cainiao000001/article/details/81603782
[4] 郭炜. 程序设计与算法(三)C++面向对象程序设计. 中国大学MOOC.
https://www.icourse163.org/course/PKU-1002029030?tid=1207491203
[5] HerofH_. C++中的虚函数表和虚函数在内存中的位置. CSDN. 2019. 08.
https://blog.csdn.net/qq_28114615/article/details/98041319
[6] _silverBlack. C++虚函数表,虚表指针,内存分布. CSDN. 2018. 07.
https://blog.csdn.net/li1914309758/article/details/79916414