Ponteiros inteligentes (C++ moderno)
Na programação em C++ moderno, a Biblioteca Padrão inclui ponteiros inteligentes, que são usados para garantir que os programas estejam livres de vazamentos de memória e de recursos e sejam protegidos contra exceções.
Uso de ponteiros inteligentes
Os ponteiros inteligentes são definidos no namespace std no arquivo de cabeçalho <memory>. Eles são essenciais para a linguagem de programação RAII ou Resource Acquisition Is Initialialization (aquisição de recurso é inicialização). O objetivo principal dessa linguagem é garantir que a aquisição de recursos ocorra ao mesmo tempo em que o objeto é inicializado, de forma que todos os recursos do objeto sejam criados e preparados em uma linha de código. Em termos práticos, o princípio fundamental da linguagem RAII é fornecer a propriedade de qualquer recurso alocado a heap, por exemplo, memória alocada dinamicamente ou identificadores de objetos do sistema, a um objeto alocado em pilha cujo destruidor contenha o código para excluir ou liberar o recurso e também qualquer código de limpeza associado.
Na maioria dos casos, quando você inicializa um ponteiro bruto ou identificador de recursos para apontar para um recurso real, transforma o ponteiro em ponteiro inteligente imediatamente. Em C++ moderno, os ponteiros brutos são usados somente em pequenos blocos de código de escopo limitado, loops ou funções auxiliares onde o desempenho é essencial e não há possibilidade de confusão sobre a propriedade.
O exemplo a seguir compara uma declaração de ponteiro bruto a uma declaração de ponteiro inteligente.
void UseRawPointer()
{
// Using a raw pointer -- not recommended.
Song* pSong = new Song(L"Nothing on You", L"Bruno Mars");
// Use pSong...
// Don't forget to delete!
delete pSong;
}
void UseSmartPointer()
{
// Declare a smart pointer on stack and pass it the raw pointer.
unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars"));
// Use song2...
wstring s = song2->duration_;
//...
} // song2 is deleted automatically here.
Conforme mostrado no exemplo, um ponteiro inteligente é um modelo de classe que você declara na pilha e inicializa usando um ponteiro bruto que aponta para um objeto alocado a heap. Depois que o ponteiro inteligente é inicializado, ele possui o ponteiro bruto. Isso significa que o ponteiro inteligente é responsável pela exclusão da memória especificada pelo ponteiro bruto. O destruidor do ponteiro inteligente contém a chamada para exclusão e, como o ponteiro inteligente é declarado na pilha, seu destruidor é chamado quando o ponteiro inteligente fica fora do escopo, mesmo se uma exceção for lançada posteriormente na pilha.
Acesse o ponteiro encapsulado usando os operadores de ponteiros familiares, -> e *, que a classe do ponteiro inteligente sobrecarrega para retornar o ponteiro bruto encapsulado.
A linguagem de ponteiro inteligente C++ é semelhante à criação de objeto em linguagens como C#: você cria o objeto e permite que o sistema cuide de sua exclusão no momento certo. A diferença é que nenhum coletor de lixo separado é executado em segundo plano; a memória é gerenciada com as regras de escopo C++ padrão de modo que o ambiente em tempo de execução seja mais rápido e mais eficiente.
Importante
Crie sempre ponteiros inteligentes em uma linha de código separada, nunca em uma lista de parâmetros, de forma que um vazamento sutil de recursos não ocorre devido a determinadas regras de alocação da lista de parâmetros.
O exemplo a seguir mostra como um tipo de ponteiro inteligente unique_ptr da Biblioteca de Modelos Padrão poderia ser usado para encapsular um ponteiro para um objeto grande.
class LargeObject
{
public:
void DoSomething(){}
};
void ProcessLargeObject(const LargeObject& lo){}
void SmartPointerDemo()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Pass a reference to a method.
ProcessLargeObject(*pLarge);
} //pLarge is deleted automatically when function block goes out of scope.
O exemplo demonstra as etapas essenciais a seguir para o uso de ponteiros inteligentes.
Declare o ponteiro inteligente como uma variável automática (local). (Não use a expressão new ou malloc no próprio ponteiro inteligente.)
No parâmetro de tipo, especifique o tipo apontado do ponteiro encapsulado.
Passe um ponteiro bruto para um objeto new no construtor do ponteiro inteligente. (Algumas funções do utilitário ou construtores de ponteiro inteligente fazem isso para você.)
Use os operadores -> e * sobrecarregados para acessar o objeto.
Deixe o ponteiro inteligente excluir o objeto.
Ponteiros inteligentes são criados para terem a maior eficiência possível em termos de memória e de desempenho. Por exemplo, o único membro de dados em unique_ptr é o ponteiro encapsulado. Isso significa que unique_ptr é exatamente do mesmo tamanho que o ponteiro, com quatro bytes ou com oito bytes. O acesso ao ponteiro encapsulado usando os operadores * e -> sobrecarregados pelo ponteiro inteligente não é significativamente mais lento que o acesso direto aos ponteiros brutos.
Os ponteiros inteligentes têm suas próprias funções de membro, que são acessadas usando a notação "dot". Por exemplo, alguns ponteiros inteligentes STL têm uma função de membro de redefinição que libera a propriedade do ponteiro. Isso é útil quando você deseja liberar a memória possuída pelo ponteiro inteligente antes que o ponteiro inteligente saia do escopo, como mostrado no exemplo a seguir.
void SmartPointerDemo2()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Free the memory before we exit function block.
pLarge.reset();
// Do some other work...
}
Os ponteiros inteligentes geralmente oferecem uma maneira de acessar diretamente seu ponteiro bruto. Os ponteiros inteligentes STL têm uma função membro get para essa finalidade e CComPtr tem um membro de classe p público. Fornecendo acesso direto ao ponteiro subjacente, você pode usar o ponteiro inteligente para gerenciar a memória em seu próprio código e ainda passar o ponteiro bruto para o código que não oferece suporte a ponteiros inteligentes.
void SmartPointerDemo4()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Pass raw pointer to a legacy API
LegacyLargeObjectFunction(pLarge.get());
}
Tipos de ponteiros inteligentes
A seção a seguir resume os diferentes tipos de ponteiros inteligentes que estão disponíveis no ambiente de programação do Windows e descreve quando usá-los.
Ponteiros Inteligentes da Biblioteca Padrão C++
Use esses ponteiros inteligentes como primeira opção para o encapsulamento de ponteiros para objetos C++ antigos simples (POCO).unique_ptr
Permite exatamente um proprietário do ponteiro subjacente. Use como a opção padrão para POCO, a menos que você tenha certeza de que precisa de um shared_ptr. Pode ser movido para um novo proprietário, mas não copiado ou compartilhado. Substitui auto_ptr, que será preterido. Compare com boost::scoped_ptr. unique_ptr é pequeno e eficiente; o tamanho é um ponteiro e oferece suporte a referências rvalue para a rápida inserção e recuperação das coleções STL. Arquivo de cabeçalho: <memory>. Para obter mais informações, consulte Como criar e usar instâncias unique_ptr e Classe unique_ptr.shared_ptr
Ponteiro inteligente contado por referência. Use quando quiser atribuir um ponteiro bruto a vários proprietários, por exemplo, ao retornar uma cópia de um ponteiro de um contêiner, porém mantendo o original. O ponteiro bruto não será excluído até que todos os proprietários de shared_ptr tenham saído do escopo ou tenham desistido da propriedade. O tamanho é de dois ponteiros; um para o objeto e um para o bloco de controle compartilhado que contém a contagem de referência. Arquivo de cabeçalho: <memory>. Para obter mais informações, consulte Como criar e usar instâncias shared_ptr e Classe shared_ptr.weak_ptr
Ponteiro inteligente de casos especiais para uso em conjunto com shared_ptr. Um weak_ptr fornece acesso a um objeto pertencente a uma ou mais instâncias de shared_ptr, mas não participa da contagem de referência. Use quando você quiser observar um objeto, mas sem exigir que ele permaneça ativo. Necessário em alguns casos para interromper referências circulares entre instâncias shared_ptr. Arquivo de cabeçalho: <memory>. Para obter mais informações, consulte Como criar e usar instâncias weak_ptr e Classe weak_ptr.
Ponteiros inteligentes para objetos COM (programação clássica do Windows)
Ao trabalhar com objetos COM, coloque os ponteiros de interface em um tipo de ponteiro inteligente apropriado. A Biblioteca de Modelos Ativos (ATL) define vários ponteiros inteligentes para várias finalidades. Você também pode usar o tipo de ponteiro inteligente _com_ptr_t, que o compilador usa ao criar classes wrapper dos arquivos .tlb. É a melhor opção quando você não quer incluir os arquivos de cabeçalho da ATL.Classe CComPtr
Use isso a menos que você não possa usar a ATL. Executa a contagem de referência usando os métodos AddRef e Release. Para obter mais informações, consulte Como criar e usar instâncias CComPtr e CComQIPtr.Classe CComQIPtr
É semelhante a CComPtr mas também fornece a sintaxe simplificada para chamar QueryInterface em objetos COM. Para obter mais informações, consulte Como criar e usar instâncias CComPtr e CComQIPtr.Classe CComHeapPtr
Ponteiro inteligente para objetos que usam CoTaskMemFree para liberar memória.Classe CComGITPtr
Ponteiro inteligente para as interfaces que são obtidas da tabela de interface global (GIT).Classe _com_ptr_t
É semelhante a CComQIPtr em funcionalidade, mas não depende de cabeçalhos da ATL.
Ponteiros inteligentes da ATL para objetos POCO
Além de ponteiros inteligentes para objetos COM, a ATL também define ponteiros inteligentes e coleções de ponteiros inteligentes, para objetos C++ antigos simples. Na programação clássica do Windows, esses tipos são alternativas úteis para as coleções STL, especialmente quando a portabilidade de código não é necessária ou quando você não quer combinar os modelos de programação da STL e da ATL.Classe CAutoPtr
Ponteiro inteligente que impõe a propriedade exclusiva transferindo a propriedade na cópia. Comparável à classe std::auto_ptr preterida.Classe CHeapPtr
Ponteiro inteligente para objetos alocados usando a função C malloc.Classe CAutoVectorPtr
Ponteiro inteligente para matrizes que são alocadas usando new[].Classe CAutoPtrArray
Classe que encapsula uma matriz de elementos CAutoPtr.Classe CAutoPtrList
Classe que encapsula métodos para manipular uma lista de nós CAutoPtr.
Consulte também
Outros recursos
Bem-vindo ao C++ (C++ moderno)