分享
 
 
 

细谈C++多态性的“动”与“静”

王朝c/c++·作者佚名  2008-06-01
窄屏简体版  字體: |||超大  

在我们讨论多态的时候,先看看什么是硬编码和软编码:硬编码就是把代码写死了,导致弹性不足,降低了可扩展性,例如在代码里的

if...else...;switch...case...

这些代码通常都属于硬编码,项目中的这些代码多了,就相当于说明这个代码的灵活性、扩展性、弹性等等的少了。

所以,我们要尽量使用软编码,通俗点就是“别把话说死了,留点转弯的余地”。多态性就是这种软编码特性的反映,下面我们一起来研究一下多态性。

多态性是一种抽象,把事物的特征抽象出来,然后事物的具体形态我们就不关心了。

例如对工人这种事物来说,他的特征就是工作,至于是什么工人,他做什么工作,我们就不用关心了,只要我们以“工人.工作”这种方式去调用。那他就会为我们工作了。

那为什么我们不抽象出其他的特征,只抽象出工作这个特征呢?因为我们只对这个特征感爱好,他的什么吃饭、睡觉、如厕等的特性我们都不关心了。有了多态,我们就可以实现软编码了!

讲解了多态的概念之后,我们来看看多态的实现(C++的实现):

多态的实现是通过虚函数表(VTable),每个类假如有虚函数,那它就有一个虚函数表,所有的对象都共享这一个VTable。这个概念也叫做动态联编,还有静态联编,这些概念都是通过在程序执行的时候表现出来的性质来定的,我们下面会看看它的“动”和“静”究竟体现在哪里。

先看一段代码:

class C0

...{

public:

void Test()

...{

cout << "call C0 Test()。" << endl;

}

};

这个类没有虚函数,调用的时候就是静态调用。调用的代码如下:

// 静态编译(早绑定 early binding)

C0 *pO0;

C0 obj0;

pO0 = &obj0;

pO0->Test();

它的反汇编代码如下:

// 直接调用函数(已经知道地址)

00401432 mov ecx,dWord ptr [ebp-0Ch]

00401435 call @ILT+160(C0::Test) (004010a5)

下面看看带虚函数的类:

class C1

...{

public:

virtual void Test()

...{

cout << "call C1 Test()" << endl;

}

};

class C11 : public C1

...{

public:

void Test()

...{

cout << "call C11 Test()" << endl;

}

};

它的调用:

C11 obj11;

C1 *pObj1;

pObj1 = &obj11;

// 这里生成的汇编代码

// 0040144A lea edx,[ebp-14h] // 寻址找到pObj1

// 0040144D mov dword ptr [ebp-1Ch],edx

pObj1->Test();

// 这里生成的汇编代码

// 00401450 mov eax,dword ptr [ebp-1Ch] // 取得虚表地址

// 00401453 mov edx,dword ptr [eax]

// 00401455 mov esi,esp

// 00401457 mov ecx,dword ptr [ebp-1Ch] // 根据虚表的位置来取得Test()函数

// 0040145A call dword ptr [edx] // 调用Test()函数

根据上述的汇编代码,我们可以知道,在多态调用函数的时候,程序执行以下步骤:

1、寻址找到pObj1

2、由于C11重载了Test虚函数,所以*pObj1指向的就是C11的VTable的地址

3、调用pObj1->Test()时,程序通过Vptr(虚表的指针,对象的首地址),找到VTable,再根据偏移调用Test函数。

由于上述的多态调用过程是一个动态的过程(在运行时去“找”函数来调用),而不是编译完就直接把函数地址摆在那里了,所以被称作“动态联编”。

上面把多态的“动”和“静”的特点结合代码说了一遍,希望能说清楚了。

下面再验证一个类的虚表的问题,假如你对虚表已经很熟悉了,就不用再往下看了。

在很多书上都已经说明了C++的对象模型,这里只是做个验证。看看这段代码:

class C1

...{

public:

virtual void Test()

...{

cout << "call C1 Test()" << endl;

}

};

class C11 : public C1

...{

public:

void Test()

...{

cout << "call C11 Test()" << endl;

}

};

class C12 : public C1

...{

public:

void Test()

...{

cout << "call C12 Test()" << endl;

}

};

我们可以知道 Test() 是虚函数,从C1派生的类必定有自己的虚表。而且根据别的资料,虚表指针是放在对象的首地址的,我们下面就来验证一下:

// 验证首地址

C11 obj110;

C11 obj111;

printf("obj110 的地址:%x ", &obj110);

printf("obj111 的地址:%x ", &obj111);

printf("obj110 虚表的地址:%x ", *(&obj110));

printf("obj111 虚表的地址:%x ", *(&obj111));

结果是:

obj110 的地址:12ff7c

obj111 的地址:12ff78

obj110 虚表的地址:432098

obj111 虚表的地址:432098

由上面的结果我们可以验证:

1、一个类一个VTABLE,而不是一个对象一个VTABLE。

2、对象的首地址的内容就是VTABLE的地址。

总结一下:

C++的多态性包括其概念和实现,本文从编译器生成的代码来讨论C++多态特性,非凡说明了为什么多态特性被称为“动态联编”,它和“静态联编”有什么不同,它们的“动”与“静”体现在哪里。另外还对对象的虚表做了些验证。好了,希望本文能对你熟悉C++的多态性有一定的帮助!

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有