C++: Ponteiros Inteligentes Avançados

Última atualização: 2026-08-26

Na aula 45, aprendemos sobre semântica de movimentação.

Agora, vamos nos aprofundar nos smart pointers — a ferramenta central do gerenciamento de memória moderno do C++.

Com smart pointers, você nunca mais precisará se preocupar com vazamentos de memória.


1. Visão Geral dos Smart Pointers

(1) 1.1 O Que São Smart Pointers?

Smart pointers são classes template que gerenciam memória dinâmica, liberando a memória automaticamente (RAII).

Três tipos de smart pointers:

Smart Pointer Função Caso de Uso
unique_ptr Propriedade exclusiva Propriedade exclusiva de um objeto
shared_ptr Propriedade compartilhada Múltiplos locais precisam acessar o mesmo objeto
weak_ptr Referência fraca Quebrar referências circulares


2. unique_ptr

(1) 2.1 Uso Básico

unique_ptr é um smart pointer com propriedade exclusiva — não pode ser copiado, apenas movido.

Exemplo: uso básico de unique_ptr (Dificuldade ⭐)

▶ Exemplo 2: Exemplo de código (Dificuldade ⭐)

CPP
#include <iostream>
#include <memory>

int main() {
 std::unique_ptr<int> p1(new int(42));
 std::cout << *p1 << std::endl; // Saída: 42
 
 // std::unique_ptr<int> p2 = p1; // ❌ Erro! Não pode copiar
 std::unique_ptr<int> p2 = std::move(p1); // ✅ Pode mover
 std::cout << *p2 << std::endl; // Saída: 42
 
 return 0;
} // p2 libera a memória automaticamente
▶ Experimente

Saída:

TEXT 📖 Somente leitura
(Saída do programa)

(2) 2.2 Deleter Personalizado

Exemplo: Usando unique_ptr para gerenciar um arquivo (Dificuldade ⭐⭐)

CPP
#include <iostream>
#include <memory>
#include <cstdio>

// Deleter personalizado: fechar arquivo
struct FileDeleter {
 void operator()(FILE* fp) const {
 if (fp) {
 fclose(fp);
 std::cout << "File closed" << std::endl;
 }
 }
};

int main() {
 std::unique_ptr<FILE, FileDeleter> file(fopen("test.txt", "w"));
 // Não precisa de fclose manual, unique_ptr chama FileDeleter automaticamente
 
 return 0;
}


3. shared_ptr

(1) 3.1 Uso Básico

shared_ptr é um smart pointer com propriedade compartilhada, gerenciando memória com contagem de referências.

Exemplo: uso básico de shared_ptr (Dificuldade ⭐)

CPP
#include <iostream>
#include <memory>

int main() {
 std::shared_ptr<int> p1 = std::make_shared<int>(42);
 std::cout << "Reference count: " << p1.use_count() << std::endl; // 1
 
 {
 std::shared_ptr<int> p2 = p1; // Cópia, contagem de referências +1
 std::cout << "Reference count: " << p1.use_count() << std::endl; // 2
 } // p2 destruído, contagem de referências -1
 
 std::cout << "Reference count: " << p1.use_count() << std::endl; // 1
 
 return 0;
}

(2) 3.2 make_shared vs new

Recomendação: Use std::make_shared em vez de new.

Comparação new make_shared
Segurança de exceção Pode vazar Seguro
Desempenho Duas alocações Uma alocação
Clareza do código Verboso Conciso
CPP
// Recomendado
auto p1 = std::make_shared<int>(42);

// Não recomendado
std::shared_ptr<int> p2(new int(42));


4. weak_ptr

(1) 4.1 Por Que Precisamos do weak_ptr?

Problema: shared_ptr pode causar referências circulares, levando a vazamentos de memória.

Exemplo: Referência circular (Dificuldade ⭐⭐⭐)

CPP
#include <iostream>
#include <memory>

struct Node {
 std::shared_ptr<Node> next; // Referência circular!
 ~Node() { std::cout << "Node destroyed" << std::endl; }
};

int main() {
 auto n1 = std::make_shared<Node>();
 auto n2 = std::make_shared<Node>();
 
 n1->next = n2; // Referência circular
 n2->next = n1;
 
 return 0;
} // ❌ n1 e n2 não serão liberados (vazamento de memória)

(2) 4.2 Quebrando Referências Circulares com weak_ptr

Solução: Substitua um dos shared_ptr por weak_ptr.

CPP
struct Node {
 std::weak_ptr<Node> next; // Usar weak_ptr, não incrementa a contagem de referências
 ~Node() { std::cout << "Node destroyed" << std::endl; }
};

int main() {
 auto n1 = std::make_shared<Node>();
 auto n2 = std::make_shared<Node>();
 
 n1->next = n2; // Não incrementa a contagem de referências
 n2->next = n1;
 
 return 0;
} // ✅ Liberado corretamente


5. Guia de Seleção de Smart Pointers

(1) 5.1 Como Escolher?

Cenário Recomendação
Propriedade exclusiva unique_ptr
Propriedade compartilhada shared_ptr
Observador weak_ptr ou ponteiro bruto
Arrays unique_ptr<T>

(2) 5.2 O Que Não Fazer

Erro Explicação
Não inicialize múltiplos shared_ptrs a partir do mesmo ponteiro bruto Causará double free
Não misture ponteiros brutos e smart pointers Quebra o RAII
Não chame delete manualmente Deixe os smart pointers gerenciarem


6. Prática: Gerenciando Recursos com Smart Pointers

▶ Exemplo 1: Usando smart pointers para gerenciar uma conexão de banco de dados (Dificuldade ⭐⭐⭐)

TEXT 📖 Somente leitura
#include <iostream>
#include <memory>

// Conexão de banco de dados simulada
class DatabaseConnection {
public:
 DatabaseConnection() {
 std::cout << "Connecting to database" << std::endl;
 }
 
 ~DatabaseConnection() {
 std::cout << "Disconnected" << std::endl;
 }
 
 void query(const std::string& sql) {
 std::cout << "Executing SQL: " << sql << std::endl;
 }
};

int main() {
 // Usar unique_ptr para gerenciar recurso exclusivo
 std::unique_ptrDatabaseConnection conn(new DatabaseConnection());
 conn->query("SELECT * FROM users");
 
 return 0;
} // Desconecta automaticamente

Saída:

TEXT 📖 Somente leitura
Connecting to database
Disconnected
Executing SQL: 

❓ Perguntas Frequentes

P: Qual é mais rápido, unique_ptr ou shared_ptr? R: unique_ptr é mais rápido (sem overhead de contagem de referências). Prefira unique_ptr.


P: Quando devo usar ponteiros brutos? R: - Quando você não é dono do recurso (observador) - Ao interagir com bibliotecas C - Quando o desempenho é extremamente crítico (mas geralmente desnecessário)


P: O shared_ptr é thread-safe? R: - Modificações na contagem de referências são thread-safe - Mas o objeto apontado não é thread-safe (precisa de proteção com mutex)


▶ Exemplo 3: Propriedade compartilhada com shared_ptr (Dificuldade ⭐)

CPP
#include <iostream>
#include <memory>

class Resource {
public:
    Resource() { std::cout << "Construct" << std::endl; }
    ~Resource() { std::cout << "Destruct" << std::endl; }
};

int main() {
    {
        std::shared_ptr<Resource> p1 = std::make_shared<Resource>();
        std::shared_ptr<Resource> p2 = p1; // Propriedade compartilhada

        std::cout << "Reference count: " << p1.use_count() << std::endl;
    }
    // Sai do escopo, destrói automaticamente

    std::cout << "Program ended" << std::endl;
    return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Construct
Destruct
Reference count: 
Program ended
💡 Dica: shared_ptr compartilha a propriedade e libera automaticamente quando a contagem de referências chega a zero. Usar make_shared é mais seguro para criação.


Ponto-Chave Resumo
unique_ptr Propriedade exclusiva, não pode ser copiado
shared_ptr Propriedade compartilhada, contagem de referências
weak_ptr Referência fraca, quebra referências circulares
make_shared Abordagem recomendada, seguro contra exceções
Princípio de seleção Prefira unique_ptr, use shared_ptr quando necessário

📖 Resumo

📝 Exercícios

  1. Básico (Dificuldade ⭐): Use unique_ptr para gerenciar um int alocado dinamicamente, criado com make_unique. Tente copiar o unique_ptr (deve falhar na compilação), então use move.

  2. Intermediário (Dificuldade ⭐⭐): Use shared_ptr para implementar múltiplos objetos compartilhando o mesmo recurso. Crie dois shared_ptrs apontando para o mesmo objeto e exiba use_count() para observar as mudanças na contagem de referências.

  3. Desafio (Dificuldade ⭐⭐⭐): Use weak_ptr para resolver um problema de referência circular com shared_ptr. Crie classes A e B que mantenham shared_ptrs um para o outro e observe o vazamento de memória. Depois troque para weak_ptr e verifique a liberação correta.



Próxima aula: Introdução à Metaprogramação de Templates (#47)

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%