C++类中的多态

一、多态的概念及分类

多态是一种 运行绑定机制,通过这种机制,实现将函数名绑定到函数具体实现代码的目的。一个函数的名称与其入口地址是紧密相连的,入口地址是该函数在内存中的起始地址。
在这里插入图片描述

1、静态多态

静态多态是编译器在编译期间完成的,编译器会根据实参类型来选择调用合适的函数,如果有合适的函数可以调用就调,没有的话就会发出警告或者报错。函数重载和泛型编程就属于静态多态。

2、动态多态

动态多态是在程序运行时根据基类的引用(指针)指向的对象来确定自己具体该调用哪一个类的虚函数。
动态多态的三个前提条件:
(a)必须存在一个继承体系结构;
(b)继承体系结构中的一些类必须具有同名的 virtual 成员函数(virtual 是关键字);
(c)至少有一个基类类型的 指针或者基类类型的 引用可用来对 virtual 成员函数进行调用。

二、动态多态详解

1、动态多态的实现过程

简单描述: 用一个父类的指针去调用子类中被重写的方法。
对于一组相关的数据类型,抽象出它们之间共同的功能集合,在基类中将共同的功能声明为公共虚函数接口,子类通过重写这些虚函数,实现各自对应的功能。在调用时,通过基类的指针来操作这些子类对象,所需执行的虚函数会自动绑定到对应的子类对象上。

2、代码案例

#include <iostream>
using namespace std;
class Shape{
public:
	Shape(void){}
	virtual void show(void){
		cout << “Shape” << endl;
	}
	virtual void foo(void){
		cout << “Shape::foo” << endl;
	}
};
class Circle:public Shape{
public:
	Circle(void){}
	void show(void){
		cout << “Circle” << endl;
	}
};
class Rectangle:public Shape{
public:
	Rectangle(void){}
	void show(void){
		cout << “Rectangle” << endl;
	}
};
int main(void){
	Circle c;
	Rectangle r;
	Shape* p = &c;
	p->show();			//Cricle
	p = &r;
	p->show();			//Rectangle
	Shape& pr = c;
	pr.show();			//Circle
	Shape& pt = r;
	pt.show();			//Rectangle
	return 0;
}

3、案例对应的虚函数表

在这里插入图片描述
上述可以看出,动态多态的实现关键在于虚函数,基类通过virtual关键字声明和实现虚函数,此时基类会拥有一张虚函数表,虚函数表会记录其对应的函数指针。当子类继承基类时,子类也会获得一张虚函数表,不同之处在于,子类如果重写了某个虚函数,其在虚函数表上的函数指针会被替换为子类的虚函数指针。

4、动态多态的实现原理—虚函数表

4.1、动态多态虚函数表的内存分布

假设有一个基类ClassA,一个继承了该基类的派生类ClassB,并且基类中有虚函数,派生类实现了基类的虚函数。
我们在代码中运用多态这个特性时,通常以两种方式起手:
(1) ClassA *a = new ClassB();
(2) ClassB b; ClassA *a = &b;
以上两种方式都是用基类指针去指向一个派生类实例,区别在于第1个用了new关键字而分配在堆上,第2个分配在栈上。
在这里插入图片描述
在这里插入图片描述

4.2、虚函数表属于类而不是对象

如果一个类中有虚函数,那么该类就有一个虚函数表。
在有继承情况下,只要基类有虚函数,子类不论实现或没实现,都有虚函数表。
这个虚函数表是属于类的,所有该类的实例化对象中都会有一个虚函数表指针去指向该类的虚函数表。
在这里插入图片描述

4.3、单继承(多重继承)情况下的虚函数表

class ClassA
{
public:
	ClassA() { cout &lt;&lt; "ClassA::ClassA()" &lt;&lt; endl; }
	virtual ~ClassA() { cout &lt;&lt; "ClassA::~ClassA()" &lt;&lt; endl; }

	void func1() { cout &lt;&lt; "ClassA::func1()" &lt;&lt; endl; }
	void func2() { cout &lt;&lt; "ClassA::func2()" &lt;&lt; endl; }

	virtual void vfunc1() { cout &lt;&lt; "ClassA::vfunc1()" &lt;&lt; endl; }
	virtual void vfunc2() { cout &lt;&lt; "ClassA::vfunc2()" &lt;&lt; endl; }
private:
	int aData;
};

class ClassB : public ClassA
{
public:
	ClassB() { cout &lt;&lt; "ClassB::ClassB()" &lt;&lt; endl; }
	virtual ~ClassB() { cout &lt;&lt; "ClassB::~ClassB()" &lt;&lt; endl; }

	void func1() { cout &lt;&lt; "ClassB::func1()" &lt;&lt; endl; }
	virtual void vfunc1() { cout &lt;&lt; "ClassB::vfunc1()" &lt;&lt; endl; }
private:
	int bData;
};

class ClassC : public ClassB
{
public:
	ClassC() { cout &lt;&lt; "ClassC::ClassC()" &lt;&lt; endl; }
	virtual ~ClassC() { cout &lt;&lt; "ClassC::~ClassC()" &lt;&lt; endl; }

	void func2() { cout &lt;&lt; "ClassC::func2()" &lt;&lt; endl; }
	virtual void vfunc2() { cout &lt;&lt; "ClassC::vfunc2()" &lt;&lt; endl; }
private:
	int cData;
}

(1) ClassA是基类, 有普通函数: func1() func2() 。虚函数: vfunc1() vfunc2() ~ClassA()

(2) ClassB继承ClassA, 有普通函数: func1()。虚函数: vfunc1() ~ClassB()

(3) ClassC继承ClassB, 有普通函数: func2()。虚函数: vfunc2() ~ClassB()

基类的虚函数表和子类的虚函数表不是同一个表。下图是基类实例与多态情形下,数据逻辑结构。注意,虚函数表是在编译时确定的,属于类而不属于某个具体的实例。虚函数在代码段,仅有一份。
在这里插入图片描述
ClassA类型的指针a能操作的范围只能是黑框中的范围,之所以实现了多态完全是因为子类的虚函数表指针与虚函数表的内容与基类不同。

同理,也就不难推导出ClassC的逻辑结构图了
类的继承情况是: ClassC继承ClassB,ClassB继承ClassA
这是一个多次单继承的情况。 (多重继承)
在这里插入图片描述

4.4、多继承情况下的虚函数表

class ClassA1
{
public:
	ClassA1() { cout << "ClassA1::ClassA1()" << endl; }
	virtual ~ClassA1() { cout << "ClassA1::~ClassA1()" << endl; }

	void func1() { cout << "ClassA1::func1()" << endl; }

	virtual void vfunc1() { cout << "ClassA1::vfunc1()" << endl; }
	virtual void vfunc2() { cout << "ClassA1::vfunc2()" << endl; }
private:
	int a1Data;
};

class ClassA2
{
public:
	ClassA2() { cout << "ClassA2::ClassA2()" << endl; }
	virtual ~ClassA2() { cout << "ClassA2::~ClassA2()" << endl; }

	void func1() { cout << "ClassA2::func1()" << endl; }

	virtual void vfunc1() { cout << "ClassA2::vfunc1()" << endl; }
	virtual void vfunc2() { cout << "ClassA2::vfunc2()" << endl; }
	virtual void vfunc4() { cout << "ClassA2::vfunc4()" << endl; }
private:
	int a2Data;
};

class ClassC : public ClassA1, public ClassA2
{
public:
	ClassC() { cout << "ClassC::ClassC()" << endl; }
	virtual ~ClassC() { cout << "ClassC::~ClassC()" << endl; }

	void func1() { cout << "ClassC::func1()" << endl; }

	virtual void vfunc1() { cout << "ClassC::vfunc1()" << endl; }
	virtual void vfunc2() { cout << "ClassC::vfunc2()" << endl; }
	virtual void vfunc3() { cout << "ClassC::vfunc3()" << endl; }
};

ClassA1是第一个基类,拥有普通函数func1(),虚函数vfunc1() vfunc2()。
ClassA2是第二个基类,拥有普通函数func1(),虚函数vfunc1() vfunc2(),vfunc4()。
ClassC依次继承ClassA1、ClassA2。普通函数func1(),虚函数vfunc1() vfunc2() vfunc3()。
在这里插入图片描述
在多继承情况下,有多少个基类就有多少个虚函数表指针,前提是基类要有虚函数才算上这个基类。
如图,虚函数表指针01指向的虚函数表是以ClassA1的虚函数表为基础的,子类的ClassC::vfunc1(),和vfunc2()的函数指针覆盖了虚函数表01中的虚函数指针01的位置、02位置。当子类有多出来的虚函数时,添加在第一个虚函数表中。
当有多个虚函数表时,虚函数表的结果是0代表没有下一个虚函数表。" * "号位置在不同操作系统中实现不同,代表有下一个虚函数表。
注意:
1.子类虚函数会覆盖每一个父类的每一个同名虚函数。
2.父类中没有的虚函数而子类有,填入第一个虚函数表中,且用父类指针是不能调用。
3.父类中有的虚函数而子类没有,则不覆盖。仅子类和该父类指针能调用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值