Question

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

Était-ce utile?

La solution

MISE À JOUR

Cette réponse est plutôt ancienne et décrit donc ce qui était "bon" à l'époque, ce 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 devez donc privilégier l'utilisation de std::unique_ptr , std::shared_ptr et std::weak_ptr .

Il existe également un std::auto_ptr . Cela ressemble beaucoup à un pointeur à portée, sauf qu'il a aussi le & "Spécial &"; capacité dangereuse à copier & # 8212; qui transfère aussi de manière inattendue la propriété! Il est obsolète dans les normes les plus récentes. Ne l'utilisez donc pas. Utilisez plutôt la boost::scoped_ptr .

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.

ANCIENNE RÉPONSE

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’existe pas de type de pointeur intelligent unique, mais ils essaient tous d’abstraire de manière pratique un pointeur brut.

Les pointeurs intelligents doivent être préférés aux pointeurs bruts. Si vous pensez que vous devez utiliser des pointeurs (considérez d’abord si vous le faites vraiment ), vous voudrez normalement utiliser un pointeur intelligent, car cela peut atténuer de nombreux problèmes liés aux pointeurs bruts, en oubliant principalement de supprimer le pointeur. objet et fuite de mémoire.

Avec les pointeurs bruts, le programmeur doit explicitement détruire l'objet lorsqu'il ne lui 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 indiquant le moment où 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 règle la plus simple utilisée implique la portée de l'objet wrapper du pointeur intelligent, tel que celui implémenté par boost::shared_ptr ou weak_ptr .

void f()
{
    {
       std::unique_ptr<MyObject> ptr(new MyObject());
       ptr->DoSomethingUseful();
    } // 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 shared_ptr les instances ne peuvent pas être copiées. Cela empêche le pointeur d'être supprimé plusieurs fois (de manière incorrecte). Vous pouvez toutefois lui renvoyer des références à d'autres fonctions que vous appelez.

Les

<=> sont utiles lorsque vous souhaitez lier la durée de vie de l'objet à un bloc de code particulier ou, si vous l'intégrez en tant que données membres dans un autre objet, à la durée de vie de cet autre objet. L'objet existe jusqu'à ce que le bloc de code contenant soit quitté ou jusqu'à ce que l'objet contenant soit lui-même détruit.

Une stratégie de pointeur intelligent plus complexe implique le décompte de références du pointeur. Cela permet au pointeur d'être copié. Lorsque la dernière & Quot; référence & Quot; à l'objet est détruit, l'objet est supprimé. Cette politique est implémentée par <=> et < a href = "http://fr.cppreference.com/w/cpp/memory/shared_ptr" rel = "nofollow noreferrer"> <=> .

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érences 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 & # 8212; la possibilité de créer une référence en suspens:

// 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 {
   std::shared_ptr<Owner> other;
};

std::shared_ptr<Owner> p1 (new Owner());
std::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 <=> afin de définir une référence faible (non comptée) à un <=>.

Autres conseils

Voici une réponse simple à cette époque du 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 offre la fonctionnalité supplémentaire de la gestion automatique de la mémoire: lorsqu'un pointeur intelligent n'est plus utilisé, sa mémoire est désallouée (voir aussi la définition plus détaillée sur Wikipedia ).
  • Quand devrais-je en utiliser un?
    Dans le code, il s’agit de suivre la propriété d’une mémoire, d’allouer ou de désaffecter; le pointeur intelligent vous évite souvent de devoir faire ces choses explicitement.
  • Mais quel pointeur intelligent dois-je utiliser dans lequel de ces cas?
    • Utilisez std::unique_ptr si vous ne souhaitez pas conserver plusieurs références. au même objet. Par exemple, utilisez-le pour un pointeur sur la mémoire qui est alloué en entrant dans une portée et désaffecté en quittant la portée.
    • Utilisez std::shared_ptr pour faire référence à votre objet à partir de plusieurs endroits - et ne souhaitez pas que votre objet soit désalloué tant que toutes ces références n'auront pas disparu
    • Utilisez std::weak_ptr lorsque vous souhaitez faire référence à votre objet depuis plusieurs endroits - pour les références pour lesquelles il est correct d'ignorer et de désallouer (ils noteront simplement que l'objet a disparu lorsque vous essayez de déréférencer).
    • N'utilisez pas les boost:: pointeurs intelligents ou std::auto_ptr sauf dans des cas particuliers pour lesquels vous pourrez en prendre connaissance si vous en avez besoin.
  • Hé, je n'ai pas demandé lequel utiliser!
    Ah, mais tu voulais vraiment l'admettre.
  • Dans quels cas devrais-je utiliser des pointeurs normaux?
    Principalement dans du code qui ignore la propriété de la mémoire. Il s’agit généralement de fonctions qui obtiennent un pointeur ailleurs, ne les allouent pas, ne les désaffectent pas et ne stockent pas une copie du pointeur qui dure plus longtemps que leur exécution.
Le

pointeur intelligent est un type de type pointeur doté de fonctionnalités supplémentaires, par exemple. désallocation automatique de la mémoire, comptage des références, etc.

Une petite introduction est disponible à la page Pointeurs intelligents: quoi, pourquoi, lesquels? .

L'un des types de pointeurs intelligents simples est std::auto_ptr (chapitre 20.4.5 de la norme C ++), qui permet de libérer automatiquement la mémoire quand elle est hors de portée et qui est plus robuste que l’utilisation simple du pointeur lorsque des exceptions sont levées, bien que moins souple.

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'il ne reste aucune référence à un objet. Cela permet d'éviter les fuites de mémoire et est facile à utiliser pour mettre en œuvre RAII .

Le sujet est traité de manière approfondie dans le livre & "Modèles C ++: le guide complet " par David Vandevoorde, Nicolai M. Josuttis , chapitre Chapitre 20. Pointeurs intelligents. Quelques sujets abordés:

Les définitions fournies par Chris, Sergdev et Llyod sont correctes. Je préfère cependant une définition plus simple, 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 lui faire faire des choses bien plus cool, y compris le comptage de références, la destruction automatique, etc. shared_ptr et auto_ptr suffisent dans la plupart des cas, mais viennent avec leur propre ensemble de petites idiosyncrasies.

Un pointeur intelligent ressemble à un pointeur normal (typé), comme & "; char * &" ;, sauf si le pointeur lui-même sort de la portée, ce qui est indiqué est également supprimé. Vous pouvez l'utiliser comme un pointeur normal, en utilisant & "; & Gt; &" ;, mais pas si vous avez besoin d'un pointeur réel sur les données. Pour cela, vous pouvez utiliser & "; & Amp; * ptr &";

.

Il est utile pour:

  • Objets qui doivent être alloués avec new, mais que vous souhaitez avoir la même durée de vie que quelque chose de cette pile. Si l'objet est affecté à un pointeur intelligent, ils seront supprimés lorsque le programme quittera cette fonction / ce bloc.

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

Vous pouvez ne pas vouloir utiliser un pointeur intelligent lorsque:

  • ... le pointeur ne doit pas posséder les données ... c’est-à-dire, lorsque vous utilisez simplement les données, mais que vous voulez que celles-ci survivent à la fonction où vous les référencez.
  • ... le pointeur intelligent ne va pas être détruit à un moment donné. Vous ne voulez pas qu'il reste dans une mémoire qui ne soit jamais détruite (comme dans un objet alloué dynamiquement mais qui ne sera pas explicitement supprimé).
  • ... deux pointeurs intelligents peuvent pointer vers les mêmes données. (Il existe cependant des pointeurs encore plus intelligents qui gèrent cela ... qui s'appelle le comptage de références . .)

Voir aussi:

La plupart des types de pointeurs intelligents gèrent l’élimination de l’objet pointeur-à-vous. C'est très pratique car vous n'avez plus à vous soucier de vous débarrasser d'objets manuellement.

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

libc est très polyvalent et traite une grande variété de scénarios de mise au rebut, y compris dans les cas où les objets doivent être & "passés au-delà des limites de la DLL &"; (le cas commun des cauchemars si différents <=> symboles sont utilisés entre votre code et les DLL).

Un pointeur intelligent est un objet qui agit comme un pointeur mais 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 proposent également des implémentations de pointeurs intelligents, chacune présentant des avantages et des inconvénients différents.

Par exemple, Boost fournit les implémentations suivantes du pointeur intelligent:

  • shared_ptr<T> est un pointeur sur T en utilisant un décompte 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érences. Il offre de meilleures performances que shared_ptr, mais nécessite que le type weak_ptr<T> fournisse son propre mécanisme de comptage de références.
  • shared_array<T> est un pointeur faible, associé à scoped_array<T> pour éviter les références circulaires.
  • scoped_ptr est semblable à std::unique_ptr, mais pour les tableaux de std::shared_ptr.
  • std::weak_ptr est semblable à std::auto_ptr, mais pour les tableaux de <=>.

Celles-ci ne représentent qu’une description linéaire de chacune d’elles et peuvent être utilisées selon les besoins. Pour plus de détails et d’exemples, vous pouvez consulter la documentation de Boost.

De plus, la bibliothèque standard C ++ fournit trois pointeurs intelligents; <=> pour la propriété unique, <=> pour la propriété partagée et <=>. <=> existait en C ++ 03 mais est maintenant obsolète.

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 à un pointeur normal, mais offre davantage 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 de pointeur standard. Ils ont un certain nombre d'avantages par rapport aux pointeurs habituels. Ils ont la garantie d’être initialisés en tant que pointeurs nuls ou pointeurs vers un objet tas. L'indirection via un pointeur nul est cochée. Aucune suppression n'est jamais nécessaire. Les objets sont automatiquement libérés lorsque le dernier pointeur les concernant est parti. Un problème important avec ces pointeurs intelligents est que, contrairement aux pointeurs classiques, ils ne respectent pas l'héritage. Les pointeurs intelligents ne sont pas attrayants pour le code polymorphe. Vous trouverez ci-dessous un exemple d'implémentation 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 sur 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 normal,

cout<<*p;
p->raise_salary(0.5);

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

  

En informatique, un pointeur intelligent   est un type de données abstrait qui   simule un pointeur tout en fournissant   des fonctionnalités supplémentaires, telles que   ramassage des ordures ou vérification des limites.   Ces fonctionnalités supplémentaires sont destinées   pour réduire les bugs causés par la mauvaise utilisation de   des pointeurs tout en maintenant l'efficacité.   Les pointeurs intelligents gardent généralement une trace de   les objets qui pointent vers eux pour la   but de la gestion de la mémoire. le   le mauvais usage des pointeurs est une source majeure   de bugs: l'allocation constante,   désallocation et référencement qui doit   être exécuté par un programme écrit   en utilisant des pointeurs le rend très probable   que des fuites de mémoire se produiront.   Les pointeurs intelligents essaient d'empêcher la mémoire   fuites en rendant la ressource   désallocation automatique: quand le   pointeur sur un objet (ou le dernier dans une   série de pointeurs) est détruite, par exemple   par exemple parce qu'il sort du champ d'application,   l'objet pointé est également détruit.

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

1) Pointeurs bruts :

T a;  
T * _ptr = &a; 

Ils détiennent une adresse mémoire à un emplacement en mémoire. À utiliser avec prudence, car les programmes deviennent complexes et difficiles à suivre.

Pointeurs avec données const ou adresse {Lecture en arrière}

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

Pointeur sur un type de données T qui est un const. Cela 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. fonctionnera. Lecture arrière: pointeur sur le type T qui est const

  T * const ptr2 ;

Un pointeur const sur un type de données T. Cela signifie que vous ne pouvez pas déplacer le pointeur, mais que vous pouvez modifier la valeur pointée par le pointeur. C'est-à-dire que *ptr2 = 19 fonctionnera mais ptr2++ ; ptr2-- etc. ne fonctionnera pas. Lecture arrière: pointeur const sur un type T

const T * const ptr3 ; 

Un pointeur const sur un type de données const T. Cela signifie que vous ne pouvez ni déplacer le pointeur ni changer le pointeur du type de données pour qu'il soit le pointeur. c'est à dire . 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

Implémenté en utilisant le comptage de références pour garder une trace du nombre " les choses " pointez sur l'objet pointé par le pointeur. Lorsque ce nombre passe à 0, l’objet est automatiquement supprimé, c’est-à-dire que l’objet l’objet est supprimé lorsque tous les share_ptr pointant sur l’objet sortent de la portée. Cela vous évite d'avoir à supprimer des objets que vous avez alloués avec new.

Pointeur faible:     Aide à gérer la référence cyclique qui survient lors de l'utilisation du pointeur partagé     Si vous avez deux objets pointés par deux pointeurs partagés et qu'un pointeur interne partagé pointe 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 sortiront de leur portée. Pour résoudre ce problème, remplacez le membre interne de shared_ptr par faible_ptr. Remarque: Pour accéder à l'élément pointé par un pointeur faible, utilisez lock (), retourne un objet faible.

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 est-ce que std :: faible_ptr est utile?

Pointeur unique:     Pointeur intelligent léger avec propriété exclusive. À utiliser lorsque le pointeur pointe sur 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 modifier 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 

Références:     Ils peuvent essentiellement être considérés comme des pointeurs constants, c’est-à-dire un pointeur qui est const et qui ne peuvent pas être déplacés avec une meilleure syntaxe.

Voir: Quoi sont les différences entre une variable de 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 souligné cette question.

Un pointeur intelligent est une classe, un wrapper d'un pointeur normal. Contrairement au pointeur normal, le cercle de vie de smart point & # 8217; est basé sur un décompte de références (nombre de fois que l’objet pointeur intelligent est attribué). Ainsi, chaque fois qu'un pointeur intelligent est affecté à un autre, le nombre de références internes plus plus. Et chaque fois que l'objet sort du champ d'application, le nombre de références moins moins.

Le pointeur automatique, même s'il a l'air similaire, est totalement différent du pointeur intelligent. Il s'agit d'une classe pratique qui libère la ressource chaque fois qu'un objet pointeur automatique sort de la portée de la variable. Dans une certaine mesure, un pointeur (vers la mémoire allouée dynamiquement) fonctionne comme une variable de pile (alloué statiquement au moment de la compilation).

Les pointeurs intelligents sont ceux pour lesquels vous n'avez pas à vous soucier de la désaffectation de la mémoire, du partage et du transfert de ressources.

Vous pouvez très bien utiliser ces pointeurs de la même manière que toute allocation fonctionne en Java. En Java, Garbage Collector fait le tour alors que dans les pointeurs intelligents, le tour est fait par les destructeurs.

Les réponses existantes sont bonnes mais ne couvrent pas ce qu'il faut faire lorsqu'un pointeur intelligent n'est pas la réponse (complète) 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 constitue une solution possible à Comment utiliser une classe abstraite comme type de renvoi de fonction? qui a été marqué comme un doublon de cette question. Cependant, la première question à poser pour savoir si vous êtes tenté de spécifier une classe de base abstraite (ou en fait une classe de base) comme type de retour en C ++ est & "Que voulez-vous vraiment dire? &"; Il existe une bonne discussion (avec d’autres références) sur la programmation orientée objet idiomatique en C ++ (et en quoi elle est différente des autres langages) dans la documentation de bibliothèque de conteneurs de pointeur boost . En résumé, en C ++, vous devez penser à la propriété. Quels pointeurs intelligents vous aident avec, mais ne sont pas la seule solution, ou toujours une solution complète (ils ne vous donnent pas une copie polymorphe) et ne sont pas toujours une solution que vous souhaitez exposer dans votre interface (et un retour de fonction sonne terriblement 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 renvoi d'une référence), vous avez modifié le retour d'une valeur à une forme de référence . Si vous avez vraiment besoin de copie, vous devrez peut-être ajouter plus de code passe-partout & "; Idiome &"; ou allez au-delà de la POO idiomatique (ou autre) de C ++ vers un polymorphisme plus générique à l'aide de bibliothèques telles que Adobe Poly ou Boost.TypeErasure .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top