web-dev-qa-db-fra.com

Je n'utilise jamais de pointeurs dans mon code C ++. Est-ce que je coding c ++ mal?

Cette question peut sembler étrange pour vous, mais j'apprends C++ tout seul par moi-même. Je n'ai personne que je pourrais demander du mentorat et je serais très heureux de recevoir des conseils.

J'ai récemment commencé à programmer en C++ (environ 3 à 4 mois intensifs avec environ 6 à 8 heures). Mon arrière-plan est Java et j'ai fait des projets plus gros dans Java avec plus de 10k LOC (qui est destiné à un étudiant universitaire comme moi grand).

J'ai utilisé C++ principalement pour la mise en œuvre et la visualisation des algorithmes, mais je vise également de plus grands projets de logiciels. Les seules bibliothèques que j'ai utilisées sont pour Catch2, Opencv et un peu de boost. L'étrange chose à propos de mon style de programmation est que je n'ai jamais utilisé les indicateurs de mon voyage; Ce n'est pas comme si je ne sais pas comment utiliser des pointeurs, mais je n'ai jamais trouvé un moment où je pense qu'un pointeur serait utile. Quand je dois stocker des données primitives, je préfère std::vector over tableau. Lorsque j'ai besoin d'utiliser un objet d'une classe, je préfère créer l'objet sur la pile et le transmettre par référence; Pas de nouveau/Supprimer, pas de pointeurs intelligents.

La raison pour laquelle je pose cette question (étrange), c'est que je me manque comme une grande zone de programmation C++. Pourriez-vous partager avec moi votre expérience et peut-être me donner des conseils?

57
Long Nguyen

C'est peut-être une bonne idée d'éviter les pointeurs, en utilisant des copies d'objets et des références chaque fois que possible, comme vous le faites. Poursuivre cette pratique.

Cependant, il y a un certain nombre de choses qui peuvent aller très tort, si vous n'êtes pas très prudent:

  • Objets "sur la pile" (la terminologie C++ de nos jours pour cela classe de stockage est "auto") doit rester valide lorsque vous utilisez leur référence. Cela fonctionne généralement bien si vous transmettez une référence à une fonction. Mais retourner une référence est condamné à l'échec: l'objet sera détruit immédiatement après return et utiliser la référence est alors ub. Le même type de problèmes se produisent lorsque vous souhaitez injecter une référence dans un objet: c'est une bombe de coche.
  • Vous ne pouvez pas utiliser le polymorphisme avec des conteneurs. Donc, si vous n'utilisez jamais de pointeurs, mais avez vector de classes avec des membres de la fonction virtual, votre code pourrait ne pas fonctionner comme vous le pensez à cause de Tranchement . Celles-ci sont des bugs extrêmement désagréables et sont une erreur commune lorsque de nouveau à C++ avec un arrière-plan Java.

Il existe également des motifs de conception très courants OO qui ne sont pas possibles sans points de pointe, tels que le modèle méthode d'usine .

Éviter les pointeurs ne devraient pas être une fin en soi. Si vous travaillez avec la visualisation, je suppose que le polymorphisme peut être votre ami. Et ici les pointeurs peuvent déverrouiller la situation. La bonne nouvelle est que les pointeurs intelligents de nos jours puissent gérer la mémoire en toute sécurité pour vous.

Alors oui, votre pratique peut très bien fonctionner. Mais cela pourrait contenir des bugs inaperçus. Et tôt ou tard, vous manquerez des fonctionnalités très utiles.

56
Christophe

Vous utilisez "Référence indirecte dans le sens général" dans votre programmation C++. Il n'y a rien de mal à ça. Il est presque aussi puissant que la programmation avec des pointeurs et une référence (dans la langue C++).


Bien que, il y a un piège technique dans la manière dont vous utilisez vector.

Pour la sécurité logicielle, je vais décrire ce piège technique en premier. Il est plus important de connaître ce piège que de répondre à votre principale question.

Lorsque des objets sont placés dans un vecteur, le vecteur contient une copie de l'objet. Ces objets ont des adresses, à partir duquel vous pouvez créer des références (dans le sens C++). Ces adresses sont stables jusqu'à la prochaine fois que vous causerez le vecteur de réaffecter (en croissance, de compensation) ou de modifier les éléments (par exemple, supprimez un élément au milieu). Si vous continuez à utiliser les adresses qui ne sont plus valides (appelées Invalidation du pointeur), il peut déclencher un "comportement non défini" (UB). Une fois que l'UB arrive, tout peut suivre - l'exactitude de l'opération ultérieure n'est plus une garantie.


Afin de permettre l'attribution d'éléments individuels dans une collection C++ (ajoutée) et de traçalisé (supprimé), ces éléments devront être attribués dans le tas.

Ici, le "tas" désigne le tas réel, c'est-à-dire le système d'allocation de mémoire dynamique en C++. Le système d'allocation de mémoire dynamique permet d'être demandés et abandonnés individuels.


Votre style de programmation, qui vous permet de pré-allouer votre vectors haut devant, est comparable au style d'allocation de mémoire statique (sur un niveau sémantique).

Le style de répartition de la mémoire statique était la norme il y a plusieurs décennies et est toujours mandatée pour certains systèmes essentiels de sécurité, tels que les systèmes de contrôle électronique des véhicules pour les parties de la traduction et des fluidiques, certains systèmes avioniques de l'aéronef et des systèmes d'armes. Cependant, il est moins puissant que le style d'allocation de mémoire dynamique, car le style d'allocation de mémoire statique met une limite sur ce qui peut être mis en œuvre. Cela dit, pour des applications avec une portée non extensible non extensible (par exemple contrôlant un aspect spécifique d'un système mécanique, et rien d'autre, ne pas avoir à interfacer avec quoi que ce soit d'autre), il est possible de rester avec un style de répartition statique de mémoire.


Certains des exemples que vous avez mentionnés sont des exemples de "idiome corporelle de poignée" (lien) .

En C++, une classe de poignée permet à ses utilisateurs d'utiliser normalement la sémantique C++ Copy pour obtenir quelque chose de similaire à des pointeurs et de références C++.

Opencv's Mat classe est une classe "poignée". Pour illustrer cela, considérez cet extrait de code:

cv::Mat matOne(cv::Size(640, 480), CV_8UC3);
cv::Mat matTwo = matOne;

Après ces deux lignes de code, matTwo _ et matOne _ Les deux font référence au même objet (la matrice ou l'image). Cela est dû à la conception et aux détails de la mise en œuvre du cv::Mat classer.

Si vous souhaitez mettre en œuvre une classe qui se comporte de la même manière, vous devrez en savoir plus sur les pointeurs et les références C++, c'est-à-dire les connaissances que vous êtes curieux et vous semblez manquer.


Un peu sur la "linguistique" du mot "sémantique".

En C++, les phrases "copier la sémantique" et "la sémantique de référence" font tous deux référence à des aspects de la syntaxe C++ et de son utilisation. Ainsi, l'utilisation du mot "sémantique" est un abus de malaise, lorsqu'il est jugé par la norme de la langue anglaise.

19
rwong

Entre références et STD :: VEC, de nombreux cas où un programme C utiliserait des pointeurs n'utilise pas de pointeurs en C++. Et à Swift, vous devez creuser très profondément dans la bibliothèque standard pour trouver quelque chose qui s'appelle "pointeur".

L'utilisation du tout est un peu inhabituelle, mais assez possible.

6
gnasher729

En plus de la réponse de Christoph , il y a aussi quelques structures de données que vous ne pouvez pas facilement construire et utiliser sans pointeurs:

  • Arbres d'objets

  • Drapeau

  • Listes liées (chaînes de délégation, par exemple)

Bien sûr, vous pouvez travailler en utilisant des pointeurs pour ces choses. Les arbres et les Dags peuvent être construits à partir d'objecs stockés dans un std::vector<>, les lier par leurs indices respectifs à ce sujet std::vector<>, et pour les listes liées, vous pouvez simplement recourir à std::list<>. Les utilisations ultérieures des pointeurs sous le capot, l'ancien remplace le pointeur explicite avec un int qui sert exactement la même fonction et souffre des mêmes problèmes. Sous le capot, les pointeurs sont des valeurs entières, mais rien qui sont utilisés pour traiter des octets en mémoire, une telle utilisation d'indices est en fait aussi mauvais que l'utilisation de void pointeurs. Mieux vaut utiliser des pointeurs dactylographiés, ceux intelligents lorsque vous devez gérer la propriété.

Combien vous avez besoin de telles structures de données, et donc des pointeurs, dépend beaucoup de votre cas d'utilisation. Alors, peut-être que vous aviez tout simplement jamais un problème qui a appelé à une véritable chaîne de délégation. Par exemple, lorsque vous exécutez une simulation physique, vous voulez simplement des matrices 3D sur lesquelles vous pouvez calculer le prochain TIMESTEP; Cela n'implique aucun DAG de fantaisie. Toutefois, si vous programmiez un logiciel de contrôle de version distribué, vous devez absolument travailler avec les pointeurs, car les commits forment un DAG, chacun référençant une ou plusieurs parrax commettre les branches de référence comme les commissions, etc. PP.

En fin de compte, les pointeurs ne sont qu'un outil. Un outil à des fins spécifiques. Parfois, un problème appelle à cet outil, et parfois cela appelle une autre. Il est tout aussi tort de ne pas utiliser des pointeurs dans le premier cas, car il est faux de les utiliser dans la suite.

Je parie que vous utilisez this pointeurs implicitement! ;-)

Sérieusement, C++ est une grande langue et la plupart des programmes n'utilisent pas tout cela. Sean Parent a donné une conversation quelque peu célèbre suggérant que les programmeurs C++ devraient jamais écrire des boucles brutes et devrait plutôt apprendre à utiliser les algorithmes fournis par les bibliothèques standard. Pour beaucoup, cela peut sembler plus extrême que d'utiliser des pointeurs.

C++ a des outils différents pour différents emplois. Vous devez utiliser des outils tels que Modèle Meta Programming, héritage, Lambdas uniquement lorsque vous en avez besoin, pas seulement parce que vous le pouvez. N'utilisez pas de pointeur lorsque vous n'avez pas besoin de.

Il y a beaucoup de fois que les gens utilisent des pointeurs quand ils n'ont pas besoin de, en particulier des pointeurs métalliques nus. Laisser les bibliothèques comme <vector> Gérer les pointeurs bruts et la répartition dynamique est tout simplement intelligent. Notez que, dans un sens, vous utilisez toujours des pointeurs, vous comptez simplement sur le code écrit par d'autres programmeurs C++ pour gérer les détails de bas niveau afin que vous puissiez vous concentrer sur un niveau d'abstraction plus élevé.

Les pointeurs sont naturels et appropriés pour certains types de solutions, comme la liaison des nœuds dans un graphique. Dans certaines solutions, ils sont essentiels, tels que l'allocation de manière dynamique des objets polymorphes. Dans la plupart des cas, vous pouvez toujours éviter les détails de bas niveau en s'appuyant sur des pointeurs intelligents, qui, comme std::vector ne sont que des classes qui gèrent des détails sur votre nom.

À un moment donné, vous devrez peut-être écrire votre propre classe d'abstraction - un conteneur spécialisé ou un nouveau type de pointeur intelligent. Même Sean Parent a reconnu que vous pourriez avoir besoin d'une boucle brute pour mettre en œuvre un nouvel algorithme, mais que vous devriez l'encapsuler.

Lorsque vous arrivez au point que vous devez créer une abstraction dépendante du pointeur (ou lorsque vous devez comprendre le code de quelqu'un d'autre ou l'interface avec une bibliothèque qui souhaite échanger des pointeurs), vous voudrez avoir eu assez d'expérience avec des pointeurs qui Vous êtes équipé pour la tâche.

Les pointeurs (parfois sous la prévision d'une adresse, de la poignée ou de référence) sont des concepts assez fondamentaux pour de nombreuses langues de programmation. Ne manquez pas l'occasion de les apprendre en contortant votre code pour les éviter quand ils sont l'outil approprié.

2
Adrian McCarthy

Les pointeurs sont un mécanisme de programmation de bas niveau. Le "point entier" de la programmationcitation requise est de créer des mécanismes de niveau de haut niveau sur des mécanismes de niveau inférieur, où, généralement, un moyen de niveau inférieur plus puissant mais plus difficile à utiliser et un moyen de niveau supérieur moins puissant mais plus facile à utiliser.

Donc, peut-être généralement, les pointeurs ont plus de sens dans le code de niveau inférieur et moins de sens dans le code de niveau supérieur.

0
Dave Cousineau

Non, tu ne fais rien de mal. Pointeurs (non références, non-références Compteurs, et non des wrappers uniques) sont un concept utilisé pour modéliser l'emplacement d'une valeur dans un espace d'adresses mémoire donné. Donc:

  • Si le domaine de votre programme est étroitement lié à la manière dont les choses sont définies en mémoire (par exemple, vous écrivez un allocator de mémoire ou quelque chose du genre), les pointeurs se levèrent tôt ou tard.
  • Sinon (et c'est le scénario le plus probable) Il est parfaitement normal que les indicateurs ne se produisent pas. [.____]
    • Sauf si vous utilisez une langue qui n'a pas d'autre moyen d'exprimer une référence, telle que C. Dans ce cas, les pointeurs se présenteront quel que soit votre domaine. Mais c'est pourquoi les gens n'utilisent généralement pas C pour les programmes qui ne fonctionnent pas dans ce domaine.

Définir faux. Si votre code fonctionne correctement, alors dans un sens simpliste, ce n'est pas faux.

Cependant, en n'utilisant pas du tout des pointeurs, vous créez un certain nombre de problèmes potentiels pour vous-même.

1) Si vous n'utilisez jamais de pointeurs, vous ne les comprendrez jamais bien. Ensuite, que ferez-vous lorsque vous devez comprendre ou interfacer avec le code écrit par une personne qui utilise des pointeurs?

2) Les pointeurs sont (IMHO - Les opinions individuelles peuvent varier) souvent plus faciles à comprendre et à coder, que les alternatives que vous utilisez.

3) Les pointeurs peuvent souvent (bien sûr pas toujours) être plus efficaces que certaines de ces alternatives, de sorte que votre objectif est de rédiger du code critique de la performance, vous vous êtes handicapé.

0
jamesqf