У меня проблема с назойливым указателем Boost. Это логический оператор преобразованияпроверяет x.get() != 0
. Однако приведенный ниже код не работает в отмеченной точке. Почему это так?
Я предполагаю, что это может быть связано с тем фактом, чтоdelete
не устанавливает указатель на 0
(или nullptr
). Если это не так, как я могу эффективно использовать навязчивый указатель? Я хотел бы иметь возможность использовать навязчивый указатель, как обычный указатель, например, в выражении x && x->foo()
, но этот артефакт, кажется, исключает это.
#include
#include
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 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;
}
(Архитектура: Дарвин 10.7, протестированы компиляторы g++ 4.7 и 4.6 с -std=c++11
)
После прополки исходного кода intrusive_ptr
, я обнаружил, что в деструкторе есть только один вызов 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;
}
}
Интуитивно, этот параметр ссылки на указатель должен назначать lvalue p
в обращение контекста к 0. Бьерн также упоминает эту идиому. Однако утверждение по-прежнему терпит неудачу в отмеченной строке, оставляя меня в неведении на этот раз.
Причина, по которой я создаю и удаляю ссылку на указатель вручную, заключается в том, что мне приходится некоторое время работать с необработанным указателем при передаче его в C API. Это означает, что я должен сослаться на него, прежде чем передать его в C API, чтобы предотвратить уничтожение, и воссоздать навязчивый указатель из необработанного указателя, когда я получу его обратно. Вот пример:
void f()
{
intrusive_ptr x = new T;
auto raw = x.get();
intrusive_ptr_add_ref(raw);
api_in(raw);
}
void g()
{
T* raw = api_out();
intrusive_ptr y(raw, false);
h(y);
}
Здесь второй параметр в конструкции y
в g()
избегает ref при возврате указателя из C API, что компенсирует ссылка на руководство в f()
.
Я понял, что ручное удаление назойливого указателя может привести к неожиданному поведению, тогда как такое использование кажется нормальным.