我对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 );
}
由于类型px
的参数T*
是左值,因此可以通过稍微更改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);
}
此处,y
中g()
构造中的第二个参数在从C API返回指针时避免了引用,这补偿了f()
中的手动引用。 / p>
我意识到手动不提供侵入式指针会导致意外行为,而这种用法似乎很好。
答案 0 :(得分:13)
问题是:为什么期望x最终转换为false?你以意想不到的方式搞乱了参考柜台!你将它减少到零,即使还有intrusive_ptr
- x - 指向对象。这不是它的工作原理。 ref计数器应该至少与指向ref计数对象的intrusive_ptr
个对象的数量一样大 - 否则它不会是ref计数器,是吗?
答案 1 :(得分:2)
阅读intrusive_ptr
上的文档我看到“销毁”对象,使用自己的术语,指针为0之间没有联系。所以,如果你想使用x && x->foo()
成语,您的intrusive_ptr_release
函数也应将指针设置为0
。
我可以在intrusive_ptr
看到设计决策。调用intrusive_ptr_release
时,只应执行销毁,不包括delete
提供的任何其他行为,因此如果您还想将指针放到0
以支持该习惯用法,您必须在该函数的代码中执行此操作,但intrusive_ptr
本身不会强制您包含比delete
本身更多的限制:也就是说,它不会强制您将指针重置为{ {1}}。