Comme je peux le constater, les pointeurs intelligents sont largement utilisés dans de nombreux projets C ++ réels.
Bien que certains pointeurs intelligents soient évidemment bénéfiques pour prendre en charge la RAII et les transferts de propriété, il existe également une tendance à utiliser des pointeurs partagés par défaut , comme moyen de "ramasser les ordures" , afin que le programmeur n'ait pas à réfléchir autant à l'allocation. .
Pourquoi les pointeurs partagés sont-ils plus populaires que l'intégration d'un bon ramasse-ordures comme Boehm GC ? (Ou êtes-vous d'accord pour dire qu'ils sont plus populaires que les GC actuels?)
Je connais deux avantages des CPG classiques par rapport au comptage de références:
- Les algorithmes GC conventionnels ne posent aucun problème avec les cycles de référence .
- Le nombre de références est généralement plus lent qu'un GC correct.
Quelles sont les raisons d'utiliser des pointeurs intelligents de comptage de références?
la source
std::unique_ptr
est suffisant et, en tant que tel, ne génère aucun point brut sur les pointeurs bruts en termes de performances d'exécution. En utilisantstd::shared_ptr
partout, vous masqueriez également la sémantique de la propriété, perdant ainsi l'un des principaux avantages des pointeurs intelligents autres que la gestion automatique des ressources: une compréhension claire de l'intention du code.Réponses:
Quelques avantages du comptage de références par rapport au ramassage des ordures:
Frais généraux bas. Les éboueurs peuvent être assez intrusifs (par exemple, geler votre programme à des moments imprévisibles pendant qu'un cycle de récupération des déchets est en cours) et gourmands en mémoire (par exemple, l'empreinte mémoire de votre processus augmente inutilement de plusieurs mégaoctets avant que la récupération des déchets ne démarre enfin)
Comportement plus prévisible. Avec le comptage de références, vous avez la garantie que votre objet sera libéré à l’instant où sa dernière référence disparaîtra. Avec la récupération de place, par contre, votre objet sera libéré "parfois", lorsque le système le traitera. Pour la RAM, cela n’est généralement pas un gros problème sur les ordinateurs de bureau ou les serveurs peu chargés, mais pour d’autres ressources (par exemple, les descripteurs de fichiers), il est souvent nécessaire de les fermer au plus vite afin d’éviter des conflits éventuels ultérieurement.
Plus simple. Le comptage des références peut être expliqué en quelques minutes et mis en œuvre en une heure ou deux. Les éboueurs, en particulier ceux qui ont des performances décentes, sont extrêmement complexes et peu de gens les comprennent.
La norme. C ++ inclut le comptage des références (via shared_ptr) et des amis dans la STL, ce qui signifie que la plupart des programmeurs C ++ le connaissent bien et que la plupart du code C ++ l’utilisera. Cependant, il n'y a pas de récupérateur de déchets C ++ standard, ce qui signifie que vous devez en choisir un et espérer que cela fonctionnera bien pour votre cas d'utilisation. Si ce n'est pas le cas, c'est à vous de résoudre le problème, pas à la langue.
En ce qui concerne les inconvénients présumés du comptage de références - ne pas détecter les cycles est un problème, mais je ne l'ai jamais rencontré personnellement au cours des dix dernières années d'utilisation du comptage de références. La plupart des structures de données sont naturellement acycliques, et si vous rencontrez une situation dans laquelle vous avez besoin de références cycliques (par exemple, un pointeur parent dans un nœud d’arbre), vous pouvez simplement utiliser un pointeur C faible ou un C brut pour la "direction arrière". Tant que vous êtes conscient du problème potentiel lors de la conception de vos structures de données, cela ne pose aucun problème.
En ce qui concerne les performances, le comptage de références n’a jamais posé de problème. J'ai eu des problèmes avec les performances de la récupération de place, en particulier avec les gels aléatoires que le GC peut subir, auxquels la seule solution ("ne pas allouer d'objets") pourrait aussi bien être reformulée comme "ne pas utiliser de GC". .
la source
make_shared
. Néanmoins, la latence est généralement le problème le plus important dans les applications en temps réel, mais le débit est généralement plus important, ce qui explique l’utilisation si répandue des GC. Je ne serais pas si prompt à parler mal d'eux.Pour obtenir de bonnes performances d'un CPG, celui-ci doit pouvoir déplacer des objets en mémoire. Dans un langage comme C ++, où vous pouvez interagir directement avec des emplacements de mémoire, cela est pratiquement impossible. (Microsoft C ++ / CLR ne compte pas car il introduit une nouvelle syntaxe pour les pointeurs gérés par le GC et constitue donc un langage différent.)
Le GC Boehm, bien qu’il soit une bonne idée, est en réalité le pire des deux mondes: vous avez besoin d’un malloc () plus lent que le bon GC, et vous perdez ainsi le comportement déterministe d’allocation / désallocation sans l’augmentation correspondante des performances d’un GC générationnel. . De plus, il est par nécessité prudent, de sorte qu'il ne collectera pas nécessairement toutes vos ordures de toute façon.
Un bon GC bien réglé peut être une bonne chose. Mais dans un langage comme le C ++, les gains sont minimes et les coûts souvent tout simplement inutiles.
Il sera intéressant de voir, cependant, que C ++ 11 devient plus populaire, si les lambdas et la sémantique de capture commencent à conduire la communauté C ++ vers les mêmes types de problèmes d’allocation et de durée de vie des objets qui ont amené la communauté Lisp à inventer les GCs dans le premier endroit.
Voir aussi ma réponse à une question connexe sur StackOverflow .
la source
C'est vrai, mais objectivement, la grande majorité du code est maintenant écrit en langage moderne avec le suivi des éboueurs.
C'est une mauvaise idée car vous devez toujours vous soucier des cycles.
Oh wow, il y a tellement de choses qui ne vont pas dans votre façon de penser:
Le GC de Boehm n’est pas un GC "approprié", au sens strict du terme. C'est vraiment affreux. Il est prudent, donc il fuit et est inefficace par conception. Voir: http://flyingfrogblog.blogspot.co.uk/search/label/boehm
Objectivement, les pointeurs partagés sont loin d'être aussi populaires que GC, car la grande majorité des développeurs utilisent maintenant les langages GC et n'ont pas besoin de pointeurs partagés. Il suffit de regarder Java et Javascript sur le marché du travail par rapport au C ++.
Vous semblez limiter votre examen au C ++ parce que, je suppose, vous pensez que le GC est un problème tangentiel. Ce n’est pas le cas (la seule façon d’obtenir un GC décent est de concevoir le langage et la machine virtuelle du début à la fin) de sorte que vous introduisez un biais de sélection. Les personnes qui veulent vraiment une bonne collecte des ordures ne collent pas avec C ++.
Vous êtes limité à C ++ mais vous souhaitez une gestion automatique de la mémoire.
la source
Sous MacOS X et iOS, ainsi que chez les développeurs utilisant Objective-C ou Swift, le comptage des références est populaire car il est géré automatiquement, et l'utilisation de la récupération de place a considérablement diminué, car Apple ne la prend plus en charge (on me dit que les applications utilisant Le ramassage des ordures va casser dans la prochaine version de MacOS X et le ramassage des ordures n’a jamais été implémenté dans iOS). En fait, je doute sérieusement qu’il y ait jamais eu beaucoup de logiciels utilisant la collecte des ordures lorsqu’ils étaient disponibles.
La raison pour se débarrasser de la récupération de place: cela n’a jamais fonctionné de manière fiable dans un environnement de style C où les pointeurs pourraient «s’échapper» vers des zones non accessibles au récupérateur de place. Apple croyait fermement que le comptage des références est plus rapide. (Vous pouvez faire ici des déclarations sur la vitesse relative, mais personne n’a réussi à convaincre Apple). Et finalement, personne n’a utilisé le ramassage des ordures.
La première chose que tout développeur MacOS X ou iOS apprend, c'est comment gérer les cycles de référence. Ce n'est donc pas un problème pour un vrai développeur.
la source
Le plus gros inconvénient de la récupération de place en C ++ est qu’il est impossible d’obtenir une réponse exacte:
En C ++, les pointeurs ne résident pas dans leur propre communauté murée, ils sont mélangés à d'autres données. En tant que tel, vous ne pouvez pas distinguer un pointeur d'autres données dont le motif binaire peut être interprété comme un pointeur valide.
Conséquence: tout le ramasse-miettes C ++ lâchera des objets à collecter.
En C ++, vous pouvez faire de l'arithmétique de pointeur pour dériver des pointeurs. En tant que tel, si vous ne trouvez pas de pointeur sur le début d'un bloc, cela ne signifie pas que ce bloc ne peut pas être référencé.
Conséquence: tout ramasse-miettes C ++ doit prendre en compte ces ajustements, en considérant toute séquence de bits qui pointe n'importe où dans un bloc, y compris juste après sa fin, comme un pointeur valide référençant le bloc.
Remarque: Aucun récupérateur de mémoire C ++ ne peut gérer du code avec des astuces comme celles-ci:
Certes, cela invoque un comportement indéfini. Mais certains codes existants sont plus intelligents que bons, et ils peuvent déclencher une désallocation préliminaire par un ramasse-miettes.
la source