gpt4 book ai didi

c++ - 对象销毁时留下 "zombie"标志

转载 作者:太空狗 更新时间:2023-10-29 19:44:10 29 4
gpt4 key购买 nike

我想显式销毁一个对象(对其及其所有字段调用析构函数),但我可能仍然持有一些指向相关对象的指针。因此,我还不想释放内存;相反,我想留下一个标志“我是一个被摧毁的物体”。

我想到了以下方法:

class BaseClass { //all objects in question derive from this class
public:
BaseClass() : destroyed(false) {}
virtual ~BaseClass() {}
private:
bool destroyed;
public:
bool isDestroyed() { return destroyed; }
void destroy() {
this->~BaseClass(); //this will call the virtual destructor of a derivative class
new(this) BaseClass();
destroyed=true;
}
};

destroy 被调用时,我基本上销毁了我拥有的任何对象(可能是衍生对象)并在同一个地方创建一个新的“僵尸”对象。因此我希望实现:

  • 之前指向此对象的任何其他指针 ptr 仍然可以调用 ptr->isDestroyed() 来验证其存在。
  • 我知道如果我不检查僵尸标志并尝试访问属于任何派生对象的字段,可能会发生不好的事情
  • 我知道僵尸对象仍然消耗与被销毁对象一样多的内存(因为它可能是 BaseClass 的派生)
  • 我仍然需要释放被销毁对象的内存。但是,我希望调用 delete 仍然正确吗?

问题:

在使用上述模式时,还有其他需要考虑的问题吗?

在僵尸对象上调用 delete 是否会正确释放前一个(正常)对象消耗的全部内存?


虽然我感谢您就如何以不同的方式进行操作的意见,并且我可能倾向于按照您的方式进行操作 - 我仍然想了解上述代码带来的所有风险。

最佳答案

您对您的问题发表了一些令人讨厌的评论。现在我认为他们不值得,尽管可能有更好的方法来做你想做的事。我知道你来自哪里,但实际上你使用析构函数的方式与你使用拒绝编写的重置函数的方式相同。实际上,调用析构函数没有任何好处,因为调用析构函数与实际删除或重置任何内容无关,除非您实际编写代码在析构函数中执行此操作。

关于您关于 placement new 的问题:

您可能已经知道,placement new 不会分配任何内存,因此调用它只会在同一位置创建对象。我知道这正是您想要的,但这不是必需的。由于您不对对象调用 delete 只是销毁,因此您可以在不初始化类的情况下将 destroyed 设置为 true。

总结一下:

  1. 如果您将析构函数用作常规虚函数,您将一无所获。不要这样做,因为如果析构函数被调用两次,您可能会遇到麻烦
  2. 对 placement new 的调用不会分配内存,只会执行不必​​要的初始化。您可以将 destroyed 设置为 true。

要正确地做您想做的事并获得析构函数的好处,您应该重载类的 new 和 delete 运算符并使用正常的析构机制。然后,您可以选择不释放内存但将其标记为无效,或者释放大部分内存但让指针指向某些标志。

编辑

根据评论,我决定总结一下我看到的所有风险以及其他人指出的风险:

  1. 在多线程环境中访问无效指针:使用您的方法可以在析构函数运行之后但在设置销毁标志之前访问一个类(关于您在评论之一中的问题 - shared_ptr 用于大多数目的线程安全)
  2. 依赖于您无法完全控制的行为:您的方法依赖于析构函数自动调用未动态分配的其他成员的析构函数的方式:这意味着您仍然必须释放专门动态分配的内存,您没有控制它的具体实现方式,您无法控制调用其他析构函数的顺序。
  3. 依赖于您无法完全控制的行为(第 2 点):您依赖于编译器实现调用其他析构函数的析构函数部分的方式,您无法判断您的代码是否可移植,甚至它将如何处理两次调用。
  4. 析构函数可能会被调用两次:根据您的实现,这可能会导致内存泄漏或堆损坏,除非您避免释放相同的内存两次。你声称你通过调用新的放置来防止这种情况 - 但是在多线程环境中,这不能进一步保证你假设所有内存分配都是由默认构造函数完成的 - 这取决于你的具体实现,这可能是也可能不是是的。
  5. 您违背了回答您问题或发表评论的每个人的更好判断 - 您可能会发现一些天才的东西,但很可能您只是将您的实现限制在它的一小部分情况下,这是在打击自己将正常工作。这就像当你使用错误的 Screwdriver 时,你最终会损坏螺丝。同样地,以不打算使用的方式使用语言结构可能会导致错误的程序——析构函数旨在从 delete 和编译器生成的代码中调用以清除堆栈。直接使用它是不明智的。

我重复我的建议——重载删除和新建

关于c++ - 对象销毁时留下 "zombie"标志,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13321922/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com