深入理解C++中多态的实现

对于C++中多态性是面向对象中很重要的特性之一,每种面向对象的语言对于多态的实现都不尽相同。

最近,学习中发现一段既陌生又熟悉的代码,大致情况如下#include


#include <iostream>

using namespace std;

class BaseClass
{
public:
virtual void Display()
{
cout<<"BaseClass::Display()"<<endl;
}

virtual void Display(int a)
{
cout<<"BaseClass::Display(int a)"<<endl;
}
};

class ExtendClass:public BaseClass
{
public:
virtual void Display()
{
cout<<"ExtendClass::Display()"<<endl;
}

virtual void Display(int a)
{
cout<<"ExtendClass::Display(int a)"<<endl;
}

virtual void Display(float a)
{
cout<<"ExtendClass::Display(float)"<<endl;
}
};

int main()
{
BaseClass* ptrBaseClass = new BaseClass();
ptrBaseClass->Display();
ptrBaseClass->Display(1);
ptrBaseClass->Display(1.1);
return 0;
}


代码的核心思想是
1.基类实现了多个虚函数
2.子类继承基类并且override所有虚函数
3.作者发现子类应该“重载一下”以满足需求,这样的需求原因可能很多

代码即熟悉是因为,overload(重载)的需求在实际应用中很多,因此不感到奇怪。
陌生的原因是,这种写法感觉很奇怪,没见过(因为是错误的,所以没人这么写)

输出结果为

BaseClass::Display()
BaseClass::Display(int a)
BaseClass::Display(int a)

并且在编译是编译器发出强制将float转换为int的警告

首先效果肯定不是原有作者要的结果,然后我开始分析原因
根据Thinking in C++中对于vptr和vtable的解释,貌似我无法找到与这种现象有关的详细解释,也许是我没有发现。

随后我便开始自己解释:
既然所有的虚函数都存放在vtable中,那么指针通过vptr查找到vtable中虚函数时,应该也能找到“float”版的函数。但是他为什么就会去调用int版本呢?通过以往经验我查看了反汇编
发现调用int和调用float部分的汇编代码一模一样,我开始认为这一切的原因出在了编译过程中。可是我始终无法从vptr和vtable的角度出发进行解释这种现象。

最后在请教了高人之后,知道了一件很重要但始终被我误解的事(都是以前教条主义惹得祸,当时学的时候就应该追根溯源)。

当诸如ptrBaseClass->Display();话出现时,编译器是如何解释的呢?
1.因为指针是基类的,因此他将查看基类的所有函数,查看是否满足要求的。
2.如果查看到且查看到的函数发现是virtual的,则在翻译成汇编时翻译成:请到vtable中偏移地址为XXX的地方寻找代码。
3.如果不是virtual,则直接把基类的代码地址获得
4.如果没有在基类的函数中查找到相同的函数,则报出找不到的错误

发现没有,其实多态在C++中的实现还是根据基类的指针来查看函数是否合法。
做个实验,如果你在子类中新增一个函数,然后通过基类进行调用,发现该函数不存在,编译错误。
因此在编译时,函数具体代码区的查找是根据基类指针来的。
当遇到多态时,无非就是告诉调用者,请到具体对象实例中vptr所指向的vtable中所指向的地址去寻找代码,这个过程是运行时完成的。

综上所述,对于我个人而言更加明确了一点,不要在多态的时候去想重载,这是不可能的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值