Программирование. Принципы и практика использования C++ Исправленное издание, стр. 116
void f(int a, int& r, const int& cr){ ++a; // изменяем локальную переменную a ++r; // изменяем объект, с которым связана ссылка r ++cr; // ошибка: cr — константная ссылка}Если хотите изменить значение передаваемого объекта, то должны использовать неконстантную ссылку: передача по значению создаст копию, а передача по константной ссылке предотвратит изменение передаваемого объекта. Итак, можно написать следующий код:
void g(int a, int& r, const int& cr){ ++a; // изменяем локальную переменную a ++r; // изменяем объект, с которым связана ссылка r int x = cr; // считываем объект, с которым связана ссылка cr}int main(){ int x = 0; int y = 0; int z = 0; g(x,y,z); // x==0; y==1; z==0 g(1,2,3); // ошибка: ссылочный аргумент r должен быть переменным g(1,y,3); // OK: поскольку ссылка cr является константной, // можно передавать литерал}Итак, если хотите изменить значение объекта, передаваемого по ссылке, следует передать объект. С формальной точки зрения целочисленный литерал
2rf()Обратите внимание на то, что для константной ссылки l-значение не требуется. С ней можно выполнять преобразования точно так же, как при инициализации или при передаче по значению. При последнем вызове
g(1,y,3)intcrg()g(1,y,3); // означает: int __compiler_generated = 3; // g(1,y,__compiler_generated)Такой объект, создаваемый компилятором, называется временным объектом (temporary object).
1. Для передачи очень маленьких объектов следует использовать передачу аргументов по значению.
2. Для передачи больших объектов, которые нельзя изменять, следует использовать передачу аргументов по константной ссылке.
3. Следует возвращать результат, а не модифицированный объект, передаваемый по ссылке.
4. Передачу по ссылке следует использовать только в необходимых случаях.
intint incr1(int a) { return a+1; } // возвращает в качестве результата // новое значениеvoid incr2(int& a) { ++a; } // модифицирует объект, // передаваемый по ссылкеint x = 7;x = incr1(x); // совершенно очевидноincr2(x); // совершенно непонятноПочему же мы все-таки используем передачу аргументов по ссылке? Иногда это оказывается важным в следующих ситуациях.
• Для манипуляций с контейнерами (например, векторами) и другими крупными объектами.
• Для функций, изменяющих сразу несколько объектов (в языке С++ функция может возвращать с помощью оператора return только одно значение).
Рассмотрим пример.
void larger(vector<int>& v1, vector<int>& v2) // каждый элемент вектора v1 становится больше // соответствующих элементов в векторах v1 и v2; // аналогично, каждый элемент вектора v2 становится меньше{ if (v1.size()!=v2.size() error("larger(): разные размеры"); for (int i=0; i<v1.size(); ++i) if (v1[i]<v2[i]) swap(v1[i],v2[i]); }void f(){ vector<int> vx; vector<int> vy; // считываем vx и vy из входного потока larger(vx,vy); // ...}Передача аргументов по ссылке — единственный разумный выбор для функции
larger()Обычно следует избегать функций, модифицирующих несколько объектов одновременно. Теоретически есть несколько альтернатив, например возвращение объекта класса, хранящего несколько значений. Однако есть множество программ, дошедших до нас из прошлого, в которых функции модифицируют один или несколько аргументов, и этот факт следует учитывать. Например, в языке Fortran — основном языке программирования, используемом для математических вычислений на протяжении более пятидесяти лет, — все аргументы передаются по ссылке. Многие программисты-вычислители копируют проекты, разработанные на языке Fortran, и вызывают функции, написанные на нем.