std:: shared_ptr
|
Défini dans l'en-tête
<memory>
|
||
|
template
<
class
T
>
class
shared_ptr
;
|
(depuis C++11) | |
std::shared_ptr
est un pointeur intelligent qui conserve la propriété partagée d'un objet via un pointeur. Plusieurs
shared_ptr
objets peuvent posséder le même objet. L'objet est détruit et sa mémoire est désallouée lorsque l'une des conditions suivantes se produit :
-
le dernier
shared_ptrrestant possédant l'objet est détruit ; -
le dernier
shared_ptrrestant possédant l'objet se voit attribuer un autre pointeur via operator= ou reset() .
L'objet est détruit en utilisant
delete-expression
ou un destructeur personnalisé qui est fourni à
shared_ptr
pendant la construction.
Un
shared_ptr
peut partager la propriété d'un objet tout en stockant un pointeur vers un autre objet. Cette fonctionnalité peut être utilisée pour pointer vers des objets membres tout en possédant l'objet auquel ils appartiennent. Le pointeur stocké est celui accessible via
get()
, les opérateurs de déréférencement et de comparaison. Le pointeur géré est celui passé au destructeur lorsque le compteur d'utilisation atteint zéro.
Un
shared_ptr
peut également ne posséder aucun objet, auquel cas il est appelé
vide
(un
shared_ptr
vide peut avoir un pointeur stocké non nul si le constructeur d'aliasing a été utilisé pour le créer).
Toutes les spécialisations de
shared_ptr
satisfont aux exigences de
CopyConstructible
,
CopyAssignable
, et
LessThanComparable
et sont
contextuellement convertibles
en
bool
.
Toutes les fonctions membres (y compris le constructeur de copie et l'assignation de copie) peuvent être appelées par plusieurs threads sur différents
shared_ptr
objets sans synchronisation supplémentaire, même si ces objets sont des copies et partagent la propriété du même objet. Si plusieurs threads d'exécution accèdent au même
shared_ptr
objet sans synchronisation et que l'un de ces accès utilise une fonction membre non-const de
shared_ptr
alors une course aux données se produira ; le
std::atomic<shared_ptr>
peut être utilisé pour prévenir la course aux données.
Table des matières |
Types membres
| Type de membre | Définition | ||||
element_type
|
|
||||
weak_type
(depuis C++17)
|
std:: weak_ptr < T > |
Fonctions membres
construit un nouveau
shared_ptr
(fonction membre publique) |
|
détruit l'objet possédé si aucun autre
shared_ptr
n'y est lié
(fonction membre publique) |
|
assigne le
shared_ptr
(fonction membre publique) |
|
Modificateurs |
|
|
remplace l'objet géré
(fonction membre publique) |
|
|
échange les objets gérés
(fonction membre publique) |
|
Observateurs |
|
|
retourne le pointeur stocké
(fonction membre publique) |
|
|
déréférence le pointeur stocké
(fonction membre publique) |
|
|
(C++17)
|
fournit un accès indexé au tableau stocké
(fonction membre publique) |
retourne le nombre d'objets
shared_ptr
référençant le même objet géré
(fonction membre publique) |
|
|
(until C++20)
|
vérifie si l'objet géré est géré uniquement par l'objet
shared_ptr
courant
(fonction membre publique) |
|
vérifie si le pointeur stocké n'est pas nul
(fonction membre publique) |
|
|
fournit un ordonnancement basé sur le propriétaire des pointeurs partagés
(fonction membre publique) |
|
|
(C++26)
|
fournit un hachage basé sur le propriétaire des pointeurs partagés
(fonction membre publique) |
|
(C++26)
|
fournit une comparaison d'égalité basée sur le propriétaire des pointeurs partagés
(fonction membre publique) |
Fonctions non membres
|
crée un pointeur partagé qui gère un nouvel objet
(modèle de fonction) |
|
|
crée un pointeur partagé qui gère un nouvel objet alloué à l'aide d'un allocateur
(modèle de fonction) |
|
|
applique
static_cast
,
dynamic_cast
,
const_cast
, ou
reinterpret_cast
au pointeur stocké
(modèle de fonction) |
|
|
retourne le suppresseur du type spécifié, s'il est possédé
(modèle de fonction) |
|
|
(supprimé en C++20)
(supprimé en C++20)
(supprimé en C++20)
(supprimé en C++20)
(supprimé en C++20)
(C++20)
|
compare avec un autre
shared_ptr
ou avec
nullptr
(modèle de fonction) |
|
affiche la valeur du pointeur stocké dans un flux de sortie
(modèle de fonction) |
|
|
(C++11)
|
spécialise l'algorithme
std::swap
(modèle de fonction) |
spécialise les opérations atomiques pour
std::shared_ptr
(modèle de fonction) |
Classes d'assistance
|
(C++20)
|
pointeur partagé atomique
(spécialisation de modèle de classe) |
|
(C++11)
|
support de hachage pour
std::shared_ptr
(spécialisation de modèle de classe) |
Guides de déduction (depuis C++17)
Notes
La propriété d'un objet ne peut être partagée que par des fonctions de construction ou d'affectation qui reçoivent un autre objet
shared_ptr
. Si un nouveau
shared_ptr
est construit uniquement en utilisant le pointeur brut sous-jacent détenu par un autre
shared_ptr
, ce nouveau
shared_ptr
supposera qu'aucune autre instance
shared_ptr
ne détient l'objet qu'il possède. Cela entraînera (à moins qu'une affectation ultérieure ne se produise) l'application répétée du destructeur au même objet lors de la destruction.
std::shared_ptr
peut être utilisé avec un
type incomplet
T
. Cependant, le constructeur à partir d'un pointeur brut (
template
<
class
Y
>
shared_ptr
(
Y
*
)
) et la fonction membre
template
<
class
Y
>
void
reset
(
Y
*
)
ne peuvent être appelés qu'avec un pointeur vers un type complet (notez que
std::unique_ptr
peut être construit à partir d'un pointeur brut vers un type incomplet).
Le
T
dans
std
::
shared_ptr
<
T
>
peut être un type de fonction : dans ce cas, il gère un pointeur vers une fonction, plutôt qu'un pointeur d'objet. Ceci est parfois utilisé pour maintenir une bibliothèque dynamique ou un plugin chargé tant que l'une de ses fonctions est référencée :
void del(void(*)()) {} void fun() {} int main() { std::shared_ptr<void()> ee(fun, del); (*ee)(); }
Notes d'implémentation
Dans une implémentation typique,
shared_ptr
ne contient que deux pointeurs :
- le pointeur stocké (celui renvoyé par get() );
- un pointeur vers le control block .
Le bloc de contrôle est un objet alloué dynamiquement qui contient :
- soit un pointeur vers l'objet géré ou l'objet géré lui-même ;
- le suppresseur (effacement de type) ;
- l'allocateur (effacement de type) ;
-
le nombre de
shared_ptrqui possèdent l'objet géré ; -
le nombre de
weak_ptrqui référencent l'objet géré.
Lorsqu'un
shared_ptr
est créé en appelant
std::make_shared
ou
std::allocate_shared
, la mémoire pour le bloc de contrôle et l'objet managé est allouée en une seule allocation. L'objet managé est construit in-situ dans un membre de données du bloc de contrôle. Lorsqu'un
shared_ptr
est créé via l'un des constructeurs de
shared_ptr
, l'objet managé et le bloc de contrôle doivent être alloués séparément. Dans ce cas, le bloc de contrôle stocke un pointeur vers l'objet managé.
Le pointeur détenu directement par le
shared_ptr
est celui retourné par
get()
, tandis que le pointeur/objet détenu par le bloc de contrôle est celui qui sera supprimé lorsque le nombre de propriétaires partagés atteint zéro. Ces pointeurs ne sont pas nécessairement égaux.
Le destructeur de
shared_ptr
décrémente le nombre de propriétaires partagés du bloc de contrôle. Si ce compteur atteint zéro, le bloc de contrôle appelle le destructeur de l'objet géré. Le bloc de contrôle ne se désalloue pas tant que le compteur
std::weak_ptr
n'atteint pas zéro également.
Dans les implémentations existantes, le nombre de weak pointers est incrémenté ( [1] , [2] ) s'il existe un shared pointer pointant vers le même bloc de contrôle.
Pour satisfaire les exigences de sécurité des threads, les compteurs de référence sont généralement incrémentés en utilisant un équivalent de std::atomic::fetch_add avec std::memory_order_relaxed (la décrémentation nécessite un ordonnancement plus fort pour détruire en toute sécurité le bloc de contrôle).
Exemple
#include <chrono> #include <iostream> #include <memory> #include <mutex> #include <thread> using namespace std::chrono_literals; struct Base { Base() { std::cout << "Base::Base()\n"; } // Note: non-virtual destructor is OK here ~Base() { std::cout << "Base::~Base()\n"; } }; struct Derived : public Base { Derived() { std::cout << "Derived::Derived()\n"; } ~Derived() { std::cout << "Derived::~Derived()\n"; } }; void print(auto rem, std::shared_ptr<Base> const& sp) { std::cout << rem << "\n\tget() = " << sp.get() << ", use_count() = " << sp.use_count() << '\n'; } void thr(std::shared_ptr<Base> p) { std::this_thread::sleep_for(987ms); std::shared_ptr<Base> lp = p; // thread-safe, even though the // shared use_count is incremented { static std::mutex io_mutex; std::lock_guard<std::mutex> lk(io_mutex); print("Local pointer in a thread:", lp); } } int main() { std::shared_ptr<Base> p = std::make_shared<Derived>(); print("Created a shared Derived (as a pointer to Base)", p); std::thread t1{thr, p}, t2{thr, p}, t3{thr, p}; p.reset(); // release ownership from main print("Shared ownership between 3 threads and released ownership from main:", p); t1.join(); t2.join(); t3.join(); std::cout << "All threads completed, the last one deleted Derived.\n"; }
Sortie possible :
Base::Base() Derived::Derived() Created a shared Derived (as a pointer to Base) get() = 0x118ac30, use_count() = 1 Shared ownership between 3 threads and released ownership from main: get() = 0, use_count() = 0 Local pointer in a thread: get() = 0x118ac30, use_count() = 5 Local pointer in a thread: get() = 0x118ac30, use_count() = 4 Local pointer in a thread: get() = 0x118ac30, use_count() = 2 Derived::~Derived() Base::~Base() All threads completed, the last one deleted Derived.
Exemple
#include <iostream> #include <memory> struct MyObj { MyObj() { std::cout << "MyObj construit\n"; } ~MyObj() { std::cout << "MyObj détruit\n"; } }; struct Container : std::enable_shared_from_this<Container> // note: héritage public { std::shared_ptr<MyObj> memberObj; void CreateMember() { memberObj = std::make_shared<MyObj>(); } std::shared_ptr<MyObj> GetAsMyObj() { // Utiliser un alias shared_ptr pour le membre return std::shared_ptr<MyObj>(shared_from_this(), memberObj.get()); } }; #define COUT(str) std::cout << '\n' << str << '\n' #define DEMO(...) std::cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << '\n' int main() { COUT("Création de conteneur partagé"); std::shared_ptr<Container> cont = std::make_shared<Container>(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Création de membre"); cont->CreateMember(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Création d'un autre conteneur partagé"); std::shared_ptr<Container> cont2 = cont; DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("GetAsMyObj"); std::shared_ptr<MyObj> myobj1 = cont->GetAsMyObj(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("Copie de l'alias obj"); std::shared_ptr<MyObj> myobj2 = myobj1; DEMO(myobj1.use_count()); DEMO(myobj2.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("Réinitialisation de cont2"); cont2.reset(); DEMO(myobj1.use_count()); DEMO(myobj2.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Réinitialisation de myobj2"); myobj2.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Réinitialisation du compteur"); cont.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); }
Sortie :
Création du conteneur partagé cont.use_count() = 1 cont->memberObj.use_count() = 0 Création du membre MyObj construit cont.use_count() = 1 cont->memberObj.use_count() = 1 Création d'un autre conteneur partagé cont.use_count() = 2 cont->memberObj.use_count() = 1 cont2.use_count() = 2 cont2->memberObj.use_count() = 1 GetAsMyObj myobj1.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1 cont2.use_count() = 3 cont2->memberObj.use_count() = 1 Copie de l'objet alias myobj1.use_count() = 4 myobj2.use_count() = 4 cont.use_count() = 4 cont->memberObj.use_count() = 1 cont2.use_count() = 4 cont2->memberObj.use_count() = 1 Réinitialisation de cont2 myobj1.use_count() = 3 myobj2.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1 Réinitialisation de myobj2 myobj1.use_count() = 2 cont.use_count() = 2 cont->memberObj.use_count() = 1 Réinitialisation de cont myobj1.use_count() = 1 cont.use_count() = 0 MyObj détruit
Voir aussi
|
(C++11)
|
pointeur intelligent avec sémantique de propriété exclusive d'objet
(modèle de classe) |
|
(C++11)
|
référence faible à un objet géré par
std::shared_ptr
(modèle de classe) |