2009-09-10 6 views
26

boost::shared_ptr a un constructeur inhabituelA quoi sert shared_ptr de boost (shared_ptr <Y> const & r, T * p)?

template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p); 

et je suis un peu perplexe quant à ce que cela serait utile. Fondamentalement, il partage la propriété avec r, mais .get() renverra p. pasr.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 ont tous deux prétendent avoir un use_count de 2 (puisqu'ils partagent la propriété du même objet).

Ainsi, la int appartenant à x existera aussi longtemps que xouy est autour. Et si je comprends bien les docs, le second int ne sera 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 comme un autre pointeur (qu'il ne possède pas) lorsqu'il est utilisé.

+7

Bonne question. +1 – GManNickG

+3

TL; DR version: Crée un pointeur sur un sous-objet de 'r'. –

Répondre

29

Il est utile lorsque vous souhaitez partager un membre de la classe et une instance de la classe est déjà un shared_ptr, comme ce qui suit:

struct A 
{ 
    int *B; // managed inside A 
}; 

shared_ptr<A> a(new A); 
shared_ptr<int> b(a, a->B); 

ils partagent le compte de l'utilisation et d'autres choses. C'est l'optimisation pour l'utilisation de la mémoire.

+1

une bonne réponse. clairement, dans cet exemple, nous voudrions garder l'objet de 'a' aussi longtemps que' b' est autour. Je pense que nous avons un gagnant. –

+0

Non seulement une optimisation pour l'utilisation de la mémoire, mais dans l'exemple spécifique, l'utilisation d'une approche différente aboutirait à un appel à 'delete (a-> B)' qui pourrait être inattendu (considérer 'struct A {int b; shared_ptr a (nouveau A); shared_ptr b (a, & a-> b) ') –

2

Vous pouvez avoir un pointeur vers un pilote ou une structure de données d'api de niveau inférieur qui peut allouer des données supplémentaires par son api de niveau inférieur ou d'autres moyens. Dans ce cas, il peut être intéressant d'augmenter le paramètre use_count mais de renvoyer les données supplémentaires si le premier pointeur possède les autres pointeurs de données.

8

développiez leiz's et piotr's réponses, cette description de shared_ptr<> 'aliasing' est d'un papier WG21, "Improving shared_ptr for C++0x, Revision 2":

III. Aliasing soutien

Les utilisateurs avancés ont souvent besoin de la capacité de créer une shared_ptr instance p qui partage la propriété avec une autre (maître) shared_ptrq mais des points à un objet qui n'est pas une base de *q. *p peut être un membre ou un élément de *q, par exemple.Cette section propose un constructeur supplémentaire qui peut être utilisé pour ce but .

Un effet secondaire intéressant de cette augmentation de puissance expressive est que maintenant les fonctions *_pointer_cast peuvent être mises en œuvre dans le code utilisateur. La fonction d'usine make_shared présenté plus loin dans ce document peut également être mis en œuvre en utilisant uniquement l'interface de shared_ptr public par le constructeur aliasing.

Impact:

Cette fonctionnalité étend l'interface de shared_ptr de manière rétrocompatible qui augmente sa puissance expressive et est donc fortement recommandé à ajouter à la C++ 0x norme . Il n'introduit aucun problème de compatibilité binaire source et .

Texte proposé:

Ajouter à shared_ptr [util.smartptr.shared] le constructeur suivant:

template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p); 

Ajouter ce qui suit à [util.smartptr. shared.const]:

template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p); 

Effets: construit une instance shared_ptr qui stocke p et actions avec la propriétér.

Postconditions:get() == p && use_count() == r.use_count().

Jets: rien.

[Note: Pour éviter la possibilité d'un pointeur ballants, l'utilisateur de ce constructeur doit veiller à ce que p reste valide au moins jusqu'à ce que le groupe de propriété de r est détruite. --end ndlr]

[Note:. Ce constructeur permet la création d'un videshared_ptr exemple avec un pointeur NULL non stocké. --end note.]

4

Vous pouvez également l'utiliser pour garder des pointeurs dynamiques casted, à savoir:

class A {}; 
class B: public A {}; 

shared_ptr<A> a(new B); 
shared_ptr<B> b(a, dynamic_cast<B*>(a.get())); 
+0

une autre utilisation intéressante qui tombe clairement dans la catégorie de "aliasing". Bon point. –

0

Pour "shared_ptr<B> b(a, dynamic_cast<B*>(a.get()));"

Je pense que ce n'est pas la méthode recommandée en utilisant le pointeur intelligent.

La méthode recommandée de faire 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 en shared_ptr<U> chaque fois que T * peut être implicitement converti en U *. En particulier , shared_ptr<T> est implicitement convertible en shared_ptr<T> const, à shared_ptr<U> où U est un de base accessible de T, et à shared_ptr<void>.

En plus de cela, nous avons aussi dynamic_pointer_cast qui pourrait directement faire la conversion sur l'objet pointeur intelligent et les deux de ces deux méthodes serait beaucoup plus sûr que la façon de pointeur brut coulée manuellement.

0

J'ai mis le constructeur aliasing shared_ptr utilisé dans ma petite bibliothèque:

http://code.google.com/p/infectorpp/ (juste mon simple, conteneur IoC)

Le point est que depuis que je avais besoin d'un shared_ptr de type connu pour être retourné d'un classe polymorphique (qui ne connaît pas le type). Je n'ai pas été en mesure de convertir implicitement shared_ptr au type dont j'avais besoin.

Dans le fichier "InfectorHelpers.hpp" (ligne 72-99), vous pouvez voir cela en action pour le type IAnyShared.

Le constructeur d'alias crée shared_ptr qui ne supprime pas les pointeurs vers lesquels pointe réellement, mais ils augmentent encore le compteur de référence à l'objet d'origine et cela peut être extrêmement utile.

Fondamentalement, vous pouvez créer un pointeur sur n'importe quel objet utilisant le constructeur d'alias et le menacer comme un 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 

maintenant nous avons à la fois « un compteur de référence » et un pointeur sur une istance de T, suffisamment de données pour créer quelque chose avec le constructeur 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 utiliser pour le constructeur aliasing, mais je n'ai jamais vu quelqu'un d'autre faire la même chose. Si vous devinez si ce code sale fonctionne, la réponse est oui.

Questions connexes