【问题标题】:What happens if an object held by a smart pointer gets deleted elsewhere?如果智能指针持有的对象在别处被删除会发生什么?
【发布时间】:2012-03-02 23:13:10
【问题描述】:

这个问题一直困扰着我,尤其是在我使用 Qt 编程时。 由于 Qt 使用对象所有权树,因此传递一个指针,例如通过myBoostSharedPtr.get() 可以隐式转移所有权。 现在考虑一些 Qt 对象被破坏并且整个对象树被破坏但智能指针仍然存在的情况,例如作为不同班级的成员。 如果智能指针之后被删除会怎样? 双重删除会带来所有令人讨厌的后果? 一些智能指针实现会阻止这种情况吗?

【问题讨论】:

标签: c++ qt smart-pointers


【解决方案1】:

我很想对 Qt 的内存模型的弱点进行吐槽,其中许多 API 仍然接受原始指针,期望客户端分配它,而接受指针的 QObject 删除它。

您的问题的答案是未定义的行为shared_ptr 没有检测指针是否被 shared_ptr 本身以外的东西删除的机制,因此它通常会尝试第二次释放指针(在悬空指针上调用 delete)。如果你想使用 shared_ptr,你必须坚持使用 shared_ptr 作为唯一的内存管理器。即使对于 Qt 自己的 QSharedPointer 也是如此。

在使用 Qt 之类的东西时,我通常会尝试使我的代码具有合理的异常安全性,即使用现已弃用的 auto_ptrunique_ptr 替换它,如果你有可用的 C++11 会更安全) .这是我之前唯一想使用auto_ptr 的地方,因为它提供了release 方法。

unique_ptr<QListWidget> widget(new QListWidget(...));
// do stuff with the widget to set it up for your GUI
some_layout.addWidget(widget.release()); // <-- release ownership so that 
                                         // the layout now becomes responsible 
                                         // for memory management
// ^^ auto_ptr works above if we don't have C++11

如果您需要在对象已经由 Qt 进行内存管理后保留一个指向您的对象的持久指针(例如:在您将它插入布局后指向您的小部件的指针),只需使用常规指针即可。由于 Qt 现在是该对象的内存管理器,因此没有什么比这更好的了。

但是,您可以通过QObject::destroyed 信号检测对象何时被销毁(以及指针何时失效)。

如果你想变得更复杂,你可以构建一个共享指针类型,它只存储 QObject 的子类。由于 QObject 提供了一个destroy 信号,这种自定义的智能指针可以通过destroy 信号检测QObject 何时被销毁,避免再次尝试删除该对象。然而,在多线程代码中依赖这个信号可能会变得很麻烦,如果你确实实现了一个共享指针,它可能会成为处理原子引用计数的相当大的负担,在构造指针的位置捕获一个删除函数(以避免模块边界新/删除不匹配)等。

【讨论】:

  • 我认为你不应该在参数列表中释放。如果addWidget 在它接管指针之前抛出,你已经泄露了。
  • @GmanNickG 这是一个很好的观点。你认为我们应该在调用成功后才释放吗?在我看来,API 接受指向客户端分配的内存的原始指针。我想一旦我通过它,如果 addWidget 中发生异常,QT 有责任在指针上调用 delete。否则,它是 QT 泄漏而不是我所看到的,并且文档没有说明 QT 是否尝试通过在 addWidget 中使用本地 try/catch 块删除指针来处理这种情况。如果它删除了 addWidget 中的指针并重新抛出,我的 unique_ptr 可能会调用 delete..
  • @GManNickG ... 在悬空指针上。这在很大程度上取决于我对 addWidget 是如何实现的了解。
  • 我同意情况很混乱,而且原则上是这样做的,因为原始指针根本没有所有权。您可能在那个 QT 中是对的,它决定使用原始指针,应该是在输入函数后立即获得所有权的那个。但也知道 QT 以前在内存管理方面做出了糟糕的决定,它可能不会。你是对的,文档真的应该说。 QT 在这方面很烂。
  • QPointer&lt;&gt; 具有您最后描述的属性。它检测包装的指针何时被其他方式删除,并在发生这种情况时将自己设置为NULL
【解决方案2】:

是的,很可能,不,他们没有,我看不出他们怎么能。您必须依赖合同,您传递指针的东西不会获得所有权(除非文档说明),如果是后者,请不要将其包装在您身边的智能指针中。

【讨论】:

    【解决方案3】:

    您不能在父 QObject 和智能指针之间拥有共享所有权并阻止其中一个删除另一个,但您可以仅使用 QWeakPointer(或 QPointer)跟踪任何 QObject 的删除.

    http://qt-project.org/doc/qt-4.8/qweakpointer.html#tracking-qobject

    更新:在 Qt 5 中,不推荐使用 QWeakPointer 来跟踪不受 QSharedPointer 管理的 QObjects 以支持 QPointer(它本身就是 undeprecated)。

    【讨论】:

    • 你不应该。不推荐使用 QWeakPointer 而不使用 QSharedPointer 的原因非常好,同样的原因它不在 C++11 中。
    • @abergmeier 我不确定你为什么投了反对票,而不是仅仅留下评论甚至自己更新答案。在撰写本文时,推荐使用 QWeakPointer 进行 QObject 跟踪。
    猜你喜欢
    • 2012-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-27
    • 1970-01-01
    • 1970-01-01
    • 2011-08-06
    相关资源
    最近更新 更多