Comment utiliser l'amplification du intrusive_ptr efficacement?

Manuel De L'Unref

J'ai un problème avec Boost est intrusif pointeur. C'est opérateur de conversion de type boolean vérifie x.get() != 0. Toutefois, le code ci-dessous ne parvient pas à le point marqué. Pourquoi est-ce le cas?

Je devine que je peut avoir à faire avec le fait que supprimer ne permet pas de définir un pointeur vers 0 (ou nullptr). Si ce n'est pas le cas, comment pourrais-je utiliser ce type de pointeur de manière efficace? Je voudrais être en mesure d'utiliser un intrusive pointeur un pointeur, par exemple, dans une expression x && x->foo(), mais cet artefact semble l'exclure.

#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;
}

(Architecture: Darwin 10.7, testé compilateurs g++ 4.7 4.6 avec -std=c++11)

De référence-à-Pointeur

Après le désherbage à travers le code source de intrusive_ptr<T>, j'ai trouvé qu'il y est un seul appel à la intrusive_ptr_release dans le destructeur:

~intrusive_ptr()
{
    if( px != 0 ) intrusive_ptr_release( px );
}

Étant donné que l'argument px de type T* est une lvalue, il devrait être possible de mettre à zéro en changeant légèrement la signature de la fonction de intrusive_ptr_release:

inline void intrusive_ptr_release(T*& p)
{
    if (--p->count == 0u)
    {
        delete p;
        p = 0;
    }
}

Intuitivement, cette référence de pointeur de pointeur de paramètre doit attribuer la lvalue de p dans le contexte de l'appel à 0. Bjarne aussi mentionne cet idiome. Cependant, l'affirmation ne parvient toujours pas à la ligne marquée, me laissant désemparés cette fois.

Exemple D'Utilisation

La raison pour laquelle je suis réf require et unref avec le pointeur manuellement, c'est que j'ai de travailler avec le pointeur brut pour un temps, lors du passage à un C API. Cela signifie que je dois ref avant de passer à l'API C afin de prévenir la destruction, et de recréer un intrusive pointeur le pointeur brut lorsque je le faire revenir. Voici un exemple:

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);
}

Ici, le deuxième paramètre dans la construction de y dans g() évite ref-ils lorsque le pointeur de retour de l'API C, ce qui compense le manuel de ref dans f().

J'ai réalisé que manuellement unreffing intrusive pointeur peut entraîner un comportement inattendu, alors que cet usage semble être fine.

OriginalL'auteur mavam | 2012-03-25