考察以下代码:
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 都不会被放进布局之中。static data members 都存放在程序的 data segment 中, 和个别的 class objects 无关。
C++ Standard 要求, 在同一个 access section (即 private, public, protected 等) 中,members 的排列只需符合较晚出现的 members 在 class object 中有着较高的地址这一条件即可。 也就是说, 各个 members 并不一定要连续排列(意味着中间可被其他区段分隔)。那么有什么会可能被插在中间呢? 第一种是为了对齐所填补的 bytes, 第二种就是编译器可能会合成一些内部使用的 data mambers, 以支持整个对象模型, vptr 就是这样的东西, 当前的所有编译器都把它安插在每一个内含 virtual function 的 class object 之内。而 vptr 又会被放在什么位置呢?传统上它被放在所有声明的 members 的最后, 但是有的编译器也把 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; };
实际上与前一段代的大小和组成是一样的, 我的理解就是, 只要你是搞革命的, 我们不管你是从哪来的。 而实际在编译器中, 编译器可以随意把 y 或 z 或什么其他东西放为第一个, 但似乎目前的编译器没人这么做。
编译器实际的处理都是把一个以上的 access section 连锁在一起, 依照声明的次序连成一个连续区块。 Access section 的多寡不会引起编译器的额外负担, 不管是在几个 sections 中 声明多个 members, 还是在一个一个 section 中声明全部的 members, 得到的 object 的大小都是一样的。
考察以下代码:
//接受两个 data members, 判断谁先出现在 class object 之中。 //如果两个 members 都是不同的 access sections 中的第一个被声明的 //此函数就可以判断哪一个 section 先出现 template< class class_type, class data_type1, class 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"; }
上述函数可以这样被调用:
access_order( &Point3d::_z, &Point3d::_y );
PS: 但是, 我在 vs2013 上运行这段代码没有成功, 显示的错误是 error C2296: “<”: 非法,左操作数包含“int Point3d::* ”类型
这就让人不淡定了, 希望如果有人看到这篇博客可以指点一二, 我先在这谢谢了!