2010-01-27 6 views
51

Y at-il des inconvénients à utiliser make_shared<T>() au lieu d'utiliser shared_ptr<T>(new T).Y at-il des inconvénients avec l'utilisation de make_shared pour créer un shared_ptr?

Boost documentation États

Il y a eu des demandes répétées de utilisateurs pour une fonction d'usine qui crée un objet d'un type donné et renvoie un shared_ptr à elle. En plus commodité et de style, une telle fonction est également exception sûre et beaucoup plus rapidement, car il peut utiliser une allocation unique à la fois l'objet et son bloc de contrôle correspondant , ce qui élimine une partie importante de la construction de shared_ptr frais généraux. Cela élimine l'une des plaintes d'efficacité majeure sur shared_ptr.

+6

On peut se demander quelles sont les autres principales plaintes concernant l'efficacité de shared_ptr? –

+0

La sécurité d'exception est un atout assez fort de «std :: make_shared». Essayez de l'utiliser autant que possible. –

+0

@ViktorSehr Mutex verrouille sur le compteur de référence quand un 'shared_ptr' est copié principalement :) – Drax

Répondre

26

Je connais au moins deux.

  • Vous devez contrôler l'allocation. Pas vraiment un gros, mais certains anciens api aiment renvoyer des pointeurs que vous devez supprimer.
  • Pas de suppresseur personnalisé. Je ne sais pas pourquoi cela n'est pas supporté, mais ce n'est pas le cas. Cela signifie que vos pointeurs partagés doivent utiliser un suppresseur de vanille.

Jolis points faibles. alors essayez de toujours utiliser make_shared.

+6

Il n'y a pas de suppresseur personnalisé car seul 'make_shared' sait comment supprimer l'objet. –

+4

Le suppresseur personnalisé est utilisé pour faire quelques astuces intéressantes qui ne se limitent pas à la suppression, c'est pourquoi deft_code l'a mentionné. – Catskul

+0

Si vous pouviez ajouter un suppresseur personnalisé, vous devriez également avoir la possibilité d'utiliser un allocateur personnalisé; disons que cela compliquerait trop l'interface. – ipapadop

37

En plus des points présentés par @deft_code, une encore plus faible:

  • Si vous utilisez weak_ptr s qui vivent après tous les shared_ptr s à un objet donné sont morts, alors la mémoire de cet objet va vivre en mémoire avec le bloc de contrôle jusqu'à ce que le dernier weak_ptr meurt. En d'autres termes, l'objet est détruit mais non désaffecté jusqu'à ce que le dernier weak_ptr soit détruit.
+1

C'est aussi le cas si vous n'utilisez pas 'make_shared'. La seule différence est que le bloc de contrôle sera dans un morceau de mémoire allouée séparément. –

+13

@Mike: sûrement pas - normalement, s'il n'y a pas shared_ptrs à l'objet, seulement weak_ptrs, alors l'objet est immédiatement supprimé. Je ne sais pas si le bloc de contrôle reste, je vais vous croire sur parole. Avec make_shared, le bloc de contrôle et l'objet cohabitent une seule allocation, donc si la mémoire de l'un d'entre eux reste, alors les deux le font (bien que je suppose que l'objet est détruit, juste la mémoire n'est pas libérée?). –

+3

Euh, ouais. Pardon. J'ai dû avoir mon autre cerveau quand j'ai écrit ça. –

14

De http://www.codesynthesis.com/~boris/blog/2010/05/24/smart-pointers-in-boost-tr1-cxx-x0/

L'autre inconvénient de la mise en œuvre make_shared() est l'augmentation de la taille de code objet. En raison de la façon dont cette optimisation est implémentée, une table virtuelle supplémentaire ainsi qu'un ensemble de fonctions virtuelles seront instanciées pour chaque type d'objet que vous utilisez avec make_shared().

+0

Est-ce que cela devrait se poursuivre à mesure que les compilateurs progresseront? – Catskul

7

Avec make partagé, vous ne pouvez pas spécifier comment allocation et désallocation de l'objet tenu sera fait.

Lorsque cela est souhaité, utilisez std::allocate_shared<T> à la place:

std::vector<std::shared_ptr<std::string>> avec; 
std::allocator<std::string> aAllocator; 
avec.push_back(std::allocate_shared<std::string>(aAllocator,"hi there!")); 

Notez que le vecteur n'a pas besoin d'être informé sur l'allocateur!

Pour faire un allocateur personnalisé, un coup d'oeil ici https://stackoverflow.com/a/542339/1149664

8

De plus, make_shared n'est pas compatible avec le modèle d'usine. En effet, l'appel à make_shared dans votre fonction d'usine appelle le code de bibliothèque, qui à son tour appelle new, auquel il n'a pas accès, car il ne peut pas appeler le (s) constructeur (s) privé (s) de la classe (constructeur (s) soyez privé, si vous suivez correctement le modèle d'usine).

Questions connexes