Qu'est ce qu'un pointeur intelligent et quand dois-je utiliser?
qu'est Ce qu'un pointeur intelligent et quand dois-je utiliser?
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.
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.
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 oustd::auto_ptr
sauf dans des cas spéciaux que vous pouvez lire sur si vous le devez.
- Utiliser
- 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.
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:
- Protection Contre Les Exceptions "1519250920 de" Détenteurs", (note, std::auto_ptr est la mise en œuvre de ce type de pointeur intelligent)
- L'Acquisition de ressources est L'initialisation (ceci est fréquemment utilisé pour l'exception-ressource sûre gestion en C++)
- Titulaire Limites
- Comptage De Référence
- Accès Concurrent Au Comptoir
- la Destruction et la Libération de la mémoire
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.
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:
- garbage collection .
- cette question de débordement de la pile concernant la propriété des données
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).
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 versT
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 queshared_ptr
, mais nécessite le typeT
pour fournir son propre mécanisme de comptage de référence. -
weak_ptr<T>
est un pointeur faible, travaillant en conjonction avecshared_ptr
pour éviter les références circulaires. -
shared_array<T>
est commeshared_ptr
, mais pour les matrices deT
. -
scoped_array<T>
est commescoped_ptr
, mais pour les matrices deT
.
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é.
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);
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.
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.
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).
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.
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 .
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é.