Revive对象从C ++析构函数?

免责声明:我知道这是糟糕的devise,我只是出于好奇而提出这个问题,以便获得有关C ++析构函数如何工作的更深层次的知识。

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

C ++的devise是否允许从类似于上述C#algorithm的析构函数中恢复对象?

编辑 :正如下面的答案指出的, GC.ReRegisterForFinalize()GC.KeepAlive(this)的问题更紧密相关。

最简洁的答案是不。 C ++不使用垃圾收集,如Java或C#。 当一个物体被摧毁时,它会立即销毁。 走了好。 join了无形的合唱团。 为峡湾等等…

用不同的话说几遍,这样就不会有任何可能的重新解释。

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

理解这一点:析构函数被调用的原因是:对象最初是在堆上分配了“new”,现在是“delete”d。 “删除”的意思是“删除”,而不是“删除也许”。 所以对象被删除。 或者,如果对象被分配到堆栈上,则执行线程退出范围,所以在范围中声明的所有对象都将被销毁。 在技​​术上,析构函数是由于对象被销毁而被调用的。 所以,物体正在被破坏。 结束。

话虽如此,C ++允许您为您的类实现自定义分配器。 如果你觉得这样,你可以编写自己的自定义内存分配和释放函数来实现你想要的任何function。 虽然这些从不用于堆栈分配对象(即局部variables)。

你实际上是在误解GC.KeepAlive在.NET中的作用。 不要用在对象的析构函数中以防止该对象被破坏 – 实际上, GC.KeepAlive()是空的,没有实现。 在这里看到.NET的源代码。

它确保在调用GC.KeepAlive 之前 ,作为parameter passing的对象不会被垃圾收集。 作为parameter passing给KeepAlive的对象可以在调用GC.KeepAlive后立即进行垃圾回收。 由于KeepAlive没有实际的实现,这纯粹是基于这样一个事实,即编译器必须保持对作为parameter passing给KeepAlive的对象的引用。 任何其他函数(不是由编译器或运行库内联的)都可以使用对象作为参数。

这是一个想法:

 C* gPhoenix= nullptr; C::~C () { gPhoenix= new C (*this); // note: loses any further-derived class ("slice") } 

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

当析构函数的主体被调用时,这个对象仍然是非常好的。 从析构函数中进行正常的成员函数调用时,它显得非常重要和正常。

拥有该物品的记忆将被收回,所以你不能在原地呆着。 而在离开身体之后,其他的破坏就会自动发生,不会受到干扰。 但是,在发生这种情况之前,您可以复制该对象。

正如已经指出的那样 , GC.KeepAlive没有这样做。

只要.NET去,可以使用GC.ReRegisterForFinalize从终结器GC.ReRegisterForFinalize ,如果您有WeakReferenceGCHandle跟踪重新GC.ReRegisterForFinalize ,您仍然可以获取它的WeakReference ,或者只是将其提供给类之外的东西。 这样做会中止销毁。

在.NET 2.0中检测垃圾收集的旧技巧已经不再适用 ,但仍然有效(现在垃圾收集可以是部分的,并且与其他线程并行)。

重点应该放在这样一个事实上,即在.NET上你使用了一个在破坏之前运行的终结器 ,并且可以阻止它。 所以,虽然在技术上是正确的,你不能在销毁之后恢复一个对象 – 用任何语言 – 你都可以接近你在.NET中描述的行为,除了使用GC.ReRegisterForFinalize


在C ++上,你已经得到了正确的答案 。

这在任何语言中都是不可能的。

你的理解有点偏离。 GC.KeepAlive会将该对象标记为垃圾收集器不可收集。 这将阻止垃圾收集策略销毁该对象,如果该对象用于垃圾收集器无法跟踪使用情况的非托pipe代码中,则该方法非常有用。 这并不意味着该对象在破坏后在内存中。

一旦对象开始销毁,代码将释放资源(内存,文件处理程序,networking连接)。 顺序通常是从最深的派生类返回到基类。 如果中间的东西是为了防止破坏,那么就不能保证这些资源能够被重新获得,而且这个对象将处于不一致的状态。

你想要的更多的是有一个std::shared_ptr跟踪副本和引用,只有当没有人需要时才销毁对象。

如果有帮助,析构函数和内存分配是不同的。

析构函数只是一个函数。 你可以明确地调用它。 如果它没有破坏性,那么再次调用它(例如当对象超出范围或被删除时)不一定是有问题的,虽然这将是非常奇怪的。 标准中可能有一个部分处理这个问题。 看下面的例子。 例如,一些STL容器显式调用析构函数,因为它们分别pipe理对象的生命周期和内存分配。

通常情况下,编译器会在自动variables超出范围时插入代码来调用析构函数,或者使用delete删除堆分配对象。 内存释放不能在析构函数内被篡改。

你可以通过提供新操作符的附加实现或使用现有的新操作符来负责内存分配,但是一般的默认行为是编译器会调用你的析构函数,这是一个整理的机会。 一些内存随后被清除的事实不在析构函数的控制之下。

 #include <iostream> #include <iomanip> namespace test { class GotNormalDestructor { public: ~GotNormalDestructor() { std::wcout << L"~GotNormalDestructor(). this=0x" << std::hex << this << L"\n"; } }; class GotVirtualDestructor { public: virtual ~GotVirtualDestructor() { std::wcout << L"~GotVirtualDestructor(). this=0x" << std::hex << this << L"\n"; } }; template <typename T> static void create_destruct_delete(wchar_t const name[]) { std::wcout << L"create_destruct_delete<" << name << L">()\n"; { T t; std::wcout << L"Destructing auto " << name << L" explicitly.\n"; t.~T(); std::wcout << L"Finished destructing " << name << L" explicitly.\n"; std::wcout << name << L" going out of scope.\n"; } std::wcout << L"Finished " << name << L" going out of scope.\n"; std::wcout << L"\n"; } template <typename T> static void new_destruct_delete(wchar_t const name[]) { std::wcout << L"new_destruct_delete<" << name << L">()\n"; T *t = new T; std::wcout << L"Destructing new " << name << L" explicitly.\n"; t->~T(); std::wcout << L"Finished destructing new " << name << L" explicitly.\n"; std::wcout << L"Deleting " << name << L".\n"; delete t; std::wcout << L"Finished deleting " << name << L".\n"; std::wcout << L"\n"; } static void test_destructor() { { std::wcout << L"\n===auto normal destructor variable===\n"; GotNormalDestructor got_normal; } { std::wcout << L"\n===auto virtual destructor variable===\n"; GotVirtualDestructor got_virtual; } { std::wcout << L"\n===new variables===\n"; new_destruct_delete<GotNormalDestructor>(L"GotNormalDestructor"); new_destruct_delete<GotVirtualDestructor>(L"GotVirtualDestructor"); } { std::wcout << L"\n===auto variables===\n"; create_destruct_delete<GotNormalDestructor>(L"GotNormalDestructor"); create_destruct_delete<GotVirtualDestructor>(L"GotVirtualDestructor"); } std::wcout << std::endl; } } int main(int argc, char *argv[]) { test::test_destructor(); return 0; } 

示例输出

 ===auto normal destructor variable=== ~GotNormalDestructor(). this=0x0x23fe1f ===auto virtual destructor variable=== ~GotVirtualDestructor(). this=0x0x23fe10 ===new variables=== new_destruct_delete<GotNormalDestructor>() Destructing new GotNormalDestructor explicitly. ~GotNormalDestructor(). this=0x0x526700 Finished destructing new GotNormalDestructor explicitly. Deleting GotNormalDestructor. ~GotNormalDestructor(). this=0x0x526700 Finished deleting GotNormalDestructor. new_destruct_delete<GotVirtualDestructor>() Destructing new GotVirtualDestructor explicitly. ~GotVirtualDestructor(). this=0x0x526700 Finished destructing new GotVirtualDestructor explicitly. Deleting GotVirtualDestructor. ~GotVirtualDestructor(). this=0x0x526700 Finished deleting GotVirtualDestructor. ===auto variables=== create_destruct_delete<GotNormalDestructor>() Destructing auto GotNormalDestructor explicitly. ~GotNormalDestructor(). this=0x0x23fdcf Finished destructing GotNormalDestructor explicitly. GotNormalDestructor going out of scope. ~GotNormalDestructor(). this=0x0x23fdcf Finished GotNormalDestructor going out of scope. create_destruct_delete<GotVirtualDestructor>() Destructing auto GotVirtualDestructor explicitly. ~GotVirtualDestructor(). this=0x0x23fdc0 Finished destructing GotVirtualDestructor explicitly. GotVirtualDestructor going out of scope. ~GotVirtualDestructor(). this=0x0x23fdc0 Finished GotVirtualDestructor going out of scope.