使用用户定义的分配器的非标准库放置

时间:2012-03-13 18:50:41

标签: c++

非标准库放置new调用用户定义的运算符new:

class T {};

void* operator new(std::size_t s, T*) {
    return new char[s];
}

int main() {
    T* t;
    new(t) T(); // calls operator new above
    return 0;
}

但是,如果我没有错,标准展示位置new不会调用默认运算符new。这种分离允许标准分配器使用allocate()中的new new来获取内存和placement new来初始化它,使用std :: initialize_fill()或construct()

现在我不明白当使用非标准贴图new时,如何将分配与自定义分配器中的初始化分开,因为非标准贴图new总是调用用户定义的operator new。应该总是强制在任何分配器中使用带有static_cast的标准placement new吗?

1 个答案:

答案 0 :(得分:2)

据我所知,你在某种程度上混淆了operator new()新运算符:它们有不同的用途,尽管它们密切相关(Scott Meyers有一个关于此的项目)在有效的C ++中,如果正确回忆;或者,至少,他在早期版本中有一个项目,因为我现在看不到它):

  • operator new()的目的是让内存可用。通常这相当于从某个地方分配内存,但在使用placement new的特殊情况下,它实际上除了返回其参数之外什么也没做。
  • new运算符的用途,即使用带有运算符调用符号(而不是函数调用符号)的表达式调用operator new()包含两个步骤:
    1. 调用匹配的operator new()
    2. 它调用匹配的构造函数在获取的位置构建一个对象

也就是说,请注意,某个程序允许替换运算符newdelete的展示位置版本!只允许替换用于实际内存分配的8个运算符(即operator new(size_t)operator delete(void*),相应的数组数组版本以及它们的std::nothrow_t版本。有关更多详细信息,请参见17.6.4.6 [replacement.functions]。 placemement版本实际上是通过他们的地址参数和(operator delete()来满足存在,以防在构造对象期间抛出异常)。

分配器的allocate()成员意味着实际提供内存,例如通过调用malloc(n)operator new(n)(但不是new char[n]),mmap()页面等,这并不意味着实际构建任何内容对象(参见17.6.3.5 [allocator.requirements],特别是表28)。实际上构造对象是分配器的construct()成员的目的。事实上,这个函数基本上需要调用placement new:

template <typename... A>
void allocator::construct(void* address, A&&... args) {
    new(address) T(std::forward<A>(args)...);
}

(假设allocator负责分配T类型的对象。

在任何情况下,所记录的分配函数(malloc()operator new())都不会调用任何其他函数。它们可以在内部以通用函数的方式实现(可能就是这种情况)但您可以在自己的分配函数中调用它们。当然,如果您使用自己的版本替换运算符,则不会调用标准库提供的版本。