关于隐藏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.
范化工作
这个…… 再说吧……
如果能有更好的布局方案…… 范化工作就白做了……