【C++进阶】多态

一、多态的概念及定义

1.1 多态的概念

多态简单来说就是多种形态

同一个行为,不同对象去完成时

会产生出不同的状态

多态分为静态多态动态多态
静态多态指的是编译时
在程序编译期间确定了程序的行为

比如:函数重载
动态多态指的是运行时
在程序运行期间,根据具体拿到的类型
确定程序的具体行为,调用具体的函数

1.2 在继承中要构成多态的两个条件

  1. 必须通过父类指针或引用调用虚函数

  2. 虚函数的重写
    函数名、参数类型、返回值都要相同

被virtual修饰的类成员函数称为虚函数

cpp 复制代码
class Person {
public:
 virtual void BuyTicket() { cout << "买票-全价" << endl;}
};

1.3 虚函数的重写(覆盖)

派生类中有一个跟基类完全相同的虚函数

(即派生类虚函数与基类虚函数的返回值类

型、函数名字、参数列表完全相同)

称子类的虚函数重写了基类的虚函数
普通函数的继承是实现继承

派生类继承了基类函数,可以使用函数

继承的是函数的实现
虚函数的继承是接口继承

派生类继承的是基类虚函数的接口

目的是为了重写,达成多态,继承的是接口

如果不实现多态,不要把函数定义成虚函数

cpp 复制代码
class Person {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
	
	// 只要父类析构加了virtual就构成多态,子类加不加都可以正确释放
	virtual ~Person() { cout << "~Person" << endl; };
};

class Student : public Person {
public: 
	// 子类可以不写virtual,因为他继承父类的接口,重写实现
    virtual void BuyTicket() { cout << "买票-半价" << endl; }
	
	~Student() { cout << "~Student" << endl; }
};

void Func(Person& p)
{ p.BuyTicket(); }

int main()
{
Person ps;
Student st;

// 构成多态后
Func(ps); // 传父类调用父类的虚函数
Func(st); // 传子类调用子类的虚函数
 return 0;
}

1.4 协变

如果是父子关系的指针或引用

返回值可以不同也构成多态

cpp 复制代码
class A{};
class B : public A {};
class Person {
public:
 virtual A* f() {return new A;}
};
class Student : public Person {
public:
 virtual B* f() {return new B;}
};

1.5 final和override

final: 修饰虚函数

表示该虚函数不能再被重写

现实中不常用,不能实现多态的虚函数

意义不大

cpp 复制代码
class Car
{
public:
 virtual void Drive() final {}
};
class Benz :public Car
{
public:
 virtual void Drive() {cout << "Benz-舒适" << endl;}
};

override: 检查派生类虚函数

是否重写了基类某个虚函数

如果没有重写编译报错

cpp 复制代码
class Car{
public:
 virtual void Drive(){}
};
class Benz :public Car {
public:
 virtual void Drive() override {cout << "Benz-舒适" << endl;}
};

1.6 重载、覆盖(重写)、隐藏(重定义)的对比

面试题经常被问到

1.7 抽象类

在虚函数后面加上 =0

这个函数就叫纯虚函数

包含纯虚函数的类叫做抽象类

抽象类不能实例化出对象

派生类继承后也不能实例化出对象

只有重写纯虚函数,派生类才能实例化出对象

cpp 复制代码
class Car
{
public:
	// 纯虚函数 --- 抽象类
	virtual void Drive() = 0;
};

int main()
{
	Car car; // 无法实例化对象
	
	return 0;
}

二、多态的原理

2.1 虚函数表

这里常考一道笔试题:sizeof(Base)是多少?

cpp 复制代码
class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}
private:
	int _b = 1;
};

int main()
{
	cout << sizeof(Base) << endl;

	return 0;
}

在32位操作系统下是8 bit

在64位操作系统下是16 bit

通过调试发现还有个指针_vfptr

这个指针叫做虚函数表指针

本质是指针数组
用来存放虚函数的地址

2.2 多态的原理

通过下面代码观察父子类
的虚表之间的关系

cpp 复制代码
class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "Base::Func2()" << endl;
	}
	void Func3()
	{
		cout << "Base::Func3()" << endl;
	}
private:
	int _b = 1;
};
class Derive : public Base
{
public:
	virtual void Func1()
	{
		cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};
int main()
{
	Base b;
	Derive d;
	return 0;
}

监视窗口

通过监视窗口可以发现
派生类对象d中也有一个虚表指针
证明基类和派生类的虚表是不一样的

结论:

虚函数表本质是存虚函数指针的指针数组

一般情况这个数组最后面放了一个nullptr

对象中存的是虚表指针

虚表存的是虚函数指针

虚函数和普通函数一样的,都是存在代码段的

派生类的虚表生成:
a.先将基类中的虚表内容拷贝一份到派生类虚表中
b.如果派生类重写了基类中某个虚函数

用派生类自己的虚函数覆盖虚表中基类的虚函数
c.派生类自己新增加的虚函数按其在派生类中的

声明次序增加到派生类虚表的最后

2.3 在多态下建议把基类的析构函数定义成虚函数

如果基类析构函数不是虚函数

就调不到派生类的析构函数

(指针类型是父类,所以调用父类的析构函数)

从而造成内存泄漏

cpp 复制代码
class Person {
public:
 	virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:
 	virtual ~Student() { cout << "~Student()" << endl; }
};

int main()
{
 	Person* ps = new Student;
 	delete ps;
 	
 	return 0;
}

形成多态的条件之一便是
只能通过父类去调用
所以子类对象只能强转成父类类型
如果父类的析构函数不是虚函数
那子类便调不到自己的析构函数
因为子类对象的类型是父类
所以只能调用父类的析构函数
子类成员无法释放从而造成内存泄漏
父类析构函数定义成虚函数便能解决问题

✨✨✨✨✨✨✨✨

本篇博客完,感谢阅读🌹

如有错误之处可评论指出

博主会耐心听取每条意见

✨✨✨✨✨✨✨✨

相关推荐
weixin_4997715512 分钟前
C++中的组合模式
开发语言·c++·算法
初级代码游戏13 分钟前
套路化编程 C# winform 自适应缩放布局
开发语言·c#·winform·自动布局·自动缩放
_waylau16 分钟前
鸿蒙架构师修炼之道-架构师的职责是什么?
开发语言·华为·harmonyos·鸿蒙
2的n次方_28 分钟前
CANN Ascend C 编程语言深度解析:异构并行架构、显式存储层级与指令级精细化控制机制
c语言·开发语言·架构
近津薪荼43 分钟前
dfs专题5——(二叉搜索树中第 K 小的元素)
c++·学习·算法·深度优先
xiaoye-duck1 小时前
吃透 C++ STL list:从基础使用到特性对比,解锁链表容器高效用法
c++·算法·stl
_F_y1 小时前
C++重点知识总结
java·jvm·c++
java干货1 小时前
为什么 “File 10“ 排在 “File 2“ 前面?解决文件名排序的终极算法:自然排序
开发语言·python·算法
_F_y1 小时前
C语言重点知识总结(含KMP详细讲解)
c语言·开发语言
毕设源码-郭学长1 小时前
【开题答辩全过程】以 基于python的二手房数据分析与可视化为例,包含答辩的问题和答案
开发语言·python·数据分析