关于Hinnant堆栈分配器的问题
Questions about Hinnant's stack allocator
我一直在使用Howard Hinnant的堆栈分配器,它就像一个魅力,但是实现的一些细节对我来说有点不清楚。
- 为什么使用全球运营商
new
和delete
?allocate()
和deallocate()
成员函数分别使用::operator new
和::operator delete
。同样,成员函数construct()
使用全局放置 new。为什么不允许任何用户定义的全局或特定于类的重载? - 为什么对齐方式设置为硬编码的 16 字节而不是
std::alignment_of<T>
? - 为什么构造函数和
max_size
具有throw()
异常规范?这不是不鼓励这样做吗(例如,请参阅第 14 项C++更有效。当分配器中发生异常时,是否真的有必要终止和中止?新的 C++11noexcept
关键字是否会改变这一点? construct()
成员函数将是完美转发(到正在调用的构造函数(的理想候选项。这是编写 C++11 一致性分配器的方式吗?- 还需要进行哪些其他更改才能使当前代码符合 C++11?
我一直在使用霍华德·欣南特的堆栈分配器,它可以工作 像一个魅力,但实现的一些细节有点 我不清楚。
很高兴它一直在为您工作。
1. 为什么使用全局运算符
new
和delete
?allocate()
和deallocate()
成员函数使用::operator new
和 分别::operator delete
。同样,成员函数construct()
使用全局放置 new。为什么不允许任何 用户定义的全局或特定于类的重载?
没有特别的原因。 请随意以最适合您的方式修改此代码。 这更像是一个例子,而且绝不是完美的。 唯一的要求是分配器和解除分配器提供正确对齐的内存,并且构造成员构造参数。
在 C++11 中,构造(和销毁(成员是可选的。 如果您在提供allocator_traits
的环境中操作,我鼓励您从分配器中删除它们。 要找出答案,只需删除它们,看看是否仍然编译。
2. 为什么对齐方式设置为硬编码的 16 字节而不是
std::alignment_of<T>
字节?
std::alignment_of<T>
可能会正常工作。 那天我可能是偏执狂。
3. 为什么构造函数和
max_size
有throw()
异常规范?这不是气馁吗(例如,请参阅更有效C++ 项目14.(?是否真的有必要在以下情况下终止和中止 分配器中发生异常?新 C++11 是否会更改此情况noexcept
关键词?
这些成员永远不会扔。 对于 C++11,我应该将它们更新为 noexcept
. 在C++11中,用noexcept
装饰事物变得更加重要,尤其是特殊成员。 在 C++11 中,可以检测表达式是否为 nothrow。 代码可以根据该答案进行分支。 已知为 nothrow 的代码更有可能导致泛型代码分支到更有效的路径。 std::move_if_noexcept
是 C++11 中的规范示例。
永远不要使用throw(type1, type2)
。 它已在 C++11 中弃用。
当你真的想说时,请使用throw()
:这永远不会抛出,如果我错了,请终止程序,以便我可以调试它。 throw()
也在 C++11 中被弃用,但有一个直接的替代品:noexcept
。
4.
construct()
成员函数将是完美转发(到正在调用的构造函数(的理想候选者。这是 编写 C++11 符合分配器的方法?
是的。 但是allocator_traits
会为您做到这一点。 让它吧。 std::lib 已经为您调试了该代码。 C++11 个容器将调用allocator_traits<YourAllocator>::construct(your_allocator, pointer, args...)
。 如果分配器实现了这些函数,allocator_traits将调用您的实现,否则它将调用经过调试的高效默认实现。
5. 要使当前代码符合 C++11,还需要进行哪些其他更改?
说实话,这个分配器并不是真正的C++03或C++11。 复制分配器时,原始副本和副本应该彼此相等。 在这种设计中,这永远不会正确。 然而,这件事仍然碰巧在很多情况下都有效。
如果要使其严格符合,则需要另一个级别的间接寻址,以便副本将指向同一缓冲区。
除此之外,C++11 分配器比 C++98/03 分配器更容易构建。 以下是您必须做的最低限度:
template <class T>
class MyAllocator
{
public:
typedef T value_type;
MyAllocator() noexcept; // only required if used
MyAllocator(const MyAllocator&) noexcept; // copies must be equal
MyAllocator(MyAllocator&&) noexcept; // not needed if copy ctor is good enough
template <class U>
MyAllocator(const MyAllocator<U>& u) noexcept; // requires: *this == MyAllocator(u)
value_type* allocate(std::size_t);
void deallocate(value_type*, std::size_t) noexcept;
};
template <class T, class U>
bool operator==(const MyAllocator<T>&, const MyAllocator<U>&) noexcept;
template <class T, class U>
bool operator!=(const MyAllocator<T>&, const MyAllocator<U>&) noexcept;
您可以选择考虑MyAllocator
可交换,并将以下嵌套类型放入分配器中:
typedef std::true_type propagate_on_container_swap;
还有其他一些类似的旋钮,您可以在 C++11 分配器上进行调整。 但是所有旋钮都有合理的默认值。
更新
上面我注意到我的堆栈分配器不符合,因为副本不相等。 我决定将此分配器更新为符合 C++11 分配器。 新的分配器称为short_allocator,在此处进行了记录。
short_allocator与堆栈分配器的不同之处在于,"内部"缓冲区不再是分配器的内部缓冲区,而是现在可以位于本地堆栈上的单独"竞技场"对象,也可以位于给定的线程或静态存储持续时间上。 arena
不是线程安全的,所以要注意这一点。如果你愿意,你可以让它线程安全,但这会减少回报(最终你会重新发明 malloc(。
这是一致的,因为分配器的副本都指向相同的外部arena
。 请注意,N
的单位现在是字节,而不是T
数。
可以通过添加 C++98/03 样板(typedefs、构造成员、销毁成员等(将此 C++11 分配器转换为 C++98/03 分配器。 一项乏味但简单的任务。
新short_allocator对这个问题的答案保持不变。
- 警告处理为错误这里有什么问题
- 最小硬币更换问题(自上而下方法)
- 为"adjacent"变量赋值时出现问题
- 我的神经网络不起作用 [XOR 问题]
- 在Ubuntu 16.04上安装Cilk时出现问题
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 编译包含字符串的代码时遇到问题
- Project Euler问题4的错误解决方案
- 问题:什么是QAbstractItemView::NoEditTriggers的反面
- 在编译C++代码(具有dlib和opencv)到WASM时面临问题
- 在进程中对同一管道进行读取和写入时C++管道出现问题
- 在C++为矢量编写自定义虚拟分配器时遇到问题
- 关于Hinnant堆栈分配器的问题
- 提升快速池分配器在函数指针方面存在问题
- 通过池分配器中的内存地址访问可用列表节点时出现问题
- 带有嵌入式竞技场的堆栈分配器的问题
- 启用-std=c++0x时,自定义分配器的编译问题
- 为什么在c++ 11中交换标准库容器会有问题(涉及分配器)
- 内存管理 - C++ 中的自定义分配器有什么问题?
- 有关自定义分配器的问题