第三章探討資料成員綁定、佈局、存取,以及虛擬繼承下的類別大小計算,內容相對前兩章較易理解。
class X {};
class Y : public virtual X {};
class Z : public virtual X {};
class A : public Y, public Z {};
在某編譯器下:
sizeof(X) == 1(空類別插入一個 char 以確保唯一地址)sizeof(Y) == 8、sizeof(Z) == 8sizeof(A) == 12(而非想像中的 16)Y/Z 為 8 bytes:
新式編譯器可能將空虛擬基類視為派生類開頭,省去 char 與填充,故 Y/Z 大小可能為 4。
A 為 12 bytes:
嵌套類型聲明應放在 class 開頭,確保非直覺綁定正確性。範例:
typedef int length;
class Point3d {
public:
void mumble(length val) { _val = val; } // length 解析為 global typedef int
length mumble() { return _val; }
private:
typedef float length; // 若放前面,上方引用將非法
length _val;
};
透過隱含的 this 指標存取。編譯器將成員函數轉換:
// 原始
Point3d Point3d::translate(const Point3d& pt) {
x += pt.x; y += pt.y; z += pt.z;
}
// 轉換後
Point3d Point3d::translate(Point3d* const this, const Point3d& pt) {
this->x += pt.x; this->y += pt.y; this->z += pt.z;
}
非靜態成員存取:&origin._y == &origin + (&Point3d::_y - 1)。減 1 是為區分「指向第一個成員」與「不指向任何成員」的指標。
class Point3d {
public:
virtual ~Point3d();
protected:
static Point3d origin;
float x, y, z;
};
&Point3d::z; // 值為 8 或 12 (視 vptr 位置),實際取值會 +1
+1 目的:區分空指標與指向第一個成員的指標。比喻:三室一廳,給共同朋友地址只給門牌號(不指向成員),給私人朋友才給房間號(需偏移量)。