删除不是使用 new 表达式构造的对象实际上可以吗?

Is it practically OK to delete object not constructed using the new expression?

迂腐地说,这可能不太好。根据 cppref:

If expression is anything else, including if it is a pointer obtained by the array form of new-expression, the behavior is undefined.

抛开这个,下面的代码在实践中是否可行(T 是非数组,并假设 new 没有被替换)?

auto p = (T*)operator new(sizeof(T));
new(p) T{};
delete p;

据说在cppref那个

When calling the allocation function, the new-expression passes the number of bytes requested as the first argument, of type std::size_t, which is exactly sizeof(T) for non-array T.

所以我想这可能没问题。不过也有人说自从C++14,

New-expressions are allowed to elide or combine allocations made through replaceable allocation functions. In case of elision, the storage may be provided by the compiler without making the call to an allocation function (this also permits optimizing out unused new-expression). In case of combining, the allocation made by a new-expression E1 may be extended to provide additional storage for another new-expression E2 if all of the following is true: [...]

Note that this optimization is only permitted when new-expressions are used, not any other methods to call a replaceable allocation function: delete[] new int[10]; can be optimized out, but operator delete(operator new(10)); cannot.

我不太确定其中的含义。那么,这在 C++14 中可以吗?

我为什么要问这个问题? (source)

有时,内存分配和初始化不能一步完成。 你必须手动分配内存,做其他事情,然后初始化对象, 例如,提供强大的异常安全性。 在这种情况下,如果无法在结果指针上使用 delete 表达式,则必须手动取消初始化和解除分配,这很繁琐。 更糟糕的是,如果同时使用新表达式和手动方法, 您必须跟踪每个对象使用了哪一个。

OP最后一段没看懂。显式调用析构函数然后释放指针有什么繁琐的地方?这就是 STL 和我见过的任何有信誉的库一直在做的事情。是的,您必须跟踪分配内存的方式,以便将其重新分配。阅读 std::vector and/or std::shared_ptr 的 api 文档可以熟悉正确的做事方式。 如果您不熟悉显式 dtor 调用的语法,这里有一个简单的片段:

void * raw_ptr= my_alloc(sizeof(my_type),alignof(my_type));
if (!raw_ptr)
     throw my_bad_alloc();
my_type* ptr {new(raw_ptr) my_type{init_params}};
raw_ptr=nullptr;
    //...
ptr->~my_type();//hello! Is it me you're lookin' for?
if (!my_dealloc(ptr))
     throw my_runtime_error();
ptr=nullptr;

如果您有 p = new(p) T{};,则此代码具有 well-defined 行为,假设没有自定义(解除)分配函数在起作用。 (它可以很容易地针对此类事情进行强化;这样做留作 reader 的练习。)

规则是你必须给non-arraydelete一个指向由non-arraynew(或基class创建的对象的指针(具有虚拟析构函数)此类对象的子对象,或空指针)。有了那个改变,你的代码就可以做到这一点。

不要求 non-array new 必须是 non-placement 形式。不可能,因为你最好能够删除 new(std::nothrow) int。这也是 new-expression 的位置,尽管人们在谈论 "placement new".

时通常不是这个意思

delete 被定义为导致调用释放函数(忽略省略情况,这在这里无关紧要,因为 new-expression[ 调用的唯一分配函数=26=] 不是可替换的全局分配函数)。如果您进行设置以便将无效参数传递给该解除分配函数,那么您会从中得到未定义的行为。但是这里它传递了正确的地址(如果使用了大小的释放函数,则传递了正确的大小),所以它是 well-defined.