Почему ссылка на c ++ считается более безопасной, чем указатель? - PullRequest
9 голосов
/ 17 января 2011

Когда компилятор c ++ генерирует очень похожий ассемблерный код для ссылки и указателя, почему использование ссылок предпочтительнее (и считается более безопасным) по сравнению с указателями?

Я видел

EDIT-1:

Я искал код ассемблера, сгенерированный g ++ для этой маленькой программы:

int main(int argc, char* argv[])
{
  int a;
  int &ra = a;
  int *pa = &a;
}

Ответы [ 9 ]

18 голосов
/ 17 января 2011

Это считается более безопасным, потому что многие люди «слышали», что это безопаснее, а затем говорили другим, которые теперь также «слышали», что это безопаснее.

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

, например

#include <vector>

int main(void)
{
    std::vector<int> v;
    v.resize(1);

    int& r = v[0];
    r = 5; // ok, reference is valid

    v.resize(1000);
    r = 6; // BOOM!;

    return 0;
}

РЕДАКТИРОВАТЬ: Поскольку, похоже, существует некоторая путаница в том, является ли ссылка псевдонимом для объекта или привязана к области памяти, вот параграф стандарта (черновик 3225, раздел [basic.life]), в котором четко указано, что ссылка привязывается к хранилищу и может пережить объект, который существовал при создании ссылки:

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

  • хранилище для нового объекта точно перекрывает место хранения, которое занимал исходный объект, и
  • новый объект того же типа, что и исходный объект (игнорируя квалификаторы cv верхнего уровня), и
  • тип исходного объекта не является const-квалифицированным, и, если тип класса, не содержит нестатических элемент данных, тип которого является постоянным или ссылочным, и
  • исходный объект был наиболее производным объектом типа T, а новый объект - наиболее производным объектом типа T (то есть они не являются подобъектами базового класса).
7 голосов
/ 17 января 2011

Зависит от того, как вы определяете «безопаснее».

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

С другой стороны, прозрачность синтаксиса, а именно то, что упомянул Александр С. о том, как выглядит на call-site передача аргумента в качестве ссылки, делает его довольно Легко не понять, что вы передаете ссылку. Следовательно, вы можете не осознавать, что должны сохранять право собственности и срок действия аргумента или что ваш аргумент может быть изменен навсегда.

6 голосов
/ 17 января 2011

Поскольку ссылки (которые являются просто псевдонимом для другой переменной) не могут быть NULL по определению, обеспечивая присущий им уровень безопасности.

2 голосов
/ 17 января 2011

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

Но только для того, чтобы показать вам, что ссылка сама по себе не делает вашу программу безопасной на 100%, подумайте:

int *p = NULL;
int &r = *p;
r = 10; /* bad things will happen here */

Или это:

int &foo() {
  int i;
  return i;
}

...

int &r = foo();
r = 10; /* again, bad things will happen here */
2 голосов
/ 17 января 2011

Это немного безопаснее, но не одно и то же. Обратите внимание, что у вас есть те же проблемы с «висячими ссылками», что и с «висящими указателями». Например, возврат ссылки из объекта с областью видимости приводит к неопределенному поведению, точно так же, как указатели:

int& f() { int x = 2; return x; }

Единственное преимущество в том, что вы не можете создать нулевую ссылку. Даже если ты стараешься:

int& null_ref = *((int*)0); // Dereferencing a null pointer is undefined in C++
                            // The variable null_ref has an undefined state.

Как члены класса, указатели предпочтительнее, так как они имеют лучшую семантику присваивания: вы не можете переназначить ссылку, когда она инициализирована. Компилятор не сможет предоставить оператор присваивания по умолчанию, если в классе есть ссылочные члены.

Следовательно, C ++ не может избавиться от указателей, и вы можете использовать их свободно: передавая аргументы как указатели, а не как (не константные) ссылки, вы даете понять на сайте вызова, что объект будет изменен. Это может добавить немного безопасности, так как вы видите невооруженным глазом, какие функции на самом деле изменяют объекты.

Я немного играю адвоката дьявола, но ссылками легко злоупотреблять.

2 голосов
/ 17 января 2011

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

РЕДАКТИРОВАТЬ: Спасибо за все ответы. Да, ссылка действительно может указывать на мусор, я забыл про висячие ссылки.

0 голосов
/ 17 января 2011

Предположим, у вас не было оператора ссылки <type>& на языке. Затем, когда вы захотите передать ссылку на объект в функцию, вам придется делать то, что сделано в C, передавать аргумент как &<myobject> и получать его в функции в качестве параметра указателя <type>* p. Тогда вам придется дисциплинировать себя , а не , чтобы сделать что-то вроде p++.

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

(Возможно, указатель const мог бы выполнить то же самое, но вы должны признать, что & чище. И его можно применять к другим переменным, кроме параметров.)

0 голосов
/ 17 января 2011

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

0 голосов
/ 17 января 2011

Ну, ответ, который вы указываете, ответьте на это. С "более безопасной" точки зрения, я думаю, что в принципе трудно написать код вроде:

int* i;
// ...
cout << *i << endl; // segfault

Как ссылка всегда инициализируется, а

MyObject* po = new MyObject(foo);
// ...
delete po;
// ...
po->doSomething(); // segfault

Но, как сказано в упомянутом вами вопросе, это не только потому, что они безопаснее, если используются ссылки ...

my2c

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...