Pour autant que je sache, C ++ 14 a été introduit std::make_unique
car, en raison de la non-spécification de l'ordre d'évaluation des paramètres, ce n'était pas sûr:
f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A
(Explication: si l'évaluation alloue d'abord la mémoire pour le pointeur brut, puis appelle g()
et une exception est levée avant la std::unique_ptr
construction, alors la mémoire est perdue.)
L'appel std::make_unique
était un moyen de contraindre l'ordre des appels, assurant ainsi la sécurité:
f(std::make_unique<MyClass>(param), g()); // Syntax B
Depuis lors, C ++ 17 a clarifié l'ordre d'évaluation, rendant la syntaxe A sûre également, alors voici ma question: y a-t-il encore une raison d'utiliser le constructeur std::make_unique
over std::unique_ptr
en C ++ 17? Peux-tu donner quelques exemples?
À partir de maintenant, la seule raison que je peux imaginer est qu'il ne permet de taper MyClass
qu'une seule fois (en supposant que vous n'ayez pas besoin de vous fier au polymorphisme avec std::unique_ptr<Base>(new Derived(param))
). Cependant, cela semble être une raison assez faible, surtout quand std::make_unique
il ne permet pas de spécifier un suppresseur alors que std::unique_ptr
le constructeur de le fait.
Et pour être clair, je ne préconise pas la suppression std::make_unique
de la bibliothèque standard (le garder a du sens au moins pour la compatibilité descendante), mais je me demande plutôt s'il existe encore des situations dans lesquelles il est fortement préféré àstd::unique_ptr
la source
std::unique_ptr
? Ce n'est pas un argument contremake_unique
std::make_unique
en premier lieu, je ne pense pas que ce serait une raison suffisante pour l'ajouter à la STL, surtout quand c'est une syntaxe moins expressive que l'utilisation du constructeur, pas plusRéponses:
Vous avez raison de dire que la raison principale a été supprimée. Il y a toujours le ne pas utiliser de nouvelles directives et que ce sont moins de raisons de frappe (pas besoin de répéter le type ou d'utiliser le mot
new
). Certes, ce ne sont pas des arguments forts mais j'aime vraiment ne pas voirnew
dans mon code.N'oubliez pas non plus la cohérence. Vous devez absolument l'utiliser,
make_shared
donc l'utilisationmake_unique
est naturelle et correspond au modèle. Il est alors trivial de changementstd::make_unique<MyClass>(param)
àstd::make_shared<MyClass>(param)
(ou l'inverse) où la syntaxe A nécessite beaucoup plus d'une ré - écriture.la source
new
je dois m'arrêter et réfléchir: combien de temps ce pointeur va-t-il vivre? Est-ce que je l'ai géré correctement? S'il y a une exception, tout est-il nettoyé correctement? Je voudrais ne pas me poser ces questions et perdre mon temps dessus et si je n'utilise pasnew
, je n'ai pas à poser ces questions.new
. Ne serait-ce pas merveilleux?std::make_shared
- imaginez un cas où l'objet alloué est gros et qu'il y a beaucoup destd::weak_ptr
-s pointant vers lui: il serait préférable de laisser l'objet supprimé dès le dernier partage Le pointeur est détruit et vit avec juste une petite zone partagée.std::make_shared
stackoverflow.com/a/20895705/8414561 où la mémoire qui a été utilisée pour stocker l'objet ne peut pas être libérée tant que le dernierstd::weak_ptr
n'est pas parti (même si tous lesstd::shared_ptr
-s pointent dessus (et par conséquent l'objet lui-même) ont déjà été détruits).make_unique
distingueT
deT[]
etT[N]
,unique_ptr(new ...)
ne fait pas.Vous pouvez facilement obtenir un comportement indéfini en passant un pointeur qui a été
new[]
édité sur aunique_ptr<T>
, ou en passant un pointeur qui a éténew
édité sur aunique_ptr<T[]>
.la source
La raison est d'avoir un code plus court sans doublons. Comparer
Vous enregistrez
MyClass
,new
et des accolades. Il ne coûte qu'un caractère de plus en make par rapport à ptr .la source
MyClass
, mais je me demandais s'il y avait une raison plus forte de l'utiliser<MyClass>
pièce dans la première variante.std::unique_ptr
elle n'est pas autorisée. Cela a à voir avec la distinctionstd::unique_ptr<T>
etstd::unique_ptr<T[]>
Chaque utilisation de
new
doit être très soigneusement vérifiée pour vérifier l'exactitude à vie; est-il supprimé? Juste une fois?Chaque utilisation de
make_unique
n'est pas pour ces caractéristiques supplémentaires; tant que l'objet propriétaire a une durée de vie "correcte", il rend récursivement le pointeur unique "correct".Maintenant, il est vrai que
unique_ptr<Foo>(new Foo())
c'est identique en tous points 1 àmake_unique<Foo>()
; il nécessite juste un simple "grep votre code source pour toutes les utilisations denew
pour les auditer".1 mensonge dans le cas général. Le transfert parfait n'est pas parfait,
{}
par défaut, les tableaux sont toutes des exceptions.la source
unique_ptr<Foo>(new Foo)
n'est pas tout à fait identique àmake_unique<Foo>()
... ce dernier le faitnew Foo()
Mais sinon, oui.std::unique_ptr
elle n'est pas autorisée. Si a à voir avec la distinctionstd::unique_ptr<T>
etstd::unique_ptr<T[]>
Ce n'est vraiment pas suffisant. S'appuyer sur une clause technique récemment introduite comme garantie de sécurité n'est pas une pratique très robuste:
new
ailleurs, par exemple en copiant-collant votre exemple.new
ailleurs (d'accord, pas beaucoup de chance).En règle générale, il est judicieux que votre code soit approprié / robuste / clairement valide sans recourir à des couches de langage, à la recherche de clauses techniques mineures ou obscures dans la norme.
(c'est essentiellement le même argument que j'ai fait ici à propos de l'ordre de destruction des tuple.)
la source
Considérez la fonction void (std :: unique_ptr (new A ()), std :: unique_ptr (new B ())) {...}
Supposons que le nouveau A () réussisse, mais que le nouveau 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 sa mémoire libérée: la mémoire fuit silencieusement et il n'y a aucun moyen de la nettoyer. En enveloppant A et B dans std :: make_uniques, vous êtes sûr que la fuite ne se produira pas:
void function (std :: make_unique (), std :: make_unique ()) {...} Le point ici est que std :: make_unique et std :: make_unique 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_unique et std :: make_shared.
la source