为何C++ vector的allocator中max_size会被调用两次?
自定义Allocator调试疑问:vector扩容时max_size被调用两次的原因
调试自定义allocator时发现,每次vector触发扩容操作时,max_size()都会被调用两次。以下是测试代码及运行输出,想请教该现象的原因:
#include <iostream> #include <memory> #include <vector> #include <typeinfo> template <typename T> class static_allocator { public: using size_type = size_t; using difference_type = ptrdiff_t; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; using value_type = T; static_allocator() {} ~static_allocator() {} template <class U> struct rebind { typedef static_allocator<U> other; }; template <class U> static_allocator(const static_allocator<U>&) { } pointer address(reference x) const { return &x; } const_pointer address(const_reference x) const { return &x; } size_type max_size() const throw() { size_type max = (size_type)(-1) / sizeof(T); std::cout << " max_size " << max << std::endl; return max ; } pointer allocate(size_type n, const_pointer hint = 0) { std::cout << " allocate " << n << std::endl; return static_cast<pointer>(malloc(n * sizeof(T))); } void deallocate(pointer p, size_type n) { std::cout << " deallocate " << n << std::endl; free(p); } void construct(pointer p, const T& val) { std::cout << " construct " << val << " for " << typeid(p).name() << std::endl; new (static_cast<void*>(p)) T(val); } void construct(pointer p) { std::cout << " construct" << typeid(p).name() << std::endl; new (static_cast<void*>(p)) T(); } void destroy(pointer p) { std::cout << " destroy " << typeid(p).name() << std::endl; p->~T(); } }; int main() { std::vector<int, static_allocator<int>> v; std::cout << "START" << std::endl; for (auto i : std::array<int, 7>{4, 8, 15, 16, 23, 42, 108}) { std::cout << "push_back " << i << std::endl; v.push_back(i); } std::cout << "END" << std::endl; }
运行输出:
START push_back 4 max_size 4611686018427387903 max_size 4611686018427387903 allocate 1 construct 4 for Pi push_back 8 max_size 4611686018427387903 max_size 4611686018427387903 allocate 2 construct 8 for Pi construct 4 for Pi destroy Pi deallocate 1 push_back 15 max_size 4611686018427387903 max_size 4611686018427387903 allocate 4 construct 15 for Pi construct 4 for Pi construct 8 for Pi destroy Pi destroy Pi deallocate 2 push_back 16 construct 16 for Pi push_back 23 max_size 4611686018427387903 max_size 4611686018427387903 allocate 8 construct 23 for Pi construct 4 for Pi construct 8 for Pi construct 15 for Pi construct 16 for Pi destroy Pi destroy Pi destroy Pi destroy Pi deallocate 4 push_back 42 construct 42 for Pi push_back 108 construct 108 for Pi END destroy Pi destroy Pi destroy Pi destroy Pi destroy Pi destroy Pi destroy Pi deallocate 8
原因解析
这个现象是由你所用的STL实现(比如GCC的libstdc++)内部的双重检查逻辑导致的,拿libstdc++举例:
第一次调用:扩容前的容量校验
vector触发扩容时,会先算出新的目标容量(一般是当前容量的2倍),然后通过内部的__check_len函数检查这个容量是否超过allocator的max_size(),防止出现内存申请溢出或非法请求,这是第一次调用的来源。第二次调用:分配内存时的参数验证
在调用自定义allocator的allocate()之前,libstdc++的分配器封装逻辑会再调用一次max_size(),确认要分配的元素数量在allocator允许的范围内,保证分配请求合法。
说白了就是STL实现里做了两层安全检查:一层在vector的扩容流程里,另一层在分配器的分配入口处。C++标准并没有限制max_size()的调用次数,只要它每次返回的结果一致,就符合规范。不同STL实现(比如MSVC的版本)可能不会有两次调用,这属于厂商实现细节的差异,不违反标准。
内容的提问来源于stack exchange,提问作者nowox
相关产品推荐
相关产品推荐

