所有的分析均针对|gcc version 4.3.4 [gcc-4_3-branch revision 152973] (SUSE Linux X86_64)|这一版本。
先从一个简单的类开始吧。如下,此简单类,非常简单,两个int成员,通过printf很容易了解到它的内存布局,本质就是一个C结构体,两个成员依次排列。
对象:|成员1 | 成员2 |
1: #include <cstdio>
2: class Class0
3: {
4: public:
5: int member1;
6: int member2;
7: };
8: int main()
9: {
10: Class0 c;
11: printf("object addr=0x%lx\nmember1 addr=0x%lx\nmember2 addr=0x%lx\n",
12: &c, &c.member1, &c.member2);
13: return 0;
14: }
# ./a.out
object addr=0x7fffea480d70
member1 addr=0x7fffea480d70 //类成员1
member2 addr=0x7fffea480d74 //类成员2
那么我们增加点复杂性,添加一个成员函数。
1: #include <cstdio>
2: class Class1
3: {
4: public:
5: int member1;
6: int member2;
7: void function1() { printf("Class1::function1"); }
8: };
9: int main()
10: {
11: Class1 c;
12: printf("object addr=0x%lx\n", &c);
13: printf("member1 addr=0x%lx\n", &c.member1);
14: printf("member1 addr=0x%lx\n", &c.member2);
15: printf("function1 addr=0x%lx\n", &Class1::function1);
16: return 0;
17: }
./a.out
object addr =0x7fff6805bf90
member1 addr=0x7fff6805bf90
member1 addr=0x7fff6805bf94
function1 addr=0x4006a0//成员函数地址在代码段。----这简直是废话,不在代码段没法玩啊。
好了搞清楚了成员函数的工作机制,我们再进一步分析,如下例子,有了继承,并且基类成员函数是一个虚函数。派生类重载了它。
1: #include <cstdio>
2: class Base
3: {
4: public:
5: virtual void function() { printf("Base::function1\n"); }
6: };
7: class Derived : public Base
8: {
9: public:
10: void function() { printf("Derived::function1\n"); }
11: };
12: int main()
13: {
14: printf("Base::function addr = 0x%lx\n", &Base::function);
15: printf("Derived::function addr = 0x%lx\n", &Derived::function);
16: Base* pb = new Derived();
17: pb->function();
18: return 0;
19: }
./a.out
Base::function addr = 0x1
Derived::function addr = 0x1
Derived::function1
1: Base* pb = new Derived();
2: pb->function();
1: #include <cstdio>
2: class Base
3: {
4: public:
5: virtual void function1() { printf("Base::function1\n"); }
6: virtual void function2() { printf("Base::function2\n"); }
7: };
8: int main()
9: {
10: printf("Base::function addr = 0x%lx\n", &Base::function1);
11: printf("Base::function addr = 0x%lx\n", &Base::function2);
12: Base* pb = new Base;
13: long* vtl = *(long**)pb;
14: printf("0x%lx\n", *(vtl));
15: printf("0x%lx\n", *(vtl + 1));
16: return 0;
17: }
# ./a.out
Base::function addr = 0x1
Base::function addr = 0x9
0x40082a
0x400812
# nm a.out | grep function
000000000040082a W _ZN4Base9function1Ev
0000000000400812 W _ZN4Base9function2Ev
# c++filt _ZN4Base9function1Ev
Base::function1()
# c++filt _ZN4Base9function2Ev
Base::function2()
1: class Base
2: {
3: public:
4: int b;
5: };
6: class Derived : public Base
7: {
8: public:
9: int d;
10: };
11: int main()
12: {
13: Derived d;
14: d.b = 2012;
15: Base* b = &d;
16: b->b = 2012;
17: }
1: #include <cstdio>
2: class Base
3: {
4: public:
5: int b;
6: };
7: class Derived : public Base
8: {
9: public:
10: int d;
11: };
12: int main()
13: {
14: Derived d;
15: printf("Derived = 0x%lx\n", &d);
16: printf("Derived.b = 0x%lx\n", &d.b);
17: printf("Derived.d = 0x%lx\n", &d.d);
18: return 0;
19: }
# ./a.out
Derived = 0x7fffece35bf0
Derived.b = 0x7fffece35bf0
Derived.d = 0x7fffece35bf4
1: #include <cstdio>
2: class Base1
3: {
4: public:
5: int b1;
6: };
7: class Base2
8: {
9: public:
10: int b2;
11: };
12: class Derived : public Base1, public Base2
13: {
14: public:
15: int d;
16: };
17: int main()
18: {
19: Derived d;
20: printf("Derived = 0x%lx\n", &d);
21: printf("Derived.b1 = 0x%lx\n", &d.b1);
22: printf("Derived.b2 = 0x%lx\n", &d.b2);
23: printf("Derived.d = 0x%lx\n", &d.d);
24: Base2* b2p = &d;
25: printf("Base2 pointer = 0x%lx\n", b2p);
26: return 0;
27: }
# ./a.out
Derived = 0x7fffedfe10e0
Derived.b1 = 0x7fffedfe10e0
Derived.b2 = 0x7fffedfe10e4
Derived.d = 0x7fffedfe10e8
Base2 pointer = 0x7fffedfe10e4
1: #include <cstdio>
2: class Base1
3: {
4: public:
5: int b1;
6: virtual void function1() { printf("Base1::function1\n"); }
7: };
8: class Base2
9: {
10: public:
11: int b2;
12: virtual void function2() { printf("Base2::function2\n"); }
13: };
14: class Derived : public Base1, public Base2
15: {
16: public:
17: int d;
18: void function1() { printf("Derived::function1\n"); }
19: void function2() { printf("Derived::function2\n"); }
20: };
21: int main()
22: {
23: Derived d;
24: printf("Derived = 0x%lx\n", &d);
25: printf("Derived.b1 = 0x%lx\n", &d.b1);
26: printf("Derived.b2 = 0x%lx\n", &d.b2);
27: printf("Derived.d = 0x%lx\n", &d.d);
28: Base2* b2p = &d;
29: printf("Base2 pointer = 0x%lx\n", b2p);
30: long* vtl = *(long**)b2p;
31: printf("0x%lx\n", *(vtl));
32: printf("0x%lx\n", *(vtl + 1));
33: vtl = *(long**)&d;
34: printf("0x%lx\n", *(vtl));
35: printf("0x%lx\n", *(vtl + 1));
36: return 0;
37: }
# ./a.out
Derived = 0x7fffa74ae400
Derived.b1 = 0x7fffa74ae408//b1没有放在最开始,因为第一个是虚表地址
Derived.b2 = 0x7fffa74ae418//b2没有放在b1后面,因为前边还有一个虚表地址
Derived.d = 0x7fffa74ae41c
Base2 pointer = 0x7fffa74ae410//base2在派生类中的起始位置,
0x4008aa//虚表2中存放的函数地址,gcc生成的中间函数
0x0//虚表2中存放的函数地址
0x4008c8//虚表1中存放的函数地址,function1
0x4008b0//虚表1中存放的函数地址,function2
# nm a.out |grep function
00000000004008e0 W _ZN5Base19function1Ev
00000000004008f8 W _ZN5Base29function2Ev
00000000004008c8 W _ZN7Derived9function1Ev
00000000004008b0 W _ZN7Derived9function2Ev
00000000004008aa W _ZThn16_N7Derived9function2Ev
# c++filt _ZN7Derived9function1Ev _ZN7Derived9function2Ev _ZThn16_N7Derived9function2Ev
Derived::function1()
Derived::function2()
non-virtual thunk to Derived::function2()
# objdump -d a.out | sed -n '/_ZThn16_N7Derived9function2Ev/,/00000/p'
00000000004008aa <_ZThn16_N7Derived9function2Ev>:
4008aa: 48 83 c7 f0 add $0xfffffffffffffff0,%rdi
4008ae: eb 00 jmp 4008b0 <_ZN7Derived9function2Ev>//中间函数跳转到了function2
00000000004008b0 <_ZN7Derived9function2Ev>: