8
votes

Quel est un bon moyen de penser aux références C ++?

Je suis en train de programmer C, principalement dans un environnement intégré, depuis des années et j'ai un modèle mental parfaitement bon de pointeurs - je n'ai pas à penser explicitement à la manière de les utiliser, je suis 100% à l'aise avec le pointeur arithmétique , tableaux de pointeurs, pointeurs-à-pointeurs, etc.

J'ai écrit très peu de C ++ et je n'ai vraiment pas de bonne façon de penser aux références. J'ai été informé dans le passé de "penser à eux comme des pointeurs qui ne peuvent pas être nuls" mais Cette question montre que c'est loin de toute l'histoire.

Donc, pour des programmeurs plus expérimentés C ++ - Comment pensez-vous des références? Pensez-vous à eux comme une sorte de pointeur spécial, ou comme leur propre chose entièrement? Qu'est-ce qu'un bon moyen pour un programmeur C d'obtenir la tête autour du concept?


0 commentaires

12 Réponses :


0
votes

Je pense à cela comme un conteneur de pointeur.


0 commentaires

2
votes

Que diriez-vous de "les pointeurs qui ne peuvent pas être nuls et ne peuvent pas être changés après l'initialisation". En outre, ils n'ont aucune taille par elles-mêmes (car ils n'ont aucune identité d'eux-mêmes).


6 commentaires

Ils ont une taille, "sous le capot", ils ne sont qu'un pointeur (avec certaines restrictions qui leur sont placées), ils sont donc de 4 octets de taille.


Correction, ils ont la taille d'un pointeur (trop utilisé pour travailler sur des systèmes 32 bits: p)


Ongle: pas nécessairement. Par exemple "int A = 1; int & b = a;" Résultats probablement dans aucun stockage utilisé sur la pile pour b. Le code sera simplement compilé de telle sorte que toutes les utilisations de l'accès au même registre ou la même fente de pile que les utilisations de A. Dans cette situation, B n'est pas "juste un pointeur sous le capot", c'est un alias purs. Bien sûr, le comportement défini est le même, mais il est mis en œuvre. Donc, c'est bien d'imaginer que c'est un pointeur, mais ce n'est peut-être pas un.


Ne peut pas être nul? Qu'en est-il de: int * p = null; int & ref = * p; int m_int = ref; // boom!


Plutôt que de "ne peut pas être nul", j'aurais probablement dû dire "peut être supposé être nulle". Bien qu'il soit certainement possible de créer une référence à NULL, il n'est pas techniquement juridique C ++ et une fonction adoptée un argument de référence peut raisonnablement supposer que c'est pas null.


@Greghewgill Ne supposez jamais que les arguments sont valides C ++, c'est horriblement insécurisé.



12
votes

Je suis habitué à penser aux références comme alias pour objet principal.

Modifier (en raison de la demande dans les commentaires):

J'avais l'habitude de réfléchir à la référence en tant que type d'aliasing, car il se comporte exactement de la même manière que la variable d'origine sans qu'il soit nécessaire de faire une manipulation supplémentaire afin d'affecter la variable référencée.


8 commentaires

J'allais dire des noms différents pour le même objet. +1 pour être plus rapide ;-).


Bien, mais pourquoi utiliser un "alias" lorsqu'un pointeur fonctionnera, quels sont les inconvénients de l'utilisation de références? Devraient-ils être utilisés uniquement pour des raisons spécifiques, et si oui pourquoi? Toutes mes excuses mais votre réponse est "Information Lite"


Parce que vous ne pouvez pas modifier l'adresse d'un point de référence à. Aussi, il agit comme une variable régulière.


@BinyWorrier: En utilisant une référence, vous supprimez également la possibilité que la référence "ne soit définie".


@Richard: Merci, mais personnellement, je sais quelles références sont, et je sais quand / où ils devraient être utilisés. J'essayais d'avoir l'affiche pour élucider leurs pensées au-delà des "références comme un alias pour l'objet principal", un pointeur peut également être vu "comme un alias pour l'objet principal". Personnellement, je ne trouve pas cette réponse utile et je suis surpris que la communauté en général pense que c'est la meilleure réponse. Va juste pour montrer. . .


@Binary La réponse à cette question ne pourrait être très utile, puisque la question demande «Quel bon moyen pour moi de penser à la référence». Donc, comme pour moi, la référence est un moyen de renommer / alias la même variable, car une fois que toute manipulation affectera l'original exactement de la même manière sans avoir besoin de la désirer ou faire une manipulation supplémentaire comme dans le cas des pointeurs que vous avez mentionnés.


@Artem: Je vois ton point, je fais vraiment. . . C'est juste qu'il y a tellement plus d'autres que cela peut être dit sur le sujet. Hower, qui suis-je à discuter avec la communauté :)


@Binary: Je suis content que vous comprenez mon point et que je n'ose même pas discuter avec le fait qu'il y ait beaucoup plus de choses à dire dans le contexte. Mais toujours que ma façon de penser aux références et j'ai posté une certaine élaboration, pourquoi je me suis habitué à penser comme ça.



2
votes

Je pense à la référence comme étant l'objet qu'il fait référence. Vous accédez à l'objet en utilisant. Symantecs (par opposition à ->), renouvelant cette idée pour moi.


0 commentaires

2
votes

Je pense que votre modèle mental des pointeurs, puis une liste de tous les cas de bord que vous avez rencontrés est le meilleur moyen.

Ceux qui ne reçoivent pas les indications vont au tarif de bien pire.

Incidemment, ils peuvent être nuls ou aucun autre emplacement de mémoire non accessible (il faut des efforts): xxx


9 commentaires

C'est encore plus facile que celui-là: char et mauvais = * (char *) null;


Ce n'est toujours pas légal pour une référence à être nulle, cependant.


"Legal" comme mal formé, mais si vous faites une API qui est toujours sensible à l'attaque, ou juste robuste, vous ne pouvez pas supposer que les paramètres passés par référence ne peuvent pas être nuls - vous avez vraiment un chèque


haha, m'a battu. 2 autres façons de définir la référence de la référence NULL sont: Char & Test = (Char ) 0; et union {char * ptr; Char & Ref; } valeur; valeur.ptr = null; C'est un code de rupture amusant!


La proportion d'un pointeur NULL est censé vous donner un comportement indéfini.


@Indeera: En acte, vous ne pouvez pas vraiment vérifier «null» comme tel, car c'est juste une valeur éventuellement illégale. Toute adresse mémoire non mappée sur la RAM accessible entraînera un crash du programme.


@ onebyone.livejournal.com: Yeap, Windows a le window isbadwriteptr blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx - Qu'est-ce que le général Unix a-t-il, et c'est mieux?


Yeap je l'ai fait en faisant des serveurs Symbian Symbian Symbian. Seulement envoyer une bosse sur des interfaces externes et avoir un validate_bytes (Démarrer, LEN) (qui n'a pas validé la page) serait bon


Le message qui passe en Symbian donnerait des erreurs au lieu de casser un serveur si le client a essayé d'envoyer de mauvais pointeurs. Tous les messages étaient jusqu'à quatre (ou étaient-ils cinq) intenses ou cordes; Le noyau a fait la copie au nom du serveur; assez robuste pour les clients, mais met des serveurs dans une position d'autorité (un serveur peut paniquer n'importe quel client, etc.)



6
votes

Je ne suis pas tout aussi friand de la vue "toujours valide", car les références peuvent deviennent invalides, par exemple xxx

donc, je pense aux références Comme non-rabondable (c'est-à-dire que vous ne pouvez pas modifier la cible d'une référence une fois que elle est initialisée) des pointeurs avec sucre syntaxique pour m'aider à vous débarrasser de la syntaxe "->" Pointeur.


7 commentaires

La réponse commune à ceci est qu'il faut invoquer un comportement non défini pour invalider une référence tandis qu'un pointeur peut être invalidé, tout à fait. Cependant, je vois ton point. En pratique, cela fait très peu de différence.


Je suis d'accord. J'ai tendance à Utilisez-les comme des poignées toujours valides: dans une portée locale, ou en tant qu'arguments de fonction.


@sbi: est le comportement ci-dessus vraiment non défini? Je peux voir que ce n'est pas très intelligent, mais non défini? Merci.


@KIM: 8.3.2 / 4 dit "Une référence doit être initialisée pour désigner un objet ou une fonction valide." Mais je conviens que cela ne semble que couvre l'initialisation. J'ai peut-être tort. (Je suis probablement le contraire d'un avocat de langue.)


Merci les gars! Je suppose que dès que nous essayons d'utiliser Réf, ce sera un comportement indéfini, cependant. Pas sûr si cela est spécifique: ed, cependant.


@xtofl: Je ne voulais pas vous choisir spécifiquement - la raison pour laquelle je l'ai apportée est parce que j'ai trébuché sur de nombreuses constructions de ce type de code réel, où les gens ont été confondus sur le pouvoir réel des références et les utilisées comme pilule magique pour résoudre des problèmes de vie.


Je dirais que vous utilisez également des références pour pointer vers des variables sur la pile, pas le tas.



5
votes

En général, vous ne pensez tout simplement pas aux références. Vous utilisez des références dans chaque fonction, sauf si vous avez un besoin spécifique d'appeler par la valeur ou la magie du pointeur.

Les références sont essentiellement des pointeurs qui soulignent toujours la même chose. Une référence n'a pas besoin d'être déréférencée et peut être accessible en tant que variable normale. C'est à peu près tout ce qu'il y en a. Vous utilisez des pointeurs lorsque vous devez faire du pointeur arithmétique ou changer ce que le pointeur pointe vers et références pour à peu près tout le reste.


3 commentaires

À l'exception des petits types, où la copie de l'adresse de l'objet renvoyé est plus chère que la copie de l'objet. Les petits types sont sur le point de dire: tout est inférieur à 64 bits.


Oui. Je ne préconiserais pas utiliser des références pour des types primitifs dans les fonctions. Mais là encore, ce n'est pas un cas où vous utiliseriez habituellement un pointeur non plus, et si vous le faites, vous devez savoir ce que vous faites.


En fait, si l'on doit être pédant, il y a quelques cas où les types de passage supérieurs à 64 bits sont importants. Par exemple, beaucoup de matériel spécialisé aujourd'hui ont des registres très larges (128 bits et plus non rares). À titre d'exemple, sur les consoles de jeux, vous transmettez habituellement vos vecteurs de valeur pour vous assurer qu'ils restent dans le registre, puis utilisent les instructions de SIMD pour fonctionner sur ces registres larges.



1
votes

Un moyen de penser à eux est d'importer un autre nom pour un objet d'une portée éventuellement différente.

Par exemple: obj o; Obj & r = o; Il y a vraiment peu de différence entre la sémantique de O et R.

Le majeur semble que le compilateur regarde la portée de O pour avoir appelé le destructeur.


0 commentaires

9
votes

Pour moi, lorsque je vois un pointeur dans le code (comme une variable locale dans une fonction ou un membre d'une classe), je dois penser à

  1. est le pointeur null ou est-il valide
  2. qui a créé l'objet il pointe de (c'est-à-dire moi ?, l'ai-je déjà fait?)
  3. qui est responsable de la suppression de la Objet
  4. Est-ce qu'il pointe toujours de la même chose Objet

    Je n'ai pas à penser à aucune de ces choses si c'est une référence, c'est le problème de quelqu'un d'autre (c'est-à-dire penser à une référence comme un champ SEP pour un pointeur)

    P.s. Oui, c'est probablement toujours mon problème, juste pas maintenant


0 commentaires

0
votes

d'un POV syntaxique, une référence est un alias pour un objet existant. D'un POV sémantique, une référence se comporte comme un pointeur avec quelques problèmes (invalidation, propriété, etc.) retirés et une syntaxe ressemblant à un objet ajouté. D'un pov pratique, préférez les références à moins que vous n'ayez la nécessité de dire «aucun objet». (La propriété des ressources n'est pas une raison de préférer les pointeurs, car cela devrait être effectué à l'aide de pointeurs intelligents.)

mise à jour : Voici une différence supplémentaire entre les références et les pointeurs qui J'ai oublié: un objet temporaire (une rvalue) liée à une référence Const aura sa durée de vie étendue à la durée de vie de la référence: xxx

ici, le retour temporaire Par la fonction est liée à résultat et ne cessera pas d'exister à la fin de l'expression, mais existera jusqu'à ce que résultat meurt. C'est bien, car en l'absence de références de rvalue et de surcharge basées sur eux (comme dans C ++ 11), cela vous permet de vous débarrasser d'une copie inutile dans l'exemple ci-dessus.

C'est un règle introduite surtout pour les références de const et il n'y a aucun moyen de y parvenir avec des pointeurs.


0 commentaires

3
votes

Les références sont le pointeur-Consts avec une syntaxe différente. c'est à dire. le référence T & est à peu près T * const Comme dans, le pointeur ne peut pas être changé. Le contenu des deux est identique - une adresse mémoire d'un t - et non plus non plus.

Puis, à part cela, la seule différence est la syntaxe :. Pour les références et -> et * pour le pointeur.

C'est ce que c'est vraiment - les références sont des pointeurs, juste avec une syntaxe différente (et ils constituent Const).


0 commentaires

0
votes

Si vous utilisez Linux, vous pouvez penser à des références comme des liens et des pointeurs durs que des liens symboliques (liens symboliques). Le lien dur n'est qu'un autre nom pour un fichier. Le fichier est "supprimé" lorsque tous les liens durs de ce fichier sont supprimés.

même à propos des références. Il suffit de substituer "lien dur" avec "référence" et "fichier" avec "valeur" (ou probablement "emplacement de mémoire"?).

Une variable est détruite lorsque toutes les références sont hors de portée.

Vous ne pouvez pas créer de lien dur avec un fichier inexistant. Similaire, il n'est pas possible de créer une référence à rien.

Cependant, vous pouvez créer un lien symbolique vers un fichier inexistant. Beaucoup comme un pointeur ininitialisé. En réalité des pointeurs non initialisés, font de certains endroits aléatoires (corrigez-moi si je me trompe). Mais ce que je veux dire, c'est que vous n'êtes pas censé les utiliser :)


0 commentaires