多态及其原理

文章目录

一个类如果是基类,它的析构函数最好加上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里去找这个函数,在用函数名修饰规则就可以找到函数的地址

相关推荐
我喜欢就喜欢18 分钟前
RapidFuzz-CPP:高效字符串相似度计算的C++利器
开发语言·c++
千帐灯无此声19 分钟前
Linux 测开:日志分析 + 定位 Bug
linux·c语言·c++·bug
莫彩20 分钟前
【Modern C++ Part7】_创建对象时使用()和{}的区别
开发语言·c++
mit6.8243 小时前
[Meetily后端框架] Whisper转录服务器 | 后端服务管理脚本
c++·人工智能·后端·python
L_autinue_Star4 小时前
手写vector容器:C++模板实战指南(从0到1掌握泛型编程)
java·c语言·开发语言·c++·学习·stl
无小道6 小时前
c++--typedef和#define的用法及区别
c语言·开发语言·汇编·c++
mit6.8247 小时前
[Vroom] 位置与矩阵 | 路由集成 | 抽象,解耦与通信
c++·人工智能·算法
ChuHsiang7 小时前
【C++】模板(二)
c++
小林C语言9 小时前
11.9 C++对象指针
c++
oioihoii9 小时前
C++11中的std::minmax与std::minmax_element:原理解析与实战
java·开发语言·c++