什么是多态

1什么是多态?TC++PL p277-278

基类通过声明并定义一系列虚函数,继承它的派生类可以重新定义基类中的这些虚函数,从而覆盖基类的版本。在调用的时候,总能正确地调用对应的虚函数版本,不管实际的对象(即p)是被声明为基类的对象还是派生类的对象,也不管指向对象的指针或引用的是基类类型还是派生类类型。这就是所谓的多态。

更直白一点,类的对象调用的是哪个版本的虚函数,跟指向对象的指针类型无关(基类或派生类),只跟对象的虚函数表指针指向的虚函数表的元素(元素即类的所有虚函数指针)有关。这样,类的对象总是能调用正确的虚函数版本,即基类的对象总能调用基类的虚函数版本,派生类的对象总是调用派生类的虚函数版本。而这一切不依赖于指向对象的指针或引用的类型。这就是多态!所以才说对象必须通过指针或引用去操作

能实现这种多态的原因:

1)当然,编译器为每个存在虚函数的类生成一个虚函数表,同时生成一个指向该表的指针。而同时为类的所有对象都生成一个指向该表的指针。这是实现多态的内部机制。

2)其次,要利用指针变量(p)的类型并不会改变指针变量指向的存储地址(这个地址存的哪个版本的虚函数,那么调用的就是哪个版本)这一特点,而通过对虚函数的间接访问(即访问虚函数的地址来调用虚函数),这样就能保证准确无误地调用对应的虚函数。

3)而如果是用下面右边的这种方式,Base p=d;这就是创建了一个base类的对象p,这个p的虚函数表指针肯定是指向base类的虚函数表。

4)所以,对象必须通过指针或引用去操作,才能取得C++的多态性行为。

Derived d(3);                                                          Derived d(3);

Base* p=&d;                                                          Base p=d;//这个算强制类型转换吗

p->display();//调用派生类版本                             p.display();//调用基类版本

要在c++中取得多态行为,被调用的函数就必须是虚函数,而对象则必须是通过指针或引用去操作的。否则编译器就会知道对象的确切类型,也就不需要运行时的多态性了。话说正是这句话不咋明白啊。。。通过指针或引用去操作的时候,编译器不知道对象的确切类型吗?因为在运行时进行动态绑定?但动态绑定又是个毛啊???

2虚函数的实现机制?

1)有虚函数的基类或派生类,编译器都为他们生成一个虚函数表(vtbl),这个虚函数表可以看成是一个数组(不同编译器实现方式不同,有的用链表实现,但原理是一样的),这个数组的元素就是该类的所有虚函数的指针,这些指针分别指向一个虚函数(其实存储的就是虚函数的地址,用指针访问)。

2)然后,编译器为每个带虚函数的类置入一个指针(虚函数表指针,vptr),这个指针指向类的vtbl。

3)声明对象时,每个对象都有一个虚函数表指针,这个vptr都被初始化为指向类的vtbl。所有对象共享一份vtbl(其实看编译器怎么实现了,因为在链接所有objective文件的时候,可能一份vtbl并不够用,这时候有的编译器会生成几分拷贝的vtbl,在生成程序成功后再删除这些拷贝)。

4)至于类的内存大小的计算,虚函数的调用可以参考hackbuteer1的文章:

3为什么用虚函数来实现多态?

为了解决类型域检查方案中的缺陷,这个方案要求遍历检查当前对象是哪个类的对象,这样时间复杂度太高,不仅影响性能,而且也不适合维护。因为一旦新定义一个派生类,就要修改这个类型域检查的代码。当然,虚函数来实现多态的代价就是要损失一些存储空间,是一个以空间换时间的方案。参考hackbuteer1文章中两种多态实现的优缺点对比。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值