C++ 允许重载
operator new
- 全局和每个类 - 通常 operator new
、operator new[]
分别与 new[]
语句和放置 operator new
一起使用。
这三个中的前两个通常因使用自定义分配器和添加跟踪而过载。但放置
operator new
看起来非常简单 - 它实际上在内部什么也没做。例如,在 Visual C++ 中,默认实现仅返回传递到调用中的地址:
//from new.h
inline void* operator new( size_t, void* where )
{
return where;
}
它还能做什么? 为什么以及如何合理地超载放置
operator new
?
正确答案是 您无法更换新的操作员位置。
§18.4.1.3 安置表格
这些函数是保留的,C++ 程序不能定义替换标准 C++ 库中版本的函数。
原理:分配和释放运算符的唯一目的是分配和释放内存,因此当给定内存时,不应该执行任何其他操作。 (该标准特别指出这些功能“故意不执行其他操作。”)
从技术上讲,放置
operator new
是除了所需内存大小之外还需要其他参数的任何operator new
。
因此,
new(std::nothrow) X
使用位置 operator new
,new(__FILE__, __LINE__) X
也是如此。
覆盖
operator new(size_t, void*)
的唯一原因可能是添加跟踪信息,但我认为对此的需求会非常低。
一个示例位于 Stroustrup 的常见问题解答中。
最明显的覆盖是复制此实现。
另一个明智的做法是添加一些检查(例如,验证请求区域内没有“绑定标记”)。
然而,我认为,由于名称查找的机制,一旦您覆盖其他类(对于给定的类),重点就不仅仅是您必须覆盖它(或者不覆盖它以防止其使用,这很好)也是,但这是一个有意识的决定)。
为保留区域定义自己的内存管理是一种很好的用途。
对同一物理数据有不同的看法(无需移动数据)是其他有趣的用途。 它还允许您将结构化文件作为缓冲区上的字符读取,然后通过在缓冲区上定义该类的对象来叠加其逻辑结构。 这个东西与文件的内存映射相结合,可以大大提高性能。 内存映射硬件... 所以,数千个申请!
放置新重载最重要的额外功能是检查地址对齐。
例如,假设某个类需要 16 字节对齐。开发人员重载 new、new[]、delete 和 delete[] - 只是为了确保一切都正确对齐。
当他尝试将他的类与使用新放置的库一起使用时,一切都工作正常...库不知道该类是否需要/需要什么对齐方式,并且尝试将对象“放置”到可能不会的地址对齐 - 大繁荣。
这种情况最简单的例子 - 尝试使用 std::vector
放置新重载允许检测指针是否未对齐 - 可能会节省调试时间。
我见过一个示例,其中两个参数 new [] 被覆盖以返回预先填充有作为附加参数传递的 char 的内存块。我不记得原始代码使用了什么(可能是 memset()),但它的功能是这样的:
#include <iostream>
#include <algorithm>
#include <new>
void* operator new [](size_t n, char c)
{
char* p = new char[n];
std::fill(p, p+n, c);
return p;
}
int main()
{
char* p = new('a') char[10];
std::cout << p[0] << p[1] << ".." << p[9] << '\n';
}
虽然我猜这不会被称为“新的放置”,因为它不执行放置。如果模板化,它可能会很有用,这样它就可以构建任何类型的数组,并填充作为第二个参数传递的对象的副本……但是无论如何,我们都有容器。
我不太确定这个问题,但以下内容会覆盖班级级别的新安置:
struct Bar {
void* operator new(size_t /* ignored */, void* where) throw() { return where; }
};
int main() {
char mem[1];
Bar* bar = new(mem) Bar;
}
我相信这是合法的 C++(并且可以使用 gcc 4.4.6 编译并运行良好)。
您可以根据需要随意更改此运算符的实现(包括删除
throw()
子句,这意味着编译器在调用构造函数之前不再检查 where
指针是否为 null)。不过请小心行事。
§18.4.1.3 很有趣。我相信这仅适用于全局运算符新函数,而不适用于特定于类的函数。
我的主要用途是创建大量对象。它的性能更好,并且在整个块中分配内存的开销更少,即使用 Win32 中的 VirtualAlloc(在对 Windows 进行编程时)。然后,您只需将该块内的 ptr 传递给新的每个对象放置,例如:
char *cp = new char[totalSize];
for(i = 0; i < count; i++, cp += ObjSize)
{
myClass *obj = new(cp) myClass;
}