我正在开发一个类似容器的类,并且我想像使用标准容器一样使用标准分配器基础结构。在网上我找到了很多关于如何单独使用 std::allocator
类,或者如何为标准容器定义自定义分配器的 Material ,但是关于如何一般使用符合标准的分配器非常少见,尤其是在 C++11 的上下文中,从编写自定义分配器的角度来看,事情似乎要容易得多,但从容器的角度来看,事情要复杂得多。
所以我的问题是关于如何以最通用的方式正确使用符合标准的分配器,具体来说:
- 首先,我什么时候应该用这种方式设计自定义容器?使用默认分配器而不是简单的 new/delete 是否存在合理的性能开销(包括缺少优化机会)?
- 我必须显式调用包含对象的析构函数吗?
- 如何区分有状态和无状态分配器?
- 如何处理有状态的分配器?
- 什么时候(如果有的话)两个实例可以互换(我什么时候可以用一个实例破坏另一个实例分配的内存)?
- 复制容器的时候也要复制吗?
- 在容器移动时可以/必须移动它们?
- 在容器的移动构造函数和移动赋值运算符中,何时可以将指针移动到分配的内存,何时必须分配不同的内存并移动元素?
- 在这种情况下是否存在异常安全问题?
我对有关 C++11 世界的答案特别感兴趣(它会改变 C++14 中的任何内容吗?)
最佳答案
在下面的所有答案中,我假设您要遵循 C++11 标准定义容器的规则。该标准不要求您以这种方式编写自定义容器。
- First of all, when should I design a custom container in this way? Is there a sensible performance overhead (including missing optimization opportunities) in using the default allocator instead of plain new/delete?
出于性能原因,自定义分配器最常见和最有效的用途之一是让它在堆栈外分配。如果您的自定义容器不能接受这样的分配器,那么您的客户端将无法执行这样的优化。
- Do I have to explicitly call contained objects' destructors?
您必须明确调用 allocator_traits<allocator_type>::destroy(alloc, ptr)
, 而后者将直接调用 value_type
的析构函数,或将调用 destroy
allocator_type
的成员.
- How do I discriminate between stateful and stateless allocators?
我不会打扰。假设分配器是有状态的。
- How to handle stateful allocators?
非常小心地遵循 C++11 中规定的规则。特别是那些在 [container.requirements.general] 中指定的分配器感知容器。规则太多,这里就不一一列举了。不过,我很乐意回答有关任何这些规则的具体问题。但第一步是获取标准的拷贝,并阅读它,至少是容器要求部分。我推荐the latest C++14 working draft为此目的。
- When (if ever) are two instances interchangeable (when can I destroy with one instance the memory allocated with another one)?
如果两个分配器比较相等,那么任何一个都可以释放从另一个分配的指针。拷贝(通过复制构造或复制分配)需要比较相等。
- They have to be copied when the container is copied?
在标准中搜索 propagate_on
和 select_on_container_copy_construction
了解细节。简单的回答是“视情况而定”。
- They can/have to be moved when the container is moved?
必须用于移动构造。移动分配取决于 propagate_on_container_move_assignment
.
- In the container's move constructor and move assignment operator, when can I move the pointer to allocated memory, and when do I have to allocate different memory and move the elements instead?
新的 move 构造的容器应该通过 move 构造 rhs 的分配器来获得它的分配器。这两个分配器需要比较相等。因此,您可以转移所有已分配内存的内存所有权,您的容器对于该指针的有效状态为 nullptr
在右侧。
移动赋值运算符可以说是最复杂的:行为取决于propagate_on_container_move_assignment
以及两个分配器是否比较相等。更完整的描述在我的“分配器备忘单”中。
- Are there issues about exception safety in this context?
是的,吨。 [allocator.requirements] 列出容器可以依赖的分配器要求。这包括哪些操作可以抛出,哪些不能抛出。
您还需要处理分配器的pointer
的可能性。实际上不是 value_type*
. [allocator.requirements] 也是寻找这些细节的地方。
祝你好运。这不是一个初学者项目。如果您有更具体的问题,请将它们发布到 SO。要开始,请直接进入标准。我不知道有关该主题的任何其他权威来源。
这是我为自己制作的一份备忘单,其中描述了分配器的行为以及容器的特殊成员。它是用英文写的,不是标准的eze。如果您发现我的备忘单和 C++14 工作草案之间有任何差异,请相信工作草案。一个已知的差异是我添加了 noexcept
以标准没有的方式规范。
Allocator behavior:
C() noexcept(is_nothrow_default_constructible<allocator_type>::value); C(const C& c);
Gets allocator from
alloc_traits::select_on_container_copy_construction(c)
.C(const C& c, const allocator_type& a);
Gets allocator from
a
.C(C&& c) noexcept(is_nothrow_move_constructible<allocator_type>::value && ...);
Gets allocator from
move(c.get_allocator())
, transfers resources.C(C&& c, const allocator_type& a);
Gets allocator from
a
. Transfers resources ifa == c.get_allocator()
. Move constructs from eachc[i]
ifa != c.get_allocator()
.C& operator=(const C& c);
If
alloc_traits::propagate_on_container_copy_assignment::value
istrue
, copy assigns allocators. In this case, if allocators are not equal prior to assignment, dumps all resources from*this
.C& operator=(C&& c) noexcept( allocator_type::propagate_on_container_move_assignment::value && is_nothrow_move_assignable<allocator_type>::value);
If
alloc_traits::propagate_on_container_move_assignment::value
istrue
, dumps resources, move assigns allocators, and transfers resources fromc
.If
alloc_traits::propagate_on_container_move_assignment::value
isfalse
andget_allocator() == c.get_allocator()
, dumps resources, and transfers resources fromc
.If
alloc_traits::propagate_on_container_move_assignment::value
isfalse
andget_allocator() != c.get_allocator()
, move assigns eachc[i]
.void swap(C& c) noexcept(!allocator_type::propagate_on_container_swap::value || __is_nothrow_swappable<allocator_type>::value);
If
alloc_traits::propagate_on_container_swap::value
istrue
, swaps allocators. Regardless, swaps resources. Undefined behavior if the allocators are unequal andpropagate_on_container_swap::value
isfalse
.
关于c++ - 在自定义容器类中使用分配器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21221527/