В наших предыдущих статьях, мы уже упоминали о том, что параметры можно передавать в функцию по значению или по указателю. Так работали при написании программ на языке C. В C++ появляется возможность передавать параметры в функции по ссылке. Рассмотрев пример из этого урока, вы заметите, что применять ссылки удобнее чем указатели.
Ссылка – это альтернативное имя переменной (её псевдоним, иными словами). Когда функция принимает параметр по ссылке, имя параметра становится псевдонимом переменной, которую мы передаём в функцию. Такой метод передачи данных позволяет функции работать со значениями переменных, которые передаются в неё, а не с копиями этих переменных.
Определяя функцию, чтобы указать, что параметр является ссылкой, необходимо перед его именем добавить амперсанд &.
Набирайте следующий пример и всё должно стать понятным:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | #include <iostream> using namespace std; void showVariables(int varInt, double varDbl); void change(int varInt, double varDbl); // передача по значению void changeRef(int& varInt, double& varDbl); // передача по ссылке void changePtr(int* varInt, double* varDbl); // передача по указателю int main() { setlocale(LC_ALL, "rus"); int varInteger = 0; double varDouble = 0.0; cout << "Передаем параметры по значению!" << endl; change(varInteger, varDouble); showVariables(varInteger, varDouble); cout << "Передаем параметры по ссылке!" << endl; changeRef(varInteger, varDouble); // передаем, как обычную переменную showVariables(varInteger, varDouble); cout << "Передаем параметры по указателю!" << endl; changePtr(&varInteger, &varDouble); // используем амперсанд, чтобы передать адрес showVariables(varInteger, varDouble); return 0; } void showVariables(int varInt, double varDbl) { cout << "Значения переменных после изменений:\n"; cout << "Первая переменная (int) = " << varInt << endl; cout << "Вторая переменная (double) = " << varDbl << endl; cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"; } //================================================================ void change(int varInt, double varDbl) { varInt = 100; varDbl = 111.11;; } //================================================================ void changeRef(int& varInt, double& varDbl) { varInt = 200; // обращаемся, как к обычной переменной varDbl = 222.22; } //================================================================ void changePtr(int* varInt, double* varDbl) { *varInt = 300; // применяем разыменование *varDbl = 333.33; } |
Начнем с функции change(). Вызвав ее в строке 17 исходного кода, передаем в неё переменные по значению. Функция благополучно создаст копии этих переменных, запишет в них данные и, завершая свою “нелегкую” работу, уничтожит эти копии. В итоге никаких изменений с передаваемыми переменными не произойдёт.
Теперь посмотрите на определение функции changeRef(), в которую параметры передаются по ссылке – строки 45 – 49. Как говорилось выше, мы использовали & , чтобы сообщить компилятору о том, что принимаемые параметры – это ссылки. Внутри самой функции мы обращаемся к ссылкам, как к обычным переменным. То есть нам не надо применять операцию разыменования, если надо изменить значение переменной:
А вызывая эту функцию из главной (строка 21), нам не надо, при передаче переменных, применять операцию взятия адреса (тот же & , но с другим подтекстом), как это необходимо делать при передаче по указателю. Результат работы функции увидим, отобразив значение переменных на экран. Значения переменных успешно изменены.
Напоследок – передача по указателю. Сразу к определению changePtr() – строки 51 – 55. Тут есть то, чего мы не использовали при передаче по ссылке – разыменование указателей для изменения значений переменных:
Так как функция должна принять переменные по указателю, а указатель хранит адрес, то при вызове (строка 25) передаём в неё адреса переменных:
Результат работы программы:
Мне не совсем хочется нагружать вас какой-то дополнительной информацией о ссылках. Они чаще всего применяются именно для передачи параметров в функцию. В роли параметров могут выступать переменные, массивы (это нам знакомо). Но что более важно – по ссылке в функцию передаются объекты структур и классов. Придет время и мы поговорим об этом.
Единственное, что касается синтаксиса, если вам понадобится объявить ссылку в программе, то ее необходимо сразу инициализировать (показать для какой переменной она создана). Например:
Так в программе мы можем использовать имя ссылки для последующего обращения к данным, которые хранит aaa. Это если например кто-то до вас написал код и дал название переменной aaa. Этот программист знает, что она хранит количество ящиков. А вы, для своего удобства, дали этой переменной псевдоним amountOfBoxes и используете это новое имя, дописывая какой-то новый код в программу.
Предлагаю посмотреть видео о параметрах функций, где в том числе рассказано и о ссылках.
Ссылка – это альтернативное имя переменной, ИМХО больше тут пояснять нечего. Переменная одна, имен несколько. По любому имени с переменной можно как угодно работать. В случае со ссылками в аргументах функций тоже самое, просто альтернативное имя переменной будет видно внутри функции.
Ну и пару примеров я бы добавил.
Т.к. указатель – это тоже переменная (просто хранит адрес объекта), то в функцию можно передавать ссылку на указатель например
void foo(int &*arr);
Ну типа того.
И еще пример, есть 2 функции:
void foo(int *a);
void bar(int &a);
Первая принимает ссылку на объект, вторая- указатель.
Где-то в программе (например в функции main есть объект:
int var = 123;
Если надо передать его в первую функцию – то мы должны получить адрес объекта:
foo(&var);
Во вторую – можно просто передавать, при этом внутри функции будет использовать альтернативное имя объекта
bar(var);
Если нам надо будет определить указатель на объект – то он тоже должен инициализироваться адресом:
int *p = &var;
Внутри первой функции с объектом работают как обычно (ведь у нас просто есть альтернативное имя):
void foo(int &a) { a = 456; }
Во второй функции мы имеем указатель, поэтому вынуждены разыменовывать его для обращения к объекту:
void bar(int *a) { *a = 789; }
// если a – указатель,
// то (*a) – объект,
// и (&(*a)) – адрес объекта Скобочки можно убрать, поставил для ясности (они отражают приоритеты).
Перепутаны названия.
“И еще пример, есть 2 функции:
void foo(int *a);
void bar(int &a);
Первая принимает ссылку на объект, вторая- указатель.”
Наоборот!
Ссылки – достаточно неприятное для начинающих понятие в C++ (хотя совершенно естественное даже для начинающих в Java или Python).
Для начинающих можно сказать так (хотя это и не совсем точо):
– ссылка на переменную – это в точности то же, что и указатель на неё, но к этой ссылке, в отличие от указателя, нельзя применять адресную арифметику (+, -, ++, –).
Это неточно по смыслу, но сразу позволяет использовать ссылки в своём коде без ошибок.
Олег, отлично что Вы уделяете внимание нашим статьям и делаете важные заметки к изложенному материалу. Спасибо и от админа сайта и, думаю, от посетителей )
Ни один начинающий тебя не понимает. Твои объяснения совсем непонятны, следующая статься твоя и понять ее очень сложно, невозможно. А вот эта статья очень легка к восприятию. Не надо больше статей как Указатели на функции
Что значит “ни один”? Не понимают только самые тупые ;-) … остальные хоть что-то, да понимают.
Программирование – это вообще очень не простая инженерная дисциплина, и объяснять её “на пальца” невозможно и вредно так же, как, например, матанализ или начертательную геометрию.
И освоить программирование смогут только те, кто будут много и упорно практиковаться и самостоятельно писать код.
P.S. А от обучения на уровне объяснений “дважды два, детки, получается иногда четыре, если не возражаете” – никто и ничему не научится, возникнет только иллюзия что “я тоже чему-то там учился”.