我可以用不同的参数覆盖全局operator new
,因此例如我可以具有:
void* operator new (std::size_t size) throw (std::bad_alloc);
void* operator new (std::size_t size, int num) throw (std::bad_alloc);
可以单独称为
int* p1 = new int; // calls new(size_t)
int* p2 = new(5) int; // calls new(size_t, int)
由于其中的每一个都可能使用一些不同的分配方案,因此我需要为每一个单独的delete()
函数。但是,delete(void*)
不能以同样的方式过载!delete(void*)
是唯一有效的签名。那么,如何处理上述情况呢?
附言:我并不是说这是个好主意。这种事情发生在我身上,所以我在c++中发现了这个"缺陷"(至少在我看来)。如果该语言允许new
重写,则必须允许delete
重写,否则它将变得无用。所以我想知道是否有办法解决这个问题,而不是这个主意。
我可以用不同的参数覆盖全局操作员新
这些被称为放置分配函数。
delete(void*)是唯一有效的签名。
没有。
首先,一些术语:delete p
等删除表达式不仅仅是一个函数调用,它调用析构函数,然后调用释放函数,这是operator delete
的一些重载,由重载解析选择。
您可以用签名覆盖operator delete
以匹配您的放置分配函数,但只有当放置新表达式调用的构造函数抛出异常时才会使用该重载,例如
struct E {
E() { throw 1; }
};
void* operator new(std::size_t n, int) throw(std::bad_alloc) { return new char[n]; }
void operator delete(void* p, int) { std::puts("hello!"); delete[] (char*)p; }
int main()
{
try {
new (1) E;
} catch (...) {
puts("caught");
}
}
放置解除分配函数与使用的放置新表达式的形式相匹配(在这种情况下,它有一个int
参数),通过重载解析找到并调用该函数来解除分配存储。
因此,您可以提供"placement-delete"函数,但不能显式调用它们。这取决于您记住如何分配对象,并确保使用相应的解除分配。
如果跟踪使用不同的new
重载分配的不同内存区域,则可以使用调用的new
版本对它们进行标记。
然后在delete
时,您可以查找地址以找到调用了哪个new
,并在每种情况下执行不同的操作。
通过这种方式,您可以确保正确的逻辑自动与每个不同的new
过载相关联。
正如巴鲁克在下面的评论中指出的那样,维护用于跟踪的数据会带来性能开销,而且只有在重载删除没有传递使用默认删除分配的任何内容的情况下,这种逻辑才会起作用。
就跟踪开销而言,在我看来,跟踪分配类型的最小开销方法是分配请求的数量,加上在分配区域开始时标记请求类型的少量额外空间(根据保守的对齐要求确定大小)。然后,您可以在删除时查看此标记区域,以确定要遵循的逻辑。
有一个与放置操作符new匹配的放置删除而且你不能直接叫它。这是因为放置删除仅用于在新对象的构造函数抛出时释放内存。
void *operator new(size_t, Area&); // placement new
void operator delete(void*, Area&); // matching placement delete
...
Area &area;
SomeType *t=new(area) SomeType();
// when SomeType() throws then `delete(t,area)` from above is called
// but you can't do this:
delete (area) t;
克服这一问题的一种常见方法是使用编写一个重载的"destroy"函数,该函数接受各种参数。
template<class T> void destroy(Area &a, T* &pt) //<-you can't do this with 'delete'
{
if (pt) {
pt->~T(); // run the destructor
a.freeMem(pt); // deallocate the object
pt=NULL; // nulls the pointer on the caller side.
}
}
简单的答案是:不要这样做。所有形式的非放置new
在C++11中都是多余的,而且非常不安全,例如,返回原始指针。如果要在自定义位置分配对象,请使用带有allocate
函数(如果有状态)或自由函数(如果没有)的类。对于new
和delete
,最好的处理方法是带着偏见将它们从程序中删除,placement new
可能除外。
编辑:它对你无用的原因是因为你试图将它用于非预期目的。你所能使用的额外参数只是日志记录或其他行为控制之类的东西。您无法真正更改new
和delete
的基本语义。如果需要有状态分配,则必须使用类。
你错了。可以提供放置删除。