關于隱藏time_成員……
1.
class C
{
public:
union
{
proxy1 property1;
proxy2 property2;
proxy3 property3;
// private: // msvc和gcc這里是不能加入private的(具體要查標準)
}
};
2.
class C
{
public:
union
{
proxy1 property1;
proxy2 property2;
proxy3 property3;
}
private:
struct impl impl_; // 一旦不放在union中
// properties就可能會被填充
};
仔細選擇union的位置,可能會起到減少總大小的效果,比如:
class C1 {
char c_;
public:
union { /* properties */ };
private:
int i_;
};
class C2 {
int i_;
char c_;
public:
union { /* properties */ };
};
因為char c_;的附近本來就會被填充,properties剛好占據那個位置。
如果這樣,就可能會很糟糕:
class C3 {
char c_;
int i_;
public:
union { /* properties */ };
};
C++標準沒有說class的layout應該怎么安排。
每個access secion中的data,必須按聲明順序。
但不同access secion之間,可以打亂順序 —— 這個也許會有一些幫助。
第2種方案依然需要計算offsetof…… 這是很不靠譜的……
3.
作為base。
struct properties
{
union
{
proxy1 property1;
proxy2 property2;
proxy3 property3;
// ...
}
};
class C : public properties
{
// data
};
這樣,可以很容易計算出C的位置:
void property_proxy::operator=( ... ) {
void* p = this;
C* c = static_cast<C*>( static_cast<properties*>(p) );
}
編譯器會正確計算出C*,即使在多繼承下也行。比如:
class C : other , public properties {};
但是,在msvc和gcc上,properties 都不會執行空基類優化…… 很囧……
仔細安排data的布局(在這2款編譯器上,將小對齊的數據放前面),也會影響整個類的大小。
2和3的方案,如果能鉆到空子,就可以幾乎沒有代價的實現property。
空子就是說,那個class本來就有一些需要被填充的空洞……
如果沒有空可鉆…… 或者沒有正確安排位置…… 會多占用一些大小。
額外大小不超過class中所有域中需要最大對齊那個成員的大小。
額外大小和properties的數量是無關的, 多少個properties都占用這么多額外大小, union嘛……
4.
還有一種方案。
其實上面的date,缺陷在于time_t time_; 這個成員會被外界訪問。
可以這樣:
class date {
class impl {
time_t time_;
friend class date;
};
public:
union
{
impl impl_;
proxy1 property1;
proxy2 property2;
proxy3 property3;
}
// ...
};
這樣,客戶代碼可以訪問的,只有impl_這個名字。
但是幾乎不能拿它作任何事情。
連它的類型名 —— date::impl —— 都是不可訪問的。
這個方案的缺陷就是 —— 通常必須從頭設計一個類型。
union中不能放非pod。只能從內建類型開始,構造一些類。
比如已經有某個非pod的類,C,想將C作為一個成員,實現Cex:
class Cex
{
union
{
C impl_; // 不行
}
};
只能用上面的property base或者property member……
5.
范化工作
這個…… 再說吧……
如果能有更好的布局方案…… 范化工作就白做了……