std::shared_ptr în C++

Pointer inteligent cu proprietate comună: obiectul este șters când ultimul shared_ptr dispare.

Sintaxă
#include <memory>
std::shared_ptr<T> p = std::make_shared<T>(argumente...);
long use_count() const noexcept;

Ce face std::shared_ptr

std::shared_ptr permite mai multor pointeri să dețină același obiect. Intern ține un contor de referințe: fiecare copie îl crește, fiecare copie distrusă îl scade, iar când ajunge la 0 obiectul este șters.

p.use_count() returnează câți shared_ptr dețin obiectul în acest moment. Este util pentru a înțelege mecanismul, dar nu te baza pe el în logica programului când lucrezi cu fire de execuție.

Folosește-l doar când proprietatea este cu adevărat împărțită. Dacă un singur loc deține obiectul, std::unique_ptr este mai simplu și mai rapid.

Ce returnează

Un obiect care deține (în comun) pointerul. use_count() returnează numărul de proprietari (long).

Exemple

Exemplul 1: Contorul de referințe

Copia din blocul interior crește contorul la 2; la ieșirea din bloc scade înapoi la 1.

#include <memory>
#include <print>
#include <string>

int main() {
    auto oras = std::make_shared<std::string>("Iași");
    std::println("proprietari: {}", oras.use_count());
    {
        std::shared_ptr<std::string> copie = oras;
        std::println("proprietari: {} ({})", oras.use_count(), *copie);
    }
    std::println("proprietari: {}", oras.use_count());
}

Rezultat

proprietari: 1
proprietari: 2 (Iași)
proprietari: 1

Exemplul 2: Un obiect folosit de mai mulți

Două conturi folosesc aceeași configurație. Obiectul este șters abia când dispare și ultimul cont.

#include <memory>
#include <print>
#include <string>

struct Config {
    std::string moneda = "lei";
    ~Config() { std::println("Config șters"); }
};

struct Cont {
    std::string titular;
    std::shared_ptr<Config> cfg;
};

int main() {
    auto cfg = std::make_shared<Config>();
    Cont a{"Ana", cfg};
    Cont b{"Mihai", cfg};
    cfg.reset();   // main nu mai deține configurația, conturile da

    std::println("{} folosește {}, proprietari: {}", a.titular, a.cfg->moneda, a.cfg.use_count());
    std::println("{} folosește {}", b.titular, b.cfg->moneda);
}

Rezultat

Ana folosește lei, proprietari: 2
Mihai folosește lei
Config șters

Greșeli frecvente

  • Doi shared_ptr care se țin unul pe altul (de exemplu părinte și copil) formează un ciclu: contorul nu ajunge niciodată la 0 și obiectele nu sunt șterse. Rupe ciclul cu std::weak_ptr.
  • Creezi doi shared_ptr separați din același pointer brut (std::shared_ptr<T> a(p), b(p);). Fiecare are contorul lui și obiectul va fi șters de două ori.

Comenzi înrudite

  • std::make_shared: Creează un obiect și un std::shared_ptr care îl deține, cu o singură alocare de memorie.
  • std::weak_ptr: Referință slabă la un obiect deținut de shared_ptr: nu îl ține în viață; lock() îl folosește în siguranță.
  • std::unique_ptr: Pointer inteligent care deține singur un obiect și îl șterge automat când iese din domeniu.
  • std::enable_shared_from_this: Clasă de bază care permite unui obiect deținut de shared_ptr să obțină un shared_ptr către el însuși.