valeur - Quelles sont les différences entre une variable de pointeur et une variable de référence en C++?




pointeur c++ (20)

Qu'est-ce qu'une référence C ++ ( pour les programmeurs C )

Une référence peut être considérée comme un pointeur constant (à ne pas confondre avec un pointeur sur une valeur constante!) Avec une indirection automatique, c'est-à-dire que le compilateur appliquera l'opérateur * à votre place.

Toutes les références doivent être initialisées avec une valeur non nulle, sinon la compilation échouera. Il n'est pas non plus possible d'obtenir l'adresse d'une référence. L'opérateur d'adresse renverra l'adresse de la valeur référencée. Il n'est pas non plus possible de faire de l'arithmétique sur des références.

Les programmeurs C peuvent ne pas aimer les références C ++ car cela ne sera plus évident en cas d'indirection ou si un argument est passé par valeur ou par pointeur sans examiner les signatures des fonctions.

Les programmeurs C ++ peuvent ne pas aimer utiliser les pointeurs car ils sont considérés comme peu sûrs - bien que les références ne soient pas vraiment plus sûres que les pointeurs constants, sauf dans les cas les plus triviaux - manquent de la commodité de l'indirection automatique et portent une connotation sémantique différente.

Considérez la déclaration suivante de la FAQ C ++ :

Même si une référence est souvent implémentée à l'aide d'une adresse dans le langage d'assemblage sous-jacent, ne considérez pas une référence comme un pointeur amusant sur un objet. Une référence est l'objet. Ce n'est pas un pointeur sur l'objet, ni une copie de l'objet. C'est l'objet.

Mais si une référence était vraiment l'objet, comment pourrait-il y avoir des références en suspens? Dans les langues non gérées, il est impossible pour les références d'être «plus sûres» que les pointeurs - il n'y a généralement pas de moyen de créer des alias fiables entre les limites de la portée!

Pourquoi je considère les références C ++ utiles

Venant d’un contexte C, les références C ++ peuvent sembler un concept quelque peu idiot, mais vous devriez quand même les utiliser à la place des pointeurs: l’indirection automatique est pratique, et les références deviennent particulièrement utiles lorsqu’il s’agit de RAII - mais pas pour des raisons de sécurité. avantage, mais plutôt parce qu’ils rendent l’écriture de code idiomatique moins compliquée.

RAII est l'un des concepts centraux du C ++, mais il interagit de manière non triviale avec la sémantique de la copie. Passer des objets par référence évite ces problèmes car aucune copie n'est impliquée. Si les références ne sont pas présentes dans la langue, vous devrez utiliser des pointeurs, qui sont plus encombrants à utiliser, enfreignant ainsi le principe de conception de langue selon lequel la solution de meilleure pratique devrait être plus facile que les alternatives.

https://code.i-harness.com

Je sais que les références sont du sucre syntaxique. Le code est donc plus facile à lire et à écrire.

Mais quelles sont les différences?

Résumé des réponses et des liens ci-dessous:

  1. Un pointeur peut être réaffecté un nombre quelconque de fois, tandis qu'une référence ne peut pas être réaffectée après la liaison.
  2. Les pointeurs peuvent pointer nulle part ( NULL ), alors qu'une référence fait toujours référence à un objet.
  3. Vous ne pouvez pas prendre l'adresse d'une référence comme vous pouvez le faire avec des pointeurs.
  4. Il n'y a pas d '"arithmétique de référence" (mais vous pouvez prendre l'adresse d'un objet pointé par une référence et faire de l'arithmétique de pointeur dessus comme dans &obj + 5 ).

Pour clarifier une idée fausse:

La norme C ++ veille à ne pas indiquer comment un compilateur peut implémenter des références, mais chaque compilateur C ++ implémente des références en tant que pointeurs. C'est-à-dire une déclaration telle que:

int &ri = i;

s'il n'est pas optimisé complètement , alloue la même quantité de mémoire qu'un pointeur et place l'adresse de i dans cette mémoire.

Ainsi, un pointeur et une référence utilisent la même quantité de mémoire.

En règle générale,

  • Utilisez des références dans les paramètres de fonction et les types de retour pour fournir des interfaces utiles et auto-documentées.
  • Utilisez des pointeurs pour implémenter des algorithmes et des structures de données.

Lecture intéressante:


Différence entre pointeur et référence

Un pointeur peut être initialisé à 0 et une référence non. En fait, une référence doit également faire référence à un objet, mais un pointeur peut être le pointeur nul:

int* p = 0;

Mais nous ne pouvons pas avoir int& p = 0;et aussiint& p=5 ; .

En fait, pour le faire correctement, nous devons d'abord avoir déclaré et défini un objet, puis nous pouvons faire référence à cet objet, de sorte que la bonne implémentation du code précédent sera:

Int x = 0;
Int y = 5;
Int& p = x;
Int& p1 = y;

Un autre point important est que nous pouvons faire la déclaration du pointeur sans initialisation, mais cela ne peut pas être fait en cas de référence qui doit toujours faire référence à une variable ou à un objet. Cependant, une telle utilisation d'un pointeur est risquée, donc nous vérifions généralement si le pointeur indique réellement quelque chose ou non. En cas de référence, aucune vérification de ce type n'est nécessaire car nous savons déjà que le référencement d'un objet lors de la déclaration est obligatoire.

Une autre différence est que le pointeur peut pointer sur un autre objet, bien que référence fasse toujours référence au même objet, prenons cet exemple:

Int a = 6, b = 5;
Int& rf = a;

Cout << rf << endl; // The result we will get is 6, because rf is referencing to the value of a.

rf = b;
cout << a << endl; // The result will be 5 because the value of b now will be stored into the address of a so the former value of a will be erased

Autre point: lorsque nous avons un modèle comme un modèle STL, un tel type de modèle de classe renvoie toujours une référence, et non un pointeur, pour faciliter la lecture ou l'attribution d'une nouvelle valeur à l'aide de l'opérateur []:

Std ::vector<int>v(10); // Initialize a vector with 10 elements
V[5] = 5; // Writing the value 5 into the 6 element of our vector, so if the returned type of operator [] was a pointer and not a reference we should write this *v[5]=5, by making a reference we overwrite the element by using the assignment "="

Contrairement aux idées reçues, il est possible d’avoir une référence NULL.

int * p = NULL;
int & r = *p;
r = 1;  // crash! (if you're lucky)

Certes, il est beaucoup plus difficile de faire avec une référence - mais si vous y parvenez, vous allez vous arracher les cheveux en essayant de le trouver. Les références ne sont pas intrinsèquement sûres en C ++!

Techniquement, il s'agit d'une référence non valide , pas d'une référence null. C ++ ne prend pas en charge les références nulles en tant que concept, contrairement à d'autres langages. Il existe également d'autres types de références non valides. Toute référence non valide soulève le spectre d' un comportement indéfini , comme le ferait un pointeur non valide.

L'erreur réelle réside dans le déréférencement du pointeur NULL, avant l'affectation à une référence. Mais je ne suis au courant d'aucun compilateur qui générera des erreurs à cette condition - l'erreur se propage à un point plus loin dans le code. C'est ce qui rend ce problème si insidieux. La plupart du temps, si vous déréférenciez un pointeur NULL, vous bloquez à cet endroit et le débogage n'est pas très long à comprendre.

Mon exemple ci-dessus est court et artificiel. Voici un exemple plus réaliste.

class MyClass
{
    ...
    virtual void DoSomething(int,int,int,int,int);
};

void Foo(const MyClass & bar)
{
    ...
    bar.DoSomething(i1,i2,i3,i4,i5);  // crash occurs here due to memory access violation - obvious why?
}

MyClass * GetInstance()
{
    if (somecondition)
        return NULL;
    ...
}

MyClass * p = GetInstance();
Foo(*p);

Je tiens à répéter que le seul moyen d'obtenir une référence null consiste à utiliser du code mal formé. Une fois que vous l'avez obtenue, vous obtenez un comportement indéfini. Il n’a jamais de sens de rechercher une référence nulle; Par exemple, vous pouvez essayer if(&bar==NULL)... mais le compilateur peut optimiser l’instruction hors existence! Une référence valide ne peut jamais être NULL. Par conséquent, du point de vue du compilateur, la comparaison est toujours fausse et il est libre d'éliminer la clause if tant que code mort. Il s'agit de l'essence du comportement indéfini.

La bonne façon d'éviter les ennuis est d'éviter de déréférencer un pointeur NULL pour créer une référence. Voici un moyen automatisé pour y parvenir.

template<typename T>
T& deref(T* p)
{
    if (p == NULL)
        throw std::invalid_argument(std::string("NULL reference"));
    return *p;
}

MyClass * p = GetInstance();
Foo(deref(p));

Pour un regard plus ancien sur ce problème d’une personne ayant de meilleures compétences en écriture, voir Références nulles de Jim Hyslop et Herb Sutter.

Pour un autre exemple des dangers liés à la déréférence d'un pointeur null, voir Exposer un comportement indéfini lors d'une tentative de transfert de code sur une autre plate-forme de Raymond Chen.


En réalité, une référence ne ressemble pas vraiment à un pointeur.

Un compilateur conserve des "références" à des variables, associant un nom à une adresse mémoire; c'est son travail de traduire n'importe quel nom de variable en une adresse mémoire lors de la compilation.

Lorsque vous créez une référence, vous indiquez uniquement au compilateur que vous attribuez un autre nom à la variable de pointeur. c'est pourquoi les références ne peuvent pas "pointer vers null", car une variable ne peut pas être, et ne pas être.

Les pointeurs sont des variables. ils contiennent l'adresse d'une autre variable ou peuvent être nuls. L'important est qu'un pointeur ait une valeur, alors qu'une référence ne contient qu'une variable à laquelle elle fait référence.

Maintenant quelques explications sur le code réel:

int a = 0;
int& b = a;

Ici, vous ne créez pas une autre variable qui pointe vers a ; vous ajoutez simplement un autre nom au contenu de la mémoire contenant la valeur de a . Cette mémoire a maintenant deux noms, a et b , et il est possible de s’adresser à l’un ou l’autre

void increment(int& n)
{
    n = n + 1;
}

int a;
increment(a);

Lors de l'appel d'une fonction, le compilateur génère généralement des espaces mémoire pour les arguments à copier. La signature de fonction définit les espaces à créer et donne le nom à utiliser pour ces espaces. La déclaration d'un paramètre en tant que référence indique simplement au compilateur d'utiliser l'espace mémoire de la variable d'entrée au lieu d'allouer un nouvel espace mémoire lors de l'appel de la méthode. Cela peut sembler étrange de dire que votre fonction manipulera directement une variable déclarée dans la portée de l'appel, mais souvenez-vous que lors de l'exécution du code compilé, il n'y a plus de portée; il y a simplement une mémoire plate et votre code de fonction peut manipuler toutes les variables.

Dans certains cas, votre compilateur pourrait ne pas être en mesure de connaître la référence lors de la compilation, comme lors de l'utilisation d'une variable externe. Ainsi, une référence peut ou non être implémentée en tant que pointeur dans le code sous-jacent. Mais dans les exemples que je vous ai donnés, cela ne sera probablement pas implémenté avec un pointeur.


Outre le sucre syntaxique, une référence est un pointeur const (et non un pointeur vers un const ). Vous devez établir à quoi il se réfère lorsque vous déclarez la variable de référence et vous ne pourrez pas le modifier ultérieurement.

Mise à jour: maintenant que j'y réfléchis un peu plus, il y a une différence importante.

La cible d'un pointeur const peut être remplacée en prenant son adresse et en utilisant une conversion constante.

La cible d'une référence ne peut en aucun cas être remplacée par UB.

Cela devrait permettre au compilateur d’optimiser davantage une référence.


Si vous voulez être vraiment pédant, il y a une chose que vous pouvez faire avec une référence que vous ne pouvez pas faire avec un pointeur: étendre la durée de vie d'un objet temporaire. En C ++, si vous liez une référence const à un objet temporaire, la durée de vie de cet objet devient la durée de vie de la référence.

std::string s1 = "123";
std::string s2 = "456";

std::string s3_copy = s1 + s2;
const std::string& s3_reference = s1 + s2;

Dans cet exemple, s3_copy copie l'objet temporaire résultant de la concaténation. Considérant que s3_reference devient essentiellement l'objet temporaire. C'est en réalité une référence à un objet temporaire qui a maintenant la même durée de vie que la référence.

Si vous essayez ceci sans le const compilation ne devrait pas aboutir. Vous ne pouvez pas lier une référence non-const à un objet temporaire, ni prendre son adresse pour cette question.


Vous avez oublié la partie la plus importante:

membre-access avec des pointeurs utilise ->
accès membre avec références utilise .

foo.bar est clairement supérieur à foo->bar de la même manière que vi est clairement supérieur à Emacs :-)


Au risque d’ajouter à la confusion, je voudrais ajouter quelques éléments; cela dépend certainement de la manière dont le compilateur implémente les références, mais dans le cas de gcc, l’idée qu’une référence ne peut pointer que sur une variable de la pile. n'est pas vraiment correct, prenons ceci par exemple:

#include <iostream>
int main(int argc, char** argv) {
    // Create a string on the heap
    std::string *str_ptr = new std::string("THIS IS A STRING");
    // Dereference the string on the heap, and assign it to the reference
    std::string &str_ref = *str_ptr;
    // Not even a compiler warning! At least with gcc
    // Now lets try to print it's value!
    std::cout << str_ref << std::endl;
    // It works! Now lets print and compare actual memory addresses
    std::cout << str_ptr << " : " << &str_ref << std::endl;
    // Exactly the same, now remember to free the memory on the heap
    delete str_ptr;
}

Ce qui sort ceci:

THIS IS A STRING
0xbb2070 : 0xbb2070

Si vous remarquez que même les adresses mémoire sont exactement les mêmes, ce qui signifie que la référence pointe avec succès vers une variable du tas! Maintenant, si vous voulez vraiment devenir bizarre, cela fonctionne aussi:

int main(int argc, char** argv) {
    // In the actual new declaration let immediately de-reference and assign it to the reference
    std::string &str_ref = *(new std::string("THIS IS A STRING"));
    // Once again, it works! (at least in gcc)
    std::cout << str_ref;
    // Once again it prints fine, however we have no pointer to the heap allocation, right? So how do we free the space we just ignorantly created?
    delete &str_ref;
    /*And, it works, because we are taking the memory address that the reference is
    storing, and deleting it, which is all a pointer is doing, just we have to specify
    the address with '&' whereas a pointer does that implicitly, this is sort of like
    calling delete &(*str_ptr); (which also compiles and runs fine).*/
}

Ce qui sort ceci:

THIS IS A STRING

Par conséquent, une référence est un pointeur sous le capot, ils sont tous deux en train de stocker une adresse mémoire, l'adresse indiquée étant sans importance, que pensez-vous qu'il se passerait si j'appelais std :: cout << str_ref; APRES avoir appelé delete & str_ref? Évidemment, cela compile bien, mais provoque une erreur de segmentation au moment de l’exécution, car elle ne pointe plus sur une variable valide; nous avons essentiellement une référence cassée qui existe toujours (jusqu’à ce qu’elle tombe hors de portée), mais qui est inutile.

En d’autres termes, une référence n’est rien d’autre qu’un pointeur dont la mécanique est abstraite, ce qui la rend plus sûre et plus facile à utiliser (pas de maths accidentel, pas de confusion entre '.' Et '->', etc.) n'essayez pas un non-sens comme mes exemples ci-dessus;)

Maintenant, quelle que soit la façon dont un compilateur traite les références, il aura toujours une sorte de pointeur sous le capot, car une référence doit faire référence à une variable spécifique à une adresse mémoire spécifique pour que celle-ci fonctionne comme prévu, il n’ya pas moyen de contourner le terme "référence").

La seule règle importante à retenir avec les références est qu'elles doivent être définies au moment de la déclaration (à l'exception d'une référence dans un en-tête, dans ce cas, elle doit être définie dans le constructeur, après que l'objet qui le contient soit il est trop tard pour le définir).

N'oubliez pas que mes exemples ci-dessus ne sont que des exemples démontrant ce qu'est une référence, vous ne voudriez jamais utiliser une référence de cette manière! Pour bien utiliser une référence, il y a déjà beaucoup de réponses sur ce qui a frappé le clou sur la tête


Il existe une différence non technique très importante entre les pointeurs et les références: un argument passé à une fonction par un pointeur est beaucoup plus visible qu'un argument passé à une fonction par une référence non const. Par exemple:

void fn1(std::string s);
void fn2(const std::string& s);
void fn3(std::string& s);
void fn4(std::string* s);

void bar() {
    std::string x;
    fn1(x);  // Cannot modify x
    fn2(x);  // Cannot modify x (without const_cast)
    fn3(x);  // CAN modify x!
    fn4(&x); // Can modify x (but is obvious about it)
}

De retour en C, un appel qui ressemble à fn(x)ne peut être passé que par valeur, il ne peut donc pas être modifié x; pour modifier un argument, vous devez passer un pointeur fn(&x). Donc, si un argument n'était pas précédé d'un, &vous saviez qu'il ne serait pas modifié. (L'inverse, &moyen modifié, n'était pas vrai car il fallait parfois passer de grandes structures en lecture seule par constpointeur.)

Certains avancent que c'est une fonctionnalité tellement utile lors de la lecture de code que les paramètres de pointeur devraient toujours être utilisés pour des paramètres modifiables plutôt que des non- constréférences, même si la fonction ne s'attend jamais à un nullptr. C'est-à-dire que ces personnes soutiennent que les signatures de fonction comme fn3()ci-dessus ne devraient pas être autorisées. Les directives de style de C ++ de Google en sont un exemple.


Il existe une différence sémantique qui peut sembler ésotérique si vous n'êtes pas habitué à étudier les langages informatiques de manière abstraite ou même académique.

Au plus haut niveau, l’idée des références est qu’elles sont des "alias" transparents. Votre ordinateur peut utiliser une adresse pour les faire fonctionner, mais vous n'êtes pas censé vous inquiéter à ce sujet: vous êtes censé les considérer comme "juste un autre nom" pour un objet existant et la syntaxe en tient compte. Ils sont plus stricts que les pointeurs de sorte que votre compilateur peut vous avertir de manière plus fiable lorsque vous êtes sur le point de créer une référence pendante que lorsque vous êtes sur le point de créer un pointeur suspendu.

Au-delà, il existe bien sûr des différences pratiques entre les pointeurs et les références. La syntaxe pour les utiliser est évidemment différente, et vous ne pouvez pas "repositionner" les références, avoir des références au néant ou des pointeurs vers des références.


J'utilise des références sauf si j'ai besoin de l'un ou l'autre de ces éléments:

  • Les pointeurs nuls peuvent être utilisés comme valeur sentinelle, souvent un moyen peu coûteux d'éviter la surcharge de fonctions ou l'utilisation d'un bool.

  • Vous pouvez faire de l'arithmétique sur un pointeur. Par exemple,p += offset;


Peu importe l'espace occupé, vous ne pouvez voir aucun effet secondaire (sans exécuter de code) de l'espace qu'il prendrait.

D'autre part, une différence majeure entre les références et les pointeurs réside dans le fait que les temporelles assignées aux références const vivent jusqu'à ce que la référence const sorte de la portée.

Par exemple:

class scope_test
{
public:
    ~scope_test() { printf("scope_test done!\n"); }
};

...

{
    const scope_test &test= scope_test();
    printf("in scope\n");
}

imprimera:

in scope
scope_test done!

C'est le mécanisme de langage qui permet à ScopeGuard de fonctionner.


Une autre utilisation intéressante des références est de fournir un argument par défaut d'un type défini par l'utilisateur:

class UDT
{
public:
   UDT() : val_d(33) {};
   UDT(int val) : val_d(val) {};
   virtual ~UDT() {};
private:
   int val_d;
};

class UDT_Derived : public UDT
{
public:
   UDT_Derived() : UDT() {};
   virtual ~UDT_Derived() {};
};

class Behavior
{
public:
   Behavior(
      const UDT &udt = UDT()
   )  {};
};

int main()
{
   Behavior b; // take default

   UDT u(88);
   Behavior c(u);

   UDT_Derived ud;
   Behavior d(ud);

   return 1;
}

La saveur par défaut utilise l'aspect 'bind const reference to' temporaire 'des références.


Bien que les références et les pointeurs soient utilisés pour accéder indirectement à une autre valeur, il existe deux différences importantes entre les références et les pointeurs. La première est qu'une référence fait toujours référence à un objet: c'est une erreur de définir une référence sans l'initialiser. Le comportement de l'affectation est la deuxième différence importante: l'affectation à une référence modifie l'objet auquel la référence est liée; il ne lie pas la référence à un autre objet. Une fois initialisée, une référence fait toujours référence au même objet sous-jacent.

Considérons ces deux fragments de programme. Dans le premier, nous assignons un pointeur à un autre:

int ival = 1024, ival2 = 2048;
int *pi = &ival, *pi2 = &ival2;
pi = pi2;    // pi now points to ival2

Après l'affectation, ival, l'objet adressé par pi reste inchangé. L'affectation change la valeur de pi en la faisant pointer vers un autre objet. Considérons maintenant un programme similaire qui assigne deux références:

int &ri = ival, &ri2 = ival2;
ri = ri2;    // assigns ival2 to ival

Cette affectation change de valeur, la valeur référencée par ri et non la référence elle-même. Après l'affectation, les deux références font toujours référence à leurs objets d'origine et la valeur de ces objets est désormais la même.


Ceci est basé sur le tutorial . Ce qui est écrit le rend plus clair:

>>> The address that locates a variable within memory is
    what we call a reference to that variable. (5th paragraph at page 63)

>>> The variable that stores the reference to another
    variable is what we call a pointer. (3rd paragraph at page 64)

Simplement pour s'en souvenir,

>>> reference stands for memory location
>>> pointer is a reference container (Maybe because we will use it for
several times, it is better to remember that reference.)

De plus, comme nous pouvons faire référence à presque tous les didacticiels de pointeur, un pointeur est un objet pris en charge par l'arithmétique de pointeur qui rend le pointeur similaire à un tableau.

Regardez la déclaration suivante,

int Tom(0);
int & alias_Tom = Tom;

alias_Tompeut être compris comme un alias of a variable(différent avec typedef, ce qui est alias of a type) Tom. Il est également correct d'oublier que la terminologie d'une telle déclaration est de créer une référence de Tom.


Il y a une différence fondamentale entre les pointeurs et les références que je n'ai vu personne avoir mentionné: les références permettent la sémantique passage par référence dans les arguments de fonction. Les pointeurs, bien qu'ils ne soient pas visibles au début, ne le sont pas: ils fournissent uniquement une sémantique de passage par valeur. Ceci a été très bien décrit dans cet article .

Cordialement, & rzej


La différence est que la variable de pointeur non constante (à ne pas confondre avec un pointeur sur constante) peut être modifiée à un moment donné au cours de l'exécution du programme, nécessite l'utilisation d'opérateurs de la sémantique du pointeur (&, *), tandis que les références peuvent être définies lors de l'initialisation only (c’est pourquoi vous pouvez les définir uniquement dans la liste d’initialisation du constructeur, mais pas autrement) et utiliser une valeur ordinaire pour accéder à la sémantique. Des références ont été introduites pour permettre la surcharge des opérateurs, comme je l'avais déjà lu dans un livre très ancien. Comme quelqu'un l'a dit dans ce fil - le pointeur peut être mis à 0 ou à la valeur que vous voulez. 0 (NULL, nullptr) signifie que le pointeur est initialisé avec rien. C’est une erreur de déréférencer le pointeur nul. Mais en réalité, le pointeur peut contenir une valeur qui ne pointe pas vers un emplacement de mémoire correct.Les références à leur tour essaient de ne pas permettre à un utilisateur d’initialiser une référence à quelque chose qui ne peut pas être référencé car vous lui fournissez toujours une valeur de type correct. Bien qu'il existe de nombreuses façons d'initialiser la variable de référence sur un mauvais emplacement mémoire, il est préférable de ne pas creuser ce détail en profondeur. Au niveau de la machine, le pointeur et la référence fonctionnent de manière uniforme - via des pointeurs. Disons que dans les références essentielles, il y a le sucre syntaxique. Les références rvalue diffèrent de celles-ci - ce sont naturellement des objets pile / tas.Bien qu'il existe de nombreuses façons d'initialiser la variable de référence sur un mauvais emplacement mémoire, il est préférable de ne pas creuser ce détail en profondeur. Au niveau de la machine, le pointeur et la référence fonctionnent de manière uniforme - via des pointeurs. Disons que dans les références essentielles, il y a le sucre syntaxique. Les références rvalue diffèrent de celles-ci - ce sont naturellement des objets pile / tas.Bien qu'il existe de nombreuses façons d'initialiser la variable de référence sur un mauvais emplacement mémoire, il est préférable de ne pas creuser ce détail en profondeur. Au niveau de la machine, le pointeur et la référence fonctionnent de manière uniforme - via des pointeurs. Disons que dans les références essentielles, il y a le sucre syntaxique. Les références rvalue diffèrent de celles-ci - ce sont naturellement des objets pile / tas.


Les références et les pointeurs peuvent être utilisés pour modifier les variables locales d'une fonction dans une autre fonction. Les deux peuvent également être utilisés pour sauvegarder la copie d'objets volumineux lorsqu'ils sont transmis en tant qu'arguments à des fonctions ou renvoyés de fonctions pour obtenir un gain d'efficacité. Malgré les similitudes ci-dessus, il existe des différences entre les références et les pointeurs.

Les références sont moins puissantes que les pointeurs

1) Une fois la référence créée, vous ne pouvez plus faire référence à un autre objet. il ne peut pas être remis en place. Cela se fait souvent avec des pointeurs.

2) Les références ne peuvent pas être NULL. Les pointeurs sont souvent définis comme NULL pour indiquer qu'ils ne pointent vers aucune chose valide.

3) Une référence doit être initialisée lors de la déclaration. Il n'y a pas de telle restriction avec des pointeurs

En raison des limitations ci-dessus, les références en C ++ ne peuvent pas être utilisées pour l'implémentation de structures de données telles que la liste liée, l'arbre, etc. En Java, les références n'ont pas les restrictions précédentes et peuvent être utilisées pour implémenter toutes les structures de données. Les références étant plus puissantes en Java, c'est la raison principale pour laquelle Java n'a pas besoin de pointeurs.

Les références sont plus sûres et plus faciles à utiliser:

1) Plus sûr: comme les références doivent être initialisées, il est peu probable qu’elles existent, telles que les pointeurs sauvages. Il est toujours possible d'avoir des références qui ne font pas référence à un emplacement valide

2) Plus facile à utiliser: les références n'ont pas besoin d'opérateur de déréférencement pour accéder à la valeur. Ils peuvent être utilisés comme des variables normales. L'opérateur '&' n'est nécessaire qu'au moment de la déclaration. De plus, les membres d'une référence d'objet sont accessibles avec l'opérateur de point ('.'), Contrairement aux pointeurs dans lesquels l'opérateur de flèche (->) est nécessaire pour accéder aux membres.

Avec les raisons ci-dessus, il existe peu d'endroits comme l'argument constructeur de copie où le pointeur ne peut pas être utilisé. Référence doit être utilisé pour passer l'argument dans le constructeur de copie. De même, les références doivent être utilisées pour surcharger certains opérateurs tels que ++ .


Une référence à un pointeur est possible en C ++, mais l'inverse n'est pas possible signifie qu'un pointeur sur une référence n'est pas possible. Une référence à un pointeur fournit une syntaxe de nettoyage permettant de modifier le pointeur. Regardez cet exemple:

#include<iostream>
using namespace std;

void swap(char * &str1, char * &str2)
{
  char *temp = str1;
  str1 = str2;
  str2 = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap(str1, str2);
  cout<<"str1 is "<<str1<<endl;
  cout<<"str2 is "<<str2<<endl;
  return 0;
}

Et considérons la version C du programme ci-dessus. En C, vous devez utiliser un pointeur à l'autre (indirection multiple), ce qui crée de la confusion et rend le programme complexe.

#include<stdio.h>
/* Swaps strings by swapping pointers */
void swap1(char **str1_ptr, char **str2_ptr)
{
  char *temp = *str1_ptr;
  *str1_ptr = *str2_ptr;
  *str2_ptr = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap1(&str1, &str2);
  printf("str1 is %s, str2 is %s", str1, str2);
  return 0;
}

Consultez la section suivante pour plus d'informations sur la référence au pointeur:

Comme je l'ai dit, un pointeur sur une référence n'est pas possible. Essayez le programme suivant:

#include <iostream>
using namespace std;

int main()
{
   int x = 10;
   int *ptr = &x;
   int &*ptr1 = ptr;
}

Une référence est un alias pour une autre variable alors qu'un pointeur contient l'adresse mémoire d'une variable. Les références sont généralement utilisées comme paramètres de fonction pour que l'objet transmis ne soit pas la copie, mais l'objet lui-même.

    void fun(int &a, int &b); // A common usage of references.
    int a = 0;
    int &b = a; // b is an alias for a. Not so common to use. 






c++-faq