3.3 data member 的存取
Static Data Members
static data member 被视为 global 变量,只在class的生命周期内可见。
每一个 static data member 只有一个实例,存放在data segment 区。
每次程序引用static member 时,就会被内部转化为对该唯一 extern 实例的直接参考操作。
Point3d origin, *pt = &origin;
Point3d::chunkSize = 250; //origin.chunkSize = 250;
Point3d::chunkSize = 250; //pt->chunkSize = 250;
通过指针和通过对象来存取 member 一样。因为经过 "." 运算符对静态成员变量的存取操作算是一种便捷的手段。(因为静态成员数据并不属于类对象)
如果chunkSize经过复杂的继承链,程序对于static members还是只有一个实例,存取路径仍然那么直接。
如果 static data member 的存取是经由函数调用(或其他某些语法) 而被存取呢?
Foobar().chunkSize = 250;
C++ Standard 明确要求 Foobar() 必须被求值,哪怕结果没有用处。可能的转化:
(void) Foobar();
Point3d.chunkSize = 250;
对一个 static data member 取地址, 得到的是指向其数据类型的指针, 而不是指向其 class member 的指针。 因为 static member 并不在内含在一个 class object 之中:
&Pointd::chunkSize;
const int* // 得到的指针类型:
如果有两个 classes,每一个都声明了一个 static member freeList, 那么当他们都被放在程序的 data segment 时, 就会导致名称冲突.编译器的解决方案是暗中对每一个 static data member 编码(name-mangling) 以获得一个独一无二的程序识别代码(有多少编译器就有多少种 name-mangling 做法)。所谓的 name-mangling 做法主要就是两点:
1.一种算法, 推导出独一无二的名称;
2.那些独一无二的名称可以轻易被推导回到原来的名称。
Nonstatic Data Member
Nonstatic data members 直接存放在每一个 class object 之中。只能通过对象存取(包括显示对象和隐式对象)。
程序员在一个 member function 中直接处理一个 nonstatic data member。 这是一个 implicit class object:
Point3d Point3d::translate(const Point3d &pt)
{
_x += pt._x;
_y += pt._y;
_z += pt._z;
}
//实际是经过一个隐式类对象的 this 指针完成的
//member function 的内部转化
Point3d Point3d::translate(Point3d *const this, const Point3d &pt)
{
this->_x += pt._x;
this->_y += pt._y;
this->_z += pt._z;
}
欲对一个 nonstatic data member 进行存取操作, 编译器需要把 class object 的起始地址加上 data member 的偏移量, 例如:
origin._y = 0.0;
地址 &origin 等于: data 成员地址 = 对象起始地址 + data 成员地址的偏移位置offset
offset += 1;
&origin + (&Point3d::_y - 1);
-1 操作: 指向 data member 的指针, 其偏移量(offset) 的值总是被加上 1, 这样可以使编译系统区分出:
Ⅰ、“一个指向 data member 的指针, 用以指出 class 的第一个 member”
Ⅱ、“一个指向 data member 的指针, 没有指出任何 member”
每一个 nonstatic data member 的偏移量在编译时起即可获知, 当 member 属于一个 base class subobject(派生自单一或多重继承串链) 也是一样。
存取一个 nonstatic data member 效率 == C struct member
对于虚拟继承略有不同, 虚拟继承将为经由 base class subobject 存取 class members 导入一层新的间接性, 例如:
Point3d *pt3d;
pt3d->_x = 0.0;
其执行效率在 _x 是一个 struct member,一个 class member,单一继承,多重继承的情况下都完全相同, 但如果 _x 是一个virtual base class 的 member,存取速度会慢一点。
从 origin 存取和从 pt 存取 有什么重大差异?
origin.x = 0.0;
pt-> = 0.0;
当 Point3d 是一个derived class, 而在其继承结构中有一个 virtual base class, 且被存取的 member 是一个从该 virtual base class 继承来的 member 时, 就会产生重大差异。
因为这个时候无法确定 pt 到底指向哪一种 class type(导致无法知道编译期这个 member 真正的 offset 位置),所以这个存取必须延迟到执行期,经由一个额外的间接导引,才能解决。但用 origin 就不存在这样的问题,因为 origin类型确定,而他继承自 virtual base class, member 的 offset 位置也在编译时期就固定了。有的编译器甚至可以静态的经由 origin 就解决掉对 _x 的存取。

浙公网安备 33010602011771号