在C++中,调用delete时是否可能防止析构函数运行?

4

具体而言,在一个库中,我有一个内存池,它覆盖了某些类的new和delete行为。我希望库的用户能够调用这些类的实例的delete方法,但我需要在稍后的特定清理操作之前保留这些实例。是否可能同时让用户使用常规的new/delete?有没有办法覆盖调用析构函数的默认行为?


3
@blgt,operator delete是用于内存释放的函数,而delete操作符则是编译器将其翻译为“析构函数调用和通过operator delete释放内存”的特殊关键字。因此,重载函数并不会阻止编译器调用析构函数。请注意不改变原意且使内容更加通俗易懂。 - Arne Mertz
如果您可以控制这些类,那么我脑海中首先想到的是使用CRTP +非虚析构函数的组合来实现您想要的,并从基类中自己完成所有所需的清理工作。不过我不确定我是否正确理解了问题。 - Marco A.
只需为您的对象实现析构函数,以便在发生清理操作之前不删除资源。 - EdChum
@EdChum,这将保留对象的资源但不保留对象本身。当其析构函数运行时,对象的生命周期仍将结束,使得任何对它的使用都是未定义的。从问题中并不清楚这是否足以解决问题,“我需要保持实例活动”意味着这还不够。 - Jonathan Wakely
@JonathanWakely,像你的答案所述,使用智能指针可以缓解这个问题,你是正确的。我需要进一步澄清OP使用语义。 - EdChum
显示剩余2条评论
3个回答

5
简短回答:不可以。 调用delete会始终触发对析构函数的调用,然后调用operator delete,就像调用new先调用相应的operator new,然后运行对象的构造函数一样。 如果想要防止用户销毁对象,则必须以某种方式防止他们a)在原始指针上调用delete和b)在堆栈上构造它们。
如果想要保留实例的生命周期,则听起来好像您想要管理其生命周期,所以自然的方法是在库内部首先创建对象。此外,在代码中使用普通的delete调用现在被认为是不良风格,因为现在有智能指针可用来自动执行这些调用。
所以,您可以在您的库中添加创建器函数,它们返回某种智能指针。这些可以是带有特殊删除器的shared_ptr或unique_ptr,该删除器不会真正删除对象,而是将其传回给您的库以稍后清理。

+1,创建者函数使用户更难创建这些类型的未管理实例,有助于确保它们始终由智能指针类型正确管理,并因此被正确处理。 - Jonathan Wakely
1
然而,我更喜欢使用带有自定义删除器的shared_ptr,而不仅仅是在簿记部分拥有一个shared_ptr的副本,否则具有weak_ptr的用户可以使一个应该已经消失并准备进行清理的对象复活。这会使清理代码变得复杂(它需要检查shared_ptr是否真正唯一,在多线程程序中很难实现),并且意味着OP对这些对象的自定义生命周期对用户可见(他们可以看出对象并没有真正死亡,只是去了一个更好的地方;-) - Jonathan Wakely
@JonathanWakely 我认为对象的创建和销毁应该尽可能对称,也就是说,两者都应该以对称的方式在同一位置发生。因此,如果我只是将一个对象扔进库中来处理它,那么我应该从同一个库中获取它。 - Arne Mertz
@JonathanWakely 对于存储的shared_ptr的观点很好,我更新了我的答案。 - Arne Mertz

4

使用智能指针代替new/delete(这本身就是一个好的实践)。

使用自定义删除器将对象所有权移动到“等待清理”的对象列表中。

有关智能指针的示例,请参考std::unique_ptrstd::shared_ptr

另一个选项(更加繁琐且更难做到正确性)是只在使用自定义分配器的容器中存储对象,并使分配器的destroydeallocate函数执行“将其移动到待清理暂存区而非实际销毁”部分。我建议选择带有自定义删除器的智能指针方法,而不是分配器方法。

这两个选项(自定义删除器和自定义分配器)都允许您控制对象“销毁”时的确切行为,将对象的实际生命周期结束与用户处理它的时刻分离开来,这无法通过delete运算符实现。


2

delete会运行析构函数。这是每个人都依赖的基本事实。如果你能够禁用析构函数调用(但你不能),那么你将破坏每个人的期望。如果你不希望用户调用析构函数,就不要让析构函数可用。一个受保护的析构函数将使delete无法调用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接