• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            西城

            指尖代碼,手上年華

            聯(lián)系 聚合 管理
              20 Posts :: 0 Stories :: 62 Comments :: 0 Trackbacks
            這兩天看了兩篇關(guān)于C++虛函數(shù)表的文章,一篇是陳浩的,http://blog.csdn.net/haoel/article/details/1948051
            還有一篇是http://www.cnblogs.com/livingintruth/archive/2012/08/03/2620599.html。照著第二篇里的例子,
            在我的電腦上測試了一下,發(fā)現(xiàn)有不少自己不懂的地方,原作者也沒有講的太清楚,所以自己查了不少資料,將一些所得
            與大家分享。
            示例的代碼(有部分改動):

            #include <cstdio>
            #include <iostream>
            #include <typeinfo>
            using namespace std;
            class Point
            {
            public:
                Point()
                {
                    cout<<"Point constructor"<<endl;
                }
              
                virtual void func_hs()
                {
                    cout<<"Point::func_hs"<<endl;
                    printf("the address of this --func_hs:%p\n",&Point::func_hs);
                }
                virtual void  func_zzy()
                {
                    cout<<"Point::func_zzy"<<endl;
                    printf("the address of this --func_zzy:%p\n",&Point::func_zzy);
                }

                static void print()
                {
                    //相對地址,在虛表指針之后.0x4 0x8 0xc --------->point to member
                    printf("&Point::x=%p\n&Point::y=%p\n&Point::z=%p\n",
                           &Point::x,&Point::y,&Point::z);
                }

                void printThis()
                {
                    //float *
                    printf("&this->x=%p\n&this->y=%p\n&this->z=%p\n",
                           &this->x,&this->y,&this->z);
                }

                void printVt()
                {
                    printf("the address of object,this:%p\nthe address of vt:%p\n",
                           this,(void*)*(int*)this);
                }
                void callVtFuncs(int num=2)
                {
                    cout<<endl<<endl;
                    
                    typedef void (*Funp)(void);
                    
                    
                    for(int i=0;i<num;i++)
                    {
                        Funp funp=(Funp)*((int*)*(int*)this+i);
                        printf("%p\n",((int*)*(int*)this+i));
                        printf("Point::callVtFuncs=>address of this fun:%p\n",funp);
                        if(i==2||i==3)
                        {
                            continue;
                        }
                        funp();
                    }
                }

                void printVirtualFunAddress()
                {
                    cout<<endl<<endl;
                       printf("func_hs:%p\nfunc_zzy:%p\nfunc_zzzy:%p\n",
                          &Point::func_hs,&Point::func_zzy,
                          &Point::func_zzzy);
                    printf("%p\n",&Point::func_zzzy);
                    
                }
                virtual ~Point()
                {
                    // printf("%p\n",&Point::~Point);
                    cout<<"Point destructor"<<endl;
                }
                virtual void  func_zzzy()
                {
                    cout<<"Point::func_zzzy"<<endl;
                    printf("the address of this --func_zzzy:%p\n",&Point::func_zzzy);
                }

            protected:
                float x,y,z;
            };

                
            int main(int argc, char *argv[])
            {
                Point point;
                Point::print();
                point.printThis();
                point.printVt();
                point.callVtFuncs(5);
                point.printVirtualFunAddress();
                printf("sizeof func:%u\n",sizeof(&main));
                printf("%p\n",&main);
                
                printf("sizeof memfunc:%u\n",sizeof(&Point::printVirtualFunAddress));
                printf("%p\n",&Point::printVirtualFunAddress);
                
                printf("%p\n",&Point::func_zzzy);

                printf("sizeof virtmemfunc:%u\n",sizeof(&Point::func_zzzy));
                cout<<typeid(point).name()<<endl;
                
                return 0;
            }


            Point類里面包括virtual desctructor共有4個(gè)virtual functions.
            輸出結(jié)果如下:

            Point constructor
            &Point::x=0x4
            &Point::y=0x8
            &Point::z=0xc
            &this->x=0xbffff624
            &this->y=0xbffff628
            &this->z=0xbffff62c
            the address of object,this:0xbffff620
            the address of vt:0x8048fc0


            0x8048fc0
            Point::callVtFuncs=>address of this fun:0x8048a12
            Point::func_hs
            the address of this --func_hs:0x1
            0x8048fc4
            Point::callVtFuncs=>address of this fun:0x8048a64
            Point::func_zzy
            the address of this --func_zzy:0x5
            0x8048fc8
            Point::callVtFuncs=>address of this fun:0x8048c8e
            0x8048fcc
            Point::callVtFuncs=>address of this fun:0x8048cda
            0x8048fd0
            Point::callVtFuncs=>address of this fun:0x8048cf8
            Point::func_zzzy
            the address of this --func_zzzy:0x11


            func_hs:0x1
            func_zzy:(nil)
            func_zzzy:0x5
            0x11
            sizeof func:4
            0x804880c
            sizeof memfunc:8
            0x8048bdc
            0x11
            sizeof virtmemfunc:8

            data member那一塊比較簡單,沒有太大的問題。主要是virtual table這塊比較復(fù)雜 。第一個(gè)小問題就是
            順序的問題。實(shí)驗(yàn)證明在vtable中虛函數(shù)指針的順序是按照聲明時(shí)的順序放置的。不管virtual desctructor
            在哪個(gè)位置,都會占據(jù)兩個(gè)slot,即有兩個(gè)virtual desctructor.至于為什么會有兩個(gè),具體我也不是太清楚,
            后面在討論。
            我第一個(gè)有一個(gè)有疑問的地方就是關(guān)于typeinfo的問題。記得《Inside the c++ object model》中,Lippman
            提到的對象模型中,將typeinfo放置在vtable的第一個(gè)slot中,而根據(jù)上面例子來看,第一個(gè)slot就是所聲明
            的第一個(gè)virtual function.那么typeinfo存在何處?

            vtable 的首地址是0x8048fc0,而從0x8048fb0開始的內(nèi)存地址信息如下:

            (gdb) x/16a 0x8048fb0
            0x8048fb0:      0x75253a63      0xa     0x0     0x8048fdc <_ZTI5Point>
            0x8048fc0 <_ZTV5Point+8>:       0x8048a12 <Point::func_hs()>    0x8048a64 <Point::func_zzy()>   0x8048c8e <Point::~Point()>   0x8048cda <Point::~Point()>
            0x8048fd0 <_ZTV5Point+24>:      0x8048cf8 <Point::func_zzzy()>  0x696f5035      0x746e  0x804a4c8 <_ZTVN10__cxxabiv117__class_type_infoE@@CXXABI_1.3+8>
            0x8048fe0 <_ZTI5Point+4>:       0x8048fd4 <_ZTS5Point>  0x3b031b01      0x98    0x12

            可以看出,在vtable之前也有關(guān)于Point類的信息,0x8048fbc處的值為
            0x8048fdc <_ZTI5Point>,而0x8048fdc處的值為:0x804a4c8 <_ZTVN10__cxxabiv117__class_type_infoE@@CXXABI_1.3+8,可以猜測這是與類型有關(guān)的。


            輸出中我在typeid(point)那一行設(shè)了斷點(diǎn),進(jìn)入函數(shù)之后可以看到:

            (gdb) s
            std::type_info::name (this=0x8048fdc)
                at /usr/lib/gcc/i686-pc-linux-gnu/4.7.1/../../../../include/c++/4.7.1/typeinfo:102
            102         { return __name[0] == '*' ? __name + 1 : __name; }
            (gdb) n
            5Point

            point的類型信息為5Point,而type_info::name傳入的參數(shù)信息就是0x8048fdc.再看用
            -fdump-class-hierarchy輸出的關(guān)于Point的類型信息:


            Vtable for Point
            Point::_ZTV5Point: 7u entries
            0     (int (*)())0
            4     (int (*)())(& _ZTI5Point)
            8     (int (*)())Point::func_hs
            12    (int (*)())Point::func_zzy
            16    (int (*)())Point::~Point
            20    (int (*)())Point::~Point
            24    (int (*)())Point::func_zzzy

            Class Point
               size=16 align=4
               base size=16 base align=4
            Point (0xb60019a0) 0
                vptr=((& Point::_ZTV5Point) + 8u)


            這樣的結(jié)果已經(jīng)相當(dāng)清楚。在g++的實(shí)現(xiàn)中,真正的typeinfo信息放置在vtable之后,其位置是通過vtable之前的地址內(nèi)
            所包含的信息所指定。在整個(gè)關(guān)于Point類的這些信息里,起始位置為&Point::_ZTV5Point,其值為0x0,之后是關(guān)于類型
            信息,然后才是vtable的入口點(diǎn)。即 vptr=((& Point::_ZTV5Point) + 8u)。point類的this指針通過類型轉(zhuǎn)化后所解
            引用得到的值即使vptr.至于size=16很好理解,1個(gè)vptr+3個(gè)float.


            另一個(gè)詭異的信息就是point to member的輸出,單個(gè)輸出是,3個(gè)virtual function分別是0x1 0x5 0x11,而在
            printVirtualFunAddress中所輸出的信息卻很詭異,分別是0x1,nil,0x5,這就是為什么要在main函數(shù)中加入測試
            函數(shù)指針大小的原因,普通函數(shù)是4個(gè)字節(jié),而成員函數(shù)的確是8個(gè)字節(jié),不管是不是virtual function.所以在printf
            參數(shù)入棧的時(shí)候,要push兩次,將第二次push的結(jié)果當(dāng)作了第二個(gè)參數(shù)的值,以此后推。所以會有上面的結(jié)果。

            最后就是那兩個(gè)desctructor的問題,在上面通過函數(shù)指針調(diào)用函數(shù)的時(shí)候要將他們都略過去。如果將virtual去掉,
            那么vtable中就沒有desctructor。
            下面是objdump出來的結(jié)果:objdump -d a.out|grep PointD

             8048932:    e8 51 03 00 00           call   8048c88 <_ZN5PointD1Ev>
             8048944:    e8 3f 03 00 00           call   8048c88 <_ZN5PointD1Ev>
            08048c88 <_ZN5PointD1Ev>:
             8048cc5:    74 0b                    je     8048cd2 <_ZN5PointD1Ev+0x4a>
            08048cd4 <_ZN5PointD0Ev>:
             8048ce0:    e8 a3 ff ff ff           call   8048c88 <_ZN5PointD1Ev>


            兩個(gè)函數(shù)中,main調(diào)用的是<_ZN5PointD1Ev>,這就是我們上面聲明的那個(gè)virtual desctrucotr.而且在內(nèi)存布局中,
            <_ZN5PointD1Ev>的位置比較靠前,其在vtable中的位置也應(yīng)在<_ZN5PointD0Ev>之前,而<_ZN5PointD0Ev>

            08048cd4 <_ZN5PointD0Ev>:
             8048cd4:    55                       push   %ebp
             8048cd5:    89 e5                    mov    %esp,%ebp
             8048cd7:    83 ec 18                 sub    $0x18,%esp
             8048cda:    8b 45 08                 mov    0x8(%ebp),%eax
             8048cdd:    89 04 24                 mov    %eax,(%esp)
             8048ce0:    e8 a3 ff ff ff           call   8048c88 <_ZN5PointD1Ev>
             8048ce5:    8b 45 08                 mov    0x8(%ebp),%eax
             8048ce8:    89 04 24                 mov    %eax,(%esp)
             8048ceb:    e8 80 f9 ff ff           call   8048670 <_ZdlPv@plt>
             8048cf0:    c9                       leave  

            的工作好像也要調(diào)用<_ZN5PointD1Ev>,這個(gè)應(yīng)該是編譯器生成的,再往深處我也不甚清楚了。



            posted on 2012-08-10 11:17 西城 閱讀(2343) 評論(2)  編輯 收藏 引用 所屬分類: C/C++

            Feedback

            # re: 關(guān)于C++ 虛函數(shù)表的一些問題(g++) 2012-08-29 19:20 izualzhy
            static void print()
            {
            //相對地址,在虛表指針之后.0x4 0x8 0xc --------->point to member
            printf("&Point::x=%p\n&Point::y=%p\n&Point::z=%p\n",
            &Point::x,&Point::y,&Point::z);
            }

            靜態(tài)函數(shù)使用非靜態(tài)成員,為什么可以編譯通過啊?  回復(fù)  更多評論
              

            # re: 關(guān)于C++ 虛函數(shù)表的一些問題(g++) 2012-08-30 19:16 西城
            @izualzhy
            我又看了一下,確實(shí)是可以的。感覺&Point::x這種東西是與具體對象無關(guān)的,每個(gè)對象都一樣,所以我覺得從行為上來說應(yīng)該是與靜態(tài)成員一樣。  回復(fù)  更多評論
              

            色婷婷噜噜久久国产精品12p | 一级a性色生活片久久无| 久久精品国产亚洲5555| 亚洲国产成人久久综合野外| 中文字幕无码精品亚洲资源网久久 | 日韩人妻无码一区二区三区久久 | 久久综合亚洲鲁鲁五月天| 亚洲AV无码久久精品狠狠爱浪潮| 精品少妇人妻av无码久久| 国产一区二区精品久久凹凸| 久久久国产精华液| 久久久久久毛片免费看| 久久99国产综合精品| 久久这里只有精品首页| 精品久久久久久无码中文字幕| 精品无码久久久久国产动漫3d| 久久www免费人成看国产片| 久久久无码人妻精品无码| 久久久久亚洲国产| 久久精品夜色噜噜亚洲A∨| 精品久久777| 一本久久知道综合久久| 久久丫忘忧草产品| 蜜臀久久99精品久久久久久| 99久久精品无码一区二区毛片 | 久久精品成人欧美大片| 7777久久亚洲中文字幕| 伊人久久大香线蕉av不变影院| 亚洲国产精品嫩草影院久久| 曰曰摸天天摸人人看久久久| 97r久久精品国产99国产精| 午夜精品久久久久久久久| 亚洲AV无码成人网站久久精品大| 无码精品久久一区二区三区| 国产免费久久久久久无码| 久久99国产精品久久久| 久久久久久久综合日本亚洲| 国产成人久久精品一区二区三区 | 狼狼综合久久久久综合网| 亚洲精品午夜国产VA久久成人 | 国产午夜精品理论片久久 |