Différences entre std :: make_unique et std :: unique_ptr avec new

130

At std::make_unique- il des avantages d'efficacité comme std::make_shared?

Par rapport à la construction manuelle std::unique_ptr:

std::make_unique<int>(1);         // vs
std::unique_ptr<int>(new int(1));
NFRCR
la source
Y make_shareda- t -il une efficacité sur la simple écriture du code à main longue?
Ed Heal
9
@EdHeal Cela peut, car make_sharedpeut allouer à la fois l'espace pour l'objet et l'espace pour le bloc de contrôle en une seule allocation. Le coût est que l'objet ne peut pas être désalloué séparément du bloc de contrôle, donc si vous en utilisez weak_ptrbeaucoup, vous pouvez finir par utiliser plus de mémoire.
bames53
C'est peut-être un bon point de départ stackoverflow.com/questions/9302296/…
Ed Heal

Réponses:

140

La motivation derrière make_uniqueest principalement double:

  • make_uniqueest sûr pour créer des temporaires, alors qu'avec une utilisation explicite de, newvous devez vous souvenir de la règle de ne pas utiliser de temporaires sans nom.

    foo(make_unique<T>(), make_unique<U>()); // exception safe
    
    foo(unique_ptr<T>(new T()), unique_ptr<U>(new U())); // unsafe*
  • L'ajout de make_uniquefinalement signifie que nous pouvons dire aux gens de «ne jamais» utiliser newplutôt que la règle précédente de «ne jamais» utiliser newsauf lorsque vous faites un unique_ptr».

Il y a aussi une troisième raison:

  • make_uniquene nécessite pas d'utilisation de type redondant. unique_ptr<T>(new T())->make_unique<T>()

Aucune des raisons n'implique l'amélioration de l'efficacité d'exécution comme le fait l'utilisation make_shared(en évitant une deuxième allocation, au prix d'une utilisation de mémoire de pointe potentiellement plus élevée).

* Il est prévu que C ++ 17 inclura un changement de règle qui signifie que ce n'est plus dangereux. Voir les documents du comité C ++ P0400R0 et P0145R3 .

bames53
la source
Il serait plus logique de dire std::unique_ptret std::shared_ptrc'est pourquoi nous pouvons dire aux gens de «ne jamais utiliser new».
Timothy Shields
2
@TimothyShields Ouais, c'est ce que je veux dire. C'est juste que dans C ++ 11, nous avons make_shared, tout make_uniquecomme le dernier élément qui manquait auparavant.
bames53
1
Pouvez-vous mentionner brièvement, ou établir un lien vers, la raison pour laquelle vous n'utilisez pas de temporaires sans nom?
Dan Nissenbaum
14
En fait, à partir stackoverflow.com/a/19472607/368896 , je l' ai ... A partir de cette réponse, pensez à l'appel de fonction suivante f: f(unique_ptr<T>(new T), function_that_can_throw());- de citer la réponse: Le compilateur est autorisé à appeler (dans l' ordre): new T, function_that_can_throw(), unique_ptr<T>(...). Évidemment, si function_that_can_throwjette réellement, vous fuyez. make_uniqueempêche ce cas. Donc, ma question est répondue.
Dan Nissenbaum
3
Une des raisons pour lesquelles j'ai dû utiliser std :: unique_ptr <T> (new T ()) était que le constructeur de T était privé. Même si l'appel à std :: make_unique était dans une méthode d'usine publique de classe T, il n'a pas été compilé car l'une des méthodes sous-jacentes de std :: make_unique n'a pas pu accéder au constructeur privé. Je ne voulais pas rendre cette méthode amie parce que je ne voulais pas me fier à l'implémentation de std :: make_unique. La seule solution était donc d'appeler new dans ma méthode factory de la classe T, puis de l'envelopper dans un std :: unique_ptr <T>.
Patrick
14

std::make_uniqueet std::make_sharedsont là pour deux raisons:

  1. Pour que vous n'ayez pas à lister explicitement les arguments de type de modèle.
  2. Sécurité d'exception supplémentaire sur l'utilisation std::unique_ptrou les std::shared_ptrconstructeurs. (Voir la section Notes ici .)

Ce n'est pas vraiment une question d'efficacité d'exécution. Il y a un peu le bloc de contrôle et l' Tattribution en même temps, mais je pense que c'est plus un bonus et moins une motivation pour que ces fonctions existent.

Timothy Shields
la source
Ils sont également là pour une sécurité exceptionnelle.
0x499602D2
@ 0x499602D2 Et ça, bon ajout. Cette page en parle.
Timothy Shields
Pour les futurs lecteurs, C ++ 17 ne permet pas l'entrelacement des arguments de fonction, donc l'argument pour la sécurité des exceptions ne tient plus. L'allocation de mémoire pour deux parallèles std::make_sharedgarantira qu'au moins l'un d'entre eux sera enveloppé dans le pointeur intelligent avant que l'autre allocation de mémoire ne se produise, donc aucune fuite.
MathBunny
7

Une raison pour laquelle vous devriez utiliser std::unique_ptr(new A())ou std::shared_ptr(new A())directement à la place std::make_*()est de ne pas pouvoir accéder au constructeur de la classe en Adehors de la portée actuelle.

Volodymyr Lashko
la source
0

Considérez l'appel de fonction

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

Supposons que new A()réussisse, mais que new B()lève une exception: vous l'attrapez pour reprendre l'exécution normale de votre programme. Malheureusement, le standard C ++ n'exige pas que l'objet A soit détruit et que sa mémoire soit libérée: la mémoire fuit silencieusement et il n'y a aucun moyen de la nettoyer. En enveloppant A et B, std::make_uniquesvous êtes sûr que la fuite ne se produira pas:

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

Le point ici est que std::make_unique<A>et std::make_unique<B>sont maintenant des objets temporaires, et le nettoyage des objets temporaires est correctement spécifié dans le standard C ++: leurs destructeurs seront déclenchés et la mémoire libérée. Donc, si vous le pouvez, préférez toujours allouer des objets en utilisant std::make_uniqueet std::make_shared.

Dhanya Gopinath
la source