Introdução: Por que seu programa "comeu" toda a memória e como corrigir isso?
\n\nVocê já escreveu um aplicativo em C++ que funcionava perfeitamente até você fechar a janela? E então descobriu que ele "comeu" um gigabyte de RAM e não devolveu nem um byte? Ou, pior ainda, seu programa travava de repente com um misterioso segmentation fault, e você passava horas procurando onde esqueceu de chamar delete?
Se sim — bem-vindo ao clube. O gerenciamento manual de memória em C++ é como fazer malabarismo com motosserras: até dá, mas é melhor deixar isso com os profissionais. Ou, no nosso caso, com os ponteiros inteligentes (smart pointers).
\n\nHoje vamos entender o que são essas feras, quais tipos existem e, o mais importante, quando usar cada um deles. Este material é uma adaptação da famosa pergunta do Stack Overflow, que recebeu milhares de visualizações. Vamos torná-la clara, dinâmica e com exemplos. Vamos lá!
\n\nO que é um ponteiro inteligente? Em palavras simples
\n\nImagine um ponteiro comum (int* ptr = new int(5);). É como uma chave de um depósito com recursos. Você abriu o depósito (alocou memória), mas se perder a chave ou esquecer de fechar a porta — os recursos desaparecem para sempre. Isso é um vazamento de memória.
Ponteiro inteligente é uma "chave com fechamento automático". É uma classe wrapper que armazena internamente um ponteiro comum e automaticamente chama delete (ou delete[]) quando o objeto não é mais necessário. Quando o ponteiro inteligente sai do escopo, seu destrutor é acionado e libera a memória. Mágica? Não, RAII (Resource Acquisition Is Initialization) — um dos pilares do C++.
A partir do C++11, os ponteiros inteligentes se tornaram parte da biblioteca padrão (<memory>). Esqueça o auto_ptr (está obsoleto e é perigoso). Agora temos três mosqueteiros:
- \n
std::unique_ptr— proprietário único. \n std::shared_ptr— recurso com "propriedade coletiva". \n std::weak_ptr— "observador" que não interfere na vida. \n
Vamos analisar cada um de forma simples e com código.
\n\nstd::unique_ptr: "É meu, e de mais ninguém!"
\n\nstd::unique_ptr é o ponteiro inteligente mais simples e rápido. Ele garante que a posse do objeto pertence a apenas um ponteiro. Você não pode copiar um unique_ptr, mas pode movê-lo (transferir a posse).
Quando usar? Quase sempre que você cria um objeto no heap e sabe que ele terá um único dono durante toda a sua vida.
\n\nExemplo: Criação e uso
\n\n#include <iostream>\n#include <memory> // para ponteiros inteligentes\n\nclass Robot {\npublic:\n Robot(const std::string& name) : name_(name) {\n std::cout << "Robô " << name_ << " ativado!\\n";\n }\n void speak() const { std::cout << name_ << ": Olá, mundo!\\n"; }\n ~Robot() { std::cout << "Robô " << name_ << " destruído.\\n"; }\nprivate:\n std::string name_;\n};\n\nint main() {\n // Criando um unique_ptr (nunca use new!)\n std::unique_ptr<Robot> r1 = std::make_unique<Robot>("R2-D2");\n\n r1->speak(); // Funciona como um ponteiro comum\n\n // std::unique_ptr<Robot> r2 = r1; // Erro! Não pode copiar\n std::unique_ptr<Robot> r2 = std::move(r1); // Move a posse\n\n if (!r1) {\n std::cout << "r1 agora está vazio, a posse foi transferida para r2\\n";\n }\n\n r2->speak(); // Funciona\n // Aqui r2 sairá do escopo e o robô será destruído automaticamente\n return 0;\n}\n\nImportante: Use std::make_unique<T>(args...) em vez de new