Ручной 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
)
Справочно-на-указатель
После пропирания исходного кода 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;
}
}
Интуитивно понятно, что этот параметр указателя на указатель должен присвоить значение l p
в контексте вызова 0. Bjarne также упоминает эту идиому . Однако в отмеченной строке утверждение все еще не выполнено, и на этот раз я ничего не понимаю.
Пример использования
Причина, по которой я вручную устанавливаю или отменяю указатель, заключается в том, что мне приходится некоторое время работать с необработанным указателем при передаче его в 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()
.
Я понял, что снятие ручного указателя с навязчивым указателем может привести к непредвиденному поведению, тогда как такое использование выглядит вполне приемлемым.