web-dev-qa-db-fra.com

Pourquoi passer par référence const au lieu de par valeur?

D'après ce que je comprends: lorsque vous passez par valeur, la fonction crée une copie locale de l'argument passé et l'utilise; lorsque la fonction se termine, elle sort du cadre. Lorsque vous passez par référence const, la fonction utilise une référence à l'argument passé qui ne peut pas être modifié. Je ne comprends pas, cependant, pourquoi on choisirait l'un plutôt que l'autre, sauf dans une situation où un argument doit être modifié et renvoyé. Si vous aviez une fonction void où rien n'est retourné, pourquoi choisir l'une plutôt que l'autre?

EDIT: Donc, fondamentalement, passer par la référence const évite de copier l'objet. Dans quelles situations la copie de l'objet est-elle bonne? Je veux dire, pourquoi ne pas simplement utiliser des références const tout le temps si cela optimise les performances tout le temps?

43
Maulrus

Il y a deux considérations principales. L'un est le coût de la copie de l'objet passé et le second est les hypothèses que le compilateur peut faire lorsque l'objet est un objet local.

Par exemple. Dans le premier formulaire, dans le corps de f, on ne peut pas supposer que a et b ne font pas référence au même objet; donc la valeur de a doit être relue après toute écriture dans b, juste au cas où. Dans le second formulaire, a ne peut pas être modifié via une écriture en b, car il est local à la fonction, donc ces relectures ne sont pas nécessaires.

void f(const Obj& a, Obj& b)
{
    // a and b could reference the same object
}

void f(Obj a, Obj& b)
{
    // a is local, b cannot be a reference to a
}

Par exemple: dans le premier exemple, le compilateur peut supposer que la valeur d'un objet local ne change pas lorsqu'un appel non lié est effectué. Sans informations sur h, le compilateur peut ne pas savoir si un objet auquel cette fonction a une référence (via un paramètre de référence) n'est pas modifié par h. Par exemple, cet objet peut faire partie d'un état global qui est modifié par h.

void g(const Obj& a)
{
    // ...
    h(); // the value of a might change
    // ...
}

void g(Obj a)
{
    // ...
    h(); // the value of a is unlikely to change
    // ...
}

Malheureusement, cet exemple n'est pas en fonte. Il est possible d'écrire une classe qui, par exemple, ajoute un pointeur sur lui-même à un objet d'état global dans son constructeur, de sorte que même un objet local de type classe pourrait être modifié par un appel de fonction globale. Malgré cela, il existe toujours plus d'opportunités d'optimisations valides pour les objets locaux car ils ne peuvent pas être aliasés directement par les références transmises ou d'autres objets préexistants.

Le passage d'un paramètre par const référence doit être choisi là où la sémantique des références est réellement requise, ou comme une amélioration des performances uniquement si le coût d'un aliasing potentiel est compensé par les frais de copie du paramètre.

49
CB Bailey

Passer des arguments par valeur et donc les copier peut être coûteux - les références const évitent cette étape coûteuse tout en promettant à l'appelant que l'objet ne sera pas modifié.

Habituellement, les types fondamentaux (int, double, ...) sont passés par valeur, tandis que les types de classe sont passés par référence const.

Il peut cependant y avoir exceptions où la valeur de passage pour les types de classe peut être bénéfique.

22
Georg Fritzsche

La création d'une copie d'un objet peut considérablement affecter les performances dans certains cas. Considérons une fonction dont l'argument sera std::vector<long> et vous voulez passer le vecteur avec 1 million d'éléments. Dans ce cas, vous voudrez utiliser la référence const au lieu de passer par valeur. Dans this SO question vous pouvez trouver une règle générale simple pour votre question.

4

Parfois, faire une copie d'un objet peut être coûteux et donc passer-par-const-référence évitera d'avoir à faire cette copie. Sinon, je dirais que vous devez simplement passer par valeur si c'est ce qui est sémantiquement requis.

2
Dean Harding

Pour éviter de faire une copie inutile, améliorant ainsi les performances.

2
Paolo Tedesco

La transmission d'un argument par valeur entraîne la surcharge d'une copie de l'objet transmis à la fonction.

Peut-être qu'un objet n'est pas copiable et vos choix sont limités.

2
pmr

En raison des avantages de performance que vous obtiendrez. Disons que vous avez un gros objet (en termes de taille en octets). Maintenant, si vous passez cet objet par valeur à une fonction, une copie inutile doit en être créée, mais vous pouvez obtenir le même effet en passant une référence const à cet objet lui-même sans créer de copie. Puisqu'une référence est normalement stockée sous la forme d'un pointeur sous les capots, le coût de passage d'une référence est simplement sizeof(pointer).

2
Naveen

Un tableau ne peut pas être transmis par valeur, c'est donc le bon moment pour utiliser un pointeur const.

2
Dovi Salomon