先看一段测试代码,
#include <iostream>
#include <list>
using namespace std;
struct A {int a; char b; long c; };
int main() {
list<int> a{1,2,3};
list<A> b;
cout << "sizeof list<int>:" << sizeof(a) << endl;
cout << "sizeof A:" << sizeof(A) << endl;
cout << "sizeof list<A>:" << sizeof(b) << endl;
return 0;
}输出
sizeof list<int>:24
sizeof A:16
sizeof list<A>:24
WTF? 不符合直觉对吗?怎么着应该和模板参数有点儿关系才对?
要搞清楚为啥,我们看一下std::list(下称stl_list)定义
/// Common part of a node in the %list.
struct _List_node_base
{
_List_node_base* _M_next;
_List_node_base* _M_prev;
}
/// The %list node header.
struct _List_node_header : public _List_node_base
{
#if _GLIBCXX_USE_CXX11_ABI
std::size_t _M_size;
#endif
}
/// An actual node in the %list.
template<typename _Tp>
struct _List_node : public __detail::_List_node_base
{
#if __cplusplus >= 201103L
__gnu_cxx::__aligned_membuf<_Tp> _M_storage;
_Tp* _M_valptr() { return _M_storage._M_ptr(); }
_Tp const* _M_valptr() const { return _M_storage._M_ptr(); }
#else
_Tp _M_data;
_Tp* _M_valptr() { return std::__addressof(_M_data); }
_Tp const* _M_valptr() const { return std::__addressof(_M_data); }
#endif
};stl_list包含一个叫header的东西,在c++11之后,这个结构在64位系统上占24字节(两个指针各8字节,一个_M_size占8字节)。可以看到这个结构跟容器内元素的类型(模板参数)是无关的。同样,跟容器元素个数list::size()也无关。我们现在讨论的是std::list<int>这个类型的size,而非整个容器占用了多少内存空间。同理sizeof(vector<int>)也是一样,和模板参数类型无关,和vector::size()无关1。
那我又要问了,这块和模板参数没关系,那容器中的数据到底咋存储的呢?
看上面的代码最后一个类_List_node,就是实际存储数据的节点类。同样继承_List_node_base,拥有双向指针和一个_M_storage。而这个类型的size就和模板参数相关了。
cout << "sizeof list_node<int>:" << sizeof(_List_node<int>) << endl; // 24 = 8(pointer) * 2 + 4(int) + 4(padding)
cout << "sizeof list_node<A>:" << sizeof(_List_node<A>) << endl; // 32 = 8(pointer) * 2 + 16(A)
cout << "sizeof list_header:" << sizeof(__detail::_List_node_header) << endl; // 24 = 8(pointer) * 2 + 8(size_t _M_size)现在,一切都明朗了。并且,还可以知道list的结构实际上是这样的,
┌─────────────────┐
│_List_node_header│
│ │
empty list │- prev = self │
│- next = self │
│- size = 0 │
└─────────────────┘
┌──────────────────────────────────┐
▼ │
┌─────────────────┐ ┌────────────┐ │
│_List_node_header│ │_List_node │ │
│ │ │ │ │
list{1} │- prev ──────────┼────────►│* prev ─────┼──┘
│- next ──────────┼────────►│* next ─────┼──┐
│- size = 1 │ │* data = 1 │ │
└─────────────────┘ └────────────┘ │
▲ │
└──────────────────────────────────┘
┌────────────────────────────────────────────┐
│ ▼
┌─────────────────┐ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│_List_node_header│ │ │_List_node │ │_List_node │ │_List_node │
│ │ │ │ │ │ │ │ │
list{1,2,3} │- prev ──────────┼─┘ │* prev ◄───┤* prev ◄───│* prev │
│- next ──────────┼──►│* next ──────┼──►│* next ──────┼──►│* next ──────┼─┐
│- size = 3 │ │* data = 1 │ │* data = 2 │ │* data = 3 │ │
└─────────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
▲ │
└─────────────────────────────────────────────────────────────────┘
see also: https://cplusplus.com/forum/general/89868/