Por que o auto_ptr está sendo preterido?

92

Ouvi dizer que auto_ptrestá sendo descontinuado no C ++ 11. Qual é a razão para isto?

Também gostaria de saber a diferença entre auto_ptre shared_ptr.

Brett
fonte
Possível duplicata de Is auto_ptr deprecated?
malat de

Respostas:

91

A substituição direta auto_ptr(ou a coisa mais próxima de uma) é unique_ptr. No que diz respeito ao "problema", é muito simples: auto_ptrtransfere a propriedade quando é atribuído. unique_ptrtambém transfere propriedade, mas graças à codificação da semântica de movimento e à magia das referências rvalue, pode fazê-lo de maneira consideravelmente mais natural. Ele também "se ajusta" ao resto da biblioteca padrão consideravelmente melhor (embora, para ser justo, parte disso seja graças ao resto da biblioteca que muda para acomodar a semântica de movimentação em vez de sempre exigir a cópia).

A mudança no nome também é (IMO) bem-vinda - auto_ptrnão diz muito sobre o que ele tenta automatizar, ao passo que unique_ptré uma descrição bastante razoável (embora concisa) do que é fornecido.

Jerry Coffin
fonte
24
Apenas uma observação sobre o auto_ptrnome: auto sugere automatic como em automatic variable, e se refere a uma coisa que auto_ptrfaz: destruir o recurso gerenciado em seu destruidor (quando ele sai do escopo).
Vincenzo Pii
13
Mais informações: Aqui está a justificativa oficial para a suspensão de uso auto_ptr: open-std.org/jtc1/sc22/wg21/docs/papers/2005/…
Howard Hinnant
@HowardHinnant interessante doc! é estranho no sentido de que se std :: sort () tem uma especialização para std :: unique_ptr para usar a semântica de movimento conforme necessário. Eu me pergunto por que std :: sort () não pode ser especializado em std :: auto_ptr para corrigir o problema de cópia mencionado no documento. Desde já, obrigado.
Hei
2
@Hei: std::sortnão tem especialização para unique_ptr. Em vez disso, foi especificado novamente para nunca mais ser copiado. Então, auto_ptrrealmente faz o trabalho com o moderno sort. Mas o C ++ 98/03 sorté apenas um exemplo de algoritmo aqui: qualquer algoritmo genérico (fornecido por std ou escrito pelo usuário) que assume que a sintaxe de cópia tem semântica de cópia provavelmente terá um erro de tempo de execução se usado com auto_ptr, porque se moveauto_ptr silenciosamente com sintaxe de cópia . A questão é muito maior do que apenas . sort
Howard Hinnant de
35

Achei as respostas existentes ótimas, mas pelo ponto de vista dos ponteiros. IMO, uma resposta ideal deve ter a resposta da perspectiva do usuário / programador.

Primeira coisa (conforme apontado por Jerry Coffin em sua resposta)

  • auto_ptr pode ser substituído por shared_ptr ou unique_ptr dependendo da situação

shared_ptr: Se você está preocupado com a liberação de recurso / memória E se você tem mais de uma função que pode estar usando o objeto AT-DIFFERENT vezes, então vá com shared_ptr.

Por DIFFERENT-Times, pense em uma situação onde o objeto-ptr é armazenado em múltiplas estruturas de dados e posteriormente acessado. Vários threads, é claro, é outro exemplo.

unique_ptr: Se tudo que você está preocupado é liberar memória, e o acesso ao objeto é SEQUENCIAL, então vá para unique_ptr.

Por SEQUENTIAL, quero dizer, em qualquer ponto o objeto será acessado a partir de um contexto. Por exemplo, um objeto que foi criado e usado imediatamente após a criação pelo criador. Após a criação, o objeto é armazenado na estrutura de dados FIRST . Então, o objeto é destruído após UMA estrutura de dados ou é movido para a SEGUNDA estrutura de dados.

A partir desta linha, irei me referir a _ptr compartilhado / exclusivo como ponteiros inteligentes. (auto_ptr também é um ponteiro inteligente, MAS por causa de falhas em seu design, para o qual eles estão sendo descontinuados, e que acho que irei apontar nas próximas linhas, eles não devem ser agrupados com ponteiro inteligente.)

O motivo mais importante para o uso de auto_ptr em favor do smart-pointer é a atribuição de semântica. Se não fosse por esse motivo, eles teriam adicionado todas as novidades da semântica de movimentação ao auto_ptr em vez de descontinuá-lo. Uma vez que a semântica de atribuição era o recurso mais rejeitado, eles queriam que esse recurso fosse embora, mas como há um código escrito que usa essa semântica (que o comitê de padrões não pode mudar), eles tiveram que deixar de lado auto_ptr, em vez de modificando-o.

No link: http://www.cplusplus.com/reference/memory/unique_ptr/operator=/

Tipo de atribuições suportadas por unqiue_ptr

  • mover atribuição (1)
  • atribuir ponteiro nulo (2)
  • atribuição de elenco de tipo (3)
  • tarefa de cópia (excluída!) (4)

De: http://www.cplusplus.com/reference/memory/auto_ptr/operator=/

Tipo de atribuições suportadas por auto_ptr

  • culpado de atribuição de cópia (4)

Agora chegando ao motivo pelo qual a atribuição de cópia em si era tão desagradável, eu tenho esta teoria:

  1. Nem todos os programadores leem livros ou padrões
  2. auto_ptr em face disso, promete a você a propriedade do objeto
  3. o little- * (trocadilho intencional), cláusula do auto_ptr, que não é lido por todos os programadores, permite a atribuição de um auto_ptr para outro e transfere a propriedade.
  4. A pesquisa mostrou que esse comportamento é destinado a 3,1415926535% de todo o uso e não intencional em outros casos.

O comportamento não intencional é realmente desagradável e, portanto, não gosto do auto_ptr.

(Para 3,1415926536% dos programadores que intencionalmente desejam transferir a propriedade, o C ++ 11 deu a eles std :: move (), o que deixou sua intenção muito clara para todos os estagiários que irão ler e manter o código.)

Ajeet Ganga
fonte
1
Já que você nunca quer dois auto_ptrvalores apontando para o mesmo objeto (já que eles não dão propriedade compartilhada, o primeiro a morrer deixará o outro com uma herança letal; isso também é válido para o unique_ptruso), você pode sugerir o que se pretendia em os restantes 96,8584073465% de todo o uso?
Marc van Leeuwen
Não posso falar por todos eles, mas acho que eles pensariam que a propriedade do objeto está sendo movida e NÃO apenas duplicada, o que é errado.
Ajeet Ganga,
@AjeetGanga Na seguinte frase 'o pequeno- * (trocadilho intencional),' você mencionou como "trocadilho intencional". Essa frase é nova para mim e de qualquer forma eu pesquisei no Google e descobri que há alguma piada que foi feita propositalmente aqui. Que piada é essa aqui? Apenas curioso para saber isso.
VINOTH ENERGETIC
@AjeetGanga Você mencionou como 'o pequeno- * (trocadilho intencional), cláusula do auto_ptr, que não é lida por todos os programadores, permite a atribuição de um auto_ptr a outro e transfere a propriedade'. Digamos que eu tenha dois ptrs automáticos como aeb para inteiro. Estou fazendo a atribuição como *a=*b;Aqui, apenas o valor de b é copiado para a. Espero que a propriedade de aeb ainda seja das mesmas pessoas. Você mencionou como a propriedade será transferida. Como será?
VINOTH ENERGETIC
@VINOTHENERGETIC Ajeet estava falando sobre atribuir um auto_ptrobjeto a si mesmo. Atribuir para / de seu valor apontado não tem efeito sobre, nem relevância para, propriedade. Espero que ainda não esteja usando auto_ptr?
underscore_d
23

shared_ptrpode ser armazenado dentro de recipientes. auto_ptrnão posso.

BTW unique_ptré realmente a auto_ptrsubstituição direta , ele combina as melhores características de ambos std::auto_ptre boost::scoped_ptr.

Ben Voigt
fonte
11

Mais uma tentativa de explicar a diferença ....

Funcionalmente, o C ++ 11's std::unique_ptré o "fixo"std::auto_ptr : ambos são adequados quando - em qualquer momento durante a execução - deve haver um único proprietário de ponteiro inteligente para um objeto apontado.

A diferença crucial está na construção da cópia ou atribuição de outro ponteiro inteligente que não expira, mostrado nas =>linhas abaixo:

   std::auto_ptr<T> ap(...);
   std::auto_ptr<T> ap2(get_ap_to_T());   // take expiring ownership
=> std::auto_ptr<T> ap3(ap);  // take un-expiring ownership ala ap3(ap.release());
   ap->xyz;  // oops... can still try to use ap, expecting it to be non-NULL

   std::unique_ptr<T> up(...);
   std::unique_ptr<T> up2(get_up_to_T());   // take expiring ownership
=> std::unique_ptr<T> up3(up);  // COMPILE ERROR: can't take un-expiring ownership
=> std::unique_ptr<T> up4(std::move(up));  // EXPLICIT code allowed
=> std::unique_ptr<T> up4(up.release());   // EXPLICIT code allowed

Acima, ap3silenciosamente "rouba" a propriedade de *ap, deixando apdefinido como a nullptr, e o problema é que isso pode acontecer facilmente, sem que o programador tenha pensado em sua segurança.

Por exemplo, se um class/ structtem um std::auto_ptrmembro, então fazer uma cópia de uma instância irá releaseo ponteiro da instância que está sendo copiada: isso é semântica estranha e perigosamente confusa, já que normalmente copiar algo não o modifica. É fácil para o autor da classe / estrutura ignorar a liberação do ponteiro ao raciocinar sobre invariantes e estado e, conseqüentemente, tentar cancelar a referência do ponteiro inteligente enquanto nulo, ou simplesmente não ter ainda o acesso / propriedade esperados dos dados apontados.

Tony Delroy
fonte
auto_ptr silenciosamente "rouba" propriedade +1
camino
3

auto_ptr não pode ser usado em contêineres STL porque tem um construtor de cópia que não atende aos requisitos do contêiner CopyConstructible . unique_ptr não implementa um construtor de cópia, portanto, os contêineres usam métodos alternativos. unique_ptr pode ser usado em contêineres e é mais rápido para algoritmos std do que shared_ptr.

#include <iostream>
#include <type_traits>
#include <vector>
#include <memory>

using namespace std;

int main() {
  cout << boolalpha;
  cout << "is_copy_constructible:" << endl;
  cout << "auto_ptr: " << is_copy_constructible< auto_ptr<int> >::value << endl;
  cout << "unique_ptr: " << is_copy_constructible< unique_ptr<int> >::value << endl;
  cout << "shared_ptr: " << is_copy_constructible< shared_ptr<int> >::value << endl;

  vector<int> i_v;
  i_v.push_back(1);
  cout << "i_v=" << i_v[0] << endl;
  vector<int> i_v2=i_v;
  cout << "i_v2=" << i_v2[0] << endl;

  vector< unique_ptr<int> > u_v;
  u_v.push_back(unique_ptr<int>(new int(2)));
  cout << "u_v=" << *u_v[0] << endl;
  //vector< unique_ptr<int> > u_v2=u_v;  //will not compile, need is_copy_constructible == true
  vector< unique_ptr<int> > u_v2 =std::move(u_v);  // but can be moved
  cout << "u_v2=" << *u_v2[0] << " length u_v: " <<u_v.size() << endl;

  vector< shared_ptr<int> > s_v;
  shared_ptr<int> s(new int(3));
  s_v.push_back(s);
  cout << "s_v=" << *s_v[0] << endl;
  vector< shared_ptr<int> > s_v2=s_v;
  cout << "s_v2=" << *s_v2[0] << endl;

  vector< auto_ptr<int> > a_v;  //USAGE ERROR

  return 0;
}

>cxx test1.cpp -o test1
test1.cpp: In function âint main()â:
test1.cpp:33:11: warning: âauto_ptrâ is deprecated (declared at /apps/hermes/sw/gcc/gcc-4.8.5/include/c++/4.8.5/backward/auto_ptr.h:87) [-Wdeprecated-declarations]
   vector< auto_ptr<int> > a_v;  //USAGE ERROR
           ^
>./test1
is_copy_constructible:
auto_ptr: false
unique_ptr: false
shared_ptr: true
i_v=1
i_v2=1
u_v=2
s_v=3
s_v2=3
edW
fonte