Ponteiro inteligente - Smart pointer
Na ciência da computação , um ponteiro inteligente é um tipo de dado abstrato que simula um ponteiro enquanto fornece recursos adicionais, como gerenciamento automático de memória ou verificação de limites . Esses recursos têm como objetivo reduzir os bugs causados pelo uso indevido de ponteiros, mantendo a eficiência. Os ponteiros inteligentes normalmente rastreiam a memória para a qual apontam e também podem ser usados para gerenciar outros recursos, como conexões de rede e identificadores de arquivo. Os ponteiros inteligentes foram popularizados pela primeira vez na linguagem de programação C ++ durante a primeira metade da década de 1990 como refutação às críticas à falta de coleta de lixo automática do C ++ .
O uso incorreto do ponteiro pode ser uma fonte importante de bugs. Os ponteiros inteligentes evitam a maioria das situações de vazamentos de memória , tornando a desalocação de memória automática. De forma mais geral, eles tornam a destruição de objetos automática: um objeto controlado por um ponteiro inteligente é automaticamente destruído ( finalizado e, em seguida, desalocado) quando o último (ou único) proprietário de um objeto é destruído, por exemplo, porque o proprietário é uma variável local, e a execução sai do escopo da variável . Os ponteiros inteligentes também eliminam os ponteiros pendentes , adiando a destruição até que um objeto não esteja mais em uso.
Se uma linguagem oferece suporte à coleta de lixo automática (por exemplo, Java ou C # ), ponteiros inteligentes são desnecessários para os aspectos de recuperação e segurança do gerenciamento de memória, mas são úteis para outros fins, como gerenciamento de residência de estrutura de dados de cache e gerenciamento de recursos de objetos como identificadores de arquivo ou soquetes de rede .
Existem vários tipos de ponteiros inteligentes. Alguns trabalham com contagem de referência , outros atribuindo a propriedade de um objeto a um ponteiro.
História
Embora o C ++ tenha popularizado o conceito de ponteiros inteligentes, especialmente a variedade de contagem de referência , o predecessor imediato de uma das linguagens que inspiraram o design do C ++ tinha referências de contagem de referência incorporadas à linguagem. C ++ foi inspirado em parte por Simula67. Antepassado do Simula67 foi Simula I. Na medida em que de Simula I elemento é análogo ao C ++ 's ponteiro sem nula , e na medida em processo de Simula I com um manequim-declaração como seu corpo actividade é análogo ao C ++ da estrutura (que por si só é análogo ao do CAR Hoare ficha em seguida -trabalho contemporâneo dos anos 1960), Simula I tinha elementos contados de referência (isto é, expressões de ponteiro que abrigam a indireção) para processos (isto é, registros) até setembro de 1965, conforme mostrado nos parágrafos citados abaixo.
Os processos podem ser referenciados individualmente. Fisicamente, uma referência de processo é um ponteiro para uma área da memória que contém os dados locais do processo e algumas informações adicionais que definem seu estado atual de execução. No entanto, pelos motivos indicados na Seção 2.2, as referências de processo são sempre indiretas, por meio de itens chamados de elementos. Formalmente, uma referência a um processo é o valor de uma expressão do elemento de tipo .
… Valores de
elemento podem ser armazenados e recuperados por atribuições e referências a variáveis de elemento e por outros meios.
A linguagem contém um mecanismo para tornar os atributos de um processo acessíveis de fora, ou seja, de dentro de outros processos. Isso é chamado de acesso remoto. Um processo é, portanto, uma estrutura de dados referenciável.
É importante notar a semelhança entre um processo cujo corpo de atividade é uma declaração fictícia, e o conceito de registro recentemente proposto por CAR Hoare e N. Wirth
Como C ++ emprestou a abordagem de Simula para alocação de memória - a nova palavra - chave ao alocar um processo / registro para obter um novo elemento para esse processo / registro - não é surpreendente que C ++ eventualmente ressuscitou o mecanismo de ponteiro inteligente contado por referência de Simula dentro do elemento como Nós vamos.
Recursos
Em C ++ , um ponteiro inteligente é implementado como uma classe de modelo que imita, por meio de sobrecarga de operador , os comportamentos de um ponteiro tradicional (bruto) (por exemplo, desreferenciamento, atribuição) enquanto fornece recursos adicionais de gerenciamento de memória.
Os ponteiros inteligentes podem facilitar a programação intencional , expressando, no tipo, como a memória do referente do ponteiro será gerenciada. Por exemplo, se uma função C ++ retorna um ponteiro, não há como saber se o chamador deve excluir a memória do referente quando o chamador terminar de fornecer as informações.
SomeType* AmbiguousFunction(); // What should be done with the result?
Tradicionalmente, as convenções de nomenclatura têm sido usadas para resolver a ambigüidade, que é uma abordagem propensa a erros e trabalhosa. C ++ 11 introduziu uma maneira de garantir o gerenciamento correto da memória, neste caso, declarando a função para retornar um unique_ptr
,
std::unique_ptr<SomeType> ObviousFunction();
A declaração do tipo de retorno da função como um unique_ptr
torna explícito o fato de que o chamador assume a propriedade do resultado, e o tempo de execução C ++ garante que a memória será recuperada automaticamente. Antes do C ++ 11 , unique_ptr pode ser substituído por auto_ptr .
Criação de novos objetos
Para facilitar a alocação de um
std::shared_ptr<SomeType>
C ++ 11 introduzido:
auto s = std::make_shared<SomeType>(constructor, parameters, here);
e da mesma forma
std::unique_ptr<some_type>
Desde C ++ 14, pode-se usar:
auto u = std::make_unique<SomeType>(constructor, parameters, here);
É preferível, em quase todas as circunstâncias, usar esses recursos em vez da new
palavra - chave.
unique_ptr
C ++ 11 apresenta std::unique_ptr
, definido no cabeçalho <memory>
.
A unique_ptr
é um contêiner para um ponteiro bruto, que unique_ptr
se diz ser o proprietário. A unique_ptr
impede explicitamente a cópia de seu ponteiro contido (como aconteceria com a atribuição normal), mas a std::move
função pode ser usada para transferir a propriedade do ponteiro contido para outro unique_ptr
. A unique_ptr
não pode ser copiado porque seu construtor de cópia e operadores de atribuição são explicitamente excluídos.
std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = p1; // Compile error.
std::unique_ptr<int> p3 = std::move(p1); // Transfers ownership. p3 now owns the memory and p1 is set to nullptr.
p3.reset(); // Deletes the memory.
p1.reset(); // Does nothing.
std::auto_ptr
foi descontinuado no C ++ 11 e completamente removido do C ++ 17 . O construtor de cópia e os operadores de atribuição de auto_ptr
não copiam realmente o ponteiro armazenado. Em vez disso, eles o transferem , deixando o auto_ptr
objeto anterior vazio. Essa era uma maneira de implementar propriedade restrita, de modo que apenas um auto_ptr
objeto pudesse possuir o ponteiro em um determinado momento. Isso significa que auto_ptr
não deve ser usado onde a semântica de cópia é necessária. Como auto_ptr
já existia com sua semântica de cópia, ele não poderia ser atualizado para ser um ponteiro apenas de movimentação sem quebrar a compatibilidade com versões anteriores com o código existente.
C ++ 11 apresenta std::shared_ptr
e std::weak_ptr
, definido no cabeçalho <memory>
. C ++ 11 também introduz std::make_shared
( std::make_unique
foi introduzido em C ++ 14) a alocação segura de memória dinâmica no paradigma RAII .
A shared_ptr
é um contêiner para um ponteiro bruto . Ele mantém a propriedade de contagem de referência de seu indicador contido em cooperação com todas as cópias do shared_ptr
. Um objeto referenciado pelo ponteiro bruto contido será destruído quando e somente quando todas as cópias do shared_ptr
forem destruídas.
std::shared_ptr<int> p0(new int(5)); // Valid, allocates 1 integer and initialize it with value 5.
std::shared_ptr<int[]> p1(new int[5]); // Valid, allocates 5 integers.
std::shared_ptr<int[]> p2 = p1; // Both now own the memory.
p1.reset(); // Memory still exists, due to p2.
p2.reset(); // Frees the memory, since no one else owns the memory.
A weak_ptr
é um contêiner para um ponteiro bruto. Ele é criado como uma cópia de um shared_ptr
. A existência ou destruição de weak_ptr
cópias de um shared_ptr
não tem efeito sobre o shared_ptr
ou suas outras cópias. Depois que todas as cópias de a shared_ptr
forem destruídas, todas as weak_ptr
cópias ficam vazias.
std::shared_ptr<int> p1 = std::make_shared<int>(5);
std::weak_ptr<int> wp1 {p1}; // p1 owns the memory.
{
std::shared_ptr<int> p2 = wp1.lock(); // Now p1 and p2 own the memory.
// p2 is initialized from a weak pointer, so you have to check if the
// memory still exists!
if (p2) {
DoSomethingWith(p2);
}
}
// p2 is destroyed. Memory is owned by p1.
p1.reset(); // Free the memory.
std::shared_ptr<int> p3 = wp1.lock();
// Memory is gone, so we get an empty shared_ptr.
if (p3) { // code will not execute
ActionThatNeedsALivePointer(p3);
}
Devido à implementação da contagem de referências de shared_ptr
uso , as referências circulares são potencialmente um problema. Uma cadeia circular pode ser quebrada alterando o código para que uma das referências seja a .
shared_ptr
weak_ptr
Múltiplos threads podem acessar simultaneamente diferentes objetos shared_ptr
e weak_ptr
objetos que apontam para o mesmo objeto.
O objeto referenciado deve ser protegido separadamente para garantir a segurança do thread .
shared_ptr
e weak_ptr
são baseados em versões usadas pelas bibliotecas Boost . O C ++ Technical Report 1 (TR1) primeiro os apresentou ao padrão, como utilitários gerais , mas o C ++ 11 adiciona mais funções, em linha com a versão Boost.
Veja também
- Contagem de referência automática
- Aquisição de recursos é inicialização (RAII)
- auto_ptr
- Ponteiro opaco
- Referência (ciência da computação)
- Boost (bibliotecas C ++)
- Apontador gordo
- Coleta de lixo na programação de computadores
Referências
Leitura adicional
- Scott Meyers (2014). C ++ moderno eficaz . Sebastopol, CA: O'Reilly Media . ISBN 978-1491903995. OCLC 884480640 .
links externos
- Ponteiros inteligentes . Design C ++ Moderno : Programação Genérica e Padrões de Design Aplicados por Andrei Alexandrescu , Addison-Wesley, 2001.
- countptr.hpp . The C ++ Standard Library - Um tutorial e referência de Nicolai M. Josuttis
- Boost Smart Pointers
- The New C ++: Smart (er) Pointers . Herb Sutter 1 de agosto de 2002
- Ponteiros inteligentes - O quê, por quê, qual? . Yonat Sharon
- Visão geral dos Smart Pointers . John M. Dlugosz
- Ponteiros inteligentes em Delphi
- Ponteiros inteligentes na ferrugem
- Ponteiros inteligentes em C ++ moderno