[C++进阶]多态

本篇文章,将带大家了解什么是多态,多态使用的注意事项和抽象类。

多态

1.多态的概念

2.多态的调用

3.多态的原理

4.多态的两个关键字

5.抽象类


多态的概念

多态的前提是继承,只有在继承的条件下才能形成多态。下面我们来一起分析。 

多态的构成条件 :

\bullet虚函数重写

\bullet父类的指针或引用调用虚函数

\bullet父子继承关系的两个虚函数要保证函数名相同、参数类型相同、返回值相同

首先我们先介绍什么是虚函数。

 

那么什么叫虚函数的重写呢?

 虚函数重写,也可称为覆盖。

派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的,参数类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。

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

class Student :public Person
{
public:
	void BuyTicket()
	{
		std::cout << "半价" << std::endl;
	}
};

上述代码便完成了对函数BuyTicket的重写。 

 

多态的调用

下面我们上代码分析

 

上图代码中,Person类与Student类中的BuyTicket 构成多态的关系。

1.Person与Student构成继承关系

2.父类中构成多态的函数为虚函数并且在子类中进行了虚函数重写

3.重写的虚函数保证了三同

4.引用是父类的指针调用虚函数

我们进行结果的输出

 

如果子类中重写的虚函数没有加virtual能不能运行成功呢。代码说明一切。

 

我们可以看到,即使子类中重写的函数不加虚函数标志virtual也是可以的。

那么如果父类不写virtual行不行呢?上代码!

 

我们可以看到,当父类不加virtual时,系统只会调用父类中的函数,因此我们可以的到如下总结。

在实现多态时,父类中的virtual必须加,子类中的virtual可加可不加 。

下面我们用一道经典题目来深入了解多态的特性。 

 

 大家思考看看这道题输出结果是多少呢。

首先我们可以看到B和A间构成的继承关系,并且func函数构成了多态。

这里的指针p调用text(),p是B类型指针,所以很多同学觉得,在text()中()是B*this。

实际上,()仍然为A*this,这里调用的func调用的是B的func。所以第一个输出是B->.

那么输出的是1还是0呢。

希望大家将这句话牢记:虚函数重写继承的是父类的接口,重写的实现。

也就是说,这里的val用的是父类中的接口,为1.所以输出为B->1.

多态的原理

虽然我们知道了多态如何使用,但作为对一门底层语言的学习,更重要的是知道它原理。

 

 如果大家觉得大小为4的话那可就掉入陷阱咯!

我们先上代码分析

 

事实上在32位机器下,这里的结果是8,在64位机器下,这里的结果是16!

这是因为它除了有一个变量外,还有
一个指针,此指针指向一个虚函数表

为什么大小会是8呢,我们来看看虚函数的内存。 

 

大家有没有发现,内存里多出的指针?

这里的指针_vfptr并不是实际意义上的指针,而是一个虚数表,用于存放此对象中所有虚函数的地址。

我们用图像来进行理解。

需要注意的是,只有存在虚函数才会有虚数表。 

 既然有虚函数就有虚数表,那父类和子类的虚函数有什么关联的地方吗?

我们来看看他们各自的内存。通过以下代码观察内存情况

class A
{
public:
	virtual void func1()
		cout << "父类func1";
	virtual void func2()
		cout << "父类func2";
private:
	int _a;
};
class B : public A
{
public:
	virtual void func1()
		cout << "子类func1";
private:
	int _b;
};
int main()
{
	A a;
	B b;
	return 0;
}

我们调用窗口进行观察。

 

从窗口我们可以获取以下信息:

1.子类和父类的虚函数表是不同的,也就是子类父类的虚函数表占取的地址不同。 

2.子类中重写的虚函数,如本例中的func1 ,所存在的地址是不同的。

3.子类中没有进行重写的虚函数,如本例中的func2,占有的地址是相同的。 

 因此我们可以得到如下结论:

子类父类函数表不同

重写的虚函数地址不同

未重写的虚函数地址不同

多态的两个关键字

1.final:修饰虚函数,被修饰的虚函数不能进行重写。

 

上述代码中我们对父类中的func1进行了final修饰,可以看到编译器提示,无法重写。 

2.override:检查子类类虚函数是否重写了基类虚函数,如果没有重写编译报错 

 

抽象类

抽象类的概念: 包含有纯虚函数的类

什么是纯虚函数?

虚函数后面写上等于0就构成了纯虚函数。如下列代码就构成了纯虚函数

virtu void func1()=0

关于虚函数的几个小结论:

  1. 析构函数最好定义为虚函数
  2. 构造函数不能定义为虚函数
  3. 静态成员函数不能是虚函数
  4. 内联函数(inline)不能是虚函数

 

 

 

 

 

  • 25
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值