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
Vous devez vous connecter pour publier un commentaire.
La question est: Pourquoi ne vous attendez x pour convertir false à la fin? Vous êtes de jouer avec la ref de compteur, de façon inattendue! Vous êtes à la baisse à zéro même si il y a encore un
intrusive_ptr
— x — que les points de l'objet. Ce n'est pas la façon dont il fonctionne. La ref compteur est censé être au moins aussi grand que le nombre deintrusive_ptr
objets qui pointent à l'ref compté objet — sinon il ne serait pas une réf contre, serait-il?intrusive_ptr
en particulier (et aussi Microsoft COM pointeurs), fonctionnent correctement lorsque l'ensemble de ses utilisateurs obéir propriété sémantique avec ses pointeurs, si ces pointeurs sont des modèles C++ ou C brutes pointeur. la Propriété sémantique, exige que tous les refcount événement changeant seulement se produire par l'intermédiaire d'un pointeur (raw ou smart) sous la condition que la propriété par le pointeur est constatée. (En d'autres termes, vous ne vous mêlez pas de ce que vous ne possédez pas.) À l'OP du code de l'échantillon, le(intrusive_ptr) x
est celui qui en a certains à la propriété de l'objet.OriginalL'auteur sellibitze
La lecture de la documentation sur
intrusive_ptr
je vois il n'y a aucun lien entre les "détruire" l'objet, à l'aide de sa propre terminologie, et le pointeur de 0. Donc, si vous voulez utiliser lex && x->foo()
idiome, votreintrusive_ptr_release
fonction doit définir le pointeur à0
trop.Je peux voir la décision de conception ici dans
intrusive_ptr
. Lorsqueintrusive_ptr_release
est appelée, que la destruction doit être effectuée, sans y compris tout autre comportement que celui fourni pardelete
, donc si vous aussi vous souhaitez placer le pointeur à0
à l'appui de l'idiome, que vous avez à faire dans votre code pour cette fonction, maisintrusive_ptr
lui-même ne pas vous forcer à inclure plus de restrictions quedelete
lui-même: c'est, il ne vous force pas à réinitialiser le pointeur de0
.intrusive_ptr_add_ref
etintrusive_ptr_release
de prendre un pointeur brut plutôt qu'unintrusive_ptr
. Cela permettrait pas pour le réglage du privéintrusive_ptr
membrepx
à0
, ou voulez-vous dire quelque chose d'autre?Ah, je vois ce que tu veux dire. Le pointeur que vous obtenez dans la fonction est copié par valeur, donc vous ne pouvez pas le modifier. Je suppose alors que cette technique peut être utilisée comme un wrapper de directement des valeurs reçues, à effectuer (localement, à l'intérieur d'une fonction) une référence compté travail avec le pointeur.
Pourriez-vous nous expliquer votre dernier commentaire? Je ne suis pas sûr si je comprends bien. Peut-être que j'ai adressé ce que tu veux dire avec ma question de modifier.
Oui, j'étais sur le point de suggérer le changement d'une
T*&
, mais je ne suis pas sûr si cela est autorisé par le contrat deintrusive_ptr
. Faire de cette fonction dans votre montage, vous obtenir le comportement que vous attendez, je crois que c'est "pratiquement".OriginalL'auteur Diego Sevilla