面向对象的三大特性之一 —— 多态

文章介绍了多态的概念,特别是在C++中的实现方式,强调了虚函数在实现多态中的关键作用。通过示例代码展示了如何使用虚函数实现不同对象调用相同函数产生不同结果。还提到了析构函数的重写以及协变在多态中的应用,并解释了虚函数表和虚表指针的工作原理。
摘要由CSDN通过智能技术生成

多态的概念:

不同的对象完成某个行为会产生不同的形态,比如我们在买票的情况,会有身高几米以下半价、学生半价等等这种特殊情况。

多态体现在继承体系中,子类继承父类,父类对象和子类对象调用相同的函数,但结果会是不一样,举个例子:

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

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

int main()
{
	Person p;
	Student s;
	p.BuyTicket();
	s.BuyTicket();
	return 0;
}

输出结果为:

买票-全价

买票-半价

其中,我们发现,类里面的函数实现,前面有个virtual,之前我们用过这个关键字事用来继承父类,这次放在函数前面是用来表示这个函数是虚函数,虚函数被继承后,子类里面如果有个函数原型相同的函数,那么就称为重写(注意,是函数原型,是指函数返回值,函数名,参数列表都相同),如果只是函数名相同,那么就构成重定义。

虚函数还有两个例外:

1、协变

协变就是父类虚函数的返回值是父类指针或引用,子类的虚函数返回值是子类的指针或引用,协变也构成多态。

2、析构函数的重写

父类和子类的析构函数函数名不相同,编译器对析构函数的名称做了特殊处理,在编译阶段会把析构函数函数名统一变成destructor,另外,只有子类重写了父类的析构函数才构成多态,这样才能正确的调用析构函数(在子类析构函数中,先析构子类对象中不属于父类的那一部分,然后自动调用父类的析构函数析构父类的那一部分)。

构成多态需要两个条件,其中一个条件就是被调用的函数必须是虚函数,且子类的虚函数必须对父类的虚函数进行重写。

那还有一个条件呢?

虚函数的参数必须是父类的指针或引用,如果用父类的对象作参数,那么当一个子类对象传过去,先进行拷贝,把子类对象中父类的那一部分拷贝过去,然后再调用函数,此时,调用的函数就一定是父类的虚函数,调用不了子类的虚函数。

对于虚函数还有一点补充:如果父类有虚函数,子类继承父类,重写的虚函数前面加不加virtual无所谓,但是最好还是加上。

虚函数的原理:

先看一段代码:

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

调试前在return 0的位置打上断点,然后按F5直接执行到断点处,观察调试面板

 我们发现,在父类和子类对象里,除了一个成员变量,还有一个指针,这个指针就是虚函数表指针,简称虚表指针,这个指针指向的空间是一个表,这个表叫做虚函数表,简称虚表,虚表里面存的是虚函数的指针。

我们可以看到,子类只重写了父类的Func1函数,Func2函数没有重写,所以,在两个对象的虚表中,Func2函数指针是同一块空间。

虚函数表本质是以数组的形式存储,vs系列的编译器一般会在最后加一个nullptr,g++不会。

总结:编译器会先把父类的虚表拷贝一份给子类,子类如果有重写父类的虚函数,那么就用自己重写的虚函数指针覆盖子类虚表中原有的。

另外,编译器在执行调用虚函数这个命令时,先是到指针或引用所指向的对象空间里面找虚表,从而拿到要调用的虚函数指针,再通过虚函数指针找到函数最后调用。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值