【发布时间】: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<T>的源码后,发现析构函数中只有一个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