Manual Unref
У меня проблема с интрузивным указателем Boost. Он логический оператор преобразования проверяет x.get() != 0
. Тем не менее, код ниже не работает в отмеченной точке. Почему это так?
Я предполагаю, что мне может быть связано с тем, что delete
не устанавливает указатель на 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, проверенные компиляторы g++ 4.7 и 4.6 с -std=c++11
)
Референс-к-Pointer
После прополки исходного кода intrusive_ptr<T>
я обнаружил, что в деструкторе есть только один вызов intrusive_ptr_release
:
~intrusive_ptr()
{
if( px != 0 ) intrusive_ptr_release( px );
}
Поскольку аргумент px
типа T*
является lvalue, его можно установить равным нулю, слегка изменив сигнатуру функции intrusive_ptr_release
:
inline void intrusive_ptr_release(T*& p)
{
if (--p->count == 0u)
{
delete p;
p = 0;
}
}
Интуитивно, этот указатель reference-to-pointer parameter должен присваивать lvalue p
в вызывающем контексте равным 0. Bjarne также упоминает эту идиому. Тем не менее, утверждение все еще терпит неудачу на отмеченной строке, оставив меня невежественным на этот раз.
Пример использования
Причина, по которой я исправляю и не исправляю указатель вручную, заключается в том, что мне нужно некоторое время работать с необработанным указателем при передаче его в C API. Это означает, что я должен отбросить его, прежде чем передавать его в API C, чтобы предотвратить разрушение, и воссоздать интрузивный указатель из исходного указателя, когда я его верну. Вот пример:
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()
избегает ссылки ref при возврате указателя из C API, который компенсирует ручной ref в f()
.
Я понял, что ручное отключение интрузивного указателя может привести к неожиданному поведению, тогда как это использование кажется прекрасным.