我想显式地销毁一个对象(调用它上面的析构函数及其所有字段),但可能会发生我仍然持有一些指向该对象的指针。因此,我不想释放记忆;相反,我想留下一种标志“我是一个被摧毁的对象”。
我想到了以下方法:
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
仍然正确吗?问题:
使用上述模式时是否还有其他问题需要考虑?
在zombie对象上调用delete
会正确释放前一个(普通)对象占用的整个内存吗?
虽然我很感激您对如何以不同方式进行操作的意见,我可能倾向于按照您的方式进行操作 - 我仍然希望了解上述代码带来的所有风险。
答案 0 :(得分:5)
有人建议使用智能指针。事实上 - 我这样做,但我的推荐是循环的。我可以使用一些成熟的垃圾收集器,但由于我知道自己在哪里(以及何时!)圈链可以被打破,我想自己利用它。
然后你可以显式null-ify(reset如果你使用shared_ptr
)其中一个循环智能指针并打破循环。
或者,如果您事先知道周期的位置,那么您还应该使用weak_ptr
代替某些shared_ptr
提前避免它们第
---编辑---
如果弱指针引用的对象仅被标记为“无效”并释放对其所有包含指针的控制(这句话是否清楚?),我会很高兴。
然后weak_ptr::expired
会让你开心:)
答案 1 :(得分:5)
你对你的问题有一些讨厌的评论。现在我不认为他们是应得的,尽管可能有更好的方法来做你想要的。我知道你来自哪里,但实际上你使用的是析构函数,就像使用你拒绝写的重置函数一样。实际上你从调用析构函数中得不到什么,因为调用析构函数与实际删除或重置任何东西无关,除非你实际编写代码在析构函数中执行它。
关于您关于展示位置的问题:
您可能已经知道,placement new不会分配任何内存,因此调用它只会在同一个地方创建对象。我明白这正是你想要的,但它不是必需的。由于你没有在对象上调用delete只是destroy,你可以在不初始化类的情况下将destroy设置为true。
总结一下:
要执行您想要正确执行的操作并获得析构函数的好处,您应该重载类的new和delete操作符并使用正常的销毁机制。然后,您可以选择不释放内存但将其标记为无效或者可能释放大部分内存,但将指针指向某些标记。
修改强>
在评论之后,我决定总结我看到的所有风险以及其他人指出的风险:
我重复我的建议 - 重载删除和新的你想要的
答案 2 :(得分:2)
与其他人一样,我建议您只使用weak_ptr
。但你问为什么你的方法不起作用。你的代码遍布全局,有一些优雅实现和关注点分离的问题,但我不会争论这些问题。相反,我只是指出你的代码非常不是线程安全的。
考虑以下两个控制线程的执行顺序:
// Thread One
if ( ! ptr -> isDestroyed() ) { // Step One
// ... interruption ...
ptr -> something(); // Step Three
另一个:
// Thread Two
ptr -> destroy(); // Step Two
到第3步到来时,指针不再有效。现在可以通过实现lock()
或类似方法来解决这个问题,但现在你已经发生了关于不释放锁的缺陷的可能性。每个人都在推荐weak_ptr
的原因是,这类问题已经在类的接口及其实现中得到了解决。
还有一个问题。你似乎想要一个可以随意杀死一个物体的设施。这等于要求对象的唯一指针是弱对象,没有强对象指针在手动删除对象时会破坏。 (我会规定这不是一个坏主意,但我必须说我不知道为什么它不适合你。)你可以在weak_ptr
和{{1}之上建立这个。 }。这些类是通用的,因此如果您想禁止shared_ptr
访问shared_ptr
,那么您可以为BaseClass
编写一个行为不同的特化。隐藏shared_ptr<BaseClass>
的一个实例以防止删除,并通过您控制的工厂方法提供此类指针。
在此模型中,shared_ptr<BaseClass>
的语义需要引起注意。第一种选择是您是想要同步还是异步操作。同步destroy()
将阻塞,直到释放所有外部指针并且不允许发出新指针。 (我假设指针上已经禁用了复制构造函数。)有两种异步destroy()
。如果仍存在外部引用,则两者中较简单的失败。在隐藏的destroy()
上调用unique()
可以轻松实现此功能。更复杂的一个就像异步I / O调用一样,将破坏安排在将来的某个时刻发生,大概一旦所有外部引用都消失了。此函数可能被称为shared_ptr()
以反映语义,因为该对象可能会或可能不会在返回时被销毁。
答案 3 :(得分:1)
我会考虑使用一个合适的智能指针模式。访问已删除对象的行为仍未定义,“僵尸”标志无法提供帮助。与已删除的对象实例关联的内存可能会立即被创建的任何其他对象占用,因此访问僵尸标志不是您可以信任的信息。
IMHO安置新操作员
new(this) BaseClass();
在您的destroy()
方法中使用的实际上并没有帮助。取决于该方法的使用方法。而不是删除已删除对象的派生对象或内部析构函数。在后一种情况下,无论如何都会释放内存。
<强>更新强>
根据你的编辑,使用共享指针/弱指针习语来解决循环引用的出现不是更好。我会认为这些是一个设计缺陷。