分类: C/C++
2009-04-30 19:37:15
C++类分布——实例分析
1#include
2 const char * global_text="aaaa";
3 class empty
4 {
5 virtual void sayHello(){printf("hello world\n");}
6
7
8 };
9 class Base
10 {
11 public:
12 Base(int i1,char i2,short i3,char i4):i(i1),j(i2),k(i3),l(i4){}
13 void print(){printf("hello world\n");};
14 virtual int work(int i){printf("this is base work %d\n",i);}
15 virtual void work2(){printf("this is base work2\n");}
16 virtual void work3(){printf("this is base work3\n");}
17 static int wangfei;
18 private:
19 int i;
20 char j;
21 short k;
22 char l;
23 };
24 int Base::wangfei=8888;
25 int global_data=7777;
26 class Derive:public Base
27 {
28 public:
29 Derive(int i1,char i2,short i3,char i4):Base(i1,i2,i3,i4){}
30 int work(int);
31 static void print(){printf("this is derive print\n");}
32 virtual void d_work1(){}
33 };
34 int Derive::work(int i)
35 {
36 printf("this is derive work %d\n",i);
37 return 10;
38
39
40 }
41 int main(int argc,char **argv)
42 {
43 char *test="hello world\n";
44 empty e;
45 printf("the size of e is %d\n",sizeof(e));
46 printf("sizeof the test is %d\n",sizeof(test));
47 Derive d(2,'c',3,'d');
48 Base b(10,'a',20,'b');
49 b=d;
50 printf("sizeof b is %d,sizeof d is %d\n",sizeof(b),sizeof(d));
51 b.work(1);
52 d.work(1);
53 Base *pointB=new Derive(4,'e',5,'f');
54 pointB->work(1);
55 printf("below will force to invoke\n");
56 int addr = *((int *)(*(int *)(pointB)));
57 printf("the addr is %p\n",addr);
58 int result=0;
59 int first=*((int *)(pointB)+1);
60 printf("the first is %d\n",first);
61 char second=*(char *)((int *)(pointB)+2);
62 printf("the second is %c\n",second);
63 short third=*(short *)((int *)(pointB)+3);
64 printf("the third is %d\n",third);
65
66 __asm__(
67 //"mov %0,%%ecx\n\tpush $2\n\tcall *%1"::"m"(pointB),"m"(addr)
68 "push $222\n\tpush %0\n\tcall *%1"::"m"(pointB),"m"(addr)
69 );
70 printf("after asm\n");
71 //call addr;
72
73 return 0;
74 }
这个例子的主要目的是为了说明各类型的成员在类中如何分配,vtable在类中的位置以及多态的内部如何实现。
假设输出为这个示例代码最后被编译成a.out的可执行文件。
1, objdump –C -t -j .data a.out|grep data
输出信息为:
08049d
08049d
08048aa4 w O .rodata 00000008 typeinfo for empty
08049d
08049d
08049d
08048aac w O .rodata 00000008 typeinfo for Base
08049d00 w .data 00000000 data_start
08049d
08048acd w O .rodata 00000008 typeinfo name for Derive
08049d
08049d
08048ab4 w O
.rodata
红色的三行说明了一些信息:
A,类的静态成员变量不是在类里面分配的,这也正好符号了我们一般的逻辑,因为我们很可能在没有一个类实例的情况下访问它这个成员(如果是public的);
B,全局变量也是在data段里面。
C,类的函数不是在类里分配的,而是联合类名和参数变成了另外一个函数名,调用的时候传相应的类的this指针进去。
D,拥有虚函数的类会多出四个字节用于存放指向vtable的指针。
E,当发生子类指针向父类指针赋值的时候,会发生截断,对应于父类的大小的区域被分给父类,其余被截掉,但是因为vtable的指针是放在类的开始的,所以,子类的虚函数指针被赋给了父类,于是调用的时候就调用了子类的函数,这就是虚函数的内部实现!!!
2, 修改第56行,如果改成:
int addr = *((int *)(*(int *)(pointB))+1(or 2 or 3));这样就可以分别调用第一第二个虚函数。
从这里可以看出,虚函数的位置在拥有虚函数的类的第一个位置。
3, 从59,61,63行可以看出,
类的数据的排列是按照它的申明顺序的。
4, 从.rodata可以看出,vtable的内容本身是放在rodata段里面的也就是说是不可修改的。
5, 如果把43行:
char *test="hello world\n";
然后*test=”good bye”;
你会看到编译的出错信息!
原因在于”hello world\n”以及所有这些待打印出来的字符串都是放在readonlydata 段的。
要修改一个只的区域的内容当然会错了。
但是test这个变量本身你是看不到的在符号表里面,因为它会被分配在堆里面,并且是 在运行时被分配的。
但是你能看到global_text的符号,它本身是被分配在data段里面的,但它所指向的内容也是在只读段里面的。
可以通过objdump –C –d –j .rodata a.out来查看所有只读段里面的内容。
注意我上面说的段是指section不对应操作系统里面的segment。
你可以用readelf –l a.out来查看具体的section 和 segment的对应关系。
一般rodata段都会被放在text segment.