void print(vector<double> v) // передача по значению; приемлемо ?
{
cout << "{ ";
for (int i = 0; i<v.size(); ++i) {
cout << v[i];
if (i!=v.size()–1) cout << ", ";
}
cout << " }\n";
}
Функцию
print()
можно применять к векторам любых размеров. Рассмотрим пример.
void f(int x)
{
vector<double> vd1(10); // небольшой вектор
vector<double> vd2(1000000); // большой вектор
vector<double> vd3(x); // вектор неопределенного размера
// ...заполняем векторы vd1, vd2, vd3 значениями...
print(vd1);
print(vd2);
print(vd3);
}
Этот код работает, но при первом вызове функции
print()
будет скопирован десяток чисел типа
double
(вероятно, 80 байт), при втором — миллионы чисел типа
double
(вероятно, восемь мегабайт), а при третьем количество копируемых чисел неизвестно. Возникает вопрос: “Зачем вообще что-то копировать?” Мы же хотим распечатать вектор, а не скопировать его. Очевидно, нам нужен способ передачи переменных функциям без их копирования. Например, если вы получили задание составить список книг, находящихся в библиотеке, то совершенно не обязательно приносить копии всех книг домой — достаточно взять адрес библиотеки, пойти туда и просмотреть все книги на месте.
Итак, нам необходим способ передачи функции
print()
“адреса” вектора, а не копии вектора. “Адрес” вектора называется
ссылкой (reference) и используется следующим образом:
void print(const vector<double>& v) // передача по константной ссылке
{
cout << "{ ";
for (int i = 0; i<v.size(); ++i) {
cout << v[i];
if (i!=v.size()–1) cout << ", ";
}
cout << " }\n";
}
Символ
&
означает ссылку, а ключевое слово
const
предотвращает случайную модификацию аргумента в функции
print()
. Кроме объявления аргумента, все остальное без изменений. Правда, теперь все операции будут производиться не над копией, а над самим аргументом, полученным по ссылке. Такие аргументы называются ссылками, потому что они ссылаются на объекты, определенные вне функции. Вызов функции
print()
остается точно таким же, как и раньше.
void f(int x)
{
vector<double> vd1(10); // небольшой вектор
vector<double> vd2(1000000); // большой вектор
vector<double> vd3(x); // вектор неопределенного размера
// ...заполняем векторы vd1, vd2, vd3 значениями...
print(vd1);
print(vd2);
print(vd3);
}
Этот механизм можно проиллюстрировать графически.
Константная ссылка обладает полезным свойством: она не позволяет случайно изменить объект, на который ссылается. Например, если мы сделаем глупую ошибку и попытаемся присвоить элементу вектора, полученного извне функции
print()
, какое-то значение, то компилятор сразу выдаст сообщение об этом.
void print(const vector<double>& v) // передача по константной ссылке
{
// ...
v[i] = 7; // ошибка: v — константа (т.е. не может изменяться)
// ...
}
Передача аргументов по константной ссылке — очень полезный и распространенный механизм. Вернемся к функции
my_find()
(см. раздел 8.5.1), выполняющей поиск строки в векторе строк. Передача по значению здесь была бы слишком неэффективной.
int my_find(vector<string> vs, string s); // передача по значению:
// копия
Если вектор содержит тысячи строк, то поиск занял бы заметный объем времени даже на быстром компьютере. Итак, мы можем улучшить функцию
my_find()
, передавая ее аргументы по константной ссылке.
// передача по ссылке: без копирования, доступ только для чтения
int my_find(const vector<string>& vs, const string& s);
8.5.5. Передача параметров по ссылке
А что делать, если мы хотим, чтобы функция модифицировала свои аргументы? Иногда это очень нужно. Например, мы можем написать функцию
init()
, которая должна присваивать начальные значения элементам вектора.
void init(vector<double>& v) // передача по ссылке
{
for (int i = 0; i<v.size(); ++i) v[i] = i;
}