Os ponteiros inteligentes em C++ (std::shared_ptr
, std::unique_ptr
e std::weak_ptr
) são utilizados para gerenciar a alocação e desalocação automática de memória, ajudando a evitar problemas como vazamentos de memória e dangling pointers (ponteiros que apontam para um local de memória que já foi desalocado).
Vamos ver a diferença entre os principais ponteiros inteligentes!
std::shared_ptr
shared_ptr
é feita, esse contador é incrementado. Quando todos os shared_ptr
que apontam para o mesmo objeto são destruídos (contador chega a zero), o objeto apontado é desalocado.Exemplo:
#include <iostream>
#include <memory>
struct Resource{
Resource(){ std::cout << "Resource acquired\n"; }
~Resource(){ std::cout << "Resource destroyed\n"; }
};
int main(){
std::shared_ptr<Resource> ptr1 = std::make_shared<Resource>();
{
std::shared_ptr<Resource> ptr2 = ptr1; // ptr2 também compartilha o objeto.
std::cout << "Reference count: " << ptr1.use_count() << "\n"; // Exibe o contador.
}
// Quando ptr2 sai do escopo, o contador é decrementado, mas o recurso ainda existe.
std::cout << "Reference count: " << ptr1.use_count() << "\n"; // Exibe o contador.
// Quando ptr1 sai do escopo, o objeto é destruído, pois o contador chega a 0.
}
Saída:
Resource acquired
Reference count: 2
Reference count: 1
Resource destroyed
std::unique_ptr
unique_ptr
pode apontar para o mesmo recurso de cada vez.Exemplo:
#include <iostream>
#include <memory>
struct Resource {
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
};
int main() {
std::unique_ptr<Resource> ptr1 = std::make_unique<Resource>();
// std::unique_ptr<Resource> ptr2 = ptr1; // Isso não compilaria, cópia não permitida.
std::unique_ptr<Resource> ptr2 = std::move(ptr1); // Transferência de posse.
if (!ptr1) {
std::cout << "ptr1 is null\n";
}
// Quando ptr2 sai do escopo, o recurso é automaticamente destruído.
}
Saída:
Resource acquired
ptr1 is null
Resource destroyed
std::weak_ptr
shared_ptr
sem contribuir para o contador de referência.shared_ptr
se referenciam mutuamente, criando um ciclo que impede a liberação da memória).lock()
, que retorna um std::shared_ptr
se o objeto ainda existir.Exemplo:
#include <iostream>
#include <memory>
struct Resource {
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
};
int main() {
std::shared_ptr<Resource> ptr1 = std::make_shared<Resource>();
std::weak_ptr<Resource> weakPtr = ptr1; // weakPtr observa, mas não contribui para o contador.
if (auto tempPtr = weakPtr.lock()) { // Verifica se o recurso ainda existe.
std::cout << "Resource is still alive\n";
} else {
std::cout << "Resource has been destroyed\n";
}
ptr1.reset(); // Libera o recurso.
if (auto tempPtr = weakPtr.lock()) {
std::cout << "Resource is still alive\n";
} else {
std::cout << "Resource has been destroyed\n";
}
}
Saída:
Resource acquired
Resource is still alive
Resource destroyed
Resource has been destroyed
Ou seja, shared_ptr
: Possuído por vários donos, com contagem de referências para gerenciar a destruição do objeto. unique_ptr
: Posse exclusiva, não pode ser copiado, apenas movido. weak_ptr
: Observa o recurso gerenciado por um shared_ptr
, mas não interfere na contagem de referências, usado para evitar ciclos de referência.