Middle
Come funziona il contatore di riferimenti in std::shared_ptr?
sobes.tech AI
Risposta dell'AI
Il contatore di riferimenti in std::shared_ptr viene memorizzato in un oggetto separato: nel blocco di controllo (control block).
Il blocco di controllo viene creato:
- Quando si crea per la prima volta uno
std::shared_ptrda un puntatore. - Quando si crea uno
std::shared_ptrcon un eliminatore o allocatore personalizzato. - Quando si utilizza
std::make_sharedostd::allocate_shared.
Questo blocco di controllo contiene almeno due contatori:
- Contatore di riferimenti forti (strong count): Aumenta alla creazione o copia di uno
std::shared_ptr. Diminuisce alla distruzione di unostd::shared_ptr. Quando questo contatore raggiunge zero, l'oggetto gestito viene liberato. - Contatore di riferimenti deboli (weak count): Aumenta alla creazione di uno
std::weak_ptrda unostd::shared_ptr. Diminuisce alla distruzione di unostd::weak_ptr. Il blocco di controllo viene liberato quando entrambi i contatori — forti e deboli — raggiungono zero.
L'uso di std::make_shared è preferibile rispetto alla creazione diretta con new, poiché può allocare l'oggetto e il blocco di controllo in un'unica area di memoria, migliorando le prestazioni e riducendo la frammentazione.
I contatori sono atomici, il che rende std::shared_ptr sicuro per l'uso in scenari multithread, anche se l'accesso all'oggetto gestito non è sincronizzato di default.
Esempio:
#include <iostream>
#include <memory>
struct MyObject {
int data;
MyObject(int d) : data(d) {
std::cout << "MyObject(" << data << ") creato\n";
}
~MyObject() {
std::cout << "MyObject(" << data << ") distrutto\n";
}
};
int main() {
std::shared_ptr<MyObject> ptr1 = std::make_shared<MyObject>(10);
// contatore forte: 1, contatore debole: 0
{
std::shared_ptr<MyObject> ptr2 = ptr1;
// contatore forte: 2, contatore debole: 0
std::weak_ptr<MyObject> weak_ptr = ptr1;
// contatore forte: 2, contatore debole: 1
if (auto locked_ptr = weak_ptr.lock()) {
// contatore forte: 3, contatore debole: 1 (temporaneamente in lock)
std::cout << "Dati oggetto: " << locked_ptr->data << std::endl;
}
// contatore forte: 2, contatore debole: 1
} // ptr2 esce dal scope
// contatore forte: 1, contatore debole: 1
// L'oggetto non è ancora stato distrutto, poiché il contatore forte == 1
{
std::weak_ptr<MyObject> weak_ptr2 = ptr1;
// contatore forte: 1, contatore debole: 2
} // weak_ptr2 esce dal scope
// contatore forte: 1, contatore debole: 1
} // ptr1 esce dal scope
// contatore forte: 0, contatore debole: 1
// L'oggetto viene distrutto, poiché il contatore forte è diventato 0
// Il blocco di controllo rimane finché il contatore debole non diventa 0