非标准库布局 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()
的表达式,包含两个步骤:- 它调用匹配的
运算符new()
- 它调用匹配的构造函数在获取的位置构造一个对象
- 它调用匹配的
也就是说,请注意程序不允许替换运算符new
和delete
的放置版本!只允许替换实际内存分配的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/