Ссылки
Ссылки
Кроме указателей С++ поддерживает также концепцию «ссылки». Подобно указателю, ссылка в С++ хранит адрес объекта. Основными отличиями являются следующие:
• Объявляются ссылки с применением оператора & вместо *.
• Ссылка должна быть инициализирована и не может в дальнейшем изменяться.
• С помощью ссылки обеспечивается прямое обращение к объекту; не предусмотрен специальный синтаксис, подобный операторам * или ->.
• Ссылка не может быть нулевой.
Ссылки в основном используются при объявлении параметров. По умолчанию в С++ используется передача параметров по значению, т.е. при передаче параметров функции последняя получает в действительности новую копию объекта. Ниже приводится определение функции, которая получает параметры, передаваемые по значению.
#include <cstdlib>
using namespace std;
double manhattanDistance(Point2D a, Point2D b)
{
return abs(b.x() - a.x()) + abs(b.y() - a.y());
}
Эта функция может вызываться следующим образом:
Point2D harlem(77.5, 50.0);
Point2D broadway(12.5, 40.0);
double distance = manhattanDistance(broadway, harlem);
Опытные С—программисты избегают операций копирования путем объявления параметров в виде указателей вместо значений:
double manhattanDistance(const Point2D *ap, const Point2D *bp)
{
return abs(bp->x() - ap->x()) + abs(bp->y() - ap->y());
}
После этого при вызове функции должны передаваться адреса вместо значений:
Point2D harlem(77.5, 50.0);
Point2D broadway(12.5, 40.0);
double distance = manhattanDistance(&broadway, &harlem);
Ссылки введены в С++ для того, чтобы сделать синтаксис менее громоздким и чтобы предотвратить передачу нулевого указателя. Если вместо указателей использовать ссылки, функция будет иметь следующий вид:
double manhattanDistance(const Point2D &a, const Point2D &b)
{
return abs(b.x() - a.x()) + abs(b.y() - a.y());
}
Ссылка объявляется аналогично указателю с использованием & вместо *. Однако при использовании ссылки можно забыть о том, что она является каким-то адресом памяти, и рассматривать ее как обычную переменную. Кроме того, вызов функции, принимающей ссылки в качестве аргументов, не требует специальной записи аргументов (не требуется задавать оператор &).
В конце концов, заменяя в списке параметров Point2D на const Point2D &, мы уменьшаем накладные расходы на вызов функции — вместо копирования 256 битов (размер четырех типов double) копируются только 64 или 128 бит, что зависит от размера указателя, принятого в целевой платформе.
В предыдущем примере использовались константные ссылки, не позволяющие модифицировать в функции объекты, обращение к которым осуществляется с помощью ссылок. Когда желателен этот побочный эффект, можно передавать неконстантную ссылку или указатель. Например:
void transpose(Point2D &point)
{
double oldX = point.x();
point.setX(point.y());
point.setY(oldX);
}
В некоторых случаях имеется ссылка и требуется вызвать функцию, которая принимает указатель и наоборот. Для преобразования ссылки в указатель можно просто использовать унарный оператор &:
Point2D point;
Point2D &ref = point;
Point2D *ptr = &ref;
Для преобразования указателя в ссылку используется унарный оператор *:
Point2D point;
Point2D *ptr = &point;
Point2D &ref = *ptr;
Ссылки и указатели представляются в памяти одинаково и часто могут использоваться вместо друг друга, из-за чего возникает естественный вопрос о том, в каких случаях что из них следует предпочесть. С одной стороны, ссылки имеют более удобный синтаксис, с другой стороны — указатели в любой момент можно вновь устанавливать на указатель другого объекта, они могут содержать нулевое значение и более явный синтаксис их применения часто является неприятностью, неожиданно оказавшейся благом. По этим причинам предпочтение часто отдается указателям, а ссылки почти исключительно используются при объявлении параметров функций совместно с ключевым словом const.