Pelo que entendi, C ++ 14 foi introduzido std::make_unique
porque, como resultado da ordem de avaliação dos parâmetros não ser especificada, isso não era seguro:
f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A
(Explicação: se a avaliação primeiro alocar a memória para o ponteiro bruto, em seguida, chamar g()
e uma exceção for lançada antes da std::unique_ptr
construção, a memória será perdida.)
Chamar std::make_unique
era uma forma de restringir a ordem da chamada, tornando as coisas seguras:
f(std::make_unique<MyClass>(param), g()); // Syntax B
Desde então, C ++ 17 esclareceu a ordem de avaliação, tornando Sintaxe Um cofre também, então aqui está a minha pergunta: existe ainda uma razão para usar std::make_unique
ao longo std::unique_ptr
do construtor em C ++ 17? Voce pode dar alguns exemplos?
A partir de agora, a única razão que posso imaginar é que ele permite digitar MyClass
apenas uma vez (supondo que você não precise confiar no polimorfismo com std::unique_ptr<Base>(new Derived(param))
). No entanto, esse parece um motivo muito fraco, especialmente quando std::make_unique
não permite especificar um deleter enquanto std::unique_ptr
o construtor de faz.
E só para ficar claro, não estou defendendo a remoção std::make_unique
da Biblioteca Padrão (mantê-la faz sentido, pelo menos para compatibilidade com versões anteriores), mas sim me perguntando se ainda há situações em que é fortemente preferidostd::unique_ptr
fonte
std::unique_ptr
? Não é um argumento contramake_unique
std::make_unique
em primeiro lugar, não acho que seria motivo suficiente para adicioná-lo ao STL, especialmente quando é uma sintaxe menos expressiva do que usar o construtor, não maisRespostas:
Você está certo de que o principal motivo foi removido. Há ainda os motivos de não usar novas diretrizes e que são menos digitadores (não precisa repetir o tipo ou usar a palavra
new
). Admito que esses não são argumentos fortes, mas eu realmente gosto de não vernew
no meu código.Também não se esqueça da consistência. Você absolutamente deveria estar usando,
make_shared
então o usomake_unique
é natural e se encaixa no padrão. Então, é trivial mudarstd::make_unique<MyClass>(param)
parastd::make_shared<MyClass>(param)
(ou o contrário) onde a sintaxe A requer muito mais reescrita.fonte
new
, preciso parar e pensar: quanto tempo esse ponteiro vai durar? Manusei corretamente? Se houver uma exceção, tudo está limpo corretamente? Gostaria de não me fazer essas perguntas e perder meu tempo com isso e, se não usarnew
, não preciso fazer essas perguntas.new
. Isso não seria maravilhoso?std::make_shared
- imagine um caso onde o objeto alocado é grande e há muitosstd::weak_ptr
-s apontando para ele: seria melhor deixar o objeto ser deletado assim que o último compartilhado O ponteiro é destruído e fica com apenas uma pequena área compartilhada.std::make_shared
stackoverflow.com/a/20895705/8414561 onde a memória que foi usada para armazenar o objeto não pode ser liberada até que o últimostd::weak_ptr
tenha ido (mesmo se todos osstd::shared_ptr
-s apontando para ele (e conseqüentemente, o próprio objeto) já foi destruído).make_unique
distingueT
deT[]
eT[N]
,unique_ptr(new ...)
não.Você pode obter facilmente um comportamento indefinido passando um ponteiro que foi
new[]
ed para aunique_ptr<T>
, ou passando um ponteiro que foinew
ed para aunique_ptr<T[]>
.fonte
O motivo é ter um código mais curto sem duplicatas. Comparar
Você salva
MyClass
,new
e chaves. Custa apenas um caráter mais na composição , em comparação com o PTR .fonte
MyClass
, mas eu queria saber se há um motivo mais forte para usá-lo<MyClass>
parte da primeira variante.std::unique_ptr
não ser permitido. Tem a ver com distinguirstd::unique_ptr<T>
estd::unique_ptr<T[]>
Cada uso de
new
tem que ser cuidadosamente auditado para verificar a exatidão ao longo da vida; ele é excluído? Apenas uma vez?Cada uso de
make_unique
não faz essas características extras; contanto que o objeto proprietário tenha vida útil "correta", ele recursivamente faz com que o ponteiro exclusivo tenha "correto".Agora, é verdade que
unique_ptr<Foo>(new Foo())
é idêntico em todas as maneiras 1 amake_unique<Foo>()
; requer apenas um simples "grep seu código-fonte para todos os usos denew
para auditá-los".1 , na verdade, um, em geral o caso. O encaminhamento perfeito não é perfeito,
{}
init padrão, arrays são exceções.fonte
unique_ptr<Foo>(new Foo)
não é exatamente idêntico amake_unique<Foo>()
... o últimonew Foo()
sim. Mas, caso contrário, sim.std::unique_ptr
não ser permitido. Tem a ver com distinguirstd::unique_ptr<T>
estd::unique_ptr<T[]>
Isso realmente não é bom o suficiente. Contar com uma cláusula técnica recentemente introduzida como garantia de segurança não é uma prática muito robusta:
new
's em outro lugar, por exemplo, copiando e colando seu exemplo.new
outro lugar (ok, certo, não há muita chance disso).Geralmente, é uma boa ideia que seu código seja apropriado / robusto / claramente válido sem recorrer a camadas de linguagem, procurando por cláusulas técnicas menores ou obscuras no padrão.
(este é essencialmente o mesmo argumento que apresentei aqui sobre a ordem de destruição da tupla.)
fonte
Considere a função void (std :: unique_ptr (new A ()), std :: unique_ptr (new B ())) {...}
Suponha que new A () seja bem-sucedido, mas new B () lance uma exceção: você o captura para retomar a execução normal do seu programa. Infelizmente, o padrão C ++ não exige que o objeto A seja destruído e sua memória desalocada: a memória vaza silenciosamente e não há como limpá-la. Ao envolver A e B em std :: make_uniques, você tem certeza de que o vazamento não ocorrerá:
void function (std :: make_unique (), std :: make_unique ()) {...} O ponto aqui é que std :: make_unique e std :: make_unique são agora objetos temporários, e a limpeza de objetos temporários é especificada corretamente em o padrão C ++: seus destruidores serão acionados e a memória liberada. Então, se você puder, sempre prefira alocar objetos usando std :: make_unique e std :: make_shared.
fonte