0.前言
本文的讨论了类实例对象的内存分布,程序由visual studio2013编写。如果你想看懂 ,请认真看内存分布中的地址
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说明
- d与_vfptr 的地址相同,说明对象的起始位置存放是虚函数表地址
- 不管是私有继承还是,私有变量,都能在2.3的测试代码中直接访问
- 单继承只有1个虚函数表,虚函数依次放在表中
2.3 测试代码
int *dp = (int *)&d;
typedef void(*fptr)(void);
int vfptr_address = *dp;
int f1_address = ((int *)vfptr_address)[0];
fptr f1p = (fptr)f1_address;
f1p();
int f2_address = ((int *)vfptr_address)[1];
fptr f2p = (fptr)f2_address;
f2p();
cout << hex << dp <<","<< hex << *(dp) << endl;
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;
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;
int* b1p = (int *)(Base1*)&d;
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说明
- 可以认为派生类对象中包含基类对象,基类对象 按继承顺序放在派生类对象中
- 函数重写时,可以看成单继承分别对基类函数中的虚函数重写
- 每次继承都创建一个虚表
- 向上转型 实质为对象偏移量的改变,即dynamic_cast的作用。 看:b1p ,b2p 在内存中的分布
3.3 测试代码
int b1_vfptr_address = *b1p;
int b1_f1_address = ((int *)b1_vfptr_address)[0];
fptr b1_f1p = (fptr)b1_f1_address;
b1_f1p();
int b1_f2_address = ((int *)b1_vfptr_address)[1];
fptr b1_f2p = (fptr)b1_f2_address;
b1_f2p();
int b1_f3_address = ((int *)b1_vfptr_address)[2];
fptr b1_f3p = (fptr)b1_f3_address;
b1_f3p();
int b2_f1_address = ((int *)b2_vfptr_address)[0];
fptr b2_f1p = (fptr)b2_f1_address;
b2_f1p();
int b2_f2_address = ((int *)b2_vfptr_address)[1];
fptr b2_f2p = (fptr)b2_f2_address;
b2_f2p();
int b2_f3_address = ((int *)b1_vfptr_address)[2];
fptr b2_f3p = (fptr)b1_f3_address;
b2_f3p();
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虚函数表地址 |
指向Base1::f3() |
0x012ffc34 |
指向的内存存着放当前地址到Base的偏移量 |
[0xfffffffc,0x00000018] , 0x012ffc34+0x00000018==0x012ffc4c |
0x012ffc38 |
Base1::b |
0x00000002 |
0x012ffc3c |
b2p , Base2::_vfptr虚函数表地址 |
指向Base2::f3() |
0x012ffc40 |
指向的内存存放着当前地址到Base的偏移 |
[0xfffffffc,0x0000000c] , 0x012ffc40+0x0000000c==0x012ffc4c |
0x012ffc44 |
Base2::c |
0x00000003 |
0x012ffc48 |
Derived::d |
0x00000004 |
0x012ffc4c |
bp , Base2::_vfptr虚函数表地址 |
指向Base::f1(),Base::f2() |
0x012ffc50 |
Base::a |
0x00000001 |
4.2 说明
虚继承的共同在基类在内存中只有一份。派生类通过记录到 共同基类的偏移量来找到共同基类
4.3 测试代码
cout << hex << dp << "," << hex << *(dp) << endl;//Base1::_vfptr 地址
dp++;
cout << hex << dp << "," << hex << *(dp) << endl;//指向的内存存放着 当前地址到Base的偏移量
cout << hex <<((int *)(*dp))[1]<<endl;//到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