Как эффективно использовать intrusive_ptr в Boost?

Manual Unref

У меня проблема с назойливым указателем 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().

Я понял, что ручное удаление назойливого указателя может привести к неожиданному поведению, тогда как такое использование кажется нормальным.

11
задан Community 23 May 2017 в 12:01
поделиться