Instrukcje: tworzenie wystąpień shared_ptr i korzystanie z nich
Typ shared_ptr
jest inteligentnym wskaźnikiem w standardowej bibliotece języka C++, która jest przeznaczona dla scenariuszy, w których więcej niż jeden właściciel musi zarządzać okresem istnienia obiektu. Po zainicjowaniu wskaźnika shared_ptr
można go kopiować, przekazywać wg wartości w argumentach funkcji oraz przypisywać do innych wystąpień wskaźnika shared_ptr
. Wszystkie wystąpienia wskazują ten sam obiekt oraz mają wspólny dostęp do jednego „bloku sterującego”, który zwiększa i zmniejsza liczbę odwołań po każdym dodaniu nowego wskaźnika shared_ptr
, wykroczeniu przez wskaźnik poza zakres lub jego zresetowaniu. Gdy licznik odwołań osiągnie zero, blok sterujący usuwa zasób pamięci i samego siebie.
Na ilustracji poniżej widać kilka wystąpień wskaźnika shared_ptr
, które wskazują jedną lokalizację w pamięci.
Pierwszy diagram przedstawia wskaźnik współużytkowany P1, który wskazuje wystąpienie MyClass, a także blok kontrolny z liczbą ref = 1. Drugi diagram przedstawia dodanie innego współużytkowanego wskaźnika P2, który wskazuje również wystąpienie MyClass i wspólny blok sterowania, który ma teraz liczbę ref 2.
Przykład konfiguracji
W poniższych przykładach przyjęto założenie, że dołączono wymagane nagłówki i zadeklarowaliśmy wymagane typy, jak pokazano poniżej:
// shared_ptr-examples.cpp
// The following examples assume these declarations:
#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
struct MediaAsset
{
virtual ~MediaAsset() = default; // make it polymorphic
};
struct Song : public MediaAsset
{
std::wstring artist;
std::wstring title;
Song(const std::wstring& artist_, const std::wstring& title_) :
artist{ artist_ }, title{ title_ } {}
};
struct Photo : public MediaAsset
{
std::wstring date;
std::wstring location;
std::wstring subject;
Photo(
const std::wstring& date_,
const std::wstring& location_,
const std::wstring& subject_) :
date{ date_ }, location{ location_ }, subject{ subject_ } {}
};
using namespace std;
int main()
{
// The examples go here, in order:
// Example 1
// Example 2
// Example 3
// Example 4
// Example 6
}
Przykład 1
Jeśli to możliwe, użyj funkcji make_shared , aby utworzyć shared_ptr
element podczas tworzenia zasobu pamięci po raz pierwszy. Funkcja make_shared
jest bezpieczna pod względem wyjątków. Używa tego samego wywołania, aby przydzielić pamięć do bloku kontrolnego i zasobu, co zmniejsza obciążenie związane z konstrukcją. Jeśli nie używasz make_shared
metody , musisz użyć wyrażenia jawnego new
do utworzenia obiektu przed przekazaniem go do konstruktora shared_ptr
. Poniższy przykład pokazuje różne sposoby deklarowania i inicjowania wskaźnika shared_ptr
razem z nowym obiektem.
// 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");
Przykład 2
W poniższym przykładzie pokazano, jak zadeklarować i zainicjować shared_ptr
wystąpienia, które przejmują współwłaścicieli obiektu przydzielonego przez inny shared_ptr
obiekt . Załóżmy, że zainicjowanym wskaźnikiem sp2
jest shared_ptr
.
//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);
Przykład 3
shared_ptr
Jest również pomocny w kontenerach biblioteki standardowej języka C++ w przypadku używania algorytmów, które kopiują elementy. We wskaźniku shared_ptr
można opakować elementy, po czym skopiować go do innych kontenerów przy założeniu, że bazowa pamięć jest zajęta tylko przez niezbędny czas, nie dłużej. Poniższy przykład pokazuje, jak używać algorytmu remove_copy_if
do wystąpień wskaźnika shared_ptr
w wektorze.
vector<shared_ptr<Song>> v {
make_shared<Song>(L"Bob Dylan", L"The Times They Are A Changing"),
make_shared<Song>(L"Aretha Franklin", L"Bridge Over Troubled Water"),
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;
}
Przykład 4
Za pomocą funkcji dynamic_pointer_cast
, static_pointer_cast
i const_pointer_cast
można rzutować wskaźnik shared_ptr
. Funkcje te przypominają operatory dynamic_cast
, static_cast
i const_cast
. W poniższym przykładzie pokazano, jak przetestować typ pochodny każdego elementu w wektorze shared_ptr
klas bazowych, a następnie skopiować elementy i wyświetlić informacje o nich.
vector<shared_ptr<MediaAsset>> assets {
make_shared<Song>(L"Himesh Reshammiya", L"Tera Surroor"),
make_shared<Song>(L"Penaz Masani", L"Tu Dil De De"),
make_shared<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;
}
Przykład 5
Wskaźnik shared_ptr
można przekazać do innej funkcji w następujące sposoby:
Przekazanie wskaźnika
shared_ptr
wg wartości. Powoduje to wywołanie konstruktora kopiującego, zwiększenie wartości licznika odwołań i przekazanie własności obiektowi wywoływanemu. W tej operacji występuje niewielka ilość narzutów, co może być istotne w zależności od liczbyshared_ptr
przekazywanych obiektów. Użyj tej opcji, gdy dorozumiany lub jawny kontrakt kodu między obiektem wywołującym i wywoływanym wymaga, aby obiekt wywoływany był właścicielem.Przekazanie wskaźnika
shared_ptr
wg odwołania lub odwołania stałego. W takim przypadku liczba odwołań nie jest zwiększana, a obiekt wywoływany może uzyskać dostęp do wskaźnika, o ile obiekt wywołujący nie wykracza poza zakres. Lub obiekt wywoływany może zdecydować się na utworzenieshared_ptr
obiektu na podstawie odwołania i zostać właścicielem udostępnionym. Opcji należy używać w sytuacjach, gdy obiekt wywołujący nic nie wie o obiekcie wywoływanym albo kiedy trzeba przekazać wskaźnikshared_ptr
, ale ze względów wydajnościowych operacje kopiowania są niepożądane.Przekazanie bazowego wskaźnika lub odwołania do bazowego obiektu. Dzięki temu obiekt wywoływany może używać obiektu, ale nie umożliwia mu współużytkowania własności ani wydłużenia okresu istnienia. Jeśli obiekt wywoływany tworzy element
shared_ptr
z pierwotnego wskaźnika, nowyshared_ptr
element jest niezależny od oryginalnego elementu i nie kontroluje bazowego zasobu. Opcję należy stosować w przypadku, gdy kontrakt między obiektami wywołującym i wywoływanym jednoznacznie określa, że obiekt wywołujący zachowuje własność nad okresem istnienia wskaźnikashared_ptr
.Podczas podejmowania decyzji, jak przekazać element
shared_ptr
, określ, czy obiekt wywoływany musi współużytkować własność bazowego zasobu. „Właściciel” to obiekt lub funkcja, która może utrzymywać istnienie bazowego zasobu tak długo, jak to konieczne. Jeśli obiekt wywołujący ma gwarantować, że obiekt wywoływany może przedłużać okres istnienia wskaźnika poza okres istnienia funkcji, należy użyć pierwszej opcji. Gdy przedłużanie okresu istnienia przez obiekt wywoływany nie ma znaczenia, należy stosować przekazywanie wg odwołania i pozwolić obiektowi wywoływanemu na opcjonalne kopiowanie wskaźnika.Jeśli musisz udzielić funkcji pomocniczej dostępu do bazowego wskaźnika i wiesz, że funkcja pomocnika używa wskaźnika i zwraca przed zwróceniem funkcji wywołującej, ta funkcja nie musi współdzielić własności bazowego wskaźnika. Potrzebuje tylko dostępu do wskaźnika w trakcie okresu istnienia obiektu wywołującego wskaźnika
shared_ptr
. W takim przypadku można bezpiecznie przekazaćshared_ptr
odwołanie lub przekazać pierwotny wskaźnik lub odwołanie do obiektu bazowego. Taki sposób przekazania jest nieco korzystniejszy pod względem obciążenia systemu, a dodatkowo może pomóc lepiej wyrazić cele programistyczne.Czasami, na przykład w konstrukcji
std::vector<shared_ptr<T>>
, może być konieczne przekazanie każdego wskaźnikashared_ptr
do treści wyrażenia lambda lub do nazwanego obiektu funkcji. Jeśli funkcja lambda lub nie przechowuje wskaźnika, przekażshared_ptr
odwołanie, aby uniknąć wywoływania konstruktora kopiowania dla każdego elementu.
void use_shared_ptr_by_value(shared_ptr<int> sp);
void use_shared_ptr_by_reference(shared_ptr<int>& sp);
void use_shared_ptr_by_const_reference(const shared_ptr<int>& sp);
void use_raw_pointer(int* p);
void use_reference(int& r);
void test() {
auto sp = make_shared<int>(5);
// Pass the shared_ptr by value.
// This invokes the copy constructor, increments the reference count, and makes the callee an owner.
use_shared_ptr_by_value(sp);
// Pass the shared_ptr by reference or const reference.
// In this case, the reference count isn't incremented.
use_shared_ptr_by_reference(sp);
use_shared_ptr_by_const_reference(sp);
// Pass the underlying pointer or a reference to the underlying object.
use_raw_pointer(sp.get());
use_reference(*sp);
// Pass the shared_ptr by value.
// This invokes the move constructor, which doesn't increment the reference count
// but in fact transfers ownership to the callee.
use_shared_ptr_by_value(move(sp));
}
Przykład 6
W przykładzie poniżej pokazano, jak wskaźnik shared_ptr
przeciąża różne operatory porównania, aby umożliwić porównywanie wskaźników w pamięci, której właścicielami są wystąpienia wskaźnika 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;
Zobacz też
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla