【问题标题】:How to use Boost's intrusive_ptr effectively?如何有效地使用 Boost 的 intrusive_ptr?
【发布时间】:2012-04-09 01:22:42
【问题描述】:

手动取消引用

我对 Boost 的侵入式指针有疑问。这是boolean conversion operator 检查x.get() != 0。但是,下面的代码在标记点失败。为什么会这样?

我猜我可能与delete does not set a pointer to 0(或nullptr)的事实有关。如果不是这样,我怎样才能有效地使用侵入式指针?我希望能够使用像常规指针这样的侵入式指针,例如,在表达式 x && x->foo() 中,但这个人工制品似乎排除了它。

#include <atomic>
#include <boost/intrusive_ptr.hpp>

struct T
{
    T() : count(0u) { }

    size_t ref_count()
    {
        return count;
    }

    std::atomic_size_t count;
};

void intrusive_ptr_add_ref(T* p)
{
    ++p->count;
}

void intrusive_ptr_release(T* p)
{
    if (--p->count == 0u)
        delete p;
}

int main()
{
    boost::intrusive_ptr<T> x;
    x = new T;
    assert(x->ref_count() == 1);

    auto raw = x.get();
    intrusive_ptr_add_ref(raw);
    intrusive_ptr_add_ref(raw);
    assert(x->ref_count() == 3);

    intrusive_ptr_release(raw);
    intrusive_ptr_release(raw);
    assert(x->ref_count() == 1);

    intrusive_ptr_release(raw); // Destroys T, ref_count() == 0.
    assert(! x); // Fails.

    return 0;
}

(架构:Darwin 10.7,使用 -std=c++11 测试编译器 g++ 4.7 和 4.6)

指针引用

翻遍intrusive_ptr&lt;T&gt;的源码后,发现析构函数中只有一个intrusive_ptr_release调用:

~intrusive_ptr()
{
    if( px != 0 ) intrusive_ptr_release( px );
}

由于T* 类型的参数px 是一个左值,应该可以通过稍微更改intrusive_ptr_release 的函数签名将其设置为零:

inline void intrusive_ptr_release(T*& p)
{
    if (--p->count == 0u)
    {
        delete p;
        p = 0;
    }
}

直观地说,这个指针引用指针参数应该将调用上下文中p 的左值分配为0。Bjarne 也是mentions this idiom。但是,断言仍然在标记的行处失败,这一次让我一无所知。

示例用法

我手动引用和取消引用指针的原因是,在将原始指针传递给 C API 时,我必须使用它一段时间。这意味着我必须在将它传递给 C API 之前对其进行引用以防止破坏,并在我取回原始指针时重新创建一个侵入式指针。这是一个例子:

void f()
{
    intrusive_ptr<T> x = new T;
    auto raw = x.get();
    intrusive_ptr_add_ref(raw);
    api_in(raw);
}

void g()
{
    T* raw = api_out();
    intrusive_ptr<T> y(raw, false);
    h(y);
}

这里,g()y 的构造中的第二个参数在从 C API 中获取指针时避免了 ref,这补偿了 f() 中的手动 ref。

我意识到手动解除插入式指针可能会导致意外行为,而这种用法似乎很好。

【问题讨论】:

    标签: c++ c++11 memory-management boost smart-pointers


    【解决方案1】:

    问题是:为什么你期望 x 最终转换为 false?你以意想不到的方式弄乱了 ref 计数器! 尽管仍有一个intrusive_ptr — x — 指向该对象,但您正在将其减少到零。这不是它的工作原理。 ref 计数器应该至少与指向 ref 计数对象的 intrusive_ptr 对象的数量一样大——否则它就不是一个 ref 计数器,不是吗?

    【讨论】:

    • 换句话说:一般的侵入式指针,尤其是intrusive_ptr(以及Microsoft COM 指针),当所有用户都遵守其指针的所有权语义,这些指针是 C++ 模板还是原始 C 指针。 所有权语义要求任何更改引用计数的事件仅在确定该指针的所有权的条件下通过指针(原始或智能)发生。 (用外行的话来说,你不干涉你不拥有的东西。)在 OP 的代码示例中,(intrusive_ptr) x 是对对象拥有一定所有权的那个。
    【解决方案2】:

    阅读intrusive_ptr 上的文档,我发现使用它自己的术语“破坏”对象与指针为 0 之间没有任何联系。所以,如果你想使用 x &amp;&amp; x-&gt;foo() 成语,你的 @987654323 @函数也应该将指针设置为0

    我可以在intrusive_ptr 中看到设计决策。当intrusive_ptr_release被调用时,只需要进行销毁,不包括delete提供的任何其他行为,所以如果你还想把指向0的指针放在支持成语,你必须这样做您的该函数的代码,但intrusive_ptr 本身并不会强制您包含比delete 本身更多的限制:也就是说,它不会强制您将指针重置为0

    【讨论】:

    • 我理解函数 intrusive_ptr_add_refintrusive_ptr_release 采用原始指针而不是 intrusive_ptr。这不允许将私有intrusive_ptr 成员px 设置为0,还是您的意思是别的?
    • 啊,我明白你的意思了。你在函数中得到的指针是按值复制的,所以你不能修改它。我猜想这种技术可以用作直接接收值的一次性包装器,以使用指针执行(在本地,在函数内部)引用计数工作。
    • 您介意解释一下您的上一条评论吗?我不确定我是否理解正确。也许我用我的问题编辑解决了你的意思。
    • 是的,我正要建议更改为T*&amp;,但我不确定intrusive_ptr 的合同是否允许这样做。在您的编辑中执行该功能您会得到您期望的行为,所以我猜它“实际上是正确的”。
    猜你喜欢
    • 1970-01-01
    • 2018-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-10
    • 2016-10-02
    • 1970-01-01
    相关资源
    最近更新 更多