看看这三个类(包括ommited):
template<class T> struct A {
std::allocator<T> allocator;
T* ptr;
};
template<class T> struct B {
T* ptr;
std::allocator<T> allocator;
};
template<class T> struct C : std::allocator<T> {
T* ptr;
};
int main(int argc, char **argv) {
std::cout << "A: " << sizeof(A<int>) << "\n";
std::cout << "B: " << sizeof(B<int>) << "\n";
std::cout << "C: " << sizeof(C<int>) << "\n";
}
如果您问我,根据对齐方式,A
或B
必须与C
具有相同的尺寸。但是,打印sizeof
声明:
A: 16
B: 16
C: 8
为什么会这样?
答案 0 :(得分:5)
通过C ++ 11,9 / 4:
类类型的完整对象和成员子对象应具有非零大小。
没有这样的限制适用于基类,只要每个对象都有一个类型和地址唯一的对即可。因此,只要“第一个”数据成员没有与基础子对象相同的类型,基础子对象可能没有大小,既不完整也不是成员。
(我把“第一”放在引号中,因为涉及访问级别的复杂性。)
事实上,1.8 / 5-6正式确定了以上内容:
5除非它是位字段(9.6),否则大多数派生对象应具有非零大小并且应占用一个或多个存储字节。 基类子对象的大小可能为零。平易可复制或标准布局类型(3.9)的对象应占用连续的存储字节。
6除非对象是零字段或零大小的基类子对象,否则该对象的地址是它占用的第一个字节的地址。如果一个是另一个的子对象,或者如果至少一个是零大小的基类子对象并且它们是不同类型的,则不是位字段的两个对象可以具有相同的地址;否则,他们应有不同的地址。
这是std::vector
的“典型”实现,减去了所有名称:
template <typename T, typename Alloc>
class vector
{
struct vbase : Alloc
{
T * data, * end, * capacity;
vbase(Alloc const & a) : Alloc(a), data(), end(), capacity() { }
};
vbase impl;
public:
vector(Alloc const & a = Alloc()) : impl(a) { }
T * data() const { return impl.data; }
T & operator[](size_t n) { return data()[n]; }
// ...
// use impl.allocate(), impl.construct() etc.
};
这基本上确保了每当分配器为空时sizeof(vector<T>) == 3 * sizeof(T*)
。