C++——多态

一、概念

去完成某个相同的行为,不同的对象完成时会产生不同的状态

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了 Person。Person对象买票全价,Student对象买票半价。


二、多态的定义及实现

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。

·虚函数:被virtual修饰的类成员函数,(必须是类的成员函数,普通的函数不行,静态成员函数也不行

·virtual关键字:可以修饰成员函数,实现虚函数的重写;也可以在菱形继承中修饰继承方式,完成虚继承,解决多重继承的数据冗余和二义性;虽然两个地方都使用了virtual关键字,但是这两者没有任何的关系

·构成多态的两个条件:

1.虚函数的重写

2.使用基类对象的指针或引用调用虚函数

满足多态:跟指向的对象有关,指向哪个对象调用的就是他的虚函数

不满足多态:跟调用的对象的类型有关,类型是什么调用的就是他的虚函数

·注意:

1.重写要求虚函数的返回值,函数名,参数类型均相同

2.重写时派生类虚函数可以不写virtual关键字

3.重写只是对函数的实现重写,函数声明,缺省值都用基类的,子类当中的缺省值无效

·虚函数重写的两个例外:

1.协变:基类与派生类虚函数反回值类型不同,即基类的返回值是基类的引用或指针,派生类的返回值是派生类的引用或指针,由于派生类的指针和引用可以赋值给基类,所以此时依然构成多态

2.析构函数的重写:虽然基类与派生类析构函数名字不同。虽然函数名不相同, 看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor

·为什么要对析构函数重写?

在以下的情况时:

Person* p1 = new Person;

Person* p2 = new Student;

delete p1;

delete p2;

当动态开辟空间时,如果不使用虚函数重写析构函数,delete调用的析构函数无法完全释放p2子类的对象,造成内存泄露。

·C++11中的final和override关键字:

1.final关键字:修饰虚函数/类,表示该虚函数/类不能再被重写/继承

class Person final//该类无法再被继承
{
};

class assistant:public teacher

{

virtual void name() final{} //该虚函数无法再被重写

};

2.override关键字:检查派生类虚函数是否重写了基类某个虚函数,如果没有完成重写就会编译报错。

class Person

{

virtual void name(){}

};

class Student:public person

{

virtual void name() override{}//检查此处是否完成重写

};
·重载、重定义(隐藏)、重写(覆盖)的对比

三、抽象类

包含纯虚函数的类,抽象类不能实例化出对象。

·纯虚函数
class Car//抽象类
{
    virtual void Drive() = 0;//纯虚函数
};
·抽象类的作用:

1.强制子类完成重写

2.用来表示一些抽象类型(难以找到实体,如车,植物等)

四、虚函数的原理

·包含虚函数的类的大小
class A
{
public:
	virtual void fun()
	{
		cout << "Fun" << endl;
	}
private:
	int _n;
};
int main()
{
	cout << sizeof(A) << endl;
	return 0;
}

输出结果为:8

原因:与普通的类不同,含有虚函数的类会自带一个_vfptr指针数组,存储了类内的虚函数表指针

·虚函数如何实现传引用或指针,调用对应虚函数的?
class A
{
public:
	virtual void fun()
	{
		cout << "AFun" << endl;
	}
private:
	int _n;
};
class B:public A
{
public:
	virtual void fun()
	{
		cout << "BFun" << endl;
	}
private:
	int _x;
};
int main()
{
	A a;
	B b;
	return 0;
}

基类完成虚函数的定义时,会自动生成一个指针数组_vfptr存储类中的虚函数表指针,以找到基类当中虚函数的实现;派生类继承了基类中的虚函数声明,当派生类完成了虚函数体的重写时,对应的函数实现的地址也会把vfptr中对应的虚函数表指针覆盖,所以我们可以看到基类和派生类中两个vfptr中的fun()函数地址是不同的;当调用虚函数时,传对应对象的基类类型的指针或引用,找到对应的虚函数表指针,就由此找到了对应的虚函数体实现,就完成了多态。

·注意

1.只有派生类中完成虚函数的重写,派生类中的虚函数表指针才会被覆盖。

2.普通函数和虚函数一样都存在与代码段。

3._vftptr指针数组规定以0(nullptr)为结尾。

4.虚函数表指针也存在于代码段。

五、静态绑定和动态绑定

1.静态绑定又称为前期绑定(早绑定),在程序的编译期间就确定了程序的行为,也成为静态多态,例如函数的重载

2.动态绑定又称为后期绑定(晚绑定),在程序的运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数(虚函数调用),也称为动态多态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值