在C++中从析构函数中复活对象?

Mat*_*Mut 48 c++ destructor lifetime

免责声明:我知道这是一个糟糕的设计,我只是出于好奇而问这个问题,以便尝试深入了解析构函数在C++中是如何工作的.

在C#中,可以编写:GC.KeepAlive(this)在类的析构函数中(参见下面的编辑),这意味着即使在析构函数调用完成后,该对象仍将在内存中存活.

C++的设计是否允许从析构函数中恢复一个类似于上述C#算法的对象?

编辑:正如下面的答案所指出的,GC.ReRegisterForFinalize()与问题的关系更为密切GC.KeepAlive(this).

Sam*_*hik 100

最简洁的答案是不.C++不使用垃圾收集,如Java或C#.当一个物体被摧毁时,它会立即被摧毁.过得好.加入合唱团看不见.渴望峡湾等......

并且用不同的词语多次说出来,这样就不可能轻易地重新解释......

析构函数作为对象销毁的一部分被调用.对象破坏包括调用析构函数和释放用于对象本身的内存.这是一个单一的过程,而不是两个独立的过程.析构函数在运行时,对象仍然存在,供析构函数使用,但它存在于借来的时间.一旦析构函数返回,对象就会被蒸发,这已成定局.一旦调用析构函数,该对象将被销毁,并且没有任何东西会改变它的命运.

理解这一点:调用析构函数的原因是:对象最初是在堆上用"new"分配的,现在它正在"删除"d."删除"表示"删除",而不是"删除可能".所以对象被删除了.或者,如果对象是在堆栈上分配的,则执行线程退出范围,因此范围中声明的所有对象都将被销毁.从技术上讲,析构函数是由于对象被销毁而被调用的.所以,对象正在被摧毁.结束.

话虽如此,C++允许您为类实现自定义分配器.如果您愿意,可以编写自己的自定义内存分配和释放功能,以实现您想要的任何功能.虽然这些从未用于堆栈分配的对象(即局部变量).

  • 析构函数作为对象销毁的一部分被调用.对象破坏包括调用析构函数和释放用于对象本身的内存.这是一个单一的过程,而不是两个独立的过程.析构函数在运行时,对象仍然存在,供析构函数使用,但它存在于借来的时间.一旦析构函数返回,对象就会被蒸发,这已成定局.一旦调用析构函数,该对象将被销毁,并且没有任何东西会改变它的命运. (32认同)
  • 理解这一点:调用析构函数的原因是:对象最初是在堆上用"new"分配的,现在它正在"删除"d."删除"表示"删除",而不是"删除可能".所以对象被删除了.或者,如果对象是在堆栈上分配的,则执行线程退出范围,因此范围中声明的所有对象都将被销毁.从技术上讲,析构函数是由于对象被销毁而被调用的.所以,对象正在被摧毁.结束. (7认同)
  • @Falco它*可能是你可以销毁对象,然后按照指向以前的内存的指针,并且足够幸运地读取有效对象.也有可能你可以按下一个指向例如0x8F3B2780的指针,并恰好读取一个相同的有效对象.然而,在这两种情况下,绝对没有*方法来确定它是否可以在不尝试的情况下工作.这两种行为都是同样不明确的,也不应该以任何方式鼓励.如果要访问该对象,*不要销毁它*. (5认同)
  • 否.对此的分配将替换对象成员的值.但是当析构函数完成时,对象仍然会被破坏.这已成定局.你不能改变对象的命运.你当然可以在第一个对象的析构函数中分配另一个与被销毁对象无关的对象,但它将是一个不同的独立对象. (3认同)

Nin*_*rry 52

你实际上是GC.KeepAlive在歪曲.NET中的内容.它不能用在对象的析构函数中以防止该对象被破坏 - 实际上,它GC.KeepAlive()是空的并且没有实现.请在此处查看.NET源代码.

它确保在调用发生之前不会将作为参数传递的对象进行垃圾回收GC.KeepAlive.作为参数传递给KeepAlive的对象可以在调用后立即进行垃圾回收GC.KeepAlive.由于KeepAlive没有实际的实现,这纯粹基于编译器必须维护对作为参数传递的对象的引用这一事实KeepAlive.也可以使用将对象作为参数的任何其他函数(未由编译器或运行时内联).

  • 在.net对象中通过引用它们来保持活动(防止被垃圾回收).线程在这里没有任何作用. (6认同)
  • @CompuChip是referenceource网站中的PITA.有关说明,请参见[此处](http://stackoverflow.com/a/30631947/3764814). (2认同)
  • 实际上`GC.KeepAlive`工作的唯一原因是因为它是由JIT特殊设置的,否则在内联函数后,JIT会看到它不需要保留引用. (2认同)

JDł*_*osz 8

这是一个想法:

C* gPhoenix= nullptr;

C::~C ()
{
gPhoenix= new C (*this);  // note: loses any further-derived class ("slice")
}
Run Code Online (Sandbox Code Playgroud)

现在,如果所涉及的对象(基础或成员)确实具有可以执行某些操作的析构函数,那么如果您delete gPhoenix;需要更复杂的机制(这取决于它真正想要实现的内容),则会遇到问题.但你没有任何真正的目标,只是好奇的探索,所以指出这一点就足够了.

当析构函数的主体被调用时,该对象仍然非常好.当您从析构函数中进行正常的成员函数调用时,它看起来非常重要和正常.

拥有该对象的内存将被回收,因此您无法就地固定.离开身体后,其他破坏会自动发生,不会受到干扰.但是,您可以在此之前复制对象.

  • 需要注意的一件事是,如果对象实际上是子类的实例,则已经调用了任何子类析构函数。在这种情况下,事情可能会变得棘手,具体取决于子类做了什么。最好的情况是,您获得当前被破坏的类的实例的有效副本,而只是丢失子类添加的任何内容。 (2认同)
  • 超类,子类:嗯,这两个术语都没有在C++中使用,所以不要担心它.在它之前使用Standard和Stroustrup中的命名法:*派生类*(和*最多派生类*),*基类*.然后在您在本地定义上下文时,可以随意使用像一般数学意义上的子类这样的术语.请注意,base和derived更难混淆! (2认同)

The*_*aot 6

正如已经指出的那样,GC.KeepAlive不这样做.

只要.NET运行,就可以从终结器中恢复使用GC.ReRegisterForFinalize,如果你有一个WeakReferenceGCHandle跟踪的ressurection,你仍然可以得到它的引用,或者只是给this类外的东西.这样做会中止破坏.

这是检测.NET 2.0中的垃圾收集不再相关的一个老技巧,但仍然有效(有点,垃圾收集现在可以是部分的,并且与其他线程并行完成).

应该强调的是,在.NET上你使用的是终结器,它在破坏之前运行,并且可以防止它.因此,虽然在技术上正确无法在销毁后恢复对象(使用任何语言),但您可以接近在.NET中描述的行为,除非使用GC.ReRegisterForFinalize.


在C++上,您已经得到了正确的答案.