混合运算符和表达式 new/delete

Mixing operator and expression new/delete

考虑以下代码:

unsigned char* a = new unsigned char[100];
void* a1 = reinterpret_cast<void*>(a);
operator delete[](a1);

short* b = new short[100];
void* b1 = reinterpret_cast<void*>(b);
operator delete[](b1);

在我看来,它是标量类型的有效语法,因为 delete-expression 等效于调用析构函数和运算符 delete。 POD 类型相同:

struct POD {
    int i;
    float f;
    char c;
};
POD* c = new POD[100];
void* c1 = reinterpret_cast<void*>(c);
operator delete[](c1);

标量和 POD 类型使用 operator new 分配并使用 delete 表达式释放的另一件事:

void* d = operator new[](sizeof(unsigned char) * 100);
unsigned char* d1 = reinterpret_cast<unsigned char*>(d);
delete[] d1;

void* e = operator new[](sizeof(short) * 100);
short* e1 = reinterpret_cast<short*>(e);
delete[] e1;

void* f = operator new[](sizeof(POD) * 100);
POD* f1 = reinterpret_cast<POD*>(f);
delete[] f1;

所有这些例子似乎都是合式的,但我没能找到它是否真的合式。有人可以确认或告诉我哪里错了吗?特别是我担心所有这些类型都有不同的对齐方式,它应该传递给 operator new/delete:

alignof(unsigned char) 1
alignof(short) 2
alignof(POD) 4


更新。有些人似乎将这个问题与 Is it safe to delete a void pointer? 混淆了。我问的是 expression newoperator delete 以及 operator newexpression delete 的混合。根据标准表达式形式是用运算符形式实现的。问题是将它们混合用于标量和 POD 类型的有效性如何?

根本无效。当你用析构函数分配一个对象数组时,运行 时间必须记住有多少对象要调用析构函数。最简单的方法是在数组之前分配一些额外的内存,并将计数存储在那里。这意味着如果您使用析构函数分配 3 个单字节对象,operator new 将被要求(比如说)11 个字节的内存 - 其中三个保存对象,8 个保存计数(在 size_t).

operator delete 想要那个 11 字节内存块的地址 - 而不是 holds/held 三个对象的 3 字节块的地址。

现在,我知道你问的是没有析构函数的内置类型 - 重点是,运行-time 库很可能会选择分配计数 无论如何为简单起见。

我认为将 new-expressionsdelete-expressions 与调用(甚至关联的) 分配函数配对 释放函数 ——如 operator delete[]() 等——导致未定义的行为。据我所知,前者可能会做一些额外的内务处理,而后者在 "raw memory" 上运行。

这是来自 ISO/IEC 14882:2014,第 5.3.5 条,它似乎明确说明了这一点(我自己设置为粗体):

In the first alternative (delete object), the value of the operand of delete may be a null pointer value, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject (1.8) representing a base class of such an object (Clause 10). If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete may be a null pointer value or a pointer value that resulted from a previous array new-expression.81 If not, the behavior is undefined. [ Note: this means that the syntax of the delete-expression must match the type of the object allocated by new, not the syntax of the new-expression. — end note ]

Some seem to confuse this question with Is it safe to delete a void pointer?.

(^-- 答案是“,但在某些情况下它可能会偶然出现......在某些情况下。”)

I am asking about mixing expression new with operator delete and operator new with expression delete.

...如果它是合法的,可能会很好地回答上述问题,不是吗?

"Nope, you can't use ordinary `delete[]`...but you can use `operator delete[]`.
Its type signature takes a `void*` instead of a typed pointer."

但这不是答案(如果是的话,编译器似乎有点固执,不只是为你做那件事。)无论哪种方式,问题是只给定一个空指针而没有类型信息,编译器不一定具有正确的 "tear-down" 代码来处理 "build-up" 它针对 new.

中给出的类型所做的任何事情

(如果您认为为基本类型做一些特殊或不同的事情可能没有意义,那么如果您有一个地址消毒器或未定义的行为消毒器类型的工具想要为 short 添加一些特殊的东西,这些东西的测量方式与另一种类型不同......也许是因为你特别要求以不同的方式测量短裤?)

由于普通删除是按照operator delete[]实现的,所以没有额外的魔力……就更少了!

因此答案几乎相同。