多态及其原理

文章目录

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

相关推荐
点云SLAM34 分钟前
C++创建文件夹和文件夹下相关操作
开发语言·c++·算法
CodeClimb1 小时前
【华为OD-E卷 - 猜字谜100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
_小柏_1 小时前
C/C++基础知识复习(46)
c语言·开发语言·c++
pl00202 小时前
C++运算符重载实例
开发语言·c++·运算符重载·单目运算符·双目运算符·流运算符
煤泥做不到的!2 小时前
挑战一个月基本掌握C++(第十二天)了解命名空间,模板,预处理器
开发语言·c++
XZHOUMIN2 小时前
【MFC】多工具栏如何保存状态(续)
c++·mfc
界面开发小八哥2 小时前
MFC扩展库BCGControlBar Pro v36.0 - 可视化管理器等全新升级
c++·mfc·bcg·界面控件·ui开发
TANGLONG2223 小时前
【初阶数据结构与算法】八大排序算法之归并排序与非比较排序(计数排序)
java·数据结构·c++·算法·面试·蓝桥杯·排序算法
橘颂TA3 小时前
【C++】数据结构 单链表的实现(企业存储用户数据的实现)
开发语言·数据结构·c++
ltwoxc4 小时前
01-c++对c的扩展(上)
c++