第三章、Data语意学
無虛繼承的空類占一個字節(jié),用于標記該類。有虛繼承的空類至少占4個字節(jié)(可能繼承的空類占很大空間)。
對齊情況
class X{
float i;//8
char j;//1
int k;//4
double b;//下面重新的字節(jié)8,上面用來對齊
};
sizeof(X)=24
class Y{
char j;//1
int k;//4
};
sizeof(Y)=8
class Z{
char j;//1
int k;//4
double b;
};
sizeof(Y)=16
1.Data Member的綁定
有兩種情況:
情況一:在類的函數里面返回的變量,之前是全局變量,但如今已經是類的變量。例如:
extern float x;
class Point3d
{
Point3d(float,float,float);
//返回的是Point3d::x,早期時時全局變量x
float X() const{return x;}
void X(float new_x) const{x=new_x;}
//...
float x,y,z;
};
早期的防御措施:
把所有成員聲明放在類的開頭。
把所有的inline函數都放在class聲明之外。
情況二:成員函數的參數列表中,當他們第一次出現的時候就適當的被決議resolved完成。
typedef int length;
class Point3d
{
//所有的length被決議為global
//_val被決議為Point3d::_val
void mumble(length val){_val=val;}
length mumble(){return _val;}
//...
typedef float length;
float _val;
};
2.Data Member的布局
可以用下面模板函數測試布局的先后
template<typename class_type,typename data_type1,typename data_type2>
char* access_order(data_type1 class_type::*mem1,data_type2 class_type::*mem2)
{
??? assert(mem1!=mem2);
??? return mem1<mem2?"member 1 occurs first":"member 2 occurs first";
}
class X{
public:
??? double j;
??? int k;
??? double b;
};
access_order(&X::b,&X::j);//測試出錯,額。。
3.成員變量的存取
對于例子:
Point3d origin,*pt=&origin;
//下面兩種有區(qū)別么?根據class Point3d和data member x的角度來說,有區(qū)別。
origin.x=0.0
pt->x=0.0;
其實只有在當Point3d是一個derived class,而在它的繼承結構中有一個virtual base class,并且存取的member(如本例x)
是一個從該virtual base class繼承而來的member時,就會有很大區(qū)別。此時對于pt,只能在運行時候才決定,經有一個而外的間接導引。
對于origin,可以在編譯時期就固定了。
情況一:x是靜態(tài)成員變量
它被提出在class之外,并視為global變量。每個member的存取都不會導致任何空間或者執(zhí)行時間的額外負擔,無論是在個別的class objects或者在static data member本身。
上面兩個都會轉為:
Point3d::x=0.0;
即使對于函數返回值
foobar().x=24.0;
都會轉為:
(void)foobar();
Point3d.x=24.0;
當對它取地址時,&Point3d::x;就會獲得內存地址:const int*
如果兩個類都聲明同樣的static member,那么它們會被改名字
情況二:非靜態(tài)成員變量
對其取地址:&origin::x;等于&origin+(&Point3d::x-1);
書上說是為了區(qū)別“一個指向data member的指針,用以指向class的第一個member”和“一個指向data member的指針,沒有指向class的第一個member”
4.繼承與數據成員
只繼承而不要多態(tài)時,派生類會根據父類的成員變量的最大長度來alignment。在繼承鏈上,注意不能把數據放在用于alignment的padding區(qū)域中,因為
class concrete1{
public:
int val;
char bit1;
}
class concrete2:public concrete1{
char bit2;
}
如果把bit2放在bit1的padding域里面,如果concrete1 *p1;concrete2 *p2=*p1;時,bit2被亂覆蓋掉了。
加上多態(tài)時,數據成員可能在虛指針的上面也可能在虛指針的下面。
多重繼承,要適當地調整位置才可以賦值。如
class Point2d{
public:
//..擁有virtual接口,所以Point2d對象之中會有vptr
protected:
float _x,_y;
};
class Point3d:public Point2d{
public:
//..擁有virtual接口,所以Point2d對象之中會有vptr
protected:
float _x;
};
class Vertex{
public:
//..擁有virtual接口,所以Point2d對象之中會有vptr
protected:
Vertex *next;
};
class Vertex3d:public Point3d,public Vertex{
public:
//..擁有virtual接口,所以Point2d對象之中會有vptr
protected:
float mumble;
};
Vertex3d v3d;Vertex *pv;Point2d *p2d;Point3d *p3d;
對于下面的賦值
pv=&v3d;//考慮到pv3d可能為0,加上偏移,需要轉換為pv=pv3d?Vertex *)(((char*)&v3d)+sizeof(Point3d)):0;
p2d=&v3d;p3d=&v3d;//直接拷貝地址就可以了。
虛擬繼承有兩種實現:1、每個派生類多加一個指針指向一個虛擬基類表2、在virtual中放置virtual base class的offset,正值則索引到函數,負值則索引到基類。
轉載于:https://www.cnblogs.com/tempal/p/3762981.html
總結
以上是生活随笔為你收集整理的第三章、Data语意学的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: XMPP iOS客户端实现三:登录、注册
- 下一篇: JS单例设计模式