C++多态实现的机制

 

1、 多态的概念

多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自百度百科)。

从使用方式上简单的说:就是将父类型的指针指向其子类的实例,然后通过父类指针调用子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。

2、 一个多态的例子

#include <iostream>
using namespace std;

class Animal
{
public:
	virtual void walk(){};
	virtual void speak(){};
};

class Dog:public Animal
{
public:
	void walk()
	{
		cout<<"I walk with four legs"<<endl;
	}
	void speak()
	{
		cout<<"I am a dog,wang wang"<<endl;
	}
};

class Chicken:public Animal
{
public:
	void walk()
	{
		cout<<"I walk with two legs"<<endl;
	}
	void speak()
	{
		cout<<"I am a chicken,ge ge"<<endl;
	}
};

void show(Animal* pAnimal)
{
	pAnimal ->speak();
	pAnimal ->walk();
	cout<<endl;
}

int main() 
{
	show(new Dog());
	show(new Chicken());
	
    cout<<sizeof(Animal)<<" "<<sizeof(Dog)<<" "<<sizeof(Chicken);

	return 0;
}


 

结果输出为:

I am a dog,wang wang

I walk with four legs

 

I am a chicken,ge ge

I walk with two legs

 

4 4 4

1、 实现多态的基本原理

在包含虚函数的类中会自动的为其建立一个虚函数表,这个表里装着虚函数的地址。多态就是基于这个虚函数表来实现的。此外,系统也会自动在类里面添加一个指针,使其指向这个虚函数表。所以上面3个类用sizeof测其大小,都为4字节,即为这个自动添加的指针的大小。

上例中的Dog类和Chicken类中看似没有虚函数,其实这两个类中的两个函数均是虚函数,是从父类继承下来得属性。所以加不加virtual效果都是一样的。

show(new Dog());

在调用此函数的时候,生成一个新的Dog实例,并用父类的指针指向它。其实在这里有个类型转换的过程,new Dog()返回的是一个Dog*类型的指针(记为pDog)。这里传参的时候使其转换为父类的指针Animal*。

我们先看一下new Dog()后生成的实例里都有些什么,在这里,由于没有成员变量,所以实例里只存在一个指针(我们记为pFun), pFun指向一个虚函数表。虚函数表里有两个成员(暂不算结束符),他们分别是Dog类中walk和speak函数的地址。如下图:

然后将此实例赋给父类的指针pAnimal,使父类指针pAnimal指向该实例,由于这里仅仅是类型变换,值没有发生变化,所以pAnimal的值是等于pDog的。在调用pAnimal ->speak();时,会发现speak是虚函数,他就会自动到虚函数表中去寻找speak函数的地址。由于pAnimal实际指向的Dog实例,所以他首先发现的是Dog实例中的PFun指针,然后找到Dog实例的虚函数表,继而找到Dog实例中的speak函数地址。所以pAnimal ->speak();不会调用Animal类中的speak函数(除非是将Animal实例赋给指针pAnimal)。同理,若将Chicken实例赋给父类指针pAnimal,进行pAnimal ->speak();时。将调用Chicken的speak成员函数。

通过以上的方法就实现了多态的功能,使父类指针具有多种形态。可以根据情况调用所有子类的方法。

注意:这里区分一个概念,“早期绑定”和“晚期绑定”。对于一般的函数调用,在编译时期就已经确定了函数的地址,即早期绑定。对于虚函数的调用,只有在运行时期才能确定函数地址,通过虚函数表确定,即晚期绑定。

【参考资料】

1.      http://www.cppblog.com/xczhang/archive/2008/01/20/41508.htmlC++虚函数表解析(转)

2.   http://www.vckbase.com/document/viewdoc/?id=950 C++中的虚函数

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值