Способ передачи параметра определяет, работает функция с копией значения или с исходным объектом. В C++ три основных способа: по значению, через указатель и по ссылке.
Зачем нужно
Некоторым функциям достаточно значения (Min), а некоторые существуют
ради изменения переданных объектов (Swap). Кроме того, копировать большой
объект при каждом вызове дорого. Тип параметра — единственное место, где
это видно: одной строки вызова недостаточно, нужно смотреть на объявление.
Как работает
Сквозной пример — обмен двух float:
По значению
void DummySwap(float lhs, float rhs) {
float tmp = lhs;
lhs = rhs;
rhs = tmp;
}
DummySwap(z, v); // z и v не изменилисьПараметры — отдельные переменные, инициализированные копиями аргументов. Обмен проходит между копиями. Присваивание параметру по значению не является присваиванием аргументу.
Через указатель
void SwapByPointer(float* lhs, float* rhs) {
float tmp = *lhs;
*lhs = *rhs;
*rhs = tmp;
}
SwapByPointer(&z, &v); // 2 1Указатель тоже передаётся по значению — копируется адрес. Но через копию адреса можно добраться до того же объекта:
void SetZero(float* ptr) {
*ptr = 0.0f; // меняет объект снаружи
ptr = nullptr; // меняет только локальную копию
}Минусы: в вызове нужно &, в теле — *, а nullptr функция сама не
отличит от годного адреса.
По ссылке
void Swap(float& lhs, float& rhs) {
float tmp = lhs;
lhs = rhs;
rhs = tmp;
}
Swap(z, v); // 2 1Параметры — ссылки, на время вызова привязанные к z и v. Тело
буквально такое же, как у DummySwap, но меняет исходные объекты. Литерал
передать нельзя: Swap(1.0f, 2.0f) — CE.
По константной ссылке
void PrintX(const int& x) {
std::cout << "x=" << x << '\n';
}
PrintX(x); // привязка к переменной
PrintX(10); // привязка к временному объектуКопии нет, а запись через параметр запрещена компилятором. Принимает и переменные, и временные значения.
Что выбирать
| Нужно | Способ |
|---|---|
| только значение, объект маленький | по значению |
| изменить объект вызывающего кода | по ссылке (T&) |
| только читать большой объект | по константной ссылке (const T&) |
| «объекта может не быть» | указатель (может быть nullptr) |
Правило с лекции: по ссылке стоит передавать любой объект больше двух
машинных слов (16 байт на большинстве систем). int по константной ссылке
передавать незачем — это делалось в лекции, чтобы разобрать механизм.
Не из лекции
Последняя строка таблицы — обобщение, а не формулировка лекции. Лекция только замечает, что параметр-указатель не проверяет адрес, а у ссылки состояния «ни с чем не связана» нет.
Тонкости
constна параметре по значению (const int y,float* const p) запрещает менять локальную копию и не меняет сигнатуру.const float*вместоfloat*— меняет: это уже про доступ к объекту.- Массив по значению не передаётся: в функцию уходит указатель на нулевой элемент, размер передают отдельно или вторым указателем на конец, см. Массив.
- Возврат значения тоже не меняет аргументы: чтобы записать результат в
a, нужноa = Max(a, b);.
Частые ошибки
- Ждать, что изменение параметра по значению видно снаружи.
- В функции с параметрами-указателями написать
lhs = rhsвместо*lhs = *rhs. - Передавать
nullptrв функцию, которая разыменовывает параметр. - Передавать литерал в параметр
T&. - Копировать большой объект по значению, когда достаточно
const T&.