首先,一个例子:
#include <cstddef>
#include <iostream>
struct S
{
S(int i) { if(i > 42) throw "up"; }
static void* operator new(std::size_t s, int i, double d, char c)
{
std::cout << "allocated with arguments: "
<<i<<", "<<d<<", "<<c<<std::endl;
return new char[s];
}
static void operator delete(void* p, int i, double d, char c)
{
std::cout << "deallocated with arguments: "
<<i<<", "<<d<<", "<<c<<std::endl;
delete[] (char*)p;
}
static void operator delete(void* p)
{
std::cout << "deallocated w/o arguments"<<std::endl;
delete[] (char*)p;
}
};
int main()
{
auto p0 = new(1, 2.0, '3') S(42);
S* p1 = nullptr;
try
{
p1 = new(4, 5.0, '6') S(43);
}catch(const char* msg)
{
std::cout << "exception: "<<msg<<std::endl;
}
delete p1;
delete p0;
}
输出:
分配有参数:1、2、3
分配有参数:4、5、6
使用参数解除分配:4、5、6
例外:向上
无参数释放
运算符 new 重载的规范定义是void *operator new(std::size_t, heap h)
我不明白这是如何规范的,因为它是不允许的:
好的,现在它是一个有效的放置形式new
:)
[basic.stc.dynamic.allocation]/1
分配函数应该是类成员函数或全局函数;如果分配函数在全局范围以外的命名空间范围内声明或在全局范围内声明为静态,则程序是格式错误的。返回类型应为void*
. 第一个参数应具有类型std::size_t
。第一个参数不应有关联的默认参数。第一个参数的值应解释为请求的分配大小。
[强调我的]
您可以重载要为 的放置形式调用的分配函数new
,请参阅 [expr.new] (对于非模板函数,[basic.stc.dynamic.allocation] 中没有明确允许,但也不禁止)。给出的位置在new(placement)
这里被概括为一个表达式列表。特定新表达式的表达式列表中的每个表达式都作为附加参数传递给分配函数。如果释放函数被调用(例如,因为被调用的 ctor 抛出异常),相同的参数加上前导(分配函数的返回值)被传递给释放函数。void*
[expr.new]/18 指出:
如果上述对象初始化的任何部分因抛出异常而终止,则已为该对象获得存储空间,并且可以找到合适的释放函数,则调用释放函数以释放构造对象的内存,之后异常继续在new-expression的上下文中传播。如果找不到明确匹配的解除分配函数,则传播异常不会导致对象的内存被释放。[注:这适用于被调用的分配函数不分配内存的情况;否则,很可能导致内存泄漏。——尾注]
和 /21
如果一个new 表达式调用一个释放函数,它会将分配函数调用返回的值作为 type 的第一个参数传递void*
。如果调用了放置解除分配函数,则传递给它的附加参数与传递给放置分配函数的参数相同,即与使用 new-placement 语法指定的参数相同。
和 /20
如果放置释放函数的声明具有相同数量的参数,并且在参数转换之后,除第一个参数类型之外的所有参数类型相同,则放置释放函数的声明与放置分配函数的声明相匹配。任何非布局释放函数都匹配非布局分配函数。如果查找找到一个匹配的释放函数,则调用该函数;否则,不会调用释放函数。如果查找找到通常释放函数的双参数形式,并且该函数被认为是放置释放函数,将被选为分配函数的匹配项,则程序是非良构的。[示例:
struct S {
// Placement allocation function:
static void* operator new(std::size_t, std::size_t);
// Usual (non-placement) deallocation function:
static void operator delete(void*, std::size_t);
};
S* p = new (0) S; // ill-formed: non-placement deallocation function matches
// placement allocation function
—结束示例]
回到 [basic.stc.dynamic.deallocation]:
1 释放函数应为类成员函数或全局函数;如果释放函数在全局范围以外的命名空间范围内声明或在全局范围内声明为静态,则程序格式错误。
2 每个解除分配函数应返回void
,其第一个参数应为void*
. 一个释放函数可以有多个参数。