一雨田的专栏

伟人将复杂的事情变简单,小人将简单的事情变复杂

用户操作
[即时聊天] [发私信] [加为好友]
一雨田ID:dylgsy
91677次访问,排名1090,好友2人,关注者13人。
一雨田
dylgsy的文章
原创 41 篇
翻译 1 篇
转载 6 篇
评论 292 篇
最近评论
heray818:我的邮箱是:heray818@126.com
heray818:你好 能否也传我一份 谢谢了啊
救援隊募集:アダルトエロ不倫
モテ度審査員:童貞セフレナンパ
temp:很好
文章分类
收藏
    相册
    好友Blog
    圈内朋友
    sankt的专栏
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 细谈C++多态性的"动"与"静"收藏

    新一篇: 身体器官工作时间表,要注意作息!! | 旧一篇: 十分钟文档化你的C++代码——DoxyGen

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


    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++的多态性有一定的帮助!谢谢!

    发表于 @ 2006年08月08日 14:30:00|评论(loading...)|编辑

    新一篇: 身体器官工作时间表,要注意作息!! | 旧一篇: 十分钟文档化你的C++代码——DoxyGen

    评论:没有评论。

    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © 一雨田