J'ai lu quelque part que lors de l'utilisation de C++, il est recommandé de ne pas utiliser de pointeurs. Pourquoi les pointeurs sont-ils une si mauvaise idée lorsque vous utilisez C++. Pour les programmeurs C habitués à utiliser des pointeurs, quelle est la meilleure alternative et approche en C++?
Je pense qu'ils signifient que vous devriez utiliser pointeurs intelligents au lieu de pointeurs réguliers.
En informatique, un pointeur intelligent est un type de données abstrait qui simule un pointeur tout en fournissant des fonctionnalités supplémentaires, telles que la collecte automatique des déchets ou la vérification des limites. Ces fonctionnalités supplémentaires sont destinées à réduire les bogues causés par l'utilisation abusive des pointeurs tout en conservant leur efficacité. Les pointeurs intelligents suivent généralement les objets vers lesquels ils pointent à des fins de gestion de la mémoire.
L'utilisation abusive des pointeurs est une source majeure de bogues: l'allocation constante, la désallocation et le référencement qui doivent être effectués par un programme écrit à l'aide de pointeurs introduit le risque de fuites de mémoire. Les pointeurs intelligents tentent d'empêcher les fuites de mémoire en rendant la désallocation des ressources automatique: lorsque le pointeur (ou le dernier d'une série de pointeurs) vers un objet est détruit, par exemple parce qu'il sort du domaine, l'objet pointé est également détruit.
En C++, l'accent serait mis sur la récupération de place et la prévention des fuites de mémoire (pour n'en nommer que deux). Les pointeurs sont une partie fondamentale du langage, donc ne pas les utiliser est à peu près impossible, sauf dans les programmes les plus classiques.
Puisque je suis celui qui a publié la polémique "n'utilisez pas de pointeurs f * cking" je pense que je devrais commenter ici.
Tout d'abord, en tant que polémique, elle représente évidemment un point de vue extrême. Il y a sont utilisations définitivement légitimes de pointeurs (bruts). Mais moi (et de nombreux programmeurs C++ professionnels) maintiens que ces cas sont extrêmement rares. Mais ce que nous voulons vraiment dire, c'est ce qui suit:
Les pointeurs bruts ne doivent en aucun cas posséder de mémoire.
Ici, "propre mémoire" signifie essentiellement qu'à un certain point delete
est appelé sur ce pointeur (mais c'est plus général que cela). Cette instruction peut être considérée comme un absolu en toute sécurité. L'exception seulement concerne l'implémentation de votre propre pointeur intelligent (ou autre stratégie de gestion de la mémoire). Et même là, vous devriez normalement encore utiliser un pointeur intelligent à bas niveau.
La raison en est assez simple: les pointeurs bruts qui possèdent leur propre mémoire introduisent une source d'erreur. Et ces erreurs sont prolifiques dans les logiciels existants: fuites de mémoire et double suppression - les deux étant une conséquence directe d'une propriété des ressources peu claire (mais allant dans la direction opposée).
Ce problème peut être entièrement éliminé, pratiquement gratuitement, en utilisant simplement des pointeurs intelligents au lieu de pointeurs bruts (mise en garde: cela nécessite toujours de la réflexion, bien sûr; des pointeurs partagés peut conduire à des cycles et donc une fois à nouveau à des fuites de mémoire - mais cela est facilement évitable).
La plupart des utilisations de pointeurs en C++ ne sont pas nécessaires.
Contrairement à d'autres langages, C++ prend en charge très fortement la sémantique des valeurs et n'a tout simplement pas besoin de l'indirection de pointeurs. Cela n'a pas été immédiatement réalisé - historiquement, C++ a été inventé pour faciliter l'orientation facile des objets en C, et reposait fortement sur la construction de graphiques d'objets qui étaient connectés par des pointeurs. Mais en C++ moderne, ce paradigme est rarement le meilleur choix, et les idiomes C++ modernes n'ont souvent pas besoin de pointeurs . Ils fonctionnent sur valeurs plutôt que sur des pointeurs.
Malheureusement, ce message n'a toujours pas fait son chemin dans de grandes parties de la communauté d'utilisateurs C++. En conséquence, la plupart du code C++ qui est écrit est toujours jonché de pointeurs superflus qui rendent le code complexe, lent et défectueux/peu fiable.
Pour quelqu'un qui connaît le C++ moderne, il est clair que vous avez très rarement besoin de pointeurs any (intelligents ou bruts; sauf lorsque vous les utilisez comme itérateurs). Le code résultant est plus court, moins complexe, plus lisible, souvent plus efficace et plus fiable.
Tout simplement parce qu'il existe des abstractions à votre disposition qui masquent les aspects les plus capricieux de l'utilisation des pointeurs, tels que l'accès à la mémoire brute et le nettoyage après vos allocations. Avec les pointeurs intelligents, les classes de conteneurs et les modèles de conception comme RAII, le besoin d'utiliser des pointeurs bruts est diminué. Cela dit, comme toute abstraction, vous devez comprendre comment elles fonctionnent réellement avant de les dépasser.
Relativement simplement, la mentalité C est "Vous avez un problème? Utilisez un pointeur". Vous pouvez le voir dans les chaînes C, les pointeurs de fonction, les pointeurs en tant qu'itérateurs, le pointeur vers le pointeur, le pointeur vide, même dans les premiers jours de C++ avec les pointeurs membres.
Mais en C++, vous pouvez utiliser des valeurs pour la plupart ou la totalité de ces tâches. Besoin d'une abstraction de fonction? std::function
. C'est une valeur qui est une fonction. std::string
? C'est une valeur, c'est une chaîne. Vous pouvez voir des approches similaires partout dans C++. Cela rend l'analyse du code beaucoup plus facile pour les humains et les compilateurs.
L'une des raisons est une application trop large des pointeurs. Ils peuvent être utilisés pour l'itération sur des conteneurs, pour éviter de copier de gros objets lors du passage à la fonction, la gestion non triviale du temps de vie, l'accès à des endroits aléatoires en mémoire, etc. Et une fois que vous les avez utilisés dans un but, d'autres fonctionnalités deviennent disponibles immédiatement indépendamment de l'intention.
La sélection d'un outil dans un but précis rend le code plus simple et l'intention plus visible - itérateurs pour les itérations, pointeurs intelligents pour la gestion du temps de vie, etc.
Outre les raisons déjà énumérées, il y en a une évidente: de meilleures optimisations. L'analyse d'aliasing est beaucoup trop compliquée en présence d'une arithmétique de pointeur, tandis que les références suggèrent un optimiseur, donc une analyse d'aliasing beaucoup plus approfondie est possible si seules des références sont utilisées.
Outre le risque de fuites de mémoire indiqué par @jmquigley, le pointeur et l'arithmétique du pointeur peuvent être considérés comme problématiques car les pointeurs peuvent pointer partout dans la mémoire provoquant des "bogues difficiles à trouver" et des "failles de sécurité".
C'est pourquoi ils ont été presque abandonnés en C # et Java.