您的位置:首页 > 编程语言 > C语言/C++

c++对象模型之Data布局

2015-07-16 23:51 369 查看
Data语意学
class X{};

class Y : publicvirtual X {};

class Z : publicvirtual X {};

class A : publicY, public Z {};

 

sizeof(X) = 1,sizeof(Y) = 4, sizeof(Z) = 4, sizeof(A) = 8;visualc++6.0上测试结果(对emptyvirtual base class 有特殊处理的编译器)

反之则对应的结果为1,8,8,12

事实上Y,Z的大小受三个因素的影响:

1.语言本身所造成的额外负担

2.编译器对于特殊情况所提供的优化处理

3.Alignment限制

有特殊处理情况下对象布局如下:

无特殊处理情况对象布局如下:

     


 

       对于nonstaticdata members直接存放在每一个classobject之中。对于继承而来的nonstatic data members也是一样,不过并没有强制定义其间的排列顺序。至于staticdata members,则被放置在程序的一个globaldata segment中,不会影响个别的classobject的大小。

 

一Data Member的绑定

1.Datamembers绑定

extern int x;

class Point3d

{

public:

       //对函数本身的分析将延迟至class声明的右边大括号出现才开始

       float X() const { return x;}

       //....

private:

       float x;

};

对memberfunctions本身的分析,会直到整个class的声明都出现了才开始即对于X()函数返回的x将是类中的定义的x。

2.Membersfunctions的argumentlist

typedef int length;

class Point3d

{

       //typedef char length;

public:

       //length被决议为global

       //_val被决议为Point3d::_val

       void mumble(length val) { _val = val; }

       length mumble() {return _val;}

 

private:

       //length必须在“本class对它的第一个参考操作”之前被看见

       //这样的声明将使先前的参考操作不合法

       typedef char length;

       length _val;

};

       上述的这种语言状况,仍然需要某种防御性程序风格:情始终把“nestedtype声明”放在class的起始处。

 

二Data Member的布局

       Nostatic data members 在classobject中的排列顺序将和其被生命的顺序一样,任何中间介入的staticdata members都不会被放进对象布局之中。

       C++ standard要求,在同一个accesssection(也就是private、public、protected等区段)中,members的排列只需符合“较晚出现的members在classobject中有较高的地址”这一条件即可。

       C++ standerd也允许编译器将多个accesssection之中的data members自由排列,不必在乎它们出现在class声明中的次序。

       值得注意的是accesssection的多寡并不会招来额外的负担。例如在一个section中声明8个members,或是在8个section中总共声明8个members,得到的object大是一样的。

 

三Data Member的存取

1.Staticdata members

存取staticmembers并不需要通过class object;若取一个staticdata memb的

地址,会得到一个指向其classmember的指针,因为static member并不内含在一个classobject中。例如:

&Point3d::chunkSize;
会得到类型如下的内存地址:

const int*
2.NonstaticData Members

Nonstatic data members 直接存放在每一个classobject之中。除非经由明确

(explic)的或暗喻(implicit)的classobject,没有办法直接存取它们。在member function 里面编译器会自动合成this指针。欲对一个nonstaticdata member进行存取操作,编译器需要把classobject的起始地址加上data member的偏移量(offset),每一个nonstaticdata member的offset在编译时期即可获知。

 

       Point3d origin, *pt;

       Origin.x = 0.0;

       pt->x = 0.0;

两种存取方式有什么重大的差异?答案是“当Point3d是一个derivedclass,而在其继承结构中有一个virtual base class,并且被存取的member(例如本例中的x)是一个从该virtualbase class继承而来的member时,就会有重大的差异。

 

四继承与Data member

1.只有继承没有多态   

在c++继承模型中,一个derivedclass object所表现出来的东西,是其自己的members加上其baseclass members的总和。至于derivedclass members 和baseclass members的排列次序并没有强制指定,一般是基类在前。

       把原本不相干的两个class凑成一对“type/subtype”会犯的错误有:1.可能会重复设计一些相同操作的函数;2.把一个class分解为两层或更多层,有可能会为了“表现class体系之抽象化”而膨胀所需空间。C++语言保证“出现在derived class中的baseclass subobject有其完整原样性”。

class Concrete

{

       private:

              int val;

              char c1;

              char c2;

              char c3;

};

sizeof(Concrete)= 8

将Concrete分裂为三层结构:

class Concrete1

{

Public:

       //….

Private:

       int val;

       char bit1;

};

class Concrete2

{

Public:

       //….

Private:

       char bit2;

};

class Concrete3

{

Public:

       //….

Private:

       char bit3;

};

可以得出sizeof(Concrete3)=16,比原先设计的多了一倍。对象布局如下:

 


 

 
关于base classsubobject在derivJedclass中保持原样原因解释



2.加上多态
多态带来的空间和存取时间的额外负担:
①导入一个virtualtable,用来存放在它所声明的每一个virtual function的地
址。这个table的元素数目一般而言是被声明的virtualfunctions的数目,再加上一个或两个slots。
②在每一个classobject中导入一个vptr,提供执行期间的链接,使每一个
object能够找到相应的virtualtable。
③加强constructor,使它能够为vptr设定初值,让它指向class所对应的virtual
table。
④加强destructor,使它能够抹消“指向class之相关virtualtable”的vptr。记
住destructor的调用次序是反向的:从derivedclass到base class。
       所有的编译器不是吧vptr放在对象的头部,就是放在对象的尾部。
五指向Data Members的指针
/*
       Pointer to Data Members
*/
#include<iostream>
usingnamespace std;
 
classPoint3d
{
       virtual ~Point3d();
 
public:
       static Point3d origin;
       float x, y, z;
};
intmain()
{
       //类型为floatPoint3d::*而并不是float*
       printf("&Point3d::x =%p\n", &Point3d::x);
       printf("&Point3d::y =%p\n", &Point3d::y);
       printf("&Point3d::z =%p\n", &Point3d::z);
}
 
#include<iostream>
usingnamespace std;
structPoint
{
       int x, y;
       double d1, d2;
}
intAdd(Point arr[], int size, int Point::*p)
{
       int sum = 0;
       for(int i = 0; i < size; i ++)
              sum += arr[i].*p;
       return sum;
}
 
doubleAdd1(Point arr[], int size, double Point::*p)
{
       int sum = 0;
       for(int i = 0; i < size; i ++)
              sum += arr[i].*p;
       return sum;
};
 
intmain()
{
       Point pp[3] = { {1,2,1.0,2.0},{3,4,1.0,2.0}, {5,6,1.0,2.0} };
       cout<<Add(pp, 3,&Point::x)<<endl<<Add(pp, 3, &Point::y)<<endl;
       cout<<Add1(pp, 3,&Point::d1)<<endl<<Add1(pp, 3, &Point::d2)<<endl;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ 数据成员布局