Retorna ponteiros inteligentes por valor.
Como você disse, se você devolvê-lo por referência, não incrementará adequadamente a contagem de referência, o que abre o risco de deletar algo no momento impróprio. Só isso já deve ser motivo suficiente para não voltar por referência. As interfaces devem ser robustas.
A preocupação com os custos é discutível hoje em dia graças à otimização do valor de retorno (RVO), então você não irá incorrer em uma seqüência incremento-incremento-decremento ou algo parecido em compiladores modernos. Portanto, a melhor maneira de retornar a shared_ptr
é simplesmente retornar por valor:
shared_ptr<T> Foo()
{
return shared_ptr<T>(/* acquire something */);
};
Esta é uma oportunidade RVO óbvia para os compiladores C ++ modernos. Eu sei que os compiladores Visual C ++ implementam RVO mesmo quando todas as otimizações estão desligadas. E com a semântica de movimentação do C ++ 11, essa preocupação é ainda menos relevante. (Mas a única maneira de ter certeza é traçar um perfil e experimentar.)
Se você ainda não está convencido, Dave Abrahams tem um artigo que defende o retorno por valor. Eu reproduzo um trecho aqui; Eu recomendo fortemente que você leia o artigo inteiro:
Seja honesto: como você se sente com o código a seguir?
std::vector<std::string> get_names();
...
std::vector<std::string> const names = get_names();
Francamente, embora eu devesse saber melhor, isso me deixa nervoso. Em princípio, quando get_names()
retorna, temos que copiar a vector
de string
s. Então, precisamos copiá-lo novamente quando inicializamos
names
e precisamos destruir a primeira cópia. Se houver N string
s no vetor, cada cópia pode exigir até N + 1 alocações de memória e uma grande quantidade de acessos de dados hostis ao cache> conforme o conteúdo da string é copiado.
Em vez de enfrentar esse tipo de ansiedade, muitas vezes recorri à passagem por referência para evitar cópias desnecessárias:
get_names(std::vector<std::string>& out_param );
...
std::vector<std::string> names;
get_names( names );
Infelizmente, essa abordagem está longe de ser ideal.
- O código cresceu 150%
- Tivemos que abandonar o
const
-ness porque estamos mudando nomes.
- Como os programadores funcionais gostam de nos lembrar, a mutação torna o código mais complexo para raciocinar ao minar a transparência referencial e o raciocínio equacional.
- Não temos mais semânticas de valor estrito para nomes.
Mas é realmente necessário bagunçar nosso código dessa forma para ganhar eficiência? Felizmente, a resposta acabou sendo não (e especialmente se você estiver usando C ++ 0x).