В языке C++ практически с самого начала его существования есть две реализации ссылочного типа: это указатели (pointers) и ссылки (references). Указатели достались от языка C. Ссылки не заменили указатели во всём, а умные/интеллектуальные указатели (smart pointers) не всегда стоит использовать вместо «сырых» (raw pointers). В C++11 появился новый тип — rvalue-ссылки — который преследует совсем иные цели.
Ссылки появились в C++ ещё в тот момент, когда он миновал этап C with Classes, для перегрузки операторов:
В первом приближении разница между указателями и ссылками такова (для сравнения — ссылки в языках Java и C♯).
Из таблицы видно, что ссылки в Java и C♯, несмотря на название, больше похожи на указатели в C и C++ (но без присущей им арифметики и косвенности — как указатели на указатели).
Указатели и ссылки различаются по константности. Ссылки больше всего напоминают в этом отношении указатели, которые нельзя изменять.
По указателю в C++ принято передавать объекты в низкоуровневые API или в такие функции, аргумент которых может равняться nullptr; их используют для внутренней организации структур данных. По ссылкам принято передавать объекты во всех остальных случаях, когда нужно избежать копирования или изменить объект внутри функции.
Бьерн Страуструп, создатель языка C++:
«В языке Java есть указатели. На самом деле там почти всё неявно оказывается указателем. Просто там их называют ссылками (references). В том, что указатели неявные, есть свои преимущества и недостатки. Попутно отмечу, что наличие подлинно локальных объектов (как в C++) также имеет как преимущества, так и недостатки. [...] А вездесущие неявно используемые в языке Java указатели (они же ссылки) закрывают путь ко всем этим возможностям [...]
Благодаря повсеместному и неявному использованию ссылок в Java, возможно, удалось упростить модель программирования и сборку мусора, но расход памяти при этом резко вырос — и соответственно выросли стоимость доступа к памяти (осуществляемого более косвенным образом) и расходы на выделение памяти.
Чего в языке Java, к счастью, нет, так это допускаемого языками C и C++ неправильного применения арифметики указателей. Но и эта проблема решается, если корректно писать код на языке C++, используя такие высокоуровневые абстракции, как потоки ввода/вывода, контейнеры и алгоритмы, а не занимаясь вознёй с указателями. [...]
Есть, однако, важная область, где указатели и действия с ними оказываются большим подспорьем: прямое и эффективное описание структур данных. Ссылок Java здесь оказывается недостаточно: например, с их помощью не опишешь операцию swap. Другой пример — простота непосредственного доступа к реальной памяти с помощью указателей: в любой системе должен быть язык, который может это сделать, и часто им оказывается C++.
„Отрицательная“ сторона наличия указателей (и массивов в стиле C) — это, конечно, возможность злоупотреблений: переполнение буфера, указатели на удалённую память, неинициализированные указатели и т. п. Но если корректно писать код на языке C++, всё не столь страшно. [...] Те, кто привык работать на C или придерживается старого стиля в языке C++, с трудом верят тому, что управление ресурсами на основе областей видимости представляет собой исключительно мощный инструмент».