Middle
Comment fonctionne le compteur de références dans std::shared_ptr?
sobes.tech IA
Réponse de l'IA
Le compteur de références dans std::shared_ptr est stocké dans un objet séparé : le bloc de contrôle (control block).
Le bloc de contrôle est créé :
- Lors de la première création d'un
std::shared_ptrà partir d'un pointeur. - Lors de la création d'un
std::shared_ptravec un destructeur ou un allocateur personnalisé. - Lors de l'utilisation de
std::make_sharedoustd::allocate_shared.
Ce bloc de contrôle contient au moins deux compteurs :
- Compteur de références fortes (strong count) : Augmente lors de la création ou de la copie d'un
std::shared_ptr. Diminue lors de la destruction d'unstd::shared_ptr. Lorsque ce compteur atteint zéro, l'objet géré est libéré. - Compteur de références faibles (weak count) : Augmente lors de la création d'un
std::weak_ptrà partir d'unstd::shared_ptr. Diminue lors de la destruction d'unstd::weak_ptr. Le bloc de contrôle est libéré lorsque les deux compteurs — forts et faibles — atteignent zéro.
L'utilisation de std::make_shared est préférée à la création directe avec new, car elle peut allouer l'objet et le bloc de contrôle en un seul bloc de mémoire, ce qui améliore les performances et réduit la fragmentation.
Les compteurs sont atomiques, ce qui rend std::shared_ptr sûr pour une utilisation dans des scénarios multithread, bien que l'accès à l'objet géré ne soit pas synchronisé par défaut.
Exemple :
#include <iostream>
#include <memory>
struct MyObject {
int data;
MyObject(int d) : data(d) {
std::cout << "MyObject(" << data << ") créé\n";
}
~MyObject() {
std::cout << "MyObject(" << data << ") détruit\n";
}
};
int main() {
std::shared_ptr<MyObject> ptr1 = std::make_shared<MyObject>(10);
// compteur fort : 1, compteur faible : 0
{
std::shared_ptr<MyObject> ptr2 = ptr1;
// compteur fort : 2, compteur faible : 0
std::weak_ptr<MyObject> weak_ptr = ptr1;
// compteur fort : 2, compteur faible : 1
if (auto locked_ptr = weak_ptr.lock()) {
// compteur fort : 3, compteur faible : 1 (temporairement dans lock)
std::cout << "Données de l'objet : " << locked_ptr->data << std::endl;
}
// compteur fort : 2, compteur faible : 1
} // ptr2 sort de la portée
// compteur fort : 1, compteur faible : 1
// L'objet n'a pas encore été détruit, car le compteur fort == 1
{
std::weak_ptr<MyObject> weak_ptr2 = ptr1;
// compteur fort : 1, compteur faible : 2
} // weak_ptr2 sort de la portée
// compteur fort : 1, compteur faible : 1
} // ptr1 sort de la portée
// compteur fort : 0, compteur faible : 1
// L'objet est détruit, car le compteur fort est devenu 0
// Le bloc de contrôle reste jusqu'à ce que le compteur faible devienne 0