IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 多态(虚函数虚函数表纯虚函数虚析构函数抽象类) -> 正文阅读

[C++知识库]多态(虚函数虚函数表纯虚函数虚析构函数抽象类)

目录

多态

静多态

动多态

虚函数

动态类型和静态类型

静态类型

动态类型(多态实现)

虚函数表(vtable)

定位虚函数

虚函数初始化时间

虚函数表的特点

虚函数应用

纯虚函数

纯虚函数声明方法

纯虚函数特点

虚析构函数

?虚函数限制

抽象类(虚基类这个好像是这么叫还是叫纯虚基类来着,记不清了.不过这个不重要)

总结


多态

多态嘛,固然就是一个物体有多种形态,在C++中就是一个函数名,有多种功能.

函数重载一般叫静多态,接下来介绍的就是动多态.

静多态

在编译阶段决定调用哪个函数

动多态

在运行过程中决定调用哪个函数

虚函数

C++在编译时需要知道调用函数(非虚函数)的地址,也就是早期绑定.

在基类中使用virtual声明函数,派生类重写该函数时,会告诉编译器不要早期绑定该函数,

动态类型和静态类型

只有在基类中有虚函数,派生类重写该函数,并且使用指针或引用指向对象时才会出现动态类型.

满足上述条件后,

派生类B,对象b.

A *a = &b;??????? //此时a的动态类型为B(也可以说a的类型就是B)

如果不满足上述条件

A *a = &b;??????? //此时a的静态类型为B,可以说他根本没有动态类型(也可以说a的类型就是A)

在基类的函数前加上virtual(虚函数)关键字,在派生类中重写该函数,运行时根据实际类型调用函数.

在基类某个函数中声明virtual,则派生类中这个函数都是virtual,不需要在显示声明.

静态类型

#include <iostream>

using namespace std;

class A
{
public:
	void show()
	{
		cout << "A show" << endl;
	}
};

class B :public A
{
public:
	void show()
	{
		cout << "B show" << endl;
	}
};

int main()
{
	B b;

	A* a = &b;	//A类对象指针指向B类对象
	a->show();    //输出A show

	return 0;
}

动态类型(多态实现)

此时我们只需要将基类中的show前加上virtual就可以实现多态,也就是动态类型(在运行阶段确定调用哪个函数)

#include <iostream>

using namespace std;

class A
{
public:
	virtual void show()
	{
		cout << "A show" << endl;
	}
};

class B :public A
{
public:
	void show()
	{
		cout << "B show" << endl;
	}
};

int main()
{
	B b;

	A* a = &b;	//A类对象指针指向B类对象
	a->show();    //输出B show

	return 0;
}

虚函数表(vtable)

编译器在基类中发现有虚函数时,会为包含虚函数的类创建一个虚函数表(vtable),该表其实就是一个一维数组,每个数组元素都是一个虚函数地址.

定位虚函数

在有虚函数的类中,编译器会另外为每一个对象提供一个虚函数表指针(vptr),这个指针指向了对象所属类的虚函数表.在程序运行时,会根据对象的类型初始化vptr,从而正确的指向虚函数表,调用正确的函数.

上述代码中指针a指向派生类B,则vptr指向派生类B中的vtable,当调用函数show时,会根据函数地址找到派生类B中的show函数.

虚函数初始化时间

在构造函数中进行vtable的创建和初始化,

构造派生类对象时,会先构造基类.也就是说会先将vptr指向基类的vtable,之后在构造子类对象的时候子类的vptr才会被初始化指向自己的vtable.(因为顺序执行编译器不知道子类是否存在)

虚函数表的特点

vtable可以继承,如果派生类没有重写虚函数,则派生类的vtable仍然使用该地址,只是这个地址的指向是由基类的vtable实现的.

基类中的虚函数地址可以被继承,派生类没有重写会由基类的vtable来实现.如果重写会修改vtable中的地址,由自身的vtable类实现.如果派生类有自己的虚函数,则会在自身的vtable中添加该函数地址.

基类和派生类的vtable中函数地址排列顺序是相同的.

虚函数应用

在面向对象程序设计中,类的继承是经常出现的.目的是保留基类的特性,可以大大减少新类的开发时间.可是从基类继承的成员很多时候不能完全满足派生类的需求,利用虚函数可以很好的解决这个问题.(当然了不同的函数名,数据名也可以实现但是会为开发带来重多不便)

纯虚函数

纯虚函数只需要声明即可

纯虚函数的基类(虚基类,抽象类)不能创建对象,但是可以创建对象的指针.

纯虚函数和虚函数的区别就在于,虚函数的基类可以创建对象,而纯虚函数的基类不能创建对象.

纯虚函数声明方法

virtual 返回值 函数名(参数列表) = 0;

纯虚函数特点

只需声明,不用实现,也就是不需要函数体

在形参列表后加 = 0 ,这样可以告诉编译器这是一个虚函数

因为只有声明,纯虚函数不能被调用,只有在派生类中进行重写才可以被使用.

如果基类中是纯虚函数,则派生类继承下去的任然是纯虚函数.

虚析构函数

当派生类释放对象时,会先调用派生类析构函数,然后调用基类析构函数.

但是如果对象开辟在堆上,定义一个基类的指针指向派生类.delete这个指针的时候如果不是虚析构函数,只会调用基类的析构函数,不会调用派生类的析构函数.会造成内存泄漏(这个是实际中C++坑最多的地方),但是如果使用虚析构函数就不会出现这种问题.

#include <iostream>

using namespace std;

class A
{
public:
	A()
	{
		cout << "A constructor" << endl;
	}

	~A()
	{
		cout << "A destructor" << endl;
	}
};

class B :public A
{
public:
	B()
	{
		cout << "B constructor" << endl;
	}

	~B()
	{
		cout << "B destructor" << endl;
	}
};

int main()
{
	A* a = new B;
	delete a;

	return 0;
}

?当使用虚析构函数时

#include <iostream>

using namespace std;

class A
{
public:
	A()
	{
		cout << "A constructor" << endl;
	}

	virtual ~A()
	{
		cout << "A destructor" << endl;
	}
};

class B :public A
{
public:
	B()
	{
		cout << "B constructor" << endl;
	}

	~B()
	{
		cout << "B destructor" << endl;
	}
};

int main()
{
	A* a = new B;
	delete a;

	return 0;
}

?虚函数限制

非成员函数不能是虚函数

静态成员函数不能是虚函数,静态成员函数是属于类的,不属于对象

内联函数不能是虚函数,编译器会忽略掉inline,使之变成一个虚函数

构造函数不能是虚函数

抽象类(虚基类这个好像是这么叫还是叫纯虚基类来着,记不清了.不过这个不重要)

有纯虚函数的基类就是抽象类,他不可以创建对象(实例化对象)

纯虚函数可以被派生类继承,如果派生类没有定义基类中所有的纯虚函数,那么这个派生类也是抽象类,同样不能创建对象.

总结

多态是一个接口多种实现,他是面向对象的核心,虚函数(结合虚函数表(vtablehe)和虚函数指针(vptr))可以实现多态(动态绑定(动态类型)).

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-01 16:38:20  更:2021-10-01 16:40:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 23:48:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码