Question Qu'est-ce qu'un pointeur intelligent et quand devrais-je en utiliser un?


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


1427
2017-09-20 00:09


origine


Réponses:


Un pointeur intelligent est une classe qui enveloppe un pointeur C ++ 'brut' (ou 'nu') pour gérer la durée de vie de l'objet pointé. Il n'y a pas un seul type de pointeur intelligent, mais tous essaient d'abstraire un pointeur brut d'une manière pratique.

Les pointeurs intelligents doivent être préférés aux pointeurs bruts. Si vous pensez que vous devez utiliser des pointeurs (d'abord, si vous vraiment do), vous voudrez normalement utiliser un pointeur intelligent, car cela peut atténuer de nombreux problèmes avec les pointeurs bruts, oubliant principalement de supprimer l'objet et la fuite de mémoire.

Avec les pointeurs bruts, le programmeur doit explicitement détruire l'objet quand 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 comparaison définit une politique quant à quand l'objet est détruit. Vous devez toujours 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

La stratégie la plus simple utilise la portée de l'objet wrapper de pointeur intelligent, 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 scoped_ptr les instances ne peuvent pas être copiées. Cela empêche le pointeur d'être supprimé plusieurs fois (incorrectement). Vous pouvez toutefois passer des références à d'autres fonctions que vous appelez.

Les pointeurs de portée sont utiles lorsque vous souhaitez lier la durée de vie de l'objet à un bloc de code particulier, ou si vous l'avez incorporé en tant que données de membre dans un autre objet, la durée de vie de cet autre objet. L'objet existe jusqu'à ce que le bloc de code conteneur soit quitté ou jusqu'à ce que l'objet conteneur soit lui-même détruit.

Une stratégie de pointeur intelligent plus complexe implique une référence en comptant le pointeur. Cela permet de copier le pointeur. 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 de comptage 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 à référencer les 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 définir une référence faible (non chiffrée) à shared_ptr.


METTRE À JOUR

Cette réponse est plutôt ancienne, et décrit donc ce qui était «bon» à l'époque, qui était des pointeurs intelligents 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 favoriser l'utilisation de std::unique_ptr, std::shared_ptr et std::weak_ptr.

Il y a aussi std::auto_ptr. Il ressemble beaucoup à un pointeur de portée, sauf qu'il a aussi la capacité dangereuse «spéciale» à copier - qui transfère aussi de manière inattendue la propriété! Il est obsolète dans les normes les plus récentes, vous ne devriez donc pas l'utiliser. Utilisez le std::unique_ptr au lieu.

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.

1640
2017-09-20 00:48



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

  • Qu'est-ce qu'un pointeur intelligent? 
    C'est un type de valeur qui peut être utilisé comme un pointeur, mais qui fournit la fonctionnalité supplémentaire de gestion automatique de la mémoire: Lorsque le pointeur n'est plus utilisé, la mémoire vers laquelle il pointe est désallouée (voir aussi la définition plus détaillée sur Wikipedia).
  • Quand devrais-je en utiliser un? 
    Dans un code qui implique le suivi de la propriété d'un morceau de mémoire, l'allocation ou la désallocation; le pointeur intelligent vous évite souvent de devoir faire ces choses explicitement.
  • Mais quel pointeur intelligent dois-je utiliser dans lequel de ces cas?
    • Utilisation std::unique_ptr lorsque vous n'avez pas l'intention de contenir plusieurs références au même objet. Par exemple, utilisez-le pour un pointeur vers la mémoire qui est alloué lors de l'entrée d'une étendue et désaffecté à la sortie de la portée.
    • Utilisation std::shared_ptr quand vous voulez faire référence à votre objet à partir de plusieurs endroits - et ne voulez pas qu'il soit désaffecté jusqu'à ce que toutes ces références soient elles-mêmes disparues.
    • Utilisation std::weak_ptr quand vous voulez faire référence à votre objet à partir de plusieurs endroits - pour les références pour lesquelles il est possible d'ignorer et de désallouer (ainsi ils noteront simplement que l'objet est parti quand vous essayez de déréférencer).
    • Ne pas utiliser le boost:: pointeurs intelligents ou std::auto_ptr sauf dans des cas particuliers que vous pouvez lire si vous le devez.
  • Hey, je n'ai pas demandé lequel utiliser! 
    Ah, mais tu voulais vraiment l'admettre.
  • Alors, quand devrais-je utiliser des pointeurs réguliers alors? 
    Surtout dans le code qui ne tient pas compte de la propriété de la mémoire. Ce serait typiquement dans les fonctions qui obtiennent un pointeur d'un autre endroit et n'allouent, ne désallouent pas ou ne stockent pas une copie du pointeur qui survit à leur exécution.

170
2018-05-09 19:06



Pointeur intelligent est un type de pointeur avec une fonctionnalité supplémentaire, par ex. désallocation automatique de la mémoire, comptage des références, etc.

Petite intro est disponible à la page Smart Pointers - Quoi, pourquoi, quoi?.

L'un des type simple pointeur intelligent est std::auto_ptr (chapitre 20.4.5 de la norme C ++), qui permet de libérer automatiquement la mémoire lorsqu'elle est hors de portée et qui est plus robuste que la simple utilisation du pointeur lorsque des exceptions sont levées, bien que moins flexible.

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

Le sujet est couvert en profondeur dans le livre "Modèles C ++: Le guide complet" par David Vandevoorde, Nicolai M. Josuttis, chapitre Chapitre 20. Smart Pointers. Quelques sujets couverts:


96
2017-09-20 00:32



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


33
2017-09-20 01:53



Un pointeur intelligent est comme un pointeur régulier (typé), comme "char *", sauf lorsque le pointeur lui-même est hors de portée, puis il est également supprimé. 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".

C'est utile pour:

  • Les objets qui doivent être attribués avec du nouveau, mais que vous souhaitez avoir la même durée de vie que quelque chose sur cette pile. Si l'objet est affecté à un pointeur intelligent, il sera supprimé lorsque le programme quittera cette fonction / bloc.

  • Les membres de données des classes, de sorte que lorsque l'objet est supprimé toutes les données possédées sont également supprimées, sans aucun code spécial dans le destructeur (vous devrez vous assurer que le destructeur est virtuel, ce qui est presque toujours une bonne chose à faire) .

Tu peux ne pas vouloir utiliser un pointeur intelligent lorsque:

  • ... le pointeur ne doit pas réellement posséder les données ... c.-à-d., lorsque vous utilisez simplement les données, mais que vous voulez qu'il survive à la fonction dans laquelle vous vous référez.
  • ... le pointeur intelligent ne sera pas lui-même détruit à un moment donné. Vous ne voulez pas qu'il reste en mémoire qui ne soit jamais détruit (comme dans un objet qui est dynamiquement alloué mais qui ne sera pas explicitement supprimé).
  • ... deux pointeurs intelligents pourraient pointer vers les mêmes données. (Il y a, cependant, des pointeurs encore plus intelligents qui vont gérer ça ... ça s'appelle comptage de référence.)

Voir également:


26
2017-09-20 00:13



La plupart des types de pointeurs intelligents gèrent l'élimination de l'objet pointeur vers vous. C'est très pratique car vous n'avez plus à 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 une grande variété de scénarios de mise au rebut, y compris les cas où les objets doivent être «passés à travers les limites de la DLL» (le cas de cauchemar commun si différent libcs sont utilisés entre votre code et les DLL).


14
2017-09-20 00:14



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

On peut mettre en œuvre son propre pointeur intelligent, mais de nombreuses bibliothèques fournissent également des implémentations de pointeurs intelligentes, chacune présentant des avantages et des inconvénients différents.

Par exemple, Renforcer fournit les implémentations de pointeur intelligent suivantes:

  • shared_ptr<T> est un pointeur vers T utiliser un nombre de références pour déterminer quand l'objet n'est plus nécessaire.
  • scoped_ptr<T> est un pointeur automatiquement supprimé lorsqu'il est hors de portée. Aucune affectation n'est possible.
  • intrusive_ptr<T>est un autre pointeur de comptage de référence. Il fournit de meilleures performances que shared_ptr, mais nécessite le type T fournir son propre mécanisme de comptage des références.
  • 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 tableaux de T.
  • scoped_array<T> est comme scoped_ptr, mais pour les tableaux de T.

Ce sont juste une description linéaire de chacun et peuvent être utilisés selon le besoin, pour plus de détails et d'exemples on peut regarder la documentation de Boost.

En outre, la bibliothèque standard C ++ fournit trois pointeurs intelligents; std::unique_ptr pour une 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 obsolète.


14
2018-03-12 09:51



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 en tant que classes de modèle qui encapsulent un pointeur et remplacent les opérateurs pointeurs standard. Ils ont un certain nombre d'avantages par rapport aux pointeurs réguliers. Ils sont garantis être initialisés en tant que pointeurs NULL ou pointeurs vers un objet tas. Indirection via un pointeur null est vérifiée. Aucune suppression n'est jamais nécessaire. Les objets sont automatiquement libérés lorsque le dernier pointeur leur 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);

9
2018-03-07 09: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 fournissant   caractéristiques supplémentaires, telles que automatique   la collecte des ordures ou la vérification des limites.   Ces fonctionnalités supplémentaires sont destinées   pour réduire les bugs causés par l'utilisation abusive de   pointeurs tout en conservant l'efficacité.   Les pointeurs intelligents gardent généralement trace de   les objets qui leur pointent pour le   but de la gestion de la mémoire. le   l'abus de pointeurs est une source majeure   des bugs: l'allocation constante,   désallocation et référencement qui doit   être effectué par un programme écrit   en utilisant des pointeurs le rend très probable   que certaines fuites de mémoire se produiront.   Les pointeurs intelligents essaient d'empêcher la mémoire   fuit en faisant la ressource   désallocation automatique: lorsque le   pointeur vers un objet (ou le dernier dans un   série de pointeurs) est détruit, pour   exemple parce que cela sort de la portée,   l'objet pointu est détruit aussi.


8
2017-09-20 00:12



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

1) Pointeurs bruts :

T a;  
T * _ptr = &a; 

Ils contiennent une adresse mémoire dans un emplacement en mémoire. Utilisez avec prudence, car les programmes deviennent difficiles à suivre.

Pointeurs avec données ou adresse const {Lire vers l'arrière}

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. c'est à dire *ptr1 = 19 ; ne fonctionnera pas. Mais vous pouvez déplacer le pointeur. c'est à dire ptr1++ , ptr1-- ; etc va fonctionner. Lecture à rebours: pointeur vers le type T qui est const

  T * const ptr2 ;

Un pointeur const vers un type de données T. Ce qui signifie que vous ne pouvez pas déplacer le pointeur mais vous pouvez changer la valeur pointée par le pointeur. c'est à dire *ptr2 = 19 travaillera mais ptr2++ ; ptr2-- etc ne fonctionnera pas. Lecture en arrière: pointeur const vers un type T

const T * const ptr3 ; 

Un pointeur const vers un type de données const T. Cela signifie que vous ne pouvez pas déplacer le pointeur ni changer le pointeur de type de données pour le pointeur. c'est à dire . ptr3-- ; ptr3++ ; *ptr3 = 19; ne fonctionnera pas

3) Smart Pointers : { #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

Implémenté en utilisant le comptage des références pour garder une trace du nombre de "choses" pointant vers l'objet pointé par le pointeur. Lorsque ce nombre passe à 0, l'objet est automatiquement supprimé, c'est-à-dire que l'objet object est supprimé lorsque tout le pointeur_ptr pointant vers l'objet est hors de portée. Cela supprime le mal de tête d'avoir à supprimer les objets que vous avez alloués en utilisant nouveau.

Pointeur faible:      Aide à gérer la référence cyclique qui se produit lors de l'utilisation de Shared Pointer     Si vous avez deux objets pointés par deux pointeurs partagés et qu'il y a un pointeur interne partagé pointant vers un autre pointeur partagé, il y aura une référence cyclique et l'objet ne sera pas supprimé lorsque les pointeurs partagés seront hors de portée. Pour résoudre ce problème, remplacez le membre interne d'un shared_ptr par weak_ptr. Note: Pour accéder à l'élément pointé par un pointeur faible, utilisez lock (), cela renvoie un 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 propriété exclusive. À utiliser 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é par l'unique ptr, utilisez la sémantique de déplacement

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 

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

Voir: Quelles sont les différences entre une variable pointeur 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 signalé cette question.


3
2018-03-03 00:58