Smart Pointers em C++
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 15 de 104.
Smart pointers são classes wrapper que gerenciam automaticamente a memória heap para você. Eles eliminam a necessidade de chamadas delete manuais ao limpar a memória quando o ponteiro sai de escopo.
Inclua o cabeçalho <memory> para usar ponteiros inteligentes.
unique_ptr - propriedade exclusiva. Apenas um ponteiro pode possuir o recurso por vez.
#include <memory>
std::unique_ptr<Player> player = std::make_unique<Player>("Hero");
player->attack(); // Use como um ponteiro comum
// Não pode copiar, mas pode transferir a posse
std::unique_ptr<Player> player2 = std::move(player);
// player agora é nullptrshared_ptr - propriedade compartilhada. Múltiplos ponteiros podem possuir o mesmo recurso. A memória é liberada quando o último proprietário é destruído.
std::shared_ptr<Player> p1 = std::make_shared<Player>("Hero");
std::shared_ptr<Player> p2 = p1; // Ambos possuem o recurso
std::cout << p1.use_count(); // Imprime 2weak_ptr - observador que não detém a posse de um shared_ptr. Útil para quebrar referências circulares.
std::shared_ptr<Player> shared = std::make_shared<Player>("Hero");
std::weak_ptr<Player> weak = shared;
if (auto locked = weak.lock()) { // Converter para shared_ptr
locked->attack();
}Use unique_ptr por padrão para propriedade única. Use shared_ptr quando múltiplos objetos precisarem compartilhar um recurso. Use weak_ptr para observar sem estender o tempo de vida.
Prefira make_unique e make_shared em vez do new bruto.
Desafio
FácilVamos construir um sistema de compartilhamento de documentos que demonstra os diferentes modelos de propriedade de smart pointers. Você verá como o unique_ptr impõe a propriedade exclusiva, como o shared_ptr permite múltiplos proprietários e como o weak_ptr pode observar sem afetar o tempo de vida.
Você criará dois arquivos para organizar seu código:
Document.h: Defina uma classeDocumentque representa um documento compartilhável. Ela deve ter:- Um atributo privado
title(string) - Um construtor que recebe um título e imprime
"Document created: <title>" - Um destruidor que imprime
"Document deleted: <title>" - Um método
getTitle()que retorna o título
- Um atributo privado
main.cpp: Demonstre todos os três tipos de smart pointers lendo um título de documento da entrada e, em seguida:- Crie um
unique_ptrpara um Document com o título de entrada - Imprima
"Unique owner: <title>" - Transfira a propriedade para outro
unique_ptrusandostd::move - Imprima
"Ownership transferred" - Crie um
shared_ptrpara um novo Document com o título"SharedDoc" - Crie um segundo
shared_ptrapontando para o mesmo documento - Imprima
"Shared owners: <count>"usandouse_count() - Crie um
weak_ptra partir de um dos shared pointers - Use
lock()para acessar o documento através do weak pointer e imprima"Weak access: <title>"
- Crie um
Lembre-se de incluir <memory> para os smart pointers. Use std::make_unique e std::make_shared para criar seus smart pointers. As mensagens do destruidor ajudarão você a ver quando cada documento é limpo automaticamente ao final do programa.
Inclua seu arquivo de cabeçalho no main.cpp usando #include "Document.h".
Folha de consulta
Smart pointers (ponteiros inteligentes) são classes wrapper que gerenciam automaticamente a memória heap. Inclua o cabeçalho <memory> para utilizá-los.
unique_ptr
Propriedade exclusiva - apenas um ponteiro pode possuir o recurso por vez:
std::unique_ptr<Player> player = std::make_unique<Player>("Hero");
player->attack(); // Use como um ponteiro comum
// Transfer ownership with std::move
std::unique_ptr<Player> player2 = std::move(player);
// player is now nullptrshared_ptr
Propriedade compartilhada - múltiplos ponteiros podem possuir o mesmo recurso. A memória é liberada quando o último proprietário é destruído:
std::shared_ptr<Player> p1 = std::make_shared<Player>("Hero");
std::shared_ptr<Player> p2 = p1; // Both own the resource
std::cout << p1.use_count(); // Prints 2weak_ptr
Observador sem propriedade de um shared_ptr. Útil para quebrar referências circulares:
std::shared_ptr<Player> shared = std::make_shared<Player>("Hero");
std::weak_ptr<Player> weak = shared;
if (auto locked = weak.lock()) { // Convert to shared_ptr
locked->attack();
}Melhores práticas: Use unique_ptr por padrão para propriedade única. Use shared_ptr quando múltiplos objetos precisarem compartilhar um recurso. Use weak_ptr para observar sem estender o tempo de vida. Prefira make_unique e make_shared em vez do new bruto.
Experimente você mesmo
#include <iostream>
#include <memory>
#include <string>
#include "Document.h"
using namespace std;
int main() {
string inputTitle;
cin >> inputTitle;
// TODO: Criar um unique_ptr para um Document com inputTitle
// usando std::make_unique
// TODO: Imprimir "Unique owner: <title>"
// TODO: Transferir a posse para outro unique_ptr usando std::move
// TODO: Imprimir "Ownership transferred"
// TODO: Criar um shared_ptr para um novo Document com o título "SharedDoc"
// usando std::make_shared
// TODO: Criar um segundo shared_ptr apontando para o mesmo documento
// TODO: Imprimir "Shared owners: <count>" usando use_count()
// TODO: Criar um weak_ptr a partir de um dos shared pointers
// TODO: Usar lock() para acessar o documento através do weak_ptr
// e imprimir "Weak access: <title>"
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de OOP
Arquivos ExternosBuild e Compilação em C++Arquivos Header e SourceNamespaces e EscopoIntrodução a OOP em C++Classes vs ObjetosO Ponteiro 'this'Métodos (Funções Membro)Atributos (Membros de Dados)Básico de Ctors e DtorsRecapitulação - Calculadora Simples4Propriedades de Classe
Membros de Instância vs EstáticosGetters e SettersFunções de Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRecapitulação - Gerenciador de Conta Bancária7Herança
Herança BásicaNíveis de Acesso na HerançaOrdem de Chamada de Ctor e DtorSobrescrita de MétodosFunções Virtuais e VTableHerança MúltiplaHerança VirtualRecapitulação - Hierarquia de Funcionários2Gerenciamento de Memória
Memória Stack vs HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Smart Pointers em C++RAII em C++Recapitulação - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de Acesso em C++Especificadores de Acesso em DetalhesOcultação de InformaçãoStruct vs ClassClasses Aninhadas e InternasRecapitulação - Sistema de Registros de Alunos8Polimorfismo
Polimorfismo de Compilação vs Tempo de ExecuçãoSobrecarga de FunçõesFunções Virtuais RevisitadasFunções Virtuais PurasClasses AbstratasDesign de Interfaces em C++Dynamic Casting e RTTIRecapitulação - Calculadora de Formas3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentaçãoListas de Inicialização do ConstrutorConstrutores DelegadosMergulho Profundo em DestrutoresRegra dos Três / Cinco / ZeroRecapitulação - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de StreamSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçãoTemplates de ClasseEspecialização de TemplatesTemplates VariádicosFundamentos de SFINAE e Type TraitsRecapitulação - Container Genérico