『coredump問題原理探究』Linux 86版6.6節単継承
6761 ワード
C++では,クラスの多態は虚関数によって継承される.では、単一継承では、クラスのメンバー変数と虚関数の分布はどうなっているのでしょうか.
例を見てみましょう.
アセンブリを見てください.
前節から分かるように,虚関数テーブルとメンバ変数のメモリレイアウトを見るには,コンストラクション関数のアセンブリを直接見ればよい.
クラスxuzhina_が表示されます.dump_c06_s4_derivedのコンストラクション関数、ベースクラスxuzhina_を呼び出すdump_c06_s4_baseコンストラクション関数(すなわち_ZN 24 xuzhina_dump_c 06_s 4_baseC 2 Eve)を作成し、虚関数テーブルを設定して自己変数を初期化します.ここで、2つの疑問があります.
1.クラスxuzhina_dump_c06_s4_derivedのメンバー変数はthis+4の位置ではなくthis+8の位置から始まる.では、this+4には何が置いてありますか?
2.虚関数テーブルポインタ0 x 80487 c 8が置かれているのはクラスxuzhina_dump_c06_s4_derivedの虚関数テーブルか、ベースクラスxuzhina_かdump_c06_s4_ベースの虚関数テーブル?
この2つの質問に答える前に、ベースクラスxuzhinaを見てみましょう.dump_c06_s4_ベースのコンストラクション関数:
ベースクラスの構造関数から分かるように、上の2つの疑問の答えは以下の通りです.
1.this+4が置かれている場所はm_a,初期化は1.
2.アドレス0 x 80487 e 0はベースクラスの虚関数テーブルである.アドレス0 x 80487 c 8にはサブクラスの虚関数テーブルが置かれている.
この2つの虚関数テーブルポインタの内容を見てみましょう.
ベースクラス虚関数テーブルポインタ:
サブクラス虚関数テーブルポインタ:
つまりサブクラスxuzhina_dump_c06_s4_derivedのオブジェクト構造は次の手順に従います.
サブクラスの虚関数テーブルの分布規則は、次のように表示されます.
1.ベースクラスの虚関数を再ロードし、ベースクラスの虚関数の宣言順序に従って並べ替えます.サブクラスの宣言順序とは関係ありません.
2.サブクラス独自の虚関数は、虚関数の宣言順に並び、重荷重虚関数の後に追加されます.
例を見てみましょう.
1 #include <stdio.h>
2 class xuzhina_dump_c06_s4_base
3 {
4 private:
5 int m_a;
6 public:
7 xuzhina_dump_c06_s4_base() { m_a = 1; }
8 virtual void inc()
9 {
10 m_a++;
11 }
12 virtual void print()
13 {
14 printf( "m_a:%d
", m_a );
15 }
16 };
17
18 class xuzhina_dump_c06_s4_derived: public xuzhina_dump_c06_s4_base
19 {
20 private:
21 int m_b;
22 int m_c;
23 public:
24 xuzhina_dump_c06_s4_derived()
25 {
26 m_b = 0;
27 m_c = 2*m_b;
28 }
29 virtual void mul()
30 {
31 m_c *= m_b;
32 }
33
34 virtual void print()
35 {
36 printf( "m_b:%d, m_c:%d
", m_b, m_c );
37 }
38 virtual void dec()
39 {
40 m_c -= m_b;
41 }
42
43 virtual void inc()
44 {
45 m_b++;
46 m_c += m_b;
47 }
48 };
49
50 int main()
51 {
52 xuzhina_dump_c06_s4_base* p = new xuzhina_dump_c06_s4_derived;
53 if ( p != NULL )
54 {
55 p->inc();
56 p->print();
57 }
58
59 return 0;
60 }
アセンブリを見てください.
(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 $0x10,(%esp)
0x080485c1 <+17>: call 0x80484a0 <_Znwj@plt>
0x080485c6 <+22>: mov %eax,%ebx
0x080485c8 <+24>: mov %ebx,(%esp)
0x080485cb <+27>: call 0x8048654 <_ZN27xuzhina_dump_c06_s4_derivedC2Ev>
0x080485d0 <+32>: mov %ebx,0x1c(%esp)
0x080485d4 <+36>: cmpl $0x0,0x1c(%esp)
0x080485d9 <+41>: je 0x8048600 <main+80>
0x080485db <+43>: mov 0x1c(%esp),%eax
0x080485df <+47>: mov (%eax),%eax
0x080485e1 <+49>: mov (%eax),%eax
0x080485e3 <+51>: mov 0x1c(%esp),%edx
0x080485e7 <+55>: mov %edx,(%esp)
0x080485ea <+58>: call *%eax
0x080485ec <+60>: mov 0x1c(%esp),%eax
0x080485f0 <+64>: mov (%eax),%eax
0x080485f2 <+66>: add $0x4,%eax
0x080485f5 <+69>: mov (%eax),%eax
0x080485f7 <+71>: mov 0x1c(%esp),%edx
0x080485fb <+75>: mov %edx,(%esp)
0x080485fe <+78>: call *%eax
0x08048600 <+80>: mov $0x0,%eax
0x08048605 <+85>: mov -0x4(%ebp),%ebx
0x08048608 <+88>: leave
0x08048609 <+89>: ret
End of assembler dump.
前節から分かるように,虚関数テーブルとメンバ変数のメモリレイアウトを見るには,コンストラクション関数のアセンブリを直接見ればよい.
(gdb) disassemble _ZN27xuzhina_dump_c06_s4_derivedC2Ev
Dump of assembler code for function _ZN27xuzhina_dump_c06_s4_derivedC2Ev:
0x08048654 <+0>: push %ebp
0x08048655 <+1>: mov %esp,%ebp
0x08048657 <+3>: sub $0x18,%esp
0x0804865a <+6>: mov 0x8(%ebp),%eax
0x0804865d <+9>: mov %eax,(%esp)
0x08048660 <+12>: call 0x804860a <_ZN24xuzhina_dump_c06_s4_baseC2Ev>
0x08048665 <+17>: mov 0x8(%ebp),%eax
0x08048668 <+20>: movl $0x80487c8,(%eax)
0x0804866e <+26>: mov 0x8(%ebp),%eax
0x08048671 <+29>: movl $0x0,0x8(%eax)
0x08048678 <+36>: mov 0x8(%ebp),%eax
0x0804867b <+39>: mov 0x8(%eax),%eax
0x0804867e <+42>: lea (%eax,%eax,1),%edx
0x08048681 <+45>: mov 0x8(%ebp),%eax
0x08048684 <+48>: mov %edx,0xc(%eax)
0x08048687 <+51>: leave
0x08048688 <+52>: ret
End of assembler dump.
クラスxuzhina_が表示されます.dump_c06_s4_derivedのコンストラクション関数、ベースクラスxuzhina_を呼び出すdump_c06_s4_baseコンストラクション関数(すなわち_ZN 24 xuzhina_dump_c 06_s 4_baseC 2 Eve)を作成し、虚関数テーブルを設定して自己変数を初期化します.ここで、2つの疑問があります.
1.クラスxuzhina_dump_c06_s4_derivedのメンバー変数はthis+4の位置ではなくthis+8の位置から始まる.では、this+4には何が置いてありますか?
2.虚関数テーブルポインタ0 x 80487 c 8が置かれているのはクラスxuzhina_dump_c06_s4_derivedの虚関数テーブルか、ベースクラスxuzhina_かdump_c06_s4_ベースの虚関数テーブル?
この2つの質問に答える前に、ベースクラスxuzhinaを見てみましょう.dump_c06_s4_ベースのコンストラクション関数:
(gdb) disassemble _ZN24xuzhina_dump_c06_s4_baseC2Ev
Dump of assembler code for function _ZN24xuzhina_dump_c06_s4_baseC2Ev:
0x0804860a <+0>: push %ebp
0x0804860b <+1>: mov %esp,%ebp
0x0804860d <+3>: mov 0x8(%ebp),%eax
0x08048610 <+6>: movl $0x80487e0,(%eax)
0x08048616 <+12>: mov 0x8(%ebp),%eax
0x08048619 <+15>: movl $0x1,0x4(%eax)
0x08048620 <+22>: pop %ebp
0x08048621 <+23>: ret
End of assembler dump.
ベースクラスの構造関数から分かるように、上の2つの疑問の答えは以下の通りです.
1.this+4が置かれている場所はm_a,初期化は1.
2.アドレス0 x 80487 e 0はベースクラスの虚関数テーブルである.アドレス0 x 80487 c 8にはサブクラスの虚関数テーブルが置かれている.
この2つの虚関数テーブルポインタの内容を見てみましょう.
ベースクラス虚関数テーブルポインタ:
(gdb) x /4wx 0x80487e0
0x80487e0 <_ZTV24xuzhina_dump_c06_s4_base+8>: 0x08048622 0x08048636 0x75783732 0x6e69687a
(gdb) info symbol 0x08048622
xuzhina_dump_c06_s4_base::inc() in section .text
(gdb) info symbol 0x08048636
xuzhina_dump_c06_s4_base::print() in section .text
サブクラス虚関数テーブルポインタ:
(gdb) x /8wx 0x80487c8
0x80487c8 <_ZTV27xuzhina_dump_c06_s4_derived+8>: 0x080486e6 0x080486a4 0x0804868a 0x080486cc
0x80487d8 <_ZTV24xuzhina_dump_c06_s4_base>: 0x00000000 0x08048830 0x08048622 0x08048636
(gdb) info symbol 0x080486e6
xuzhina_dump_c06_s4_derived::inc() in section .text
(gdb) info symbol 0x080486a4
xuzhina_dump_c06_s4_derived::print() in section .text
(gdb) info symbol 0x0804868a
xuzhina_dump_c06_s4_derived::mul() in section .text
(gdb) info symbol 0x080486cc
xuzhina_dump_c06_s4_derived::dec() in section .text
つまりサブクラスxuzhina_dump_c06_s4_derivedのオブジェクト構造は次の手順に従います.
サブクラスの虚関数テーブルの分布規則は、次のように表示されます.
1.ベースクラスの虚関数を再ロードし、ベースクラスの虚関数の宣言順序に従って並べ替えます.サブクラスの宣言順序とは関係ありません.
2.サブクラス独自の虚関数は、虚関数の宣言順に並び、重荷重虚関数の後に追加されます.