Подтвердить что ты не робот

Действительно ли лучше всего использовать ссылки на указатели на С++ во всех ситуациях?

Часто задаваемые вопросы по С++:

"Используйте ссылки, когда сможете, и указатели, когда вам нужно".

Я чувствую, что это не так просто, как указано выше. Причины, по которым ссылки, как правило, лучше для указателей, были хорошо обсуждены. Поэтому я бы предпочел обсудить особые случаи, когда ссылки имеют смысл, но очень необычны и просто не чувствуют себя хорошо:

Для меня наиболее очевидным случаем является их использование в общих структурах данных как дерево или связанный список. Их интерфейсы могут быть реализованы с использованием ссылок и более безопасными, но я не видел единственной реализации, которая использовала бы ссылки, например:

Node *parent = n.parent(); // parent might be unset -> pointer
Node &child = n.child(5); // child is always valid -> reference

Хотя это может показаться привлекательным, это приводит к следующему коду:

if(parent == &child) ... //  weird, unusual at least?

Мне интересно, это то, что означает чистый С++?

Каждый раз, когда Ive пытались слепо использовать ссылки везде, где это возможно, Ive сталкивался со всеми подобными несоответствиями и заканчивал указателями смешивания и ссылками различными уродливыми способами.

В заключение, почему оператор new не возвращает ссылку на новый экземпляр вместо указателя в первую очередь? (и выбрасывает в случае ошибки):

Node &n = new Node;
delete &n;

Это абсурдный пример, но разве это не будет то, что было бы "чистым С++"?

4b9b3361

Ответ 1

Ссылка не может быть изменена для обращения к другому объекту после его инициализации. Но указатель может быть изменен для ссылки на разные объекты во время выполнения.

Общее правило большого пальца

Всякий раз, когда вы хотите, чтобы переменная указывала на разные объекты в течение ее срока службы, используйте указатель.

Если вы просто хотите использовать переменную в качестве псевдонима для другой переменной, используйте ссылку.

В качестве параметров

Мы иногда передаем параметры как ссылки (const/non-const). Это поможет избежать копирования всего объекта при каждом вызове функции. Следовательно, это имеет смысл при передаче объекта большого размера.

Если функция должна получить некоторый параметр, чем может быть нулевым, вы можете использовать указатель. Но, опять же, вы всегда можете определить статический нулевой объект для любого класса и его можно использовать.

В качестве возвращаемых значений

Всякий раз, когда мы перегружаем такие операторы, как [] или < <, мы возвращаем ссылки. Это потому, что, если я говорю, vec [0] = 10, он должен фактически присвоить значение 10 позиции 0 в vec. Здесь я не мог использовать указатель по понятным причинам. * vec [0] = 10 не просто выглядит красиво. Кроме того, вы должны гарантировать, что когда вы возвращаете ссылку, она всегда ссылается на объект (он никогда не должен быть нулевым).

Опять же, когда функция может возвращать значения NULL, используйте указатель, иначе вам может потребоваться объявить статический нулевой объект для этого класса.

В качестве элементов данных

В конструкторе должны быть инициализированы ссылки. Следовательно, они не могут быть изменены после создания объекта.

Следовательно, если вам нужна переменная, которая должна указывать на разные объекты в течение жизненного цикла класса, указатель будет хорошим выбором.

Ответ 2

Я думаю, что общие структуры данных подпадают под пункт "Использовать указатели, когда вам нужно". То есть, поскольку ссылки являются постоянными (т.е. они всегда относятся к одному и тому же объекту), использование ссылок в связанном списке или структуре дерева node означает, что вы не могли вставлять новые узлы или удалять старые из структуры данных - как правило, побеждая цель.

Как кто-то сказал в комментариях, я думаю, что совет действительно применим к аргументам функции. Передача ссылок, а не указателей позволяет избежать добавления логики обработки нулевого указателя к каждой функции, что приносит множество преимуществ с точки зрения размера и ясности кода.

Ответ 3

Если вы знакомы с столбцами NULL и NOT NULL в реляционной базе данных или с разницей между обязательным и необязательным элементом в XSD, вы можете провести полезную параллель из такого домена, чтобы вести вас.

Ссылки будут стоять за переменные, которые являются фиксированными, автономными и не обнуляемыми. Указатели для тех, кому у вас есть настоящая необходимость, иногда передавать "нет значения" или где вам явно нужно выполнять арифметику указателей (например, с массивом). Это отнюдь не единственные различия, но из этого следует лишь большая часть различного синтаксиса.

Рекоммендовать: reference = NOT NULL и только чтение. Pointer = гибкий и немного более опасный.

Ответ 4

"Новый" оператор выделяет память (как и malloc в C). Поскольку на С++ не реализован сборщик мусора (например, Java или С#), у вас может закончиться нехватка памяти. Поэтому, когда новый не может выделить память, возвращается значение NULL. Таким образом, вы можете проверить, было ли выделение успешным.

Вы не можете использовать ссылку с новым оператором из-за возможности присвоения значения NULL, вызванного нехваткой памяти.