版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1344553
类的多继承大致可以分为两种情况.一种是无共同基类的.一种是有共同基类的.
先看一下第一种情况:
1 #include <stdio.h>
2 class xuzhina_dump_c06_s5_mother
3 {
4 private:
5 int m_age;
6 int m_beauty;
7 public:
8 virtual void print()
9 {
10 printf( "mother\n" );
11 }
12
13 virtual void setBeauty( int age, int beauty )
14 {
15 m_age = age - 5;
16 m_beauty = beauty;
17 }
18 };
19
20 class xuzhina_dump_c06_s5_father
21 {
22 private:
23 int m_strong;
24 int m_age;
25 public:
26 virtual void print()
27 {
28 printf( "father\n" );
29 }
30 virtual void setStrong( int strong, int age )
31 {
32 m_strong = strong;
33 m_age = age;
34 }
35 };
36
37 class xuzhina_dump_c06_s5_child: public xuzhina_dump_c06_s5_father,
38 public xuzhina_dump_c06_s5_mother
39
40 {
41 private:
42 bool m_newMind;
43 public:
44 virtual void print()
45 {
46 printf( "child\n" );
47 }
48
49 virtual void setGender( bool gender )
50 {
51 m_newMind = true;
52 if ( gender )
53 {
54 setBeauty( 10, 10 );
55 }
56 else
57 {
58 setStrong( 20,20 );
59 }
60 }
61 };
62
63 int main()
64 {
65 xuzhina_dump_c06_s5_child* child = new xuzhina_dump_c06_s5_child;
66 child->setGender( false );
67 child->print();
68
69 xuzhina_dump_c06_s5_father* f = child;
70 f->print();
71
72 xuzhina_dump_c06_s5_mother* m = child;
73 m->print();
74
75 return 0;
76 }
看一下main函数的汇编:
(gdb) disassemble main
Dump of assembler code for function main:
0x080485b0 <+0>: push %ebp
0x080485b1 <+1>: mov %esp,%ebp
0x080485b3 <+3>: push %ebx
0x080485b4 <+4>: and $0xfffffff0,%esp
0x080485b7 <+7>: sub $0x20,%esp
0x080485ba <+10>: movl $0x1c,(%esp)
0x080485c1 <+17>: call 0x8048490 <_Znwj@plt>
0x080485c6 <+22>: mov %eax,%ebx
0x080485c8 <+24>: mov %ebx,(%esp)
0x080485cb <+27>: call 0x8048746 <_ZN25xuzhina_dump_c06_s5_childC2Ev>
0x080485d0 <+32>: mov %ebx,0x1c(%esp)
0x080485d4 <+36>: mov 0x1c(%esp),%eax
0x080485d8 <+40>: mov (%eax),%eax
0x080485da <+42>: add $0x8,%eax
0x080485dd <+45>: mov (%eax),%eax
0x080485df <+47>: movl $0x0,0x4(%esp)
0x080485e7 <+55>: mov 0x1c(%esp),%edx
0x080485eb <+59>: mov %edx,(%esp)
0x080485ee <+62>: call *%eax
0x080485f0 <+64>: mov 0x1c(%esp),%eax
0x080485f4 <+68>: mov (%eax),%eax
0x080485f6 <+70>: mov (%eax),%eax
0x080485f8 <+72>: mov 0x1c(%esp),%edx
0x080485fc <+76>: mov %edx,(%esp)
0x080485ff <+79>: call *%eax
0x08048601 <+81>: mov 0x1c(%esp),%eax
0x08048605 <+85>: mov %eax,0x18(%esp)
0x08048609 <+89>: mov 0x18(%esp),%eax
0x0804860d <+93>: mov (%eax),%eax
0x0804860f <+95>: mov (%eax),%eax
0x08048611 <+97>: mov 0x18(%esp),%edx
0x08048615 <+101>: mov %edx,(%esp)
0x08048618 <+104>: call *%eax
0x0804861a <+106>: cmpl $0x0,0x1c(%esp)
0x0804861f <+111>: je 0x804862a <main+122>
0x08048621 <+113>: mov 0x1c(%esp),%eax
0x08048625 <+117>: add $0xc,%eax
0x08048628 <+120>: jmp 0x804862f <main+127>
0x0804862a <+122>: mov $0x0,%eax
0x0804862f <+127>: mov %eax,0x14(%esp)
0x08048633 <+131>: mov 0x14(%esp),%eax
0x08048637 <+135>: mov (%eax),%eax
0x08048639 <+137>: mov (%eax),%eax
0x0804863b <+139>: mov 0x14(%esp),%edx
0x0804863f <+143>: mov %edx,(%esp)
0x08048642 <+146>: call *%eax
0x08048644 <+148>: mov $0x0,%eax
0x08048649 <+153>: mov -0x4(%ebp),%ebx
0x0804864c <+156>: leave
0x0804864d <+157>: ret
End of assembler dump.
由上面的汇编,可以看到,对象child的地址存放在esp+0x1c
而下面这几条指令
0x08048601 <+81>: mov 0x1c(%esp),%eax
0x08048605 <+85>: mov %eax,0x18(%esp)
0x08048609 <+89>: mov 0x18(%esp),%eax
0x0804860d <+93>: mov (%eax),%eax
0x0804860f <+95>: mov (%eax),%eax
0x08048611 <+97>: mov 0x18(%esp),%edx
0x08048615 <+101>: mov %edx,(%esp)
0x08048618 <+104>: call *%eax
0x08048621 <+113>: mov 0x1c(%esp),%eax
0x08048625 <+117>: add $0xc,%eax
0x0804862f <+127>: mov %eax,0x14(%esp)
0x08048633 <+131>: mov 0x14(%esp),%eax
0x08048637 <+135>: mov (%eax),%eax
0x08048639 <+137>: mov (%eax),%eax
0x0804863b <+139>: mov 0x14(%esp),%edx
0x0804863f <+143>: mov %edx,(%esp)
0x08048642 <+146>: call *%eax
由于是和代码
69 xuzhina_dump_c06_s5_father* f = child;
70 f->print();
71
72 xuzhina_dump_c06_s5_mother* m = child;
73 m->print();
相对应的.
从
0x08048625 <+117>: add $0xc,%eax
可以看到非常奇怪的现象,当类xuzhina_dump_c06_s5_child的指针转换成类xuzhina_dump_c06_s5_mother的指针时,并不是直接赋值过去,而是比预料的地址加了一个偏移值.
那么这个偏移值0xC是怎么来的呢?根据上一节的经验,看一下构造函数的汇编:
(gdb) disassemble _ZN25xuzhina_dump_c06_s5_childC2Ev
Dump of assembler code for function _ZN25xuzhina_dump_c06_s5_childC2Ev:
0x08048746 <+0>: push %ebp
0x08048747 <+1>: mov %esp,%ebp
0x08048749 <+3>: sub $0x18,%esp
0x0804874c <+6>: mov 0x8(%ebp),%eax
0x0804874f <+9>: mov %eax,(%esp)
0x08048752 <+12>: call 0x804872a <_ZN26xuzhina_dump_c06_s5_fatherC2Ev>
0x08048757 <+17>: mov 0x8(%ebp),%eax
0x0804875a <+20>: add $0xc,%eax
0x0804875d <+23>: mov %eax,(%esp)
0x08048760 <+26>: call 0x8048738 <_ZN26xuzhina_dump_c06_s5_motherC2Ev>
0x08048765 <+31>: mov 0x8(%ebp),%eax
0x08048768 <+34>: movl $0x8048848,(%eax)
0x0804876e <+40>: mov 0x8(%ebp),%eax
0x08048771 <+43>: movl $0x804885c,0xc(%eax)
0x08048778 <+50>: leave
0x08048779 <+51>: ret
End of assembler dump.
由上面,类xuzhina_dump_c06_s5_child的对象实际就是两个类xuzhina_dump_c06_s5_father,xuzhina_dump_c06_s5_mother的对象组合,偏移值0xC刚好是类xuzhina_dump_c06_s5_father的大小(虚函数表指针+两个成员变量m_strong,m_age).
且由0x08048768, 0x08048771两条指令来看,类xuzhina_dump_c06_s5_child存放两个虚函数表,分别存放在0x8048848, 0x804885c.
看一下这两个表分别存放着什么东西.
第一个:
(gdb) x /4x 0x8048848
0x8048848 <_ZTV25xuzhina_dump_c06_s5_child+8>: 0x080486a8 0x08048690 0x080486c4 0xfffffff4
(gdb) info symbol 0x080486a8
xuzhina_dump_c06_s5_child::print() in section .text of /home/buckxu/work/6/5/xuzhina_dump_c6_s5
(gdb) info symbol 0x08048690
xuzhina_dump_c06_s5_father::setStrong(int, int) in section .text of /home/buckxu/work/6/5/xuzhina_dump_c6_s5
(gdb) info symbol 0x080486c4
xuzhina_dump_c06_s5_child::setGender(bool) in section .text of /home/buckxu/work/6/5/xuzhina_dump_c6_s5
第二个:
(gdb) x /4x 0x804885c
0x804885c <_ZTV25xuzhina_dump_c06_s5_child+28>: 0x080486bc 0x08048662 0x00000000 0x00000000
(gdb) info symbol 0x080486bc
non-virtual thunk to xuzhina_dump_c06_s5_child::print() in section .text of /home/buckxu/work/6/5/xuzhina_dump_c6_s5
(gdb) info symbol 0x08048662
xuzhina_dump_c06_s5_mother::setBeauty(int, int) in section .text of /home/buckxu/work/6/5/xuzhina_dump_c6_s5
(gdb) disassemble 0x080486bc
Dump of assembler code for function _ZThn12_N25xuzhina_dump_c06_s5_child5printEv:
0x080486bc <+0>: subl $0xc,0x4(%esp)
0x080486c1 <+5>: jmp 0x80486a8 <_ZN25xuzhina_dump_c06_s5_child5printEv>
End of assembler dump.
从上面看,类xuzhina_dump_c06_s5_child的对象内存分布如下:
由上可知,在多继承情况下,有这样的结论:
1. 有多个虚函数表.
2. 类对象的大小就是等于各个基类的大小(虚函数表指针+成员变量)与自身成员变量之和.
3. 各个基类在子类里的”隐含对象”顺序是按照继承顺序来排列,和基类的声明/定义顺序无关.
4. 当子类对象指针转换成基类指针,实际上是把子类对象的对应基类”隐含对象”地址赋值给基类指针.
对于有共同基类的多继承,也可以按照上面思路来探索.对于基类和子类有同名虚函数,也可以这样探索.