A quoi sert shared_ptr (shared_ptr < Y > const & amp; r, T * p) de boost?
-
05-07-2019 - |
Question
boost :: shared_ptr
a un constructeur inhabituel
template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p);
et je suis un peu perplexe quant à l'utilité de cela. Il partage la propriété avec r
, mais .get ()
renverra p
. pas r.get ()
!
Cela signifie que vous pouvez faire quelque chose comme ceci:
int main() {
boost::shared_ptr<int> x(new int);
boost::shared_ptr<int> y(x, new int);
std::cout << x.get() << std::endl;
std::cout << y.get() << std::endl;
std::cout << x.use_count() << std::endl;
std::cout << y.use_count() << std::endl;
}
Et vous obtiendrez ceci:
0x8c66008
0x8c66030
2
2
Notez que les pointeurs sont séparés, mais ils prétendent tous deux avoir un nombre_utilisations
de 2 (puisqu'ils partagent la propriété du même objet).
Ainsi, le int
appartenant à x
existera aussi longtemps que x
ou y
est autour. Et si je comprends bien la documentation, le second int
n'est jamais détruit. J'ai confirmé cela avec le programme de test suivant:
struct T {
T() { std::cout << "T()" << std::endl; }
~T() { std::cout << "~T()" << std::endl; }
};
int main() {
boost::shared_ptr<T> x(new T);
boost::shared_ptr<T> y(x, new T);
std::cout << x.get() << std::endl;
std::cout << y.get() << std::endl;
std::cout << x.use_count() << std::endl;
std::cout << y.use_count() << std::endl;
}
Cette sortie (comme prévu):
T()
T()
0x96c2008
0x96c2030
2
2
~T()
Alors ... quelle est l'utilité de cette construction inhabituelle qui partage la propriété d'un pointeur, mais agit comme un autre pointeur (qu'il ne possède pas) lorsqu'elle est utilisée.
La solution
Cela est utile lorsque vous souhaitez partager un membre de la classe et qu'une instance de la classe est déjà un shared_ptr, comme suit:
struct A
{
int *B; // managed inside A
};
shared_ptr<A> a( new A );
shared_ptr<int> b( a, a->B );
ils partagent le nombre d'utilisations et d'autres choses. C'est l'optimisation de l'utilisation de la mémoire.
Autres conseils
Pour en savoir plus sur leiz et piotr's réponses, cette description de shared_ptr < >
"aliasing" provient d'un document du WG21, " Amélioration de shared_ptr
pour C ++ 0x, révision 2 ":
III. Prise en charge du crénelage
Les utilisateurs avancés ont souvent besoin de la possibilité de créer un
shared_ptr
instancep
qui partage la propriété avec un autre (maître)shared_ptr
q
mais pointe sur un objet qui n'est pas une base de* q
.* p
peut être un membre ou un élément de* q
, par exemple. Ce la section propose un complément constructeur qui peut être utilisé pour cela but.Un effet secondaire intéressant de cette augmentation du pouvoir d'expression est que maintenant les fonctions
* _ pointer_cast
peuvent être mis en œuvre dans le code utilisateur. le Présentation de la fonction d'usinemake_shared
plus tard dans ce document peut également être mis en œuvre en utilisant uniquement le public interface deshared_ptr
via le constructeur de repliement.Impact:
Cette fonctionnalité étend l'interface de
shared_ptr
dans une version rétro-compatible manière qui augmente son expressif pouvoir et est donc fortement recommandé d'être ajouté au C ++ 0x la norme. Il n'introduit aucune source et problèmes de compatibilité binaire.Texte proposé:
Ajouter à
shared_ptr
[util.smartptr.shared] ce qui suit constructeur:template<class Y> shared_ptr( shared_ptr<Y> const & r, T * p );
Ajouter ce qui suit à [util.smartptr.shared.const]:
<*>Effets: Construit une instance
shared_ptr
qui stockep
et partage la propriété avecr
.Post-conditions:
get () == p & amp; & amp; use_count () == r.use_count ()
.Lance: rien.
[Remarque: Pour éviter la possibilité d’un pointeur en suspension, l’utilisateur de ce constructeur doit s'assurer que
p
reste valide au moins jusqu'à ce que le groupe de propriété der
soit détruit. - note de fin.][Remarque: Ce constructeur permet de créer un vide
code_partagé
. instance avec un pointeur stocké non NULL. - note de fin.]
Vous pouvez également l'utiliser pour conserver des pointeurs dynamiques, c.-à-d.:
class A {};
class B: public A {};
shared_ptr<A> a(new B);
shared_ptr<B> b(a, dynamic_cast<B*>(a.get()));
Vous pouvez avoir un pointeur sur un pilote ou la structure de données d'une API de niveau inférieur pouvant allouer des données supplémentaires par son API de niveau inférieur ou par un autre moyen. Dans ce cas, il peut être intéressant d'augmenter le nombre_utilisations, mais de renvoyer les données supplémentaires si le premier pointeur est propriétaire des autres pointeurs de données.
J'ai utilisé le constructeur d'alias de shared_ptr dans ma petite bibliothèque:
http://code.google.com/p/infectorpp/ (seulement mon conteneur IoC simple)
Le fait est que puisque j'avais besoin d'un shared_ptr de type connu pour être renvoyé d'une classe polymorphe (qui ne connaît pas le type). Je n'ai pas pu convertir implicitement le shared_ptr au type dont j'avais besoin.
Dans le fichier & InfectorHelpers.hpp " (lignes 72 à 99), vous pouvez voir cela en action pour le type IAnyShared.
Le constructeur d'alias crée un objet shared_ptr qui ne supprime pas les pointeurs vers lesquels il pointe, mais il augmente toujours le compteur de références vers l'objet d'origine, ce qui peut s'avérer extrêmement utile.
En principe, vous pouvez créer un pointeur sur n'importe quoi à l'aide du constructeur d'alias et le menacer comme compteur de référence.
//my class
std::shared_ptr<T> ist;
int a; //dummy variable. I need its adress
virtual std::shared_ptr<int> getReferenceCounter(){
return std::shared_ptr<int>(ist,&a); //not intended for dereferencing
}
virtual void* getPtr(); //return raw pointer to T
nous avons maintenant à la fois un "compteur de référence" et et un pointeur sur une résistance de T, suffisamment de données pour créer quelque chose avec le constructeur d'aliasing
std::shared_ptr<T> aPtr( any->getReferenceCounter(), //share same ref counter
static_cast<T*>(any->getPtr()) ); //potentially unsafe cast!
Je ne prétends pas avoir inventé cette utilisation pour le constructeur d'aliasing, mais je n'ai jamais vu quelqu'un d'autre faire de même. Si vous devinez si ce code corrompu fonctionne, la réponse est oui.
For & shared_ptr < B > b (a, dynamic_cast < B * > (a.get ()));
"
Je pense que ce n'est pas la méthode recommandée avec le pointeur intelligent.
La méthode recommandée pour effectuer cette conversion de type devrait être:
shared_ptr<B> b(a);
Depuis dans le document Boost, il est mentionné que:
shared_ptr < T >
peut être implicitement converti enshared_ptr < U >
à chaque fois que T * peut être implicitement converti en U *. Dans En particulier,shared_ptr < T >
est convertible implicitement enshared_ptr < T > const
, versshared_ptr < U >
où U est un base accessible de T, et àshared_ptr < void >
.
De plus, nous avons également dynamic_pointer_cast . qui pourrait effectuer directement la conversion sur un objet Smart Pointer et ces deux méthodes seraient beaucoup plus sûres que la méthode du pointeur brut de conversion manuelle.