web-dev-qa-db-fra.com

Avantages de l'utilisation de std :: make_unique par rapport au nouvel opérateur

Quels sont les avantages d'utiliser std::make_unique par rapport à l'opérateur new pour initialiser un std::unique_ptr?

En d'autres termes, pourquoi

std::unique_ptr<SomeObject> a = std::make_unique(SomeObject(...))

mieux que de faire

std::unique_ptr<SomeObject> a = new SomeObject(...)

J'ai essayé beaucoup de recherches en ligne et je sais qu'il est judicieux d'éviter l'opérateur new en C++ moderne, mais je ne suis pas sûr des avantages de ce scénario précis. Empêche-t-il tout type de fuite de mémoire qui pourrait arriver? Est-il plus rapide de faire un std::make_unique que d'utiliser new?

86
niting

Avantages

  • make_unique enseigne aux utilisateurs "ne dites jamais new/delete et new[]/delete[]" sans avertissements.

  • make_unique partage deux avantages avec make_shared (à l'exclusion du troisième avantage, efficacité accrue). Tout d'abord, unique_ptr<LongTypeName> up(new LongTypeName(args)) doit mentionner LongTypeName deux fois, tandis que auto up = make_unique<LongTypeName>(args) le mentionne une fois.

  • make_unique empêche la fuite non spécifiée d'ordre d'évaluation déclenchée par des expressions telles que foo(unique_ptr<X>(new X), unique_ptr<Y>(new Y)). (Suivre le conseil "ne jamais dire new" est plus simple que "ne jamais dire new, à moins que vous ne le donniez immédiatement à un unique_ptr".)

  • make_unique est soigneusement implémenté pour la sécurité des exceptions et il est recommandé d'appeler directement les constructeurs unique_ptr.

Quand ne pas utiliser make_unique

  • N'utilisez pas make_unique si vous avez besoin d'un suppresseur personnalisé ou si vous utilisez un pointeur brut venant d'ailleurs.

Sources

  1. Proposition de std::make_unique .
  2. Solution GotW # 89 de Herb Sutter: pointeurs intelligents
87
101010

La différence est que std::make_unique renvoie un objet de type std::unique_ptr et new renvoie un pointeur sur l'objet créé. Pour les échecs d'allocation de mémoire, ils lanceront tous les deux. Attends, ce n'est pas si simple. Lire plus loin.

Considérons une telle fonction ci-dessous:

_void func(ClassA* a, ClassB* b){
     ......
}
_

Lorsque vous passez un appel comme func(new A(), new B()); Le compilateur peut choisir d’évaluer les arguments de la fonction de gauche à droite ou dans l’ordre de son choix. Supposons une évaluation de gauche à droite: que se passe-t-il lorsque la première expression new réussit mais que la seconde expression new lève?

Le véritable danger ici est lorsque vous attrapez une telle exception; Oui, vous avez peut-être intercepté l'exception levée par new B() et repris l'exécution normale, mais new A() a déjà réussi et sa mémoire sera silencieusement filtrée. Personne pour le nettoyer ... * sanglots ...

Mais avec _make_unique_, vous ne pouvez pas avoir une fuite, car la pile se déroulera (et le destructeur de l'objet créé précédemment sera exécuté). Par conséquent, avoir une préférence pour _make_unique_ vous contraindra vers exception . Dans ce cas, _std::make_unique_ indique "Sécurité d'exception de base" que la mémoire allouée et l'objet créé par new ne seront jamais orphelins, peu importe le type de mémoire. . Même jusqu'à la fin des temps ... :-)

Vous devriez lire Herb Sutter GoTW102

39
WhiZTiM