9

这是对这个问题的跟进。假设我有这个代码:

class Class {
    public virtual method()
    {
        this->~Class();
        new( this ) Class();
    }
};

Class* object = new Class();
object->method();
delete object;

这是这个答案所暗示的简化版本。

method()现在,一旦从对象生命周期内调用析构函数object,调用代码中的指针变量就会变得无效。然后在同一位置创建新对象。

这是否使指向调用中对象的指针再次有效?

4

6 回答 6

8

这在 3.8:7 中得到明确批准:

3.8 对象生命周期 [basic.life]

7 - 如果在对象的生命周期结束后[...],在原始对象占用的存储位置创建一个新对象,则可以使用指向原始对象的指针[...]操作新对象,如果:(在这种情况下满足各种要求)

给出的例子是:

struct C {
  int i;
  void f();
  const C& operator=( const C& );
};
const C& C::operator=( const C& other) {
  if ( this != &other ) {
    this->~C(); // lifetime of *this ends
    new (this) C(other); // new object of type C created
    f(); // well-defined
  }
  return *this;
}
于 2012-09-20T13:33:43.380 回答
4

严格来说,这很好。但是,如果不加倍小心,它就会变成一块丑陋的UB。例如,任何调用此方法的派生类都不会重新构造正确的类型,或者如果Class()抛出异常会发生什么情况。此外,这并没有真正完成任何事情。

它不是严格意义上的 UB,而是一大堆废话和失败,应该一见即焚。

于 2012-09-20T12:53:47.923 回答
4

指针在object任何时候都不会变得无效(假设你的析构函数没有调用delete this)。您的对象从未被释放,它只是调用它的析构函数,即它已经清理了它的内部状态(关于实现,请注意标准严格定义对象在析构函数调用后被销毁)。由于您已使用placement new 在完全相同的地址实例化新对象,因此技术上没问题。

C++ 标准的第 3.8.7 节涵盖了这个确切的场景:

如果在一个对象的生命周期结束之后并且在对象占用的存储空间被重用或释放之前,在原始对象占用的存储位置创建一个新对象,一个指向原始对象的指针,一个指向原始对象的引用引用原始对象,或者原始对象的名称将自动引用新对象,一旦新对象的生命周期开始,可用于操作新对象 [...]

也就是说,这只是作为学习代码,作为生产代码才有趣,这太可怕了:)

于 2012-09-20T12:57:26.387 回答
0

指针只知道它的地址,一旦你确认新对象的地址就是指针所指向的地址,答案是肯定的。

在某些情况下,人们会认为地址不会改变,但在某些情况下它确实会改变,例如在使用 C 时realloc()。但那是另一回事了。

于 2012-09-20T12:55:00.967 回答
0

您可能需要重新考虑显式调用析构函数。如果您要执行的代码恰好位于析构函数中,则将该代码移至新方法并从析构函数调用该方法以保留当前功能。析构函数实际上是用于超出范围的对象。

于 2012-09-20T13:30:50.397 回答
-2

在已销毁对象的位置创建新对象不会使任何指针再次有效。它们可能指向一个有效的新对象,但不是您最初引用的对象。

您应该保证在销毁原始对象之前已删除所有引用或以某种方式将其标记为无效。

这将是一个特别难以调试的情况。

于 2012-09-20T12:58:57.560 回答