将原始操作符new、放置new和标准删除合并是否合法



伙计们!出于好奇——以下代码可能不合法,是吗?

T *p = ::operator new(sizeof(T)); // allocate memory for a T
new (p) T; // construct a T into the allocated memory
delete p; //delete the object using the standard delete operator

否。你只能delete你从new得到的东西——没有例外。

至少有一种情况下它是明确未定义的:如果您为T重载了operator newoperator delete,那么它将尝试使用::operator new分配内存,但使用T::operator delete删除它。除非您的T::operator delete纯粹是::operator delete的包装,否则这将导致问题。

除此之外,我认为这可能是有定义的。该标准非常具体地说明了new expression使用分配函数(§5.3.4/10(分配内存的事实,只要您没有提供T::operator new,分配函数就会是::operator new

然后,您的placement new表达式初始化对象,就像§5.3.4/15的第一个要点中对新表达式所描述的那样。

然后我们来到毁灭的一边。根据$5.3.5/1的说法:"delete表达式运算符会破坏由新表达式创建的最派生的对象(1.8(或数组。"这需要您使用新表达式来创建对象——您已经使用了新表达式。您使用了一个新的位置,这是新表达式的可能性之一,并在§5.3.4/1中进行了规定。

下一个适用的要求似乎是:"操作数应具有指针类型,或具有指向指针类型的单个转换函数(12.3.2(的类类型。"同样,您的表达式也满足这一要求。

我将引用更多的要求,不做进一步的评论,只是你的代码似乎满足了所有这些要求(有些限制了删除表达式的实现,而不是你可以在其中使用的指针(:

  • (§5.3.5/2(:"在第一种选择(删除对象(中,删除操作数的值应为指向非数组对象的指针或指向表示此类对象基类的子对象(1.8(的指针(第10条(。如果不是,则行为未定义。">

  • (§5.3.5/3(:"在第一种选择(删除对象(中,如果操作数的静态类型与其动态类型不同,则静态类型应为操作数动态类型的基类,静态类型应具有虚拟析构函数或行为未定义

  • (§5.3.5/4(:"在第一种选择(删除对象(中,如果操作数的静态类型与其动态类型不同,则静态类型应为操作数动态类型的基类,静态类型应具有虚拟析构函数或行为未定义。">

  • (§5.3.5/6(:"删除表达式将调用被删除的对象或数组元素的析构函数(如果有的话(。">

  • (§5.3.5/7(:删除表达式将调用解除分配函数(3.7.3.2(。

关于::operator newT::operator new的最初警告,我认为您在delete expression中使用的指针满足所有要求,因此应该定义行为。

说了这么多,我当然希望这纯粹是学术兴趣——尽管在我看来代码确实有定义的行为,但这充其量也是一个糟糕的想法。

离开DeadMG的正确断言,对代码进行轻微更改是没有问题的:

unsigned char* addr = new unsigned char[sizeof(MySimpleStructure)];
MySimpleStructure* p = new (addr) MySimpleStructure;
delete [] addr;

由于我们是delete,而addr是由new返回的,因此这是合法的。当然,在addr被删除后,p就不应该被访问(当时它是一个悬空指针(。还要注意的是,通过放置新分配给pMySimpleStructure将不会调用其去构造器。

最新更新