Comment : créer et utiliser des instances shared_ptr
Le type shared_ptr est un pointeur intelligent de la bibliothèque standard C++ conçu pour des scénarios dans lesquels plusieurs propriétaires peuvent devoir gérer la durée de vie de l'objet en mémoire. Après avoir initialisé shared_ptr, vous pouvez le copier, passez-le par valeur dans des arguments de fonction, et assignez-le à d'autres instances shared_ptr. Tous les instances pointent vers le même objet, et partage l'accès à un « bloc de contrôle » qui incrémente et décrémente le décompte de références chaque fois qu'un nouvel shared_ptr est ajouté, est hors de portée, ou est réinitialisé. Quand le décompte de références atteint zéro, le bloc de contrôle supprime la ressource mémoire et lui-même.
L'illustration suivante représente plusieurs instances shared_ptr qui pointent vers un emplacement de mémoire.
Exemple
Autant que possible, utilisez la fonction make_shared (<memory>) pour créer un shared_ptr lorsque la ressource mémoire est créée pour la première fois. make_shared est protégé contre les exceptions. Il utilise le même appel pour allouer de la mémoire pour le bloc de contrôle et pour la ressource, et réduit de ce fait la charge mémoire de construction. Si vous n'utilisez pas make_shared, vous devez alors utiliser une nouvelle expression explicite pour créer l'objet avant de la passer au constructeur shared_ptr. L'exemple suivant indique différentes façons de déclarer et d'initialiser shared_ptr avec un nouvel objet.
// Use make_shared function when possible.
auto sp1 = make_shared<Song>(L"The Beatles", L"Im Happy Just to Dance With You");
// Ok, but slightly less efficient.
// Note: Using new expression as constructor argument
// creates no named variable for other code to access.
shared_ptr<Song> sp2(new Song(L"Lady Gaga", L"Just Dance"));
// When initialization must be separate from declaration, e.g. class members,
// initialize with nullptr to make your programming intent explicit.
shared_ptr<Song> sp5(nullptr);
//Equivalent to: shared_ptr<Song> sp5;
//...
sp5 = make_shared<Song>(L"Elton John", L"I'm Still Standing");
L'exemple suivant montre comment déclarer et initialiser les instances shared_ptr qui prennent en charge la propriété partagée d'un objet qui a déjà été alloué par un autre shared_ptr. Supposons que sp2 est un shared_ptr initialisé.
//Initialize with copy constructor. Increments ref count.
auto sp3(sp2);
//Initialize via assignment. Increments ref count.
auto sp4 = sp2;
//Initialize with nullptr. sp7 is empty.
shared_ptr<Song> sp7(nullptr);
// Initialize with another shared_ptr. sp1 and sp2
// swap pointers as well as ref counts.
sp1.swap(sp2);
shared_ptr est également utile dans des conteneurs STL (Standard Template Library) lorsque vous utilisez des algorithmes qui copie des éléments. Vous pouvez encapsuler des éléments dans un shared_ptr, et le copier dans d'autres conteneurs à condition que la mémoire sous-jacente soit valide tant que cela est nécessaire, et pas plus longtemps. L'exemple suivant montre comment utiliser les algorithmes replace_copy_if avec les instances shared_ptr dans un vecteur.
vector<shared_ptr<Song>> v;
v.push_back(make_shared<Song>(L"Bob Dylan", L"The Times They Are A Changing"));
v.push_back(make_shared<Song>(L"Aretha Franklin", L"Bridge Over Troubled Water"));
v.push_back(make_shared<Song>(L"Thal�a", L"Entre El Mar y Una Estrella"));
vector<shared_ptr<Song>> v2;
remove_copy_if(v.begin(), v.end(), back_inserter(v2), [] (shared_ptr<Song> s)
{
return s->artist.compare(L"Bob Dylan") == 0;
});
for (const auto& s : v2)
{
wcout << s->artist << L":" << s->title << endl;
}
Utilisez dynamic_pointer_cast, static_pointer_castet const_pointer_cast, pour caster un shared_ptr. Ces fonctions sont semblables aux dynamic_cast, static_cast, et aux opérateurs const_cast. L'exemple suivant indique comment tester le type dérivé de chaque élément dans un vecteur shared_ptr de classes de base, puis copier les éléments et afficher les informations les concernant.
vector<shared_ptr<MediaAsset>> assets;
assets.push_back(shared_ptr<Song>(new Song(L"Himesh Reshammiya", L"Tera Surroor")));
assets.push_back(shared_ptr<Song>(new Song(L"Penaz Masani", L"Tu Dil De De")));
assets.push_back(shared_ptr<Photo>(new Photo(L"2011-04-06", L"Redmond, WA", L"Soccer field at Microsoft.")));
vector<shared_ptr<MediaAsset>> photos;
copy_if(assets.begin(), assets.end(), back_inserter(photos), [] (shared_ptr<MediaAsset> p) -> bool
{
// Use dynamic_pointer_cast to test whether
// element is a shared_ptr<Photo>.
shared_ptr<Photo> temp = dynamic_pointer_cast<Photo>(p);
return temp.get() != nullptr;
});
for (const auto& p : photos)
{
// We know that the photos vector contains only
// shared_ptr<Photo> objects, so use static_cast.
wcout << "Photo location: " << (static_pointer_cast<Photo>(p))->location_ << endl;
}
Vous pouvez passer un shared_ptr à une autre fonction des façons suivantes :
Passez le shared_ptr par valeur. Il appelle le constructeur de copie, incrémente le décompte de références, et fait de l'appelé un propriétaire. Il existe une petite surcharge dans cette opération, qui peut être significative selon le nombre d'objets shared_ptr que vous passez. Utilisez cette option lorsque le contrat de code (implicite ou explicite) entre l'appelant et l'appelé requiert que l'appelé soit un propriétaire.
Passez le shared_ptr par référence ou référence const. Dans ce cas, le décompte de références n'est pas incrémenté, et l'appelé peut accéder au pointeur tant que l'appelant n'est pas hors de portée. Sinon, l'appelé peut décider de créer un shared_ptr selon la référence, et de devenir ainsi un propriétaire partagé. Utilisez cette option lorsque l'appelant n'a pas connaissance de l'appelé, ou lorsque vous devez passer un shared_ptr et que vous voulez empêcher l'opération de copie pour des raisons de performance.
Passez le pointeur sous-jacent ou une référence à l'objet sous-jacent. Cela permet à l'appelé d'utiliser l'objet, mais ne lui permet pas de partager la propriété ou d'étendre la durée de vie. Si l'appelé crée un shared_ptr depuis le pointeur brut, le nouvel shared_ptr est indépendant de l'original, et ne contrôle pas la ressource sous-jacente. Utilisez cette option lorsque le contrat entre l'appelant et l'appelé spécifie clairement que l'appelant conserve la propriété de la durée de vie de shared_ptr.
Lorsque vous choisissez comment passer un shared_ptr, déterminez si l'appelé doit partager la propriété de la ressource sous-jacente. Un « propriétaire » est un objet ou une fonction qui peut garder la ressource sous-jacente active tant qu'il en a besoin. Si l'appelant doit garantir que l'appelé puisse prolonger la vie du pointeur au delà de la durée de vie de la fonction, utilisez le première option. Si vous ne vous préoccupez pas de si l'appelé étend la durée de vie ou non, alors passez par référence et laissez l'appelé le copier ou non.
Si vous devez donner un accès à la fonction d'assistance au pointeur sous-jacent, et que vous savez que la fonction d'assistance simplement utilisera le pointeur et retournera avant que la fonction appelante ne retourne, alors cette fonction ne doit pas partager la propriété du pointeur sous-jacent. Elle doit simplement accéder au pointeur dans la durée de vie du shared_ptrde l'appelant. Dans ce cas, il est possible de passer le shared_ptr par référence, ou passez le pointeur brut ou une référence à l'objet sous-jacent. Passe de cette façon fournit un léger avantage en termes de performances, et peut également vous aider à exprimer votre intention de programmation.
Parfois, par exemple dans un std:vector<shared_ptr<T>>, vous devrez peut-être passer chaque shared_ptr dans un corps d'expression lambda ou un objet fonction nommé. Si la fonction ou le lambda ne stocke pas le pointeur, passez alors le shared_ptr par référence pour éviter d'appeler le constructeur de copie pour chaque élément.
L'exemple suivant montre comment shared_ptr surcharge différents opérateurs de comparaison pour activer des comparaisons de pointeur dans la mémoire qui est possédée par les instances shared_ptr.
// Initialize two separate raw pointers.
// Note that they contain the same values.
auto song1 = new Song(L"Village People", L"YMCA");
auto song2 = new Song(L"Village People", L"YMCA");
// Create two unrelated shared_ptrs.
shared_ptr<Song> p1(song1);
shared_ptr<Song> p2(song2);
// Unrelated shared_ptrs are never equal.
wcout << "p1 < p2 = " << std::boolalpha << (p1 < p2) << endl;
wcout << "p1 == p2 = " << std::boolalpha <<(p1 == p2) << endl;
// Related shared_ptr instances are always equal.
shared_ptr<Song> p3(p2);
wcout << "p3 == p2 = " << std::boolalpha << (p3 == p2) << endl;