Diferenças entre std :: make_unique e std :: unique_ptr com new

130

Tem std::make_uniquealgum benefício em eficiência como std::make_shared?

Comparado à construção manual std::unique_ptr:

std::make_unique<int>(1);         // vs
std::unique_ptr<int>(new int(1));
NFRCR
fonte
Tem make_sharedalguma eficiência em escrever apenas o código da mão longa?
Ed Heal #
9
@EdHeal Pode, porque make_sharedpode alocar o espaço para o objeto e o espaço para o bloco de controle juntos em uma única alocação. O custo disso é que o objeto não pode ser desalocado separadamente do bloco de controle; portanto, se você usa weak_ptrmuito, pode acabar usando mais memória.
usar o seguinte comando
Talvez este seja um bom ponto de partida stackoverflow.com/questions/9302296/...
Ed curar

Respostas:

140

A motivação por trás make_uniqueé basicamente dupla:

  • make_uniqueé seguro para criar temporários, enquanto que com o uso explícito de newvocê é necessário lembrar a regra de não usar temporários sem nome.

    foo(make_unique<T>(), make_unique<U>()); // exception safe
    
    foo(unique_ptr<T>(new T()), unique_ptr<U>(new U())); // unsafe*
  • A adição de make_uniquefinalmente significa que podemos dizer às pessoas para 'nunca' usarem, newem vez da regra anterior, para '' nunca 'usar, newexceto quando você faz um unique_ptr'.

Há também uma terceira razão:

  • make_uniquenão requer uso de tipo redundante. unique_ptr<T>(new T())->make_unique<T>()

Nenhum dos motivos envolve a melhoria da eficiência do tempo de execução da maneira que o uso make_shared(devido a evitar uma segunda alocação, ao custo do uso de memória potencialmente mais alto).

* É esperado que o C ++ 17 inclua uma alteração de regra que signifique que isso não é mais seguro. Consulte os documentos do comitê C ++ P0400R0 e P0145R3 .

bames53
fonte
Faria mais sentido dizer std::unique_ptre std::shared_ptré por isso que podemos dizer às pessoas para "nunca usarem new".
Timothy Shields
2
@ TimothyShields Sim, é isso que eu quero dizer. É que temos em C ++ 11 make_sharede também make_uniquea peça final que estava faltando anteriormente.
usar o seguinte comando
1
Você poderia mencionar brevemente, ou vincular, o motivo de não usar temporários sem nome?
Dan Nissenbaum 03/03
14
Na verdade, a partir stackoverflow.com/a/19472607/368896 , eu tenho isso ... A partir desse resposta, considere o seguinte chamada de função f: f(unique_ptr<T>(new T), function_that_can_throw());- para citar a resposta: O compilador é permitido a chamada (em ordem): new T, function_that_can_throw(), unique_ptr<T>(...). Obviamente, se function_that_can_throwrealmente joga, então você vaza. make_uniqueimpede este caso. Então, minha pergunta é respondida.
Dan Nissenbaum 03/03
3
Uma razão pela qual eu tive que usar std :: unique_ptr <T> (new T ()) foi porque o construtor de T era privado. Mesmo que a chamada para std :: make_unique estivesse em um método público de fábrica da classe T, ela não foi compilada porque um dos métodos subjacentes de std :: make_unique não pôde acessar o construtor privado. Eu não queria fazer esse método amigo, porque não queria confiar na implementação de std :: make_unique. Portanto, a única solução foi, chamar novo no meu método de fábrica da classe T e envolvê-lo em um std :: unique_ptr <T>.
Patrick
14

std::make_uniquee std::make_sharedexistem por dois motivos:

  1. Para que você não precise listar explicitamente os argumentos do tipo de modelo.
  2. Exceção adicional de segurança sobre o uso std::unique_ptrou std::shared_ptrconstrutores. (Veja a seção Notas aqui .)

Não se trata realmente de eficiência de tempo de execução. Há um pouco sobre o bloco de controle e a Tatribuição de todos de uma vez, mas acho que isso é mais um bônus e menos uma motivação para que essas funções existam.

Timothy Shields
fonte
Eles também estão lá por segurança de exceção.
0456602D2
@ 0x499602D2 E isso, boa adição. Esta página fala sobre isso.
Timothy Shields
Para futuros leitores, o C ++ 17 não permite a intercalação de argumentos de função, portanto o argumento para segurança de exceção não é mais válido. A alocação de memória para dois paralelos std::make_sharedgarantirá que pelo menos um deles seja empacotado no ponteiro inteligente antes que a outra alocação de memória ocorra, portanto, não haverá vazamentos.
MathBunny
7

Uma razão pela qual você precisaria usar std::unique_ptr(new A())ou std::shared_ptr(new A())diretamente, em vez de std::make_*()não conseguir acessar o construtor da classe Afora do escopo atual.

Volodymyr Lashko
fonte
0

Considere chamada de função

void function(std::unique_ptr<A>(new A()), std::unique_ptr<B>(new B())) { ... }

Suponha que new tenha A()êxito, mas new B()lança uma exceção: você a 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 seja desalocada: a memória vaza silenciosamente e não há como limpá-lo. Ao envolver A e B, std::make_uniquesvocê tem certeza de que o vazamento não ocorrerá:

void function(std::make_unique<A>(), std::make_unique<B>()) { ... }

O ponto aqui é que, std::make_unique<A>e std::make_unique<B>agora são objetos temporários, a limpeza de objetos temporários está especificada corretamente no padrão C ++: seus destruidores serão acionados e a memória liberada. Portanto, se puder, sempre prefira alocar objetos usando std::make_uniquee std::make_shared.

Dhanya Gopinath
fonte