web-dev-qa-db-fra.com

C ++: Argument passant "passé par référence"

Je comprends que comme pour toute autre variable, le type d'un paramètre détermine l'interaction entre le paramètre et son argument. Ma question est la suivante: quel est le raisonnement derrière lequel vous référeriez un paramètre vs pourquoi vous ne le feriez pas? Pourquoi certaines fonctions font-elles référence à des paramètres et d'autres non? Ayant du mal à comprendre les avantages de le faire, quelqu'un pourrait-il expliquer?

20
Tyler

Cet article m'a beaucoup aidé.

Veuillez oublier les pointeurs pour l'instant. Et prenez ceci avec un grain de sel.

Une référence c'est l'objet. Lorsque vous passez par référence, vous passez l'objet.

Lorsque vous passez par valeur, vous passez un copie de l'objet; un autre objet. Il peut avoir le même état, mais c'est une instance différente; un clone.

Il peut donc être judicieux de passer par référence si vous:

  • besoin de modifier le objet à l'intérieur de la fonction
  • n'ont pas besoin (ou ne veulent pas) de modifier l'objet, mais voudraient éviter de le copier juste pour le passer à une fonction. Ce serait une référence const.

Et il peut être judicieux de passer par la valeur si vous:

  • voulez partir d'un identique twin , et laisser le original twin non perturbé
  • ne se soucient pas du coût de copie de l'objet (par exemple, je ne passerais pas un int par référence à moins que je voulais le modifier).

Ici, jetez un œil à ce code:

#include<iostream>

struct Foo {
  Foo() { }
  void describe() const {
    std::cout<<"Foo at address "<<this<<std::endl;
  }  
};

void byvalue(Foo foo) {
  std::cout<<"called byvalue"<<std::endl;
  foo.describe();
}

void byreference(Foo& foo) {
  std::cout<<"called byreference"<<std::endl;  
  foo.describe();
}

int main() {
  Foo foo;
  std::cout<<"Original Foo"<<std::endl;
  foo.describe();
  byreference(foo);
  byvalue(foo);
}

Et compilez-le comme ceci: g++ example.cpp

Exécuter: ./a.out

Et vérifiez la sortie (les adresses réelles peuvent être différentes sur votre ordinateur, mais le point restera):

Original Foo
Foo at address 0x7fff65f77a0f
called byreference
Foo at address 0x7fff65f77a0f
called byvalue
Foo at address 0x7fff65f779f0

Remarquez comment le called byreference l'adresse est la même que Original Foo adresse (les deux sont 0x7fff65f77a0f). Et remarquez comment le called byvalue l'adresse est différente (c'est 0x7fff65f779f0).

Prenez-le d'un cran. Modifiez le code pour qu'il se présente comme suit:

#include<iostream>
#include<unistd.h> // for sleeping

struct Foo {
  Foo() { }
  Foo(const Foo&) {
    sleep(10); // assume that building from a copy takes TEN seconds!
  }
  void describe() const {
    std::cout<<"Foo at address "<<this<<std::endl;
  }  
};

void byvalue(Foo foo) {
  std::cout<<"called byvalue"<<std::endl;
  foo.describe();
}

void byreference(Foo& foo) {
  std::cout<<"called byreference"<<std::endl;  
  foo.describe();
}

int main() {
  Foo foo;
  std::cout<<"Original Foo"<<std::endl;
  foo.describe();
  byreference(foo);
  byvalue(foo);  
}

Compilez-le de la même manière et notez la sortie (commentaires non dans la sortie; inclus pour plus de clarté):

Original Foo
Foo at address 0x7fff64d64a0e
called byreference
Foo at address 0x7fff64d64a0e # this point is reached "immediately"
called byvalue # this point is reached TEN SECONDS later
Foo at address 0x7fff64d64a0f

Ainsi, le code est censé exagérer le coût d'une copie: lorsque vous avez appelé par référence, ce coût n'était PAS engagé. Lorsque vous avez appelé par valeur, vous avez dû attendre dix secondes.

Remarque: mon code a été compilé dans OS X 10.7.4 à l'aide de GCC 4.8.1. Si vous êtes sous Windows, vous aurez peut-être besoin de quelque chose de différent de unitsd.h pour que l'appel sleep fonctionne.

Peut-être que cela aide.

23
Escualo

Par référence, il faut également passer manuellement la variable par pointeur, mais par référence, il ne permettra pas à l'utilisateur de gérer le pointeur "facile à gâcher".

1
TerrenceSun

Avantages d'utiliser le passage par référence: vous n'avez pas besoin de créer une copie des données que vous passez juste le pointeur vers celui-ci en mémoire. (énorme gain de performance, pensez si vous aviez un objet massif que vous avez fait passer). Vous pouvez "renvoyer" plusieurs valeurs. Je sais que certaines fonctions en c/c ++ renvoient un nombre et l'un des paramètres est un pointeur vers les données qui sont manipulées.

Inconvénients de l'utilisation de la référence par référence: vous devez être prudent en modifiant les données transmises car cela pourrait provoquer des effets secondaires que vous souhaitiez ou non.

1
dbarnes