c++ - 带有用户定义分配器的新非标准库放置

标签 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 来获取内存并使用 std::initialize_fill() 或 construct() 来初始化它>

现在我不明白当使用非标准放置 new 时如何将分配与自定义分配器中的初始化分开,因为非标准放置 new 总是调用用户定义的运算符 new。是否应该在任何分配器中始终强制使用带有 static_cast 的标准布局 new?

最佳答案

据我所知,您在某种程度上混淆了 operator new()new 运算符:尽管它们密切相关,但它们具有不同的目的(Scott如果没记错的话,Meyers 在《Effective C++》中对此有一个条目;或者,至少,他在早期版本中有一个条目,因为我现在看不到它):

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

也就是说,请注意程序不允许替换运算符newdelete 的放置版本!只允许替换实际内存分配的8个运算符(即operator new(size_t)operator delete(void*)、对应的数组数组版本,以及它们的 std::nothrow_t 版本)。有关更多详细信息,请参阅 17.6.4.6 [替换功能]。放置版本实际上是为了传递它们的地址参数(对于操作符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())都不会调用任何其他函数。它们可能在内部以通用函数的形式实现(可能就是这种情况),但您可以在自己的分配函数中调用它们。当然,如果您用自己的版本替换运算符,则不会调用标准库提供的版本。

关于c++ - 带有用户定义分配器的新非标准库放置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9690202/

相关文章:

c++ - 求除数的个数?

c++ - 在图片框中以 cv::Mat 格式显示网络摄像头提要

c++ - 竞争条件 2 个线程交替

c++ - 为什么我的 SDL/C++ 图像消失了?

c++ - 仿真程序

c++ - 将自定义 Apache 2.4 模块与 Linux 上的 httpd/apr 库静态链接时出错

c++ - 将 unsigned int 转换为字节数组并返回

c++ - std::set 带有自定义比较器的操作

c++ - 使用地址值初始化指针

c++ - Visual Studio 2012 中的 Qt5.0.1 静态链接