• 【转】C++类的实例分布


    原文:http://blog.chinaunix.net/u2/67984/showart_1915540.html

    #include <stdio.h>
     const char * global_text="aaaa";
     class empty
     {
     virtual void sayHello(){printf("hello world ");}
     };
     class Base
     {
     public:
     Base(int i1,char i2,short i3,char i4):i(i1),j(i2),k(i3),l(i4){}
     void print(){printf("hello world ");};
     virtual int work(int i){printf("this is base work %d ",i);}
     virtual void work2(){printf("this is base work2 ");}
     virtual void work3(){printf("this is base work3 ");}
     static int wangfei;
     private:
     int i;
     char j;
     short k;
     char l;
     };
     int Base::wangfei=8888;
     int global_data=7777;
     class Derive:public Base
     {
     public:
     Derive(int i1,char i2,short i3,char i4):Base(i1,i2,i3,i4){}
     int work(int);
     static void print(){printf("this is derive print ");}
     virtual void d_work1(){}
     };
     int Derive::work(int i)
     {
         printf("this is derive work %d ",i);
         return 10;
     }
     int main(int argc,char **argv)
     {
     char *test="hello world ";
     empty e;
     printf("the size of e is %d ",sizeof(e));
     printf("sizeof the test is %d ",sizeof(test));
     Derive d(2,'c',3,'d');
     Base b(10,'a',20,'b');
     b=d;
     printf("sizeof b is %d,sizeof d is %d ",sizeof(b),sizeof(d));
     b.work(1);
     d.work(1);
     Base *pointB=new Derive(4,'e',5,'f');
     pointB->work(1);
     printf("below will force to invoke ");
     int addr = *((int *)(*(int *)(pointB)));
     printf("the addr is %p ",addr);
     int result=0;
     int first=*((int *)(pointB)+1);
     printf("the first is %d ",first);
     char second=*(char *)((int *)(pointB)+2);
     printf("the second is %c ",second);
     short third=*(short *)((int *)(pointB)+3);
     printf("the third is %d ",third);
     __asm__(
             //"mov %0,%%ecx push $2 call *%1"::"m"(pointB),"m"(addr)

             "push $222 push %0 call *%1"::"m"(pointB),"m"(addr)
     );
     printf("after asm ");
         //call addr;

     return 0;
     }


    这个例子的主要目的是为了说明各类型的成员在类中如何分配,vtable在类中的位置以及多态的内部如何实现。

    二:分析:

    假设输出为这个示例代码最后被编译成a.out的可执行文件。

    1,  objdump –C -t -j .data a.out|grep data

    输出信息为:

    08048940 l    d  .rodata        00000000

    08049d00 l    d  .data  00000000

    08049d08 l     O .data  00000000              p.0

    08048aa4  w    O .rodata        00000008              typeinfo for empty

    08048940 g     O .rodata        00000004              _fp_hw

    08049d10 g     O .data  00000004              Base::wangfei

    08049d04 g     O .data  00000000              .hidden __dso_handle

    08049d14 g     O .data  00000004              global_data

    08048a68  w    O .rodata        00000018              vtable for Derive

    08048aac  w    O .rodata        00000008              typeinfo for Base

    08048a80  w    O .rodata        00000014              vtable for Base

    08049d00  w      .data  00000000              data_start

    08049d0c g     O .data  00000004              global_text

    08048ac0  w    O .rodata        00000007              typeinfo name for empty

    08048acd  w    O .rodata        00000008              typeinfo name for Derive

    08048ac7  w    O .rodata        00000006              typeinfo name for Base

    08049d18 g       *ABS*  00000000              _edata

    08048a98  w    O .rodata        0000000c              vtable for empty

    08048944 g     O .rodata        00000004              _IO_stdin_used

    08049d00 g       .data  00000000              __data_start

    08048ab4  w    O .rodata        0000000c              typeinfo for Derive

     

    红色的三行说明了一些信息:

    A,类的静态成员变量不是在类里面分配的,这也正好符号了我们一般的逻辑,因为我们很可能在没有一个类实例的情况下访问它这个成员(如果是public的);

    B,全局变量也是在data段里面。

    C,类的函数不是在类里分配的,而是联合类名和参数变成了另外一个函数名,调用的时候传相应的类的this指针进去。

    D,拥有虚函数的类会多出四个字节用于存放指向vtable的指针。

    E,当发生子类指针向父类指针赋值的时候,会发生截断,对应于父类的大小的区域被分给父类,其余被截掉,但是因为vtable的指针是放在类的开始的,所以,子类的虚函数指针被赋给了父类,于是调用的时候就调用了子类的函数,这就是虚函数的内部实现!!!

     

    2,  修改第56行,如果改成:

    int addr = *((int *)(*(int *)(pointB))+1or 2 or 3);这样就可以分别调用第一第二个虚函数。

    从这里可以看出,虚函数的位置在拥有虚函数的类的第一个位置。

    3,  596163行可以看出,

    类的数据的排列是按照它的申明顺序的。

    4,  .rodata可以看出,vtable的内容本身是放在rodata段里面的也就是说是不可修改的。

    5,  如果把43行:

    char *test="hello world ";

    然后*test=”good bye”;

    你会看到编译的出错信息!

    原因在于”hello world ”以及所有这些待打印出来的字符串都是放在readonlydata 段的。

    要修改一个只的区域的内容当然会错了。

    但是test这个变量本身你是看不到的在符号表里面,因为它会被分配在堆里面,并且是 在运行时被分配的。

    但是你能看到global_text的符号,它本身是被分配在data段里面的,但它所指向的内容也是在只读段里面的。

    可以通过objdump –C –d –j .rodata a.out来查看所有只读段里面的内容。

    注意我上面说的段是指section不对应操作系统里面的segment

    你可以用readelf –l a.out来查看具体的section  segment的对应关系。

    一般rodata段都会被放在text segment.

    阅读(731) | 评论(0) | 转发(0) |
    给主人留下些什么吧!~~
    评论热议
  • 相关阅读:
    17款加速效率的CSS工具
    我为什么向后端工程师推荐Node.js
    八款开源 Android 游戏引擎 (巨好的资源)
    50个必备的实用jQuery代码段
    $.getJSON()跨域请求
    javascript獲得服務器端控件的ID
    (转)8款在线CSS优化工具/组织和压缩CSS
    10 个文件和文档的比较工具
    40个有创意的jQuery图片和内容滑动及弹出插件收藏集之四
    MBP换硬盘的过程
  • 原文地址:https://www.cnblogs.com/black/p/5171944.html
Copyright © 2020-2023  润新知