O mundo em que Bjarne vive é muito ... acadêmico, por falta de um termo melhor. Se o seu código puder ser projetado e estruturado de modo que os objetos tenham hierarquias relacionais muito deliberadas, de modo que as relações de propriedade sejam rígidas e inflexíveis, o código flui em uma direção (de alto nível para baixo) e os objetos só falam com os mais baixos a hierarquia, então você não encontrará muita necessidade de shared_ptr
. É algo que você usa nas raras ocasiões em que alguém tem que quebrar as regras. Caso contrário, você pode colocar tudo em vector
s ou outras estruturas de dados que usam semântica de valores, e unique_ptr
s para coisas que você tem que alocar individualmente.
Embora seja um ótimo mundo para se viver, não é o que você consegue fazer o tempo todo. Se você não puder organizar seu código dessa forma, porque o design do sistema que você está tentando fazer significa que é impossível (ou apenas profundamente desagradável), então você vai se encontrar precisando propriedade compartilhada de objetos cada vez mais.
Nesse sistema, segurar ponteiros nus não é ... exatamente perigoso, mas levanta questões. O melhor de shared_ptr
é que ele fornece garantias sintáticas razoáveis sobre o tempo de vida do objeto. Pode ser quebrado? Claro. Mas as pessoas também podem const_cast
coisas; cuidados básicos e alimentação de shared_ptr
devem fornecer qualidade de vida razoável para os objetos alocados cuja propriedade deve ser compartilhada.
Em seguida, existem weak_ptr
s, que não podem ser usados na ausência de shared_ptr
. Se o seu sistema estiver rigidamente estruturado, você poderá armazenar um ponteiro nu em algum objeto, sabendo que a estrutura do aplicativo garante que o objeto apontado sobreviverá a você. Você pode chamar uma função que retorna um ponteiro para algum valor interno ou externo (encontre um objeto chamado X, por exemplo). No código adequadamente estruturado, essa função só estaria disponível para você se o tempo de vida do objeto fosse superior ao seu; assim, armazenar esse ponteiro nu em seu objeto é bom.
Como essa rigidez nem sempre é possível de ser alcançada em sistemas reais, você precisa de alguma maneira de garantir a vida útil. Às vezes, você não precisa de propriedade total; às vezes, você só precisa saber quando o ponteiro está bom ou ruim. É aí que entra weak_ptr
. Houve casos em que poderia ter usado unique_ptr
ou boost::scoped_ptr
, mas tive que usar shared_ptr
porque especificamente
Uma maneira segura de sobreviver quando o estado do mundo é indeterminado.
Isso poderia ter sido feito por alguma chamada de função para obter o ponteiro, em vez de via weak_ptr
? Sim, mas isso poderia mais facilmente ser quebrado. Uma função que retorna um ponteiro nu não tem como sintaticamente sugerir que o usuário não faça algo como armazenar esse ponteiro a longo prazo. Retornar um shared_ptr
também torna muito fácil para alguém simplesmente armazená-lo e potencialmente prolongar o tempo de vida de um objeto. No entanto, retornar um weak_ptr
sugere que armazenar o shared_ptr
obtido de lock
é uma ... idéia duvidosa. Isso não vai impedi-lo de fazer isso, mas nada em C ++ impede você de quebrar o código. weak_ptr
fornece alguma resistência mínima ao fazer a coisa natural.
Agora, isso não quer dizer que shared_ptr
não pode ser usado em excesso ; certamente pode. Especialmente antes de unique_ptr
, houve muitos casos em que usei apenas boost::shared_ptr
porque precisava passar um ponteiro RAII ou colocá-lo em uma lista. Sem mover a semântica e unique_ptr
, boost::shared_ptr
era a única solução real.
E você pode usá-lo em lugares onde é completamente desnecessário. Como dito acima, a estrutura correta do código pode eliminar a necessidade de alguns usos de shared_ptr
. Mas se o seu sistema não puder ser estruturado como tal e ainda assim fizer o que for necessário, shared_ptr
será de uso significativo.