【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;
}

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

✨✨✨✨✨✨✨✨

本篇博客完,感谢阅读🌹

如有错误之处可评论指出

博主会耐心听取每条意见

✨✨✨✨✨✨✨✨

相关推荐
云 无 心 以 出 岫5 分钟前
贪心算法QwQ
数据结构·c++·算法·贪心算法
独好紫罗兰23 分钟前
洛谷题单3-P5719 【深基4.例3】分类平均-python-流程图重构
开发语言·python·算法
换一颗红豆28 分钟前
【C++ 多态】—— 礼器九鼎,釉下乾坤,多态中的 “风水寻龙诀“
c++
篝火悟者36 分钟前
自学-C语言-基础-数组、函数、指针、结构体和共同体、文件
c语言·开发语言
随便昵称1 小时前
蓝桥杯专项复习——前缀和和差分
c++·算法·前缀和·蓝桥杯
commonbelive1 小时前
团体程序设计天梯赛——L1-100 四项全能
c++
genispan1 小时前
QT/C++ 多线程并发下载实践
开发语言·c++·qt
-代号95271 小时前
【JavaScript】十三、事件监听与事件类型
开发语言·javascript·ecmascript
virelin_Y.lin1 小时前
系统与网络安全------Windows系统安全(7)
windows·web安全·系统安全·ftp
写代码的小王吧2 小时前
【Java可执行命令】(十)JAR文件签名工具 jarsigner:通过数字签名及验证保证代码信任与安全,深入解析 Java的 jarsigner命令~
java·开发语言·网络·安全·web安全·网络安全·jar