Sei que o título parece familiar, pois há muitas perguntas semelhantes, mas estou pedindo um aspecto diferente do problema (sei a diferença entre colocar as coisas na pilha e colocá-las na pilha).
Em Java, sempre posso retornar referências a objetos "locais"
public Thing calculateThing() {
Thing thing = new Thing();
// do calculations and modify thing
return thing;
}
Em C ++, para fazer algo semelhante, tenho 2 opções
(1) Eu posso usar referências sempre que precisar "retornar" um objeto
void calculateThing(Thing& thing) {
// do calculations and modify thing
}
Então use assim
Thing thing;
calculateThing(thing);
(2) Ou posso retornar um ponteiro para um objeto alocado dinamicamente
Thing* calculateThing() {
Thing* thing(new Thing());
// do calculations and modify thing
return thing;
}
Então use assim
Thing* thing = calculateThing();
delete thing;
Usando a primeira abordagem, não precisarei liberar memória manualmente, mas para mim isso dificulta a leitura do código. O problema com a segunda abordagem é, vou ter que lembrar delete thing;
, o que não parece muito bom. Eu não quero retornar um valor copiado porque é ineficiente (eu acho), então aqui estão as perguntas
- Existe uma terceira solução (que não requer cópia do valor)?
- Existe algum problema se eu continuar com a primeira solução?
- Quando e por que devo usar a segunda solução?
fonte
Respostas:
Prove.
Consulte RVO e NRVO e na semântica de movimento C ++ 0x. Na maioria dos casos, no C ++ 03, um parâmetro out é apenas uma boa maneira de tornar seu código feio, e no C ++ 0x você realmente se machucaria usando um parâmetro out.
Basta escrever um código limpo, retornar por valor. Se o desempenho for um problema, analise-o (pare de adivinhar) e descubra o que você pode fazer para corrigi-lo. Provavelmente não retornará itens das funções.
Dito isto, se você estiver decidido a escrever dessa maneira, provavelmente desejará fazer o parâmetro out. Evita alocação dinâmica de memória, que é mais segura e geralmente mais rápida. Requer que você tenha alguma maneira de construir o objeto antes de chamar a função, o que nem sempre faz sentido para todos os objetos.
Se você deseja usar a alocação dinâmica, o mínimo que pode ser feito é colocá-lo em um ponteiro inteligente. (Isso deve ser feito o tempo todo de qualquer maneira). Então você não se preocupa em excluir nada, as coisas são seguras contra exceções etc. O único problema é que provavelmente é mais lento do que retornar por valor de qualquer maneira!
fonte
Basta criar o objeto e devolvê-lo
Eu acho que você fará um favor a si mesmo se esquecer a otimização e apenas escrever código legível (você precisará executar um criador de perfil mais tarde - mas não pré-otimize).
fonte
Thing thing();
declara uma função local e retorna aThing
.Basta retornar um objeto como este:
Isso chamará o construtor de cópia no Things, portanto, você pode querer fazer sua própria implementação disso. Como isso:
Isso pode ser um pouco mais lento, mas pode não ser um problema.
Atualizar
O compilador provavelmente otimizará a chamada para o construtor de cópias, para que não haja sobrecarga extra. (Como dreamlax apontou no comentário).
fonte
Thing thing();
declara uma função local retornando aThing
, também, o padrão permite que o compilador omita o construtor de cópia no caso apresentado; qualquer compilador moderno provavelmente fará isso.Você tentou usar ponteiros inteligentes (se o objeto é realmente grande e pesado), como auto_ptr:
fonte
auto_ptr
s estão obsoletos; useshared_ptr
ou emunique_ptr
vez disso.Uma maneira rápida de determinar se um construtor de cópias está sendo chamado é adicionar o log ao construtor de cópias da sua classe:
Chamada
someFunction
; o número de linhas "Copiador de construção foi chamado" que você obterá variará entre 0, 1 e 2. Se você não obtiver nenhuma, seu compilador otimizou o valor de retorno (o que é permitido fazer). Se você receber não obter 0, e seu construtor de cópia é ridiculamente caro, em seguida, procurar formas alternativas para retornam instâncias de suas funções.fonte
Em primeiro lugar, você tem um erro no código, você quer
Thing *thing(new Thing());
e somentereturn thing;
.shared_ptr<Thing>
. Deref-lo como tho era um ponteiro. Será excluído para você quando a última referência aoThing
contido sair do escopo.fonte
Tenho certeza de que um especialista em C ++ virá com uma resposta melhor, mas pessoalmente eu gosto da segunda abordagem. O uso de ponteiros inteligentes ajuda no problema de esquecer
delete
e, como você diz, parece mais limpo do que ter que criar um objeto antes da mão (e ainda ter que excluí-lo se desejar alocá-lo no heap).fonte