Como faço para implementar um construtor de cópia para uma classe que possui uma unique_ptr
variável de membro? Estou considerando apenas o C ++ 11.
c++
c++11
unique-ptr
codefx
fonte
fonte
std::vector
.unique_ptr
, provavelmente deseja um construtor de movimentação, se seu objetivo for colocar os dados em astd::vector
. Por outro lado, o padrão C ++ 11 criou automaticamente construtores de movimento, então talvez você queira um construtor de cópia ...Respostas:
Uma vez que o
unique_ptr
não pode ser compartilhado, você precisa copiar profundamente seu conteúdo ou convertê-lounique_ptr
em umshared_ptr
.Você pode, como o NPE mencionou, usar um move-ctor em vez de um copy-ctor, mas isso resultaria em semânticas diferentes de sua classe. Um move-ctor precisaria fazer o membro como móvel explicitamente via
std::move
:Ter um conjunto completo de operadores necessários também leva a
Se quiser usar sua classe em a
std::vector
, você basicamente terá que decidir se o vetor deve ser o único proprietário de um objeto, caso em que seria suficiente para tornar a classe móvel, mas não copiável. Se você deixar de fora o copy-ctor e a atribuição de cópia, o compilador irá guiá-lo sobre como usar um std :: vector com tipos move-only.fonte
int
. Se você tiver umunique_ptr<Base>
que armazene umDerived
, o acima será dividido.A( const A& a ) : up_( a.up_ ? new int( *a.up_ ) : nullptr) {}
value_ptr
-unique_ptr
mais informações do apagador / copiador.O caso usual de alguém ter um
unique_ptr
em uma classe é ser capaz de usar herança (caso contrário, um objeto simples faria o mesmo, consulte RAII). Para este caso, não há uma resposta apropriada neste tópico até agora .Então, aqui está o ponto de partida:
... e o objetivo é, como disse, tornar
Foo
copiável.Para isso, é necessário fazer uma cópia detalhada do ponteiro contido para garantir que a classe derivada seja copiada corretamente.
Isso pode ser feito adicionando o seguinte código:
Existem basicamente duas coisas acontecendo aqui:
A primeira é a adição de construtores de cópia e movimentação, que são excluídos implicitamente à
Foo
medida que o construtor de cópia deunique_ptr
é excluído. O construtor de movimento pode ser adicionado simplesmente por= default
... o que é apenas para deixar o compilador saber que o construtor de movimento usual não deve ser excluído (isso funciona, poisunique_ptr
já tem um construtor de movimento que pode ser usado neste caso).Para o construtor de cópia de
Foo
, não há mecanismo semelhante, pois não há construtor de cópia deunique_ptr
. Portanto, é preciso construir um novounique_ptr
, preenchê-lo com uma cópia da ponta original e usá-lo como membro da classe copiada.No caso de herança estar envolvida, a cópia da ponta original deve ser feita com cuidado. A razão é que fazer uma cópia simples via
std::unique_ptr<Base>(*ptr)
código acima resultaria em fracionamento, ou seja, apenas o componente base do objeto é copiado, enquanto a parte derivada está faltando.Para evitar isso, a cópia deve ser feita por meio do padrão de clone. A ideia é fazer a cópia por meio de uma função virtual
clone_impl()
que retorna umBase*
na classe base. Na classe derivada, no entanto, ele é estendido por meio de covariância para retornar aDerived*
, e esse ponteiro aponta para uma cópia recém-criada da classe derivada. A classe base pode então acessar esse novo objeto por meio do ponteiro da classe baseBase*
, envolvê-lo em umunique_ptr
e retorná-lo por meio daclone()
função real que é chamada de fora.fonte
unique_ptr
quando a contenção direta faria o contrário. A resposta??? Herança .unique_ptr
maisoptional
para tipos anuláveis.clone_impl
in base, o compilador não dirá se você o esqueceu na classe derivada. Você poderia, no entanto, usar outra classe baseCloneable
e implementar um virtual puroclone_impl
lá. Então o compilador reclamará se você esquecer na classe derivada.Experimente este auxiliar para criar cópias profundas e lidar com a origem de unique_ptr nulo.
Por exemplo:
fonte
Daniel Frey mencionou sobre a solução de cópia, gostaria de falar sobre como mover o unique_ptr
Eles são chamados de construtor de movimento e atribuição de movimento
você poderia usá-los assim
Você precisa envolver aec por std :: move porque eles têm um nome std :: move está dizendo ao compilador para transformar o valor em referência de rvalue quaisquer que sejam os parâmetros. Em sentido técnico, std :: move é uma analogia com algo como " std :: rvalue "
Depois de mover, o recurso do unique_ptr é transferido para outro unique_ptr
Existem muitos tópicos que documentam a referência de rvalue; este é muito fácil para começar .
Editar:
O objeto movido deve permanecer válido, mas em estado não especificado .
C ++ primer 5, ch13 também dá uma boa explicação sobre como "mover" o objeto
fonte
a
depois de chamar std :: move (a) nob
construtor de movimento? É totalmente inválido?Eu sugiro usar make_unique
fonte
unique_ptr
não é copiável, é apenas móvel.Isso afetará diretamente o Teste, que é, em seu segundo exemplo, também apenas móvel e não copiável.
Na verdade, é bom que você use o
unique_ptr
que o protege de um grande erro.Por exemplo, o principal problema com seu primeiro código é que o ponteiro nunca é excluído, o que é muito, muito ruim. Diga, você consertaria isso:
Isso também é ruim. O que acontece, se você copiar
Test
? Haverá duas classes com um ponteiro que aponta para o mesmo endereço.Quando um
Test
é destruído, ele também destrói o ponteiro. Quando o segundoTest
for destruído, ele tentará remover a memória atrás do ponteiro também. Mas ele já foi excluído e obteremos algum erro de runtime de acesso à memória ruim (ou comportamento indefinido se não tivermos sorte).Portanto, a maneira certa é implementar o construtor de cópia e o operador de atribuição de cópia, para que o comportamento seja claro e possamos criar uma cópia.
unique_ptr
está muito à nossa frente aqui. Ele tem o significado semântico: “ Eu souunique
, então você não pode simplesmente me copiar. ” Assim, nos impede de cometer o erro de implementar agora os operadores em questão.Você pode definir o construtor de cópia e o operador de atribuição de cópia para um comportamento especial e seu código funcionará. Mas você é, com razão (!), Forçado a fazer isso.
Moral da história: use sempre
unique_ptr
nesse tipo de situação.fonte