我怎样才能明智地重载新的放置运算符?

问题描述 投票:0回答:9

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

c++ visual-c++ memory-management operator-overloading
9个回答
17
投票

正确答案是 您无法更换新的操作员位置

§18.4.1.3 安置表格
这些函数是保留的,C++ 程序不能定义替换标准 C++ 库中版本的函数。

原理:分配和释放运算符的唯一目的是分配和释放内存,因此当给定内存时,不应该执行任何其他操作。 (该标准特别指出这些功能“故意不执行其他操作。”)


5
投票

从技术上讲,放置

operator new
是除了所需内存大小之外还需要其他参数的任何
operator new

因此,

new(std::nothrow) X
使用位置
operator new
new(__FILE__, __LINE__) X
也是如此。

覆盖

operator new(size_t, void*)
的唯一原因可能是添加跟踪信息,但我认为对此的需求会非常低。


4
投票

一个示例位于 Stroustrup 的常见问题解答中。


1
投票

最明显的覆盖是复制此实现。

另一个明智的做法是添加一些检查(例如,验证请求区域内没有“绑定标记”)。

然而,我认为,由于名称查找的机制,一旦您覆盖其他类(对于给定的类),重点就不仅仅是您必须覆盖它(或者不覆盖它以防止其使用,这很好)也是,但这是一个有意识的决定)。


1
投票

为保留区域定义自己的内存管理是一种很好的用途。

对同一物理数据有不同的看法(无需移动数据)是其他有趣的用途。 它还允许您将结构化文件作为缓冲区上的字符读取,然后通过在缓冲区上定义该类的对象来叠加其逻辑结构。 这个东西与文件的内存映射相结合,可以大大提高性能。 内存映射硬件... 所以,数千个申请!


1
投票

放置新重载最重要的额外功能是检查地址对齐。

例如,假设某个类需要 16 字节对齐。开发人员重载 new、new[]、delete 和 delete[] - 只是为了确保一切都正确对齐。

当他尝试将他的类与使用新放置的库一起使用时,一切都工作正常...库不知道该类是否需要/需要什么对齐方式,并且尝试将对象“放置”到可能不会的地址对齐 - 大繁荣。

这种情况最简单的例子 - 尝试使用 std::vector,其中 T 需要非标准对齐。

放置新重载允许检测指针是否未对齐 - 可能会节省调试时间。


0
投票

我见过一个示例,其中两个参数 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';
}

虽然我猜这不会被称为“新的放置”,因为它不执行放置。如果模板化,它可能会很有用,这样它就可以构建任何类型的数组,并填充作为第二个参数传递的对象的副本……但是无论如何,我们都有容器。


0
投票

我不太确定这个问题,但以下内容会覆盖班级级别的新安置:

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 很有趣。我相信这仅适用于全局运算符新函数,而不适用于特定于类的函数。


-1
投票

我的主要用途是创建大量对象。它的性能更好,并且在整个块中分配内存的开销更少,即使用 Win32 中的 VirtualAlloc(在对 Windows 进行编程时)。然后,您只需将该块内的 ptr 传递给新的每个对象放置,例如:

char *cp = new char[totalSize];

for(i = 0; i < count; i++, cp += ObjSize)        
{                                                        
    myClass *obj = new(cp) myClass;             
}
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.