Namespaces
Variants

std:: shared_ptr

From cppreference.net
Memory management library
( exposition only* )
Allocators
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Memory resources
Uninitialized storage (until C++20)
( until C++20* )
( until C++20* )
( until C++20* )

Garbage collector support (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
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_ptr restant possédant l'objet est détruit ;
  • le dernier shared_ptr restant 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
T (jusqu'en C++17)
std:: remove_extent_t < T > (depuis C++17)
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)
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)
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

pointeur partagé atomique
(spécialisation de modèle de classe)
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_ptr qui possèdent l'objet géré ;
  • le nombre de weak_ptr qui 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)