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 os relacionamentos de propriedade sejam rígidos e inflexíveis, o código fluirá em uma direção (de alto nível para baixo), e os objetos só falarão com os de baixo nível. hierarquia, então você não encontrará muita necessidade shared_ptr
. É algo que você usa nessas raras ocasiões em que alguém tem que quebrar as regras. Mas, caso contrário, você pode colocar tudo em vector
s ou outras estruturas de dados que usam semântica de valor unique_ptr
es para coisas que você deve alocar individualmente.
Embora esse seja um ótimo mundo para se viver, não é o que você pode fazer o tempo todo. Se você não pode organizar seu código dessa maneira, porque o design do sistema que você está tentando criar significa que é impossível (ou apenas profundamente desagradável), você se encontrará cada vez mais necessitando de propriedade compartilhada de objetos. .
Nesse sistema, manter ponteiros nus não é exatamente perigoso, mas levanta questões. O melhor de tudo shared_ptr
é que ele fornece garantias sintáticas razoáveis sobre a vida útil 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 objetos alocados cuja propriedade deve ser compartilhada.
Depois, há weak_ptr
s, que não podem ser usados na ausência de a shared_ptr
. Se o seu sistema estiver rigidamente estruturado, você poderá armazenar um ponteiro nu para algum objeto, seguro de que a estrutura do aplicativo garante que o objeto apontado sobreviverá a você. Você pode chamar uma função que retorne um ponteiro para algum valor interno ou externo (localize o objeto chamado X, por exemplo). No código estruturado adequadamente, essa função só estaria disponível se a vida útil do objeto exceder a sua; portanto, é bom armazenar o ponteiro nu no seu objeto.
Como essa rigidez nem sempre é possível em sistemas reais, você precisa de uma maneira de garantir razoavelmente a vida útil. Às vezes, você não precisa de propriedade total; às vezes, você só precisa saber quando o ponteiro é ruim ou bom. É aí que weak_ptr
entra. Houve casos em que eu poderia ter usado um unique_ptr
or boost::scoped_ptr
, mas tive que usar um shared_ptr
porque eu precisava especificamente fornecer a alguém um ponteiro "volátil". Um ponteiro com vida útil indeterminada, e eles poderiam consultar quando esse ponteiro foi destruído.
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 ser mais facilmente quebrado. Uma função que retorna um ponteiro nu não tem como sugerir sintaticamente que o usuário não faça algo como armazenar esse ponteiro a longo prazo. O retorno de um shared_ptr
também torna muito fácil para alguém simplesmente armazená-lo e potencialmente prolongar a vida útil de um objeto. Retornar um weak_ptr
no entanto sugere fortemente que armazenar o que shared_ptr
você recebe lock
é uma ... idéia duvidosa. Isso não impedirá que você faça isso, mas nada no C ++ o impede 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 possa ser usado em excesso ; certamente pode. Especialmente antes unique_ptr
, havia muitos casos em que eu apenas usava um boost::shared_ptr
porque precisava passar um ponteiro RAII ou colocá-lo em uma lista. Sem movimento semântica e unique_ptr
, boost::shared_ptr
era a única solução real.
E você pode usá-lo em locais onde é desnecessário. Como mencionado acima, a estrutura de código adequada pode eliminar a necessidade de alguns usos de shared_ptr
. Mas se o seu sistema não puder ser estruturado como tal e ainda fizer o que for necessário, shared_ptr
será de uso significativo.