Qu'est ce qu'un pointeur intelligent et quand dois-je utiliser?

qu'est Ce qu'un pointeur intelligent et quand dois-je utiliser?

1472
demandé sur 0x6900 2008-09-20 04:09:24

14 réponses

Un pointeur intelligent est une classe qui encapsule une 'raw' (ou 'nue') pointeur C++, pour gérer la durée de vie de l'objet pointé. Il n'y a pas qu'un seul type pointeur intelligent, mais tous essayer de résumé un pointeur brut d'une manière pratique.

pointeurs Intelligents doivent être préférés aux premières pointeurs. Si vous sentez que vous avez besoin d'utiliser des pointeurs (d'abord considérer si vous vraiment ), vous voulez normalement utiliser un pointeur intelligent que cela peut soulager de nombreux problèmes avec des pointeurs, principalement oublier de supprimer l'objet et les fuites de mémoire.

Avec des pointeurs, le programmeur doit explicitement la destruction de l'objet lorsqu'il n'est plus utile.

// Need to create the object to achieve some goal
MyObject* ptr = new MyObject(); 
ptr->DoSomething(); // Use the object in some way
delete ptr; // Destroy the object. Done with it.
// Wait, what if DoSomething() raises an exception...?

Un pointeur intelligent par rapport définit une politique que lorsque l'objet est détruit. Vous avez encore à créer l'objet, mais vous n'avez plus à vous soucier de le détruire.

SomeSmartPtr<MyObject> ptr(new MyObject());
ptr->DoSomething(); // Use the object in some way.

// Destruction of the object happens, depending 
// on the policy the smart pointer class uses.

// Destruction would happen even if DoSomething() 
// raises an exception

le la politique la plus simple en usage implique la portée de l'objet smart pointer wrapper, tel que mis en œuvre par boost::scoped_ptr ou std::unique_ptr .

void f()
{
    {
       boost::scoped_ptr<MyObject> ptr(new MyObject());
       ptr->DoSomethingUseful();
    } // boost::scopted_ptr goes out of scope -- 
      // the MyObject is automatically destroyed.

    // ptr->Oops(); // Compile error: "ptr" not defined
                    // since it is no longer in scope.
}

notez que les instances scoped_ptr ne peuvent pas être copiées. Ceci empêche le pointeur d'être supprimé plusieurs fois (incorrectement). Vous pouvez, cependant, le passage références à d'autres fonctions que vous appelez.

Les pointeurs

sont utiles lorsque vous voulez lier la durée de vie de l'objet à un bloc particulier de code, ou si vous l'avez intégré comme données de membre à l'intérieur d'un autre objet, la durée de vie de cet autre objet. L'objet existe jusqu'à ce que le bloc contenant le code soit sorti, ou jusqu'à ce que l'objet contenant soit lui-même détruit.

une politique de pointeur intelligent plus complexe implique le comptage de référence du pointeur. Cela permet le pointeur à être copié. Lorsque la dernière "référence" à l'objet est détruite, l'objet est supprimé. Cette politique est mise en œuvre par boost::shared_ptr et std::shared_ptr .

void f()
{
    typedef std::shared_ptr<MyObject> MyObjectPtr; // nice short alias
    MyObjectPtr p1; // Empty

    {
        MyObjectPtr p2(new MyObject());
        // There is now one "reference" to the created object
        p1 = p2; // Copy the pointer.
        // There are now two references to the object.
    } // p2 is destroyed, leaving one reference to the object.
} // p1 is destroyed, leaving a reference count of zero. 
  // The object is deleted.

les pointeurs comptés de référence sont très utiles lorsque la durée de vie de votre objet est beaucoup plus compliquée, et n'est pas liée directement à une section de code particulière ou à un autre objet.

il y a un inconvénient à faire référence à des pointeurs comptés - la possibilité de créer une référence pendante:

// Create the smart pointer on the heap
MyObjectPtr* pp = new MyObjectPtr(new MyObject())
// Hmm, we forgot to destroy the smart pointer,
// because of that, the object is never destroyed!

une autre possibilité est de créer des références circulaires:

struct Owner {
   boost::shared_ptr<Owner> other;
};

boost::shared_ptr<Owner> p1 (new Owner());
boost::shared_ptr<Owner> p2 (new Owner());
p1->other = p2; // p1 references p2
p2->other = p1; // p2 references p1

// Oops, the reference count of of p1 and p2 never goes to zero!
// The objects are never destroyed!

pour contourner ce problème, Boost et C++11 ont défini un weak_ptr pour définir une référence faible (non comptabilisée) à un shared_ptr .


mise à jour

Cette réponse est assez vieille, et donc décrit ce qui était " bon " à l'époque, qui était intelligent pointeurs fournis par la bibliothèque Boost. Depuis C++11, la bibliothèque standard a fourni suffisamment de types de pointeurs intelligents, et vous devriez donc préférer l'utilisation de std::unique_ptr , std::shared_ptr et std::weak_ptr .

il y a aussi std::auto_ptr . Il est très semblable à un pointeur scoped, sauf qu'il a aussi la capacité "spéciale" dangereuse d'être copié - qui aussi transfert inattendu de propriété! il est déprécié dans les normes les plus récentes, donc vous ne devriez pas l'utiliser. Utilisez le std::unique_ptr à la place.

std::auto_ptr<MyObject> p1 (new MyObject());
std::auto_ptr<MyObject> p2 = p1; // Copy and transfer ownership. 
                                 // p1 gets set to empty!
p2->DoSomething(); // Works.
p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.
1671
répondu Lloyd 2017-10-12 22:15:59

Voici une réponse simple pour ces jours de C++ moderne:

  • qu'est Ce qu'un pointeur intelligent?

    C'est un type dont les valeurs peuvent être utilisées comme des pointeurs, mais qui fournit la fonctionnalité supplémentaire de gestion de mémoire automatique: Lorsqu'un pointeur intelligent n'est plus utilisé, la mémoire qu'il pointe est désallocée (voir aussi la définition plus détaillée sur Wikipedia ).
  • Quand dois-je l'utiliser?

    En code qui implique le suivi de la propriété d'un morceau de mémoire, l'allocation ou la dé-allocation; le pointeur intelligent vous épargne souvent le besoin de faire ces choses explicitement.
  • mais quel pointeur intelligent utiliser dans lequel de ces cas?
    • Utiliser std::unique_ptr quand vous n'avez pas l'intention de les détenir de multiples références pour le même objet. Par exemple, utilisez-le pour un pointeur vers la mémoire qui est alloué à l'entrée d'une portée et qui est dé-alloué à la sortie de la portée.
    • utilisez std::shared_ptr lorsque vous voulez faire référence à votre objet à partir de plusieurs endroits - et ne voulez pas qu'il soit dé-attribué jusqu'à ce que toutes ces références sont eux-mêmes partis.
    • utilisez std::weak_ptr lorsque vous voulez vous référer à votre objet à partir de plusieurs endroits - pour les références pour lesquelles il est correct d'ignorer et de désallouer (donc ils noteront juste que l'objet est parti quand vous essayez de déréférencer).
    • N'utilisez pas les boost:: pointeurs intelligents ou std::auto_ptr sauf dans des cas spéciaux que vous pouvez lire sur si vous le devez.
  • Hé, je n'ai pas demandé lequel utiliser!

    Ah, mais tu voulais vraiment l'admettre.
  • alors quand devrais-je utiliser des pointeurs réguliers?

    La plupart du temps en code qui est inconscient de la propriété de la mémoire. Ce serait typiquement dans les fonctions qui obtiennent un pointeur d'un autre endroit et ne pas allouer, dé-allouer ou stocker une copie du pointeur qui dépasse leur exécution.
182
répondu einpoklum 2018-10-11 10:36:59

pointeur intelligent est un type de pointeur avec certaines fonctionnalités supplémentaires, par exemple la désallocation automatique de la mémoire, le comptage de référence, etc.

petite intro est disponible à la page pointeurs intelligents-quoi, pourquoi, qui? .

L'un des types simples de pointeur intelligent est std::auto_ptr (chapitre 20.4.5 de la norme C++), qui permet de désallouer la mémoire automatiquement quand il de portée et qui est plus robuste que la simple utilisation de pointeur lorsque des exceptions sont lancées, bien que moins flexible.

un autre type pratique est boost::shared_ptr qui implémente le comptage de référence et désalloque automatiquement la mémoire lorsqu'il ne reste aucune référence à l'objet. Cela permet d'éviter les fuites de mémoire et est facile à utiliser pour mettre en œuvre RAII .

le sujet est traité en profondeur dans le livre " C++ Templates: The Complete Guide" Par David Vandevoorde, Nicolai M. Josuttis , chapitre 20. Les Pointeurs Intelligents. Quelques sujets traités:

99
répondu sergtk 2016-08-17 18:12:03

les définitions fournies par Chris, Sergdev et Llyod sont correctes. Je préfère une définition plus simple si, juste pour garder ma vie simple: Un pointeur intelligent est simplement une classe qui surcharge les opérateurs -> et * . Ce qui signifie que votre objet ressemble sémantiquement à un pointeur, mais vous pouvez le faire faire des choses plus fraîches, y compris le comptage de référence, la destruction automatique, etc. shared_ptr et auto_ptr sont suffisantes dans la plupart des cas, mais viennent avec leur propre ensemble de petites particularités.

34
répondu Sridhar Iyer 2017-04-07 14:45:31

un pointeur intelligent est comme un pointeur régulier (dactylographié), comme" char*", sauf quand le pointeur lui-même sort de la portée alors ce qu'il pointe est supprimé aussi bien. Vous pouvez l'utiliser comme un pointeur régulier, en utilisant "->", mais pas si vous avez besoin d'un pointeur réel vers les données. Pour cela, vous pouvez utiliser "&*ptr".

il est utile pour:

  • objets qui doivent être alloués avec nouveau, mais que vous souhaitez avoir le même la vie comme quelque chose sur cette pile. Si l'objet est assigné à un pointeur intelligent, alors ils seront supprimés lorsque le programme sort de cette fonction/bloc.

  • données membres de classes, de sorte que lorsque l'objet est supprimé toutes les données possédées est supprimé ainsi, sans aucun code spécial dans le destructeur (vous aurez besoin d'être sûr que le destructeur est virtuel, ce qui est presque toujours une bonne chose à faire).

Vous pouvez pas vouloir utiliser un pointeur intelligent quand:

  • ... le pointeur ne devrait pas réellement les données... c'est-à-dire, lorsque vous utilisez simplement les données, mais que vous voulez qu'elles survivent à la fonction où vous les référencez.
  • ... le pointeur intelligent ne sera pas lui-même détruit à un moment donné. Vous ne voulez pas qu'il s'assoit dans la mémoire qui ne se détruit jamais (comme dans un objet qui est dynamiquement alloué mais ne sera pas explicitement supprimé).
  • ... deux pointeurs intelligents peut-être les mêmes données. (Il y a, cependant, même des conseils plus intelligents qui vont gérer cela... cela s'appelle comptage de référence .)

voir aussi:

27
répondu markets 2017-05-23 12:02:48

la plupart des types de pointeurs intelligents gérer la disposition du pointeur-à l'objet pour vous. C'est très pratique car vous n'avez plus besoin de penser à vous débarrasser des objets manuellement.

les pointeurs intelligents les plus couramment utilisés sont std::tr1::shared_ptr (ou boost::shared_ptr ), et, moins souvent, std::auto_ptr . Je recommande l'utilisation régulière de shared_ptr .

shared_ptr est très polyvalent et traite d'une grande variété de scénarios d'élimination, y compris les cas où les objets doivent être " passés à travers les frontières DLL "(le cas de cauchemar commun si différent libc s sont utilisés entre votre code et les DLLs).

15
répondu Chris Jester-Young 2008-09-20 00:14:52

un pointeur intelligent est un objet qui agit comme un pointeur, mais qui permet en outre de contrôler la construction, la destruction, la copie, le déplacement et le déréférencement.

on peut implémenter son propre pointeur intelligent, mais de nombreuses bibliothèques fournissent également des implémentations de pointeur intelligent avec des avantages et des inconvénients différents.

par exemple, Boost fournit les implémentations de pointeur intelligent suivantes:

  • shared_ptr<T> est un pointeur vers T utilisant un nombre de référence pour déterminer quand l'objet n'est plus nécessaire.
  • scoped_ptr<T> est un pointeur automatiquement supprimé lorsqu'il sort du champ d'application. Aucune affectation n'est possible.
  • intrusive_ptr<T> est un autre indicateur de comptage de référence. Il offre de meilleures performances que shared_ptr , mais nécessite le type T pour fournir son propre mécanisme de comptage de référence.
  • weak_ptr<T> est un pointeur faible, travaillant en conjonction avec shared_ptr pour éviter les références circulaires.
  • shared_array<T> est comme shared_ptr , mais pour les matrices de T .
  • scoped_array<T> est comme scoped_ptr , mais pour les matrices de T .

ce ne sont qu'une description linéaire de chacun et peut être utilisé selon les besoins, pour plus de détails et des exemples on peut regarder la documentation de Boost.

de plus, la bibliothèque standard C++ fournit trois pointeurs intelligents: std::unique_ptr pour la propriété unique, std::shared_ptr pour la propriété partagée et std::weak_ptr . std::auto_ptr existait en C++03 mais est maintenant déprécié.

15
répondu Saqlain 2013-03-12 10:03:48

voici le lien pour des réponses similaires: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

Un pointeur intelligent est un objet qui agit, ressemble et se sent comme un pointeur normal, mais offre plus de fonctionnalités. En C++, Les pointeurs intelligents sont implémentés sous forme de classes de modèles qui encapsulent un pointeur et supplantent les opérateurs de pointeur standard. Ils ont un certain nombre d'avantages par rapport régulier de pointeurs. Ils sont garantie d'être initialisé soit en pointeur nul, soit en pointeur vers un objet tas. L'effet indirect via un pointeur nul est vérifié. Aucune suppression n'est jamais nécessaire. Les objets sont automatiquement libérés lorsque le dernier pointeur vers eux a disparu. Un problème important avec ces pointeurs intelligents est que contrairement aux pointeurs réguliers, ils ne respectent pas l'héritage. Les pointeurs intelligents ne sont pas attrayants pour le code polymorphe. Ci-dessous est un exemple pour la mise en œuvre de pointeurs intelligents.

exemple:

template <class X>
class smart_pointer
{
          public:
               smart_pointer();                          // makes a null pointer
               smart_pointer(const X& x)            // makes pointer to copy of x

               X& operator *( );
               const X& operator*( ) const;
               X* operator->() const;

               smart_pointer(const smart_pointer <X> &);
               const smart_pointer <X> & operator =(const smart_pointer<X>&);
               ~smart_pointer();
          private:
               //...
};

cette classe implémente un pointeur intelligent vers un objet de type X. l'objet lui-même est situé sur le tas. Voici comment l'utiliser:

smart_pointer <employee> p= employee("Harris",1333);

comme les autres opérateurs surchargés, p se comportera comme un pointeur régulier,

cout<<*p;
p->raise_salary(0.5);
10
répondu Santosh 2014-03-07 09:03:03

http://en.wikipedia.org/wiki/Smart_pointer

En informatique, un pointeur intelligent est un type de données abstrait qui simule un pointeur tout en offrant caractéristiques supplémentaires, comme automatique collecte des ordures ou vérification des limites. Ces fonctions supplémentaires sont destinés pour réduire les bugs causés par le mauvais usage de les pointeurs, tout en conservant l'efficacité. Les pointeurs intelligents suivent généralement les objets qui pointer vers eux pour le objectif de gestion de la mémoire. Le l'utilisation abusive des pointeurs est une source importante des bogues: l'allocation constante, deallocation et de référencement qui doit être effectuée par un programme écrit l'utilisation de pointeurs rend très probable que quelques fuites de mémoire se produit. Les pointeurs intelligents tentent d'empêcher la mémoire fuites en faisant la ressource désallocation automatique: lorsque le pointeur vers un objet (ou le dernier d'une série de pointeurs) est détruit, pour exemple, parce qu'il va hors de portée, l'objet pointu est aussi détruit.

8
répondu Jorge Ferreira 2008-09-20 00:12:10

soit T une classe dans ce tutoriel Les pointeurs en C++ peuvent être divisés en 3 types:

1) "1519180920 Brut" pointeurs :

T a;  
T * _ptr = &a; 

ils détiennent une adresse mémoire à un endroit dans la mémoire. Utilisez avec prudence, car les programmes deviennent complexes difficile à suivre.

pointeurs avec les données de const ou l'adresse { Read backwards }

T a ; 
const T * ptr1 = &a ; 
T const * ptr1 = &a ;

pointeur vers un type de données T qui est un const. Ce qui signifie que vous ne pouvez pas changer le type de données en utilisant le pointeur. ie *ptr1 = 19 ; ne fonctionnera pas. Mais vous pouvez déplacer le pointeur. ie ptr1++ , ptr1-- ; etc fonctionnera. Lire à l'envers: pointeur pour taper T qui est const

  T * const ptr2 ;

const pointeur vers un type de données T . Ce qui signifie que vous ne peut pas déplacer le pointeur, mais vous pouvez changer la valeur pointée par le pointeur. ie *ptr2 = 19 fonctionnera mais ptr2++ ; ptr2-- etc ne fonctionnera pas. Lire à l'envers: const pointer vers un type T

const T * const ptr3 ; 

const pointeur const T type de données . Ce qui signifie que vous ne pouvez ni déplacer le pointeur ni changer le pointeur de type de données pour être le pointeur. IE. ptr3-- ; ptr3++ ; *ptr3 = 19; ne fonctionnera pas

3) Pointeurs Intelligents : { #include <memory> }

Pointeur Partagé :

  T a ; 
     //shared_ptr<T> shptr(new T) ; not recommended but works 
     shared_ptr<T> shptr = make_shared<T>(); // faster + exception safe

     std::cout << shptr.use_count() ; // 1 //  gives the number of " 
things " pointing to it. 
     T * temp = shptr.get(); // gives a pointer to object

     // shared_pointer used like a regular pointer to call member functions
      shptr->memFn();
     (*shptr).memFn(); 

    //
     shptr.reset() ; // frees the object pointed to be the ptr 
     shptr = nullptr ; // frees the object 
     shptr = make_shared<T>() ; // frees the original object and points to new object

mis en œuvre en utilisant le comptage de référence pour garder une trace de la façon dont beaucoup de "choses" pointent vers l'objet pointé par le pointeur. Lorsque ce nombre va à 0, l'objet est automatiquement supprimé, ie objected est supprimé lorsque tout share_ptr pointant vers l'objet sort de la portée. Cela élimine le mal de tête de devoir supprimer des objets que vous avez alloués en utilisant new.

Pointeur Faible: Aide à gérer la référence cyclique qui apparaît lors de L'utilisation D'un pointeur partagé Si vous avez deux objets pointus à par deux pointeurs partagés et il y a un pointeur partagé interne pointant l'un vers l'autre pointeur partagé alors il y aura une référence cyclique et l'objet ne sera pas supprimé lorsque les pointeurs partagés sortent de la portée. Pour résoudre ce problème , changez le membre interne de shared_ptr en weak_ptr. Note: pour accéder à l'élément pointé par un pointeur faible use lock() , cela renvoie un fichier weak_ptr.

T a ; 
shared_ptr<T> shr = make_shared<T>() ; 
weak_ptr<T> wk = shr ; // initialize a weak_ptr from a shared_ptr 
wk.lock()->memFn() ; // use lock to get a shared_ptr 
//   ^^^ Can lead to exception if the shared ptr has gone out of scope
if(!wk.expired()) wk.lock()->memFn() ;
// Check if shared ptr has gone out of scope before access

voir: quand std:: weak_ptr est-il utile?

Pointeur Unique: Pointeur intelligent léger avec la propriété exclusive. Utilisez lorsque le pointeur pointe vers des objets uniques sans partager les objets entre les pointeurs.

unique_ptr<T> uptr(new T);
uptr->memFn(); 

//T * ptr = uptr.release(); // uptr becomes null and object is pointed to by ptr
uptr.reset() ; // deletes the object pointed to by uptr 

pour changer l'objet pointé vers le ptr unique, utilisez la sémantique des mouvements

unique_ptr<T> uptr1(new T);
unique_ptr<T> uptr2(new T);
uptr2 = std::move(uptr1); 
// object pointed by uptr2 is deleted and 
// object pointed by uptr1 is pointed to by uptr2
// uptr1 becomes null 

références : Ils peuvent essentiellement être bien comme const pointeurs, c'est à dire un pointeur const et ne peut pas être déplacé avec une meilleure syntaxe.

voir: quelles sont les différences entre une variable indicatrice et une variable de référence en C++?

r-value reference : reference to a temporary object   
l-value reference : reference to an object whose address can be obtained
const reference : reference to a data type which is const and cannot be modified 

référence : https://www.youtube.com/channel/UCEOGtxYTB6vo6MQ-WQ9W_nQ Merci à André d'avoir souligné cette question.

4
répondu nnrales 2017-05-23 11:47:28

Un pointeur intelligent est une classe, une enveloppe d'un pointeur normal. Contrairement aux pointeurs normaux, le cercle de vie de smart point est basé sur un comptage de référence (combien de fois l'objet pointeur intelligent est assigné). Ainsi, chaque fois qu'un pointeur intelligent est assigné à un autre, la référence interne compte plus. Et chaque fois que l'objet sort du champ d'application, la référence compte moins moins.

pointeur automatique, bien que semble similaire, est totalement différent de pointeur intelligent. C'est un classe pratique qui désalloque la ressource chaque fois qu'un objet pointeur automatique sort de la portée variable. Dans une certaine mesure, il fait fonctionner un pointeur (sur une mémoire dynamiquement allouée) de la même manière qu'une variable de pile (statically assigned in compiling time).

3
répondu Trombe 2017-08-07 07:54:02

les pointeurs intelligents sont ceux où vous n'avez pas à vous soucier de la dé-Allocation de la mémoire, du partage des ressources et du transfert.

vous pouvez très bien utiliser ces pointeurs de la même manière que n'importe quelle allocation fonctionne en Java. Dans java Garbage Collector fait le tour, tandis que dans Smart Pointers, le tour est fait par des destructeurs.

2
répondu Daksh 2016-11-07 04:07:54

Les réponses sont bonnes, mais ne couvrent pas quoi faire quand un pointeur intelligent n'est pas la (complet) réponse au problème que vous essayez de résoudre.

entre autres choses (bien expliqué dans d'autres réponses) l'utilisation d'un pointeur intelligent est une solution possible à comment utiliser une classe abstraite comme type de retour de fonction? qui a été marqué comme une copie de cette question. Cependant, la première question à se poser si tenté de spécifier un résumé (ou en fait, n'importe quelle) classe de base comme type de retour dans C++ est "que voulez-vous vraiment dire?". Il y a une bonne discussion (avec d'autres références) sur la programmation idiomatique orientée objet en C++ (et en quoi c'est différent des autres langues) dans la documentation de la boost point container library . En résumé, en C++ , vous devez penser à la propriété. Smart pointeurs vous aider, mais ne sont pas la seule solution, ou toujours une solution complète (ils ne vous donnent pas polymorphes copie) et ne sont pas toujours une solution, vous souhaitez exposer dans votre interface (et un retour de fonction sonne beaucoup comme une interface). Il pourrait suffire de renvoyer une référence, par exemple. Mais dans tous ces cas (pointeur intelligent, conteneur de pointeur ou simplement retour d'une référence) vous avez changé le retour d'une valeur à une certaine forme de référence . Si vous avez vraiment besoin de copie vous pouvez avoir besoin d'ajouter plus passe-partout "idiome" ou se déplacer au-delà de l'OOP idiomatique (ou autre) en C++ à un polymorphisme plus générique en utilisant des bibliothèques comme Adobe Poly ou Boost.TypeErasure .

1
répondu da77a 2018-01-26 03:10:57

j'aimerais ajouter un point de plus à la question ci-dessus, smart pointer std::shared_ptr n'a pas d'opérateur subscript et ne supporte pas l'arithmétique de ponter, nous pouvons utiliser get() pour obtenir un pointeur intégré.

1
répondu suresh m 2018-02-07 13:57:49