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

多态的概念:

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

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

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++不会。

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值