Eu tenho um método que retorna um objeto por valor. O método vem de uma biblioteca que eu não tenho controle. Para o manuseio adicional do objeto, quero continuar trabalhando com um unique_ptr nesse objeto. Aqui está um exemplo:
#include <iostream>
#include <memory>
class Bla {
public:
Bla() { std::cout << "Constructor!\n"; }
~Bla() { std::cout << "Destructor!\n"; }
};
Bla GetBla() {
Bla bla;
return std::move(bla);
}
int main() {
auto bla = std::make_unique<Bla>(GetBla());
}
O exemplo produz a seguinte saída:
Constructor!
Destructor!
Destructor!
Destructor!
Por que o destruidor de Bla é chamado 3 vezes aqui? A maneira como eu crio o unique_prt está correta?
c++
unique-ptr
Tobi S.
fonte
fonte
std::move
não move nada. Apenas transmite de um tipo para outro.std::move
onreturn
é um grande erro.Respostas:
De fato, há três vezes que uma instância de
Bla
é construída.Não volte por movimento. Basta retornar
bla
, na maioria dos casos a cópia será elidida.Observe que
make_unique<Bla>
sempre constrói uma nova instância. Nesse caso, porque você está passando outra instância, ela se torna cópia-construção.Uma dica de que a construção da cópia ocorre é que o construtor padrão é chamado apenas uma vez, enquanto o destruidor é chamado 3 vezes. Isso ocorre porque nos outros 2 casos o construtor implícito de cópia (ou movimentação) é invocado (
Bla::Bla(Bla const&)
).fonte
// 2nd construction (return by copy)
não está correto? O valor retornado será movido construído? Veja isto (eu posso estar errado): stackoverflow.com/a/60487169/5735010 .O compilador pode até avisar que
Não tenho 100% de certeza, mas acho que você recebe as três chamadas de desctrutor de:
bla
deGetBla()
GetBla()
depois de ter sido usado emstd::make_unique<Bla>(GetBla());
std::unique_ptr
A maneira mais fácil é
std::make_uniqe
chamar o construtor padrão deBla
:Resultado
fonte
A maneira correta de criar
unique_ptr
:No entanto, seu código cria três instâncias de
Bla
:bla
emGetBla()
função.GetBla()
.make_unique()
cria mais uma instância.NOTA:
GetBla()
valor de retorno é uma cópia do objeto localbla
.GetBla()
retornadomove
, a eliminação de cópias é suprimida.fonte
Para realmente ver o que está acontecendo nos bastidores, você pode usar um depurador ou definir o construtor de cópias . Eu adicionei o construtor de cópia no seu código. Experimente o código fornecido abaixo:
NOTA:
std::move
não move nada. Ele só lança delvalue
referência pararvalue
referência e seu objeto retornado poderia ter sido construída através de um construtor de movimento (e copiar elisão poderiam ser suprimidos), mas o compilador não implicitamente declaradomove
construtor porque você definiu o destruidor (e eu adicionei construtor de cópia no meu exemplo)Saídas:
Veja meus comentários abaixo:
bla
é construído em funçãoGetBla()
via construtor padrão.GetBla()
função é do objeto criado na construído-cópia# 1
.bla
O objeto (construído em # 1) é destruído e seu destruidor é chamado.std::make_unique<Bla>
chamanew
e chama o construtor apropriado e escolhe ocopy
construtor.fonte