码迷,mamicode.com
首页 > 编程语言 > 详细

C++对象模型——Data Member的绑定(第三章)

时间:2015-08-03 01:17:43      阅读:223      评论:0      收藏:0      [点我收藏+]

标签:

3.1    Data Member的绑定 (The Binding of a Data Member)

    考虑下面这段代码:
// 某个foo.h头文件,从某处含入
extern float x;
// 程序员的Point3d.h文件
class Point3d {
public:
    Point3d(float, float, float);
    // 问题:被传回和被设定的x是哪一个x?
    float X() const { return x; }
    void X(float new_x) const { x = new_x; }
private:
    float x, y, z;
};
    Point3d::X()传回哪一个x?是 class 内部的那个x,还是外部(extern)的那个x?现在的答案是内部的.但过去的答案并不是这样.
    在C++最早的编译器上,如果在Point3d::X()的两个函数实例中对x进行参阅(引用)操作,这操作将会指向global x object.这样的绑定结果几乎普遍存在,并因此导出早期C++的两种防御性程序设计风格:
    1.    把所有的data members放在 class 声明开始处,以确保正确的绑定:
class Pointed
{
    // 防御性程序设计风格 #1
    // 在class声明开始先放置所有的data members
    float x, y, z;
public:
    float X() const { return x; }
    // ...etc...
};
    2.    把所有的inline functions,不管大小都放在 class 声明之外:
class Point3d
{
public:
    // 防御性程序设计风格 #2
    // 把所有的inlines都移到class之外
    Pointed();
    float X() const;
    void X(float) const;
    // ...etc...
};
inline float POint3d::X() const {
    return x;
}
    这些程序设计风格事实上到今天还存在,这个古老的语言规则被称为"member rewriting rule",大意是"一个inline函数实体,在整个class声明未被完全看见之前,是不会被评估求值(evaluated)的".C++ Standard以"member scope resolution rules"来精炼这个"rewriting rule",其效果是,如果一个inline函数在 class 声明后立刻被定义的话,那么就还是会对其评估求值(evaluated).也就是说,如果写下面的代码:
extern int x;
class Point3d {
public:
    // 对于函数本身的分析将延迟直至class声明的右括号出现才开始
    float X()const { return x; }
private:
    float x;
};
    对于member functions本身的分析,会直到整个 class 的声明都出现了才开始.因此,在一个 inline member function内的一个data member绑定操作,会在整个 class 声明完成后才发生.

3.2    Data Member的布局 (Data Member Layout)

    已知下面一组data members:
class Point3d {
public:
    // ...
private:
    float x;
    static List<Point3d *> *freeList;
    float y;
    static const int chunkSize = 250;
    float z;
};
    Nonstatic data members在 class object中的排列顺序将和其被声明的顺序一样,任何中间介入的 static data members如freeList和chunkSize都不会被放进对象布局中.在上述例子中,每一个Point3d对象是由三个 float 组成,次序是x,y,z.static data members存放在程序的data segment中,和个别的 class objects无关.
    C++ Standard要求,在同一个access section(也就是 private,public,protected 等区段)中,members的排列只需符合"较晚出现的members在class object中有较高的地址"这一条件即可.也就是说,各个members并不一定得连续排列,什么东西可能介入声明的members之间呢?members的边界调整(alignment)可能就需要填充一些bytes.对于C和C++而言,这的确是真的,对当前的C++编译器实现情况而言,这也是真的.
    编译器还可能合成一些内部使用的data members,以支持整个对象模型,vptr就是这样的东西,当前所有的编译器都把它插入在每一个"内含virtual function之class"的object,vptr会放在什么位置呢?传统上它被放在所有明确声明的members的最后,不过如今也有一些编译器把vptr放在一个 class object的最前端.C++ Standard对布局持放任态度,允许编译器把那些内部产生出来的members自由放在任何位置上,甚至放在那些被程序员声明出来的members之间.
    C++ Standard也允许编译器将多个access sections中的data members自由排列,不必在乎它们出现中在 class 声明中的次序,也就是说,下面这样的声明:
class Point3d {
public:
    // ...
private:
    float x;
    static List<Point3d *> *freeList;
private:
    float y;
    static const int chunkSize = 250;
private:
    float z;
};
    其 class object的大小和组成都和先前声明的那个相同,但是members的排列次序则视编译器而定.
    当前编译器都是把一个以上的access sections连锁在一起,依照声明的次序成为一个连续区块.Access sections的多少并不会导致额外负担,例如在一个section中声明8个members,或者在8个sections中总共声明8个members,得到的object大小是一样的.
    下面这个 template function,接受两个data members,然后判断谁先出现在 class object中.如果两个members都是不同的access sections中的第一个被声明者,此函数可以用来判断哪一个section先出现:
template <class class_type1, class class_type2, class class_type3>
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";
}
    上述函数可以这样被调用:
access_order(&Point3d::z, &Point3d::y);
    于是class_type会被绑定为Point3d,而data_type1和data_type2会被绑定为float.


版权声明:本文为博主原创文章,未经博主允许不得转载。

C++对象模型——Data Member的绑定(第三章)

标签:

原文地址:http://blog.csdn.net/yiranant/article/details/47220705

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!