Tutorial: como criar e usar instâncias shared_ptr
O tipo de shared_ptr
é um ponteiro inteligente na biblioteca padrão C++ projetada para cenários em que mais de um proprietário precisa gerenciar o tempo de vida de um objeto. Depois de inicializar um shared_ptr
, você pode copiá-lo, passá-lo pelo valor em argumentos de função e atribuí-lo a outras instâncias shared_ptr
. Todas as instâncias apontam para o mesmo objeto e compartilham o acesso a um "bloco de controle" que incrementa e decrementa a contagem de referência sempre que um novo shared_ptr
é adicionado, sai do escopo ou é redefinido. Quando a contagem de referência alcança zero, o bloco de controle exclui o recurso de memória e ele mesmo.
A ilustração a seguir mostra várias instâncias shared_ptr
que apontam para um local da memória.
O primeiro diagrama mostra um ponteiro compartilhado, P1, que aponta para uma instância MyClass, bem como um bloco de controle com contagem de ref = 1. O segundo diagrama mostra a adição de outro ponteiro compartilhado, P2, que também aponta para a instância MyClass e para o bloco de controle compartilhado, que agora tem uma contagem de ref de 2.
Exemplo de configuração
Os exemplos a seguir supõem que você incluiu os cabeçalhos necessários e declarou os tipos necessários, conforme mostrado aqui:
// 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
}
Exemplo 1
Sempre que possível, use a função make_shared para criar um shared_ptr
quando o recurso de memória for criado pela primeira vez. make_shared
é à prova de exceção. Ele usa a mesma chamada para alocar a memória para o bloco de controle e o recurso, que reduz a sobrecarga de construção. Se você não usar make_shared
, deverá usar uma expressão new
explícita para criar o objeto antes de passá-lo para o construtor shared_ptr
. O exemplo a seguir mostra várias maneiras de declarar e inicializar um shared_ptr
junto com um novo objeto.
// 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");
Exemplo 2
O exemplo a seguir mostra como declarar e inicializar instâncias shared_ptr
que assumem a posse compartilhada de um objeto que já foi alocado por outro shared_ptr
. Suponha que sp2
seja um shared_ptr
inicializado.
//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);
Exemplo 3
shared_ptr
também será útil em contêineres da Biblioteca Padrão do C++ quando você estiver usando algoritmos que copiam elementos. Você pode encapsular os elementos em um shared_ptr
e, em seguida, copiá-lo em outros contêineres sabendo que a memória subjacente é válida somente pelo tempo em que for necessária. O exemplo a seguir mostra como usar o algoritmo remove_copy_if
em instâncias shared_ptr
em um vetor.
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;
}
Exemplo 4
Você pode usar dynamic_pointer_cast
, static_pointer_cast
e const_pointer_cast
para converter um shared_ptr
. Essas funções se parecem com os operadores dynamic_cast
, static_cast
e const_cast
. O exemplo a seguir mostra como testar o tipo derivado de cada elemento em um vetor de shared_ptr
de classes base e copia os elementos e exibe informações sobre eles.
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;
}
Exemplo 5
Você pode passar um shared_ptr
para outra função das seguintes maneiras:
Passar o
shared_ptr
por valor. Isso invoca o construtor de cópia, incrementa a contagem de referência e torna o receptor um proprietário. Há uma pequena quantidade de sobrecarga nessa operação, que pode ser significativa dependendo de quantos objetosshared_ptr
você está passando. Use essa opção quando o contrato de código implícito ou explícito entre o chamador e o receptor exigir que o receptor seja um proprietário.Passar o
shared_ptr
por referência ou referência const. Nesse caso, a contagem de referência não é incrementada e o receptor pode acessar o ponteiro desde que o chamador não saia do escopo. Ou o receptor pode optar por criar umshared_ptr
com base na referência e se tornar um proprietário compartilhado. Use esta opção quando o chamador não tiver conhecimento do receptor ou quando você precisar passar umshared_ptr
e quiser evitar a operação de cópia por motivos de desempenho.Passar o ponteiro subjacente ou uma referência ao objeto subjacente. Isso permite que o receptor use o objeto, mas não o habilita a compartilhar a propriedade ou estender o tempo de vida. Se o receptor criar um
shared_ptr
do ponteiro bruto, o novoshared_ptr
será independente do original e não controlará o recurso subjacente. Use essa opção quando o contrato entre o chamador e o receptor claramente especificar que o chamador retém a propriedade do tempo de vidashared_ptr
.Ao decidir como passar um
shared_ptr
, determine se o receptor deve compartilhar a propriedade do recurso subjacente. Um "proprietário" é um objeto ou uma função que pode manter o recurso subjacente ativo pelo tempo necessário. Se o chamador precisar garantir que o receptor pode estender o tempo do ponteiro para além do seu tempo de vida (da função), use a primeira opção. Se não se importar se o receptor estende o tempo de vida, passe por referência e permita ou não que o receptor o copie.Se você precisar conceder a uma função auxiliar acesso ao ponteiro subjacente e souber que a função auxiliar usa o ponteiro e retornar antes que a função de chamada retorne, essa função não precisará compartilhar a propriedade do ponteiro subjacente. Ela precisa apenas acessar o ponteiro dentro do tempo de vida do
shared_ptr
do chamador. Nesse caso, é seguro passar oshared_ptr
por referência ou passar o ponteiro bruto ou uma referência ao objeto subjacente. Passar dessa maneira fornece um pequeno benefício de desempenho e também pode ajudar a expressar sua intenção de programação.Às vezes, por exemplo em um
std::vector<shared_ptr<T>>
, você poderá precisar passar cadashared_ptr
a um corpo da expressão lambda ou a um objeto de função. Se o lambda ou a função não armazenar o ponteiro, passeshared_ptr
por referência para evitar invocar o construtor de cópia para cada elemento.
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));
}
Exemplo 6
O exemplo a seguir mostra como shared_ptr
sobrecarrega vários operadores de comparação para habilitar comparações do ponteiro na memória que pertence às instâncias 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;
Confira também
Comentários
https://aka.ms/ContentUserFeedback.
Em breve: Ao longo de 2024, eliminaremos os problemas do GitHub como o mecanismo de comentários para conteúdo e o substituiremos por um novo sistema de comentários. Para obter mais informações, consulteEnviar e exibir comentários de