С++: Аргумент "Передано по ссылке"

Я понимаю, что с любой другой переменной тип параметра определяет взаимодействие между параметром и его аргументом. Мой вопрос заключается в том, что объясняет, почему вы ссылаетесь на параметр и почему вы этого не сделали? Почему некоторые параметры параметров ссылки, а некоторые нет? Не могли бы вы понять преимущества этого, может кто-нибудь объяснить?

Ответ 1

Возможность проходить по ссылке существует по двум причинам:

  • Чтобы изменить значение аргументов функции
  • Чтобы избежать создания экземпляров объекта по причинам производительности

Пример для изменения аргумента

void get5and6(int *f, int *s)  // using pointers
{
    *f = 5;
    *s = 6;
}

это можно использовать как:

int f = 0, s = 0;
get5and6(&f,&s);     // f & s will now be 5 & 6

ИЛИ

void get5and6(int &f, int &s)  // using references
{
    f = 5;
    s = 6;
}

это можно использовать как:

int f = 0, s = 0;
get5and6(f,s);     // f & s will now be 5 & 6

Когда мы проходим по ссылке, мы передаем адрес переменной. Передача по ссылке аналогична передаче указателя - только адрес передается в обоих случаях.

Например,

void SaveGame(GameState& gameState)
{
    gameState.update();
    gameState.saveToFile("save.sav");
}

GameState gs;
SaveGame(gs)

ИЛИ

void SaveGame(GameState* gameState)
{
    gameState->update();
    gameState->saveToFile("save.sav");
}

GameState gs;
SaveGame(&gs);


Поскольку передается только адрес, значение переменной (которое может быть действительно огромным для огромных объектов) не нужно копировать. Поэтому переход по ссылке улучшает производительность, особенно когда:
  • Объект, переданный функции, огромен (я бы использовал здесь вариант указателя так, чтобы вызывающий пользователь знал, что функция может изменить значение переменной)
  • Функция может быть вызвана много раз (например, в цикле)

Кроме того, прочитайте ссылки const. Когда он используется, аргумент не может быть изменен в функции.

Ответ 2

Эта статья мне очень помогла.

Забудьте о указателях сейчас. И возьмите это с солью.

Ссылка - это объект. Когда вы проходите по ссылке, вы передаете объект.

Когда вы передаете по значению, вы передаете копию объекта; другой объект. Он может иметь одно и то же состояние, но это другой экземпляр; клон.

Таким образом, имеет смысл перейти по ссылке, если вы:

  • необходимо изменить объект внутри функции
  • не нужно (или не хочет) изменять объект, но не хочет копировать его, чтобы передать его функции. Это будет ссылка const.

И может иметь смысл пройти по значению, если вы:

  • нужно начинать с идентичного близнеца и оставлять оригинальное двойное невозмутимое
  • не заботятся о стоимости копирования объекта (например, я бы не передал int по ссылке, если я не захотел его модифицировать).

Вот, посмотрите на этот код:

#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);
}

И скомпилируйте его вот так: g++ example.cpp

Запустите его: ./a.out

И проверьте вывод (фактические адреса могут отличаться на вашем компьютере, но точка останется):

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

Обратите внимание, что адрес called byreference совпадает с адресом Original Foo (оба являются 0x7fff65f77a0f). И обратите внимание, что адрес called byvalue отличается (это 0x7fff65f779f0).

Поднимите его. Измените код, чтобы выглядеть следующим образом:

#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);  
}

Скомпилируйте его таким же образом и обратите внимание на вывод (комментарии не на выходе, включены для ясности):

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

Итак, код предназначен для преувеличения стоимости копии: когда вы позвонили по ссылке, эта стоимость НЕ была произведена. Когда вы вызвали по значению, вам пришлось ждать десять секунд.

Примечание: мой код был скомпилирован в OS X 10.7.4 с использованием GCC 4.8.1. Если вы находитесь в окнах, вам может понадобиться что-то отличное от unitsd.h, чтобы вызвать вызов sleep.

Возможно, это помогает.

Ответ 3

Плюсы использования pass by reference: вам не нужно создавать копию данных, которые вы передаете только указателю на нее в памяти. (огромный выигрыш в производительности, если у вас был массивный объект, который вы проходили). Вы можете "вернуть" несколько значений. Я знаю, что некоторые функции в c/С++ возвращают число, и один из параметров является указателем на данные, которые обрабатываются.

Недостатки использования pass by reference: вы должны быть осторожны, изменяя переданные данные, поскольку это может вызвать побочные эффекты, которые вы можете или не хотите.

Ответ 4

Посредством ссылки в равной степени вручную передать переменную указателем, но по ссылке не позволит пользователю обрабатывать указатель "easy to mess up".