Я понимаю, что с любой другой переменной тип параметра определяет взаимодействие между параметром и его аргументом. Мой вопрос заключается в том, что объясняет, почему вы ссылаетесь на параметр и почему вы этого не сделали? Почему некоторые параметры параметров ссылки, а некоторые нет? Не могли бы вы понять преимущества этого, может кто-нибудь объяснить?
С++: Аргумент "Передано по ссылке"
Ответ 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".