C++オブジェクトメモリレイアウト:シングル継承、マルチ継承、ダミー継承
19844 ワード
0.はじめに
本明細書では、visual studio 2013によって作成されたクラスインスタンスオブジェクトのメモリ分布について説明する.理解したい場合は、メモリ分布のアドレスをよく見てください.
1.簡単な例
1.1メモリの分散
アドレス
変数名
値
0x0093f9a0
instance
-
0x0093f9a0
a
0x00000001
0x0093f9a4
b,c
0xcccc0302(-859045118)
1.2説明
instanceはaのアドレスと同じです
1.3テストコード
2.単一継承
2.1メモリの分散
アドレス
変数名
値
0x00b8f7c0
d
-
0x00b8f7c0
_vfptr虚関数テーブルアドレス
-
0x00b8f7c4
Base::a
0x00000001
0x00b8f7c8
Base::b,Base::c
0xcccc0302(-859045118)
0x00b8f7cc
Derived::d
0x00000001
2.2説明 dと_vfptrのアドレスは同じで、説明対象の開始位置格納は虚函数表アドレス である.プライベート継承でもプライベート変数でも2.3のテストコードで に直接アクセスできます.単一継承は1つの虚関数テーブルのみであり、虚関数はテーブルの に順次配置される.
2.3テストコード
3.マルチ継承
3.1メモリ分布
アドレス
変数名
値
0x0116f8b8
&d,b1p , Base1::_vfptr虚関数テーブルアドレス
-
0x0116f8bc
Base1::a
0x00000001
0x0116f8c0
Base1::b,Base1::c
0xcccc0302(-859045118)
0x0116f8c4
b2p , Base2::_vfptr虚関数テーブルアドレス
-
0x0116f8c8
Base2::a
0x00000001
0x0116f8cc
Base2::b,Base2::c
0xcccc0302(-859045118)
0x0116f8d0
Derived::d
0x00000001
3.2説明は、派生クラスオブジェクトにベースクラスオブジェクトが含まれていると考えることができ、ベースクラスオブジェクトは、派生クラスオブジェクトに継承順に配置する .関数書き換えの場合、単一継承がベースクラス関数の虚関数書き換え をそれぞれ書き換えるものと見なすことができる.継承ごとにダミーテーブル が作成されます.アップシフト実質的にオブジェクトオフセット量の変化、すなわちdynamic_castの役割.見てください:b 1 p,b 2 pのメモリにおける分布 3.3テストコード
4.ダミー継承
4.1メモリ分布
アドレス
変数名
値
0x012ffc30
dp ,b1p , Base1::_vfptr虚関数テーブルアドレス
指向Base 1::f 3()
0x012ffc34
指向するメモリは現在のアドレスからベースへのオフセット量を格納している.
[0xfffffffc,0x00000018] , 0x012ffc34+0x00000018==0x012ffc4c
0x012ffc38
Base1::b
0x00000002
0x012ffc3c
b2p , Base2::_vfptr虚関数テーブルアドレス
指向Base 2::f 3()
0x012ffc40
指向するメモリには、現在のアドレスからBaseへのオフセットが格納されています.
[0xfffffffc,0x0000000c] , 0x012ffc40+0x0000000c==0x012ffc4c
0x012ffc44
Base2::c
0x00000003
0x012ffc48
Derived::d
0x00000004
0x012ffc4c
bp , Base2::_vfptr虚関数テーブルアドレス
指向Base::f 1()、Base::f 2()
0x012ffc50
Base::a
0x00000001
4.2説明
ダミー継承の共通は、ベースクラスでメモリに1つしかありません.派生クラス共通ベースクラスに記録されたオフセット量によって共通ベースクラスが見つかります
4.3テストコード
本明細書では、visual studio 2013によって作成されたクラスインスタンスオブジェクトのメモリ分布について説明する.理解したい場合は、メモリ分布のアドレスをよく見てください.
1.簡単な例
class A{
int a=1;
char b=2;//
char c=3;//
};
A instance;
1.1メモリの分散
アドレス
変数名
値
0x0093f9a0
instance
-
0x0093f9a0
a
0x00000001
0x0093f9a4
b,c
0xcccc0302(-859045118)
1.2説明
instanceはaのアドレスと同じです
1.3テストコード
int *sp = (int *)&instance;
cout << *(sp++) << endl;
cout << *(sp) << endl;
:
1
-859045118
2.単一継承
class Base{
int a=1;
char b=2;
char c = 3;
virtual void f1(){
cout << "Base::f1()" << endl;
}
};
class Derived : private Base{
int d=4;
void f1(){
cout << "Derived::f1()" << endl;
}
virtual void f2(){
cout << "Derived::f2()" << endl;
}
void f3(){
cout << "Derived::f3()" << endl;
}
};
Derived d;
2.1メモリの分散
アドレス
変数名
値
0x00b8f7c0
d
-
0x00b8f7c0
_vfptr虚関数テーブルアドレス
-
0x00b8f7c4
Base::a
0x00000001
0x00b8f7c8
Base::b,Base::c
0xcccc0302(-859045118)
0x00b8f7cc
Derived::d
0x00000001
2.2説明
2.3テストコード
int *dp = (int *)&d;
//
typedef void(*fptr)(void);
//f1
int vfptr_address = *dp;//
int f1_address = ((int *)vfptr_address)[0];//
fptr f1p = (fptr)f1_address;// f
f1p();// f, "Derived::f1()"
//f2
int f2_address = ((int *)vfptr_address)[1];//
fptr f2p = (fptr)f2_address;// f
f2p();// f, "Derived::f2()" ,
//fptr fp = (fptr)(((int*)(*sp))[0]);//
cout << hex << dp <<","<< hex << *(dp) << endl;//vfptr ,
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;//a
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;//b,c
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;//d
3.マルチ継承
class Base1{
int a=1;
char b=2;
char c = 3;
virtual void f1(){
cout << "Base1::f1()" << endl;
}
virtual void f2(){
cout << "Base1::f2()" << endl;
}
};
class Base2{
int a = 1;
char b = 2;
char c = 3;
virtual void f1(){
cout << "Base2::f1()" << endl;
}
virtual void f2(){
cout << "Base2::f2()" << endl;
}
};
class Derived : private Base1, Base2{
int d=4;
void f1(){
cout << "Derived::f1()" << endl;
}
void f3(){
cout << "Derived::f3()" << endl;
}
};
Derived d;
// Base1
int* b1p = (int *)(Base1*)&d;
// Base2
int* b2p = (int *)(Base2*)&d;
3.1メモリ分布
アドレス
変数名
値
0x0116f8b8
&d,b1p , Base1::_vfptr虚関数テーブルアドレス
-
0x0116f8bc
Base1::a
0x00000001
0x0116f8c0
Base1::b,Base1::c
0xcccc0302(-859045118)
0x0116f8c4
b2p , Base2::_vfptr虚関数テーブルアドレス
-
0x0116f8c8
Base2::a
0x00000001
0x0116f8cc
Base2::b,Base2::c
0xcccc0302(-859045118)
0x0116f8d0
Derived::d
0x00000001
3.2説明
//Base1
int b1_vfptr_address = *b1p;//
int b1_f1_address = ((int *)b1_vfptr_address)[0];//
fptr b1_f1p = (fptr)b1_f1_address;// f
b1_f1p();// f, "Derived::f1()"
int b1_f2_address = ((int *)b1_vfptr_address)[1];//
fptr b1_f2p = (fptr)b1_f2_address;// f
b1_f2p();// "Base1::f2()" ,
int b1_f3_address = ((int *)b1_vfptr_address)[2];//
fptr b1_f3p = (fptr)b1_f3_address;// f
b1_f3p();// "Derived::f3()"
//Base2
int b2_f1_address = ((int *)b2_vfptr_address)[0];//
fptr b2_f1p = (fptr)b2_f1_address;// f
b2_f1p();// f, "Derived::f1()"
int b2_f2_address = ((int *)b2_vfptr_address)[1];//
fptr b2_f2p = (fptr)b2_f2_address;// f
b2_f2p();// f, "Base2::f2()" ,
int b2_f3_address = ((int *)b1_vfptr_address)[2];//
fptr b2_f3p = (fptr)b1_f3_address;// f
b2_f3p();// f, "Derived::f3()" ,
cout << hex << b1p << "," << hex << *(b1p) << endl;//Base1::_vfptr ,
b1p++;
cout << hex << b1p << "," << hex << *(b1p) << endl;//Base1::a
b1p++;
cout << hex << b1p << "," << hex << *(b1p) << endl;//Base2::b,Base2::c
cout << hex << b2p << "," << hex << *(b2p) << endl;//Base2::_vfptr ,
b2p++;
cout << hex << b2p << "," << hex << *(b2p) << endl;//Base2::a
b2p++;
cout << hex << b2p << "," << hex << *(b2p) << endl;//Base2::b,Base2::c
b2p++;
cout << hex << b2p << "," << hex << *(b2p) << endl;//Derived::d
b2p++;
cout << hex << b2p << "," << hex << *(b2p) << endl;//Derived::d
4.ダミー継承
class Base{
public:
int a = 1;
virtual void f1(){
cout << "Base::f1()" << endl;
}
virtual void f2(){
cout << "Base1::f2()" << endl;
}
};
class Base1 :public virtual Base {
public:
int b=2;
virtual void f3(){
cout << "Base1::f3()" << endl;
}
};
class Base2 :public virtual Base {
public:
int c = 3;
virtual void f3(){
cout << "Base2::f3()" << endl;
}
};
class Derived : public Base1, public Base2{
public:
int d=4;
void f1(){
cout << "Derived::f1()" << endl;
}
virtual void f4(){
cout << "Derived::f4()" << endl;
}
};
Derived d;
int* dp = (int *)&d;
int* bp = (int *)(Base*)&d;
int* b1p = (int *)(Base1*)&d;
int* b2p = (int *)(Base2*)&d;
4.1メモリ分布
アドレス
変数名
値
0x012ffc30
dp ,b1p , Base1::_vfptr虚関数テーブルアドレス
指向Base 1::f 3()
0x012ffc34
指向するメモリは現在のアドレスからベースへのオフセット量を格納している.
[0xfffffffc,0x00000018] , 0x012ffc34+0x00000018==0x012ffc4c
0x012ffc38
Base1::b
0x00000002
0x012ffc3c
b2p , Base2::_vfptr虚関数テーブルアドレス
指向Base 2::f 3()
0x012ffc40
指向するメモリには、現在のアドレスからBaseへのオフセットが格納されています.
[0xfffffffc,0x0000000c] , 0x012ffc40+0x0000000c==0x012ffc4c
0x012ffc44
Base2::c
0x00000003
0x012ffc48
Derived::d
0x00000004
0x012ffc4c
bp , Base2::_vfptr虚関数テーブルアドレス
指向Base::f 1()、Base::f 2()
0x012ffc50
Base::a
0x00000001
4.2説明
ダミー継承の共通は、ベースクラスでメモリに1つしかありません.派生クラス共通ベースクラスに記録されたオフセット量によって共通ベースクラスが見つかります
4.3テストコード
cout << hex << dp << "," << hex << *(dp) << endl;//Base1::_vfptr
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;// Base
cout << hex <int *)(*dp))[1]<// Base 18, 24
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;//Base1::b
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;//Base2::_vfptr
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;// Base
cout << hex << ((int *)(*dp))[1] << endl;// Base c, 12
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;//Base2::c
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;//Derived::d
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;//Base::_vfptr
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;//Base::a