【问题标题】:Tracking down owner of a shared_ptr?追踪 shared_ptr 的所有者?
【发布时间】:2016-11-27 23:15:02
【问题描述】:

在我们的应用程序中,我们即将(终于..)从原始指针切换到使用 C++11 smart_ptr 模板。

我们的应用程序中确实偶尔会出现错误,因为(非 C++)对象仍然保留对我们 C++ 对象的引用,导致过去在访问 then-dealloc'd 对象时崩溃。

不确定这是否是一个愚蠢的问题 - 但有没有办法利用 smart_ptr 对象和 'dump' 在没有预期的情况下仍然持有 C++ 对象的对象再引用一个吗?

我想我要的是在某个时间点列出smart_ptr<MyClass>的所有所有者的方法。

非常感谢任何建议!

【问题讨论】:

  • smart_ptr<> 不保留所有者列表,它保留所有者计数。也就是说,您可以立即将所有者列表添加到实现中(在调试中)。
  • 您打算如何将非 C++ 代码合并到您的智能指针方案中?
  • @n.m.这是一个使用 Obj-C++ 编写的高级代码的 Cocoa 应用程序。低级业务逻辑独立于平台和 C++,不知道更高级别的非 C++ 代码。话虽如此,从技术上讲,Obj-C++ C++(或超集),尽管只是持有对 C++ 对象的引用
  • 在非 C++ 代码中使用 shared_ptr/weak_ptr。
  • 不确定 Valgrind 对shared_ptr 有多大帮助,但你试过了吗?

标签: c++ c++11 c++14 shared-ptr smart-pointers


【解决方案1】:

没有。如果不创建自己的智能指针类来包装std::unique_ptrstd::shared_ptr(忽略已弃用的std::auto_ptr)来跟踪此信息,则无法做到这一点。 标准类本身不跟踪此信息(成本太高)。

另一种选择是修改标准库实现的代码以跟踪该信息。由于您可以继续使用标准名称,因此对您的代码的侵入性较小。但可能比仅仅包装类和使用包装器更棘手。

【讨论】:

    【解决方案2】:

    不要相信这对于任何开箱即用的 c++ 智能指针都是可能的。你可以简单地包装一个 shared_ptr 来达到同样的效果。

    template<typename T>
    class mySmartPtr : boost::noncopyable{
        public:
             // This method should be the only way this object can be copied
             // as the copy constructors are made private
             // The newOwnerName parameter can be used to populate m_onwers.
             static mySmartPtr<T> newOwner(mySmartPtr<T>&, std::string newOnwerName); 
        private:
             std::shared_ptr<T> m_ptr;
             static std::vector<std::string> m_owners;
    };
    

    【讨论】:

    • "boost::noncopyable" - 为什么?如果您不希望该类可复制,只需将复制和移动运算符声明为=delete。无需涉及提升。
    • 当然。 =delete 也可以,但目前还没有被所有编译器支持。目标是停止复制该对象,并且有多种方法可以实现它。如何做到这一点并不是我所说的核心。
    • @cplusplusrat :问题中指定标准的代码(在本例中为 C++14),如果未指定,则为当前版本的标准(目前为 C++14)。
    • :),就像我说的,如何将复制构造函数设为私有根本不是我的解决方案的核心。如果boost::noncopyable 让您感到不舒服,请使用delete
    【解决方案3】:

    我们的应用程序中确实偶尔会出现错误,因为(非 C++)对象仍然保留对我们 C++ 对象的引用,导致过去在访问 then-dealloc'd 对象时崩溃。

    这是严重的,绝不能忽视。

    如果您在您的对象上给予第 3 方库组件观察者状态,那么第 3 方组件的观察者不能超过您的对象。

    此问题有 3 个常见原因:

    1. 第 3 方组件的生命周期管理不当(您在关闭第 3 方观察者之前删除了观察到的对象)

    2. 未正确检测到交叉案例(导致上述 1)

    3. 如果您是组件,您必须从第 3 方框架中接受有关何时可以处置您的对象的命令。

    在涉及shared_ptrs 之前,您应该首先证明shared_ptr 可以合法地销毁该对象。如果第 3 方组件有“注销”方法,那么您可以通过以下方式解决此问题:

    1. 确保第 3 方组件的所有权和您观察到的对象由同一个 shared_ptr 控制,或者
    2. 在 shared_ptr 上使用自定义删除器,以在最终删除之前取消注册第三方对象上的受控对象。

    如果这些都不清楚,那么是时候仔细看看你的对象生命周期了。它通常有助于绘制序列图。

    【讨论】:

    • “这很严重,不容忽视。” - 因此我的问题。
    • 情况有点复杂:我们的应用程序是一个复杂的 UI 应用程序,有几十个视图/视图控制器,其中大部分保持对低级 C++ 对象的引用。一些错误(当然存在于任何重要的应用程序中)会导致一些视图控制器(或可能只是其中的一部分——检查 OS X 编程中的块)一直存在。这个想法是通过 smart_ptrs 收集更多细节来基本上追踪应用程序更高级别的错误
    • @atv 如果你有一个复杂的手工制作的 ui 层,它从你的核心保持内部状态指针,请考虑不同的设计。我经常发现尝试接近“ui 与核心处于不同的进程”的理想很有用,在简单的近序列化接口中尽可能多地进行通信。 (实际上可能不需要一直到一个单独的进程,实际上是序列化:但是平面数据而不是状态指针,ids jn 而不是指针等)。 Ui 很乱,让它访问核心引擎的胆量是无限错误的良药。
    • @ATV objective-c(和 swift)块是可怕的,因为它们几乎在默认情况下会导致循环引用(如您所见)。如果我是你(并且我以前做过),我会将所有 .m 文件重命名为 .mm 并将 c++ 生命周期跟踪器放入每个块中。然后仔细重新设计每个块,使其仅引用 self 的 __weak 副本而不是 self(在回调块中引用 self 是 100% 的资源泄漏机会)。因此,我为 NSObject 派生类型构建了一个客观的 c++ 版本的weak_ptr/shared_ptr。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-31
    • 1970-01-01
    • 1970-01-01
    • 2019-07-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多