Передача аргументов по значению и по ссылке

Последнее обновление: 03.03.2023

Передача аргументов по значению

Аргументы, которые представляют переменные или константы, могут передаваться в функцию по значению (by value) и по ссылке (by reference).

При передаче аргументов по значению функция получает копию значения переменных и констант. Например:

#include <iostream>
 
void square(int);   // прототип функции

int main()
{
    int n {4};
    std::cout << "Before square: n = " << n << std::endl;
    square(n);
    std::cout << "After square: n = " << n << std::endl;
}

void square(int m)
{
    m = m * m;  // изменяем значение параметра
    std::cout << "In square: m = " << m << std::endl;
}

Функция square принимает число типа int и возводит его в квадрат. В функции main перед и после выполнения функции square происходит вывод на консоль значения переменной n, которая передается в square в качестве аргумента.

И при выполнении мы увидим, что изменение параметра m в функции square действуют только в рамках этой функции. Значение переменной n, которое передается в функцию, никак не изменяется:

Before square: n = 4
In square: m = 16
After square: n = 4

Почему так происходит? При компиляции функции для ее параметров выделяются отдельные участки памяти. При вызове функции вычисляются значения аргументов, которые передаются на место параметров. И затем значения аргументов заносятся в эти участки памяти. То есть функция манипулирует копиями значений объектов, а не самими объектами.

Передача параметров по ссылке

При передаче параметров по ссылке передается ссылка на объект, через которую мы можем манипулировать самим объектов, а не просто его значением. Так, перепишем предыдущий пример, используя передачу по ссылке:

#include <iostream>
 
void square(int&);   // прототип функции
 
int main()
{
    int n {4};
    std::cout << "Before square: n = " << n << std::endl;
    square(n);
    std::cout << "After square: n = " << n << std::endl;
}
void square(int& m)
{
    m = m * m;  // изменяем значение параметра
    std::cout << "In square: m = " << m << std::endl;
}

Теперь параметр m передается по ссылке. Ссылочный параметр связывается непосредственно с объектом, поэтому через ссылку можно менять сам объект. То есть здесь при вызове функции параметр m в функции square будет представлять тот же объект, что и переменная n

И если мы скомпилируем и запустим программу, то результат будет иным:

Before square: n = 4
In square: m = 16
After square: n = 16

Передача по ссылке позволяет возвратить из функции сразу несколько значений. Также передача параметров по ссылке является более эффективной при передаче очень больших объектов. Поскольку в этом случае не происходит копирования значений, а функция использует сам объект, а не его значение.

От передачи аргументов по ссылке следует отличать передачу ссылок в качестве аргументов:

#include <iostream>

void square(int);   // прототип функции
 
int main()
{
    int n = 4;
    int &nRef = n;  // ссылка на переменную n
    std::cout << "Before square: n = " << n << std::endl;
    square(nRef);
    std::cout << "After square: n = " << n << std::endl;
}
void square(int m)
{
    m = m * m;  // изменяем значение параметра
    std::cout << "In square: m = " << m << std::endl;
}

Если функция принимает аргументы по значению, то изменение параметров внутри функции также никак не скажется на внешних объектах, даже если при вызове функции в нее передаются ссылки на объекты.

Before square: n = 4
In square: m = 16
After square: n = 4

Передача параметров по значению больше подходит для передачи в функцию небольших объектов, значения которых копируются в определенные участки памяти, которые потом использует функция.

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

Преобразования типов

Передача параметров по значению и по ссылке отличаются еще одним важным моментом. С++ может автоматически преобразовывать значения одних типов в другие, в том числе если подобные преобразования сопровождаются потерей точности (например, преобразование от типа double к типу int). Но при передаче параметров по ссылке неявные автоматические преобразования типов исключены. Так, рассмотрим пример:

#include <iostream>
 
void printVal(int);
void printRef(int&);

int main()
{
    double value{3.14159};
    printVal(value);    // 3
    printRef(value);    // ! Ошибка
}
void printVal(int n)
{
    std::cout << n << std::endl;
}
void printRef(int& n)
{
    std::cout << n << std::endl;
}

Здесь определены две практически идентичные функции. Только функция printVal получает параметр по значению, а функция printRef - по ссылке. При вызове в обе функции передается число типа double. Но параметр обоих функций представляет тип int. И если при передаче по значению переданное число double успешно преобразуется в int (пусть и с потерей точности), то при передаче по ссылке мы столкнемся с ошибкой на этапе компиляции. Это еще одна причина, почему нередко рекомендуется передавать значения по ссылки - исключается вероятность предвиденных и иногда нежелательных преобразований типов.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850