多态及其原理

文章目录

一个类如果是基类,它的析构函数最好加上virtual

构成多态的条件

  1. 必须通过基类的指针或者引用调用虚函数
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

多态,不同对象传递过去,调用不同函数

//// 多态调用看的指向的对象

//// 普通对象(普通调用(不构成多态)),看当前者类型

虚函数

只有成员函数才可以加virtual

加上virtual它就叫做虚函数

作用:完成重写

重写

虚函数重写的一些细节:

// 重写的条件本来是虚函数+三同(即派生类虚函数与基类虚函数的

返回值类型、函数名字、参数列表完全相同),但是有一些例外

// 1、派生类的重写虚函数可以不加virtual -- (建议大家都加上)

// 2、协变,返回的值可以不同,但是要求返回值必须是父子关系指针和引用

重载 重写 隐藏

函数重载发生在同一作用域

重写和隐藏 发生在基类和派生类

隐藏只要函数名一样就符合条件

为什么析构函数要搞成符合多态?

场景一

cpp 复制代码
class Person
{
public:
	~Person()
	{
		cout << "~Person()" << endl;
	}
};
class Student:public Person
{
public:	
   ~Student()
	{
		cout << "~Student()" << endl;
	}
};
int main()
{
	Person ps;
	Student st;
	return 0;	
}

这种情况下程序走的好着呢

也没上多态,不是也可以吗?为什么非得把父类和子类的析构函数搞成虚函数重写呢?

因为下面的场景下,不构成重写会造成内存泄漏

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

		delete[] _a;
	}
private:
	int* _a = new int[10];
};
int main()
{
	/*Person ps;
	Student st;*/

	Person* p = new Person;//基类指针既可以指向基类,也可以指向派生类
	delete p;

	p = new Student;  
	delete p;	//p->destructor() + operator delete(p)(free) 	
				//p是基类指针,指向Student,多态条件一满足
				//这里我们期望p指向谁,调用谁的析构
			//如果析构函数不构成多态,那么p->destructor() 是普通调用,只看当前者p的类型,永远只调用~person
	return 0;
}

运行结果

delete p 做了2件事

p->destructor() + operator delete§(free)

如果析构函数不构成多态,那么p->destructor() 是普通调用,只看当前者p的类型,永远只调用~person

这不是我们期望的,这里我们期望p指向谁,调用谁的析构

基类指针既可以指向基类,也可以指向派生类

那么我们就需要满足多态的条件

p是基类指针,指向Student,调用析构函数。多态条件一满足

编译器帮助我们把类析构函数都被处理成destructor这个统一的名字

形参类型一样,函数名一样,析构又没有返回值,那么就需加上virtual即可

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

		delete[] _a;
	}
private:
	int* _a = new int[10];
};
int main()
{
	/*Person ps;
	Student st;*/

	Person* p = new Person;//基类指针既可以指向基类,也可以指向派生类
	delete p;

	p = new Student;  
	delete p;	//p->destructor() + operator delete(p)(free) 	
				//p是基类指针,指向Student,多态条件一满足
				//这里我们期望p指向谁,调用谁的析构
			//如果析构函数不构成多态,那么p->destructor() 是普通调用,只看当前者p的类型,永远只调用~person
	return 0;
}

原理

预热

Base的大小是多少呢?

那么虚函数存在哪里呢?

存在了代码段(常量区)

回顾我们类和对象大小时的知识,成员函数也不保存在对象中,而是存放在公共的代码段

这里的vfptr是虚函数表指针,存的只是虚函数的地址!

成员函数加了virtual就会放到虚函数表指针里面

对于基类指针或引用指向父类或者子类是如何调用不同的函数呢?

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

	int _a = 1;
};

class Student : public Person {
public:
	virtual void BuyTicket() { cout << "买票-半价" << endl; }
	int _b = 1;
};

void Func(Person& p)
{
	// 符合多态,运行时到指向对象的虚函数表中找调用函数的地址
	p.BuyTicket();
}

int main()
{
	Person Mike;
	Func(Mike);

	Student Johnson;
	Func(Johnson);

	return 0;
}

Mike和Johnson的监视中看到他们的虚函数表里面存的地址不同

P指针指向基类和指向子类时调用的函数是不同的地址

符合多态的话,P指向父类就是父类,P指向子类,完成切片后看到的还是一个子类,那么P到底是指向父类还是子类就不得而知了。

但是我不管,运行时,符合多态,运行时到指向对象的虚函数表中找调用函数的地址

在编译时就确定成员函数的地址

如果不符合多态,那么就看调用者P的类型,去Person里去找这个函数,在用函数名修饰规则就可以找到函数的地址

相关推荐
王维同学6 小时前
Winsock 协议与名称空间 Provider 目录
网络·c++·windows·安全
拳里剑气8 小时前
C++算法:BFS解决FloodFill算法
c++·算法·bfs·宽度优先
爱写代码的小朋友8 小时前
从零开始学 Win32 API:C++ 窗口编程实战(VS Code + MinGW-w64 命令行详解)
开发语言·c++
小小晓.8 小时前
C++:语句和作用域
开发语言·c++
fqbqrr9 小时前
2607C++,soui,xplayer视频播放器
c++·soui
zh路西法10 小时前
【10天速通ROS2-PX4无人机】(四) 关掉GPS和气压计,纯激光定位还能飞吗
c++·无人机·px4·ros2·卡尔曼滤波·fastlio2
yyds_yyd_1008611 小时前
1464. 数组中两元素的最大乘积(2026.07.27)
数据结构·c++·算法·leetcode
王老师青少年编程12 小时前
csp信奥赛C++高频考点专项训练:【排序算法】案例7:最高分数的学生姓名
c++·排序算法·csp·高频考点·信奥赛·最高分数的学生姓名
库克克13 小时前
【C++】set 与multiset
开发语言·c++
Mortalbreeze13 小时前
深入 Linux Socket 编程:端口号、网络字节序与 struct sockaddr 详解
linux·服务器·网络·c++