Каковы различия между переменной-указателем и ссылочной переменной в C ++?

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

Но каковы различия?

11.09.2008 20:03:57
Я думаю, что пункт 2 должен быть «Указатель может быть NULL, но ссылка - нет. Только искаженный код может создать ссылку NULL, и его поведение не определено».
Mark Ransom 8.10.2010 17:21:24
Указатели - это просто другой тип объекта, и, как и любой объект в C ++, они могут быть переменными. Ссылки с другой стороны никогда не являются объектами, а только переменными.
Kerrek SB 16.06.2012 10:14:02
Компилируется без предупреждений: int &x = *(int*)0;на gcc. Ссылка действительно может указывать на NULL.
Calmarius 13.08.2012 09:00:00
ссылка на псевдоним переменной
Khaled.K 23.12.2013 08:53:04
Мне нравится, что самое первое предложение - полная ошибка. Ссылки имеют свою семантику.
Lightness Races in Orbit 1.06.2014 01:58:45
30 ОТВЕТОВ

Помимо синтаксического сахара, ссылка - это constуказатель ( не указатель на a const). Вы должны установить, к чему это относится, когда объявляете ссылочную переменную, и вы не можете изменить ее позже.

Обновление: теперь, когда я думаю об этом, есть важное отличие.

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

Цель ссылки не может быть заменена каким-либо образом, кроме UB.

Это должно позволить компилятору оптимизировать работу со ссылкой.

124
12.04.2018 15:59:44
Я думаю, что это лучший ответ на сегодняшний день. Другие говорят о ссылках и указателях, как будто они разные звери, а затем рассказывают, как они отличаются по поведению. Это не облегчает имхо. Я всегда понимал ссылки как T* constимеющие различный синтаксический сахар (это исключает много * и & из вашего кода).
Carlo Wood 10.01.2017 01:34:05
«Цель константного указателя может быть заменена путем взятия его адреса и использования константного приведения». Это неопределенное поведение. См. Stackoverflow.com/questions/25209838/… для получения подробной информации.
dgnuff 22.06.2018 04:51:40
Попытка изменить либо референт ссылки, либо значение константного указателя (или любого константного скаляра) является противозаконным равенством. Что вы можете сделать: удалить квалификацию const, которая была добавлена ​​неявным преобразованием: int i; int const *pci = &i; /* implicit conv to const int* */ int *pi = const_cast<int*>(pci);все в порядке.
curiousguy 29.06.2018 16:00:18
Разница здесь UB против буквально невозможной. В C ++ нет синтаксиса, который позволял бы вам изменять ориентиры.
user3458 29.06.2018 16:03:01
Не сложно, сложнее, вы можете просто получить доступ к области памяти указателя, который моделирует эту ссылку, и изменить ее содержимое. Это, безусловно, можно сделать.
Nicolas Bousquet 17.11.2018 21:39:21
  1. Указатель может быть переназначен:

    int x = 5;
    int y = 6;
    int *p;
    p = &x;
    p = &y;
    *p = 10;
    assert(x == 5);
    assert(y == 10);

    Ссылка не может и должна быть назначена при инициализации:

    int x = 5;
    int y = 6;
    int &r = x;
  2. Указатель имеет свой собственный адрес и размер памяти в стеке (4 байта в x86), тогда как ссылка использует тот же адрес памяти (с исходной переменной), но также занимает некоторое место в стеке. Поскольку ссылка имеет тот же адрес, что и сама исходная переменная, можно с уверенностью рассматривать ссылку как другое имя для той же переменной. Примечание. То, на что указывает указатель, может быть в стеке или куче. Так же ссылка. Мое утверждение в этом утверждении не в том, что указатель должен указывать на стек. Указатель - это просто переменная, которая содержит адрес памяти. Эта переменная находится в стеке. Поскольку ссылка имеет свое собственное пространство в стеке, а адрес совпадает с переменной, на которую она ссылается. Больше в стеке против кучи, Это означает, что существует реальный адрес ссылки, о которой вам не сообщит компилятор.

    int x = 0;
    int &r = x;
    int *p = &x;
    int *p2 = &r;
    assert(p == p2);
  3. Вы можете иметь указатели на указатели на указатели, предлагающие дополнительные уровни косвенности. Принимая во внимание, что ссылки предлагают только один уровень косвенности.

    int x = 0;
    int y = 0;
    int *p = &x;
    int *q = &y;
    int **pp = &p;
    pp = &q;//*pp = q
    **pp = 4;
    assert(y == 4);
    assert(x == 0);
  4. Указатель может быть назначен nullptrнапрямую, а ссылка - нет. Если вы достаточно стараетесь, и знаете, как, вы можете сделать адрес ссылки nullptr. Аналогично, если вы попытаетесь достаточно усердно, у вас может быть ссылка на указатель, и тогда эта ссылка может содержать nullptr.

    int *p = nullptr;
    int &r = nullptr; <--- compiling error
    int &r = *p;  <--- likely no compiling error, especially if the nullptr is hidden behind a function call, yet it refers to a non-existent int at address 0
  5. Указатели могут перебирать массив, вы можете использовать ++для перехода к следующему элементу, на который указывает указатель, и + 4для перехода к 5-му элементу. Это не имеет значения, на какой размер объекта указывает указатель.

  6. *Чтобы получить доступ к области памяти, на которую он указывает, указатель должен быть разыменован , тогда как ссылка может использоваться напрямую. Указатель на класс / структуру использует ->для доступа к его членам, тогда как ссылка использует ..

  7. Ссылки не могут быть вставлены в массив, тогда как указатели могут быть (упоминается пользователем @litb)

  8. Const ссылки могут быть связаны с временными. Указатели не могут (не без некоторой косвенности):

    const int &x = int(12); //legal C++
    int *y = &int(12); //illegal to dereference a temporary.

    Это делает const&более безопасным использование в списках аргументов и так далее.

1682
22.03.2020 22:47:36
... но разыменование NULL не определено. Например, вы не можете проверить, является ли ссылка NULL (например, & ref == NULL).
Pat Notz 11.09.2008 22:07:03
Номер 2 не соответствует действительности. Ссылки - это не просто «другое имя для той же переменной». Ссылки могут передаваться функциям, храниться в классах и т. Д. Способом, очень похожим на указатели. Они существуют независимо от переменных, на которые они указывают.
Derek Park 12.09.2008 23:37:54
Брайан, стек не актуален. Ссылки и указатели не должны занимать место в стеке. Они оба могут быть размещены в куче.
Derek Park 19.09.2008 04:33:38
Брайан, тот факт, что переменная (в данном случае указатель или ссылка) требует места, не означает, что она требует места в стеке. Указатели и ссылки могут не только указывать на кучу, они могут фактически быть размещены в куче.
Derek Park 19.09.2008 15:26:29
еще одна важная разница: ссылки не могут быть объединены в массив
Johannes Schaub - litb 27.02.2009 21:10:17

Ссылка никогда не может быть NULL.

44
1.06.2014 22:37:05
См. Ответ Марка Рэнсома на контрпример. Это наиболее часто утверждаемый миф о ссылках, но это миф. Единственная гарантия, которую вы имеете по стандарту, состоит в том, что у вас сразу же появляется UB, когда у вас есть ссылка NULL. Но это все равно, что сказать: «Эта машина безопасна, она никогда не может сойти с дороги. (Мы не несем никакой ответственности за то, что может случиться, если вы все равно
cmaster - reinstate monica 13.06.2014 11:36:07
@cmaster: в действующей программе ссылка не может быть нулевой. Но указатель может. Это не миф, это факт.
user541686 8.08.2014 04:42:07
@ Mehrdad Да, действительные программы остаются в дороге. Но нет никакого барьера трафика, чтобы обеспечить выполнение вашей программы. На больших участках дороги фактически отсутствуют маркировки. Поэтому очень легко сойти с дороги ночью. И для отладки таких ошибок крайне важно, чтобы вы знали, что это может произойти: нулевая ссылка может распространиться до сбоя вашей программы, так же как и нулевой указатель. И когда это происходит, у вас есть такой код void Foo::bar() { virtual_baz(); }segfaults. Если вы не знаете, что ссылки могут быть нулевыми, вы не можете отследить нулевое значение до его источника.
cmaster - reinstate monica 29.12.2014 10:43:30
int * p = NULL; int & r = * p; ссылка, указывающая на NULL; if (r) {} -> boOm;) -
sree 4.10.2015 12:43:33
@sree int &r=*p;- неопределенное поведение. В этот момент, вы не имеете «ссылочную указывающий на NULL,» у вас есть программа , которая уже не может быть мотивированной о вообще .
cdhowie 28.03.2017 18:46:25

Вопреки распространенному мнению, возможно иметь ссылку на NULL.

int * p = NULL;
int & r = *p;
r = 1;  // crash! (if you're lucky)

Конечно, справиться с этим гораздо сложнее, но если вы справитесь с этим, вы порвете волосы, пытаясь найти его. Ссылки не являются безопасными в C ++!

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

Фактическая ошибка заключается в разыменовании указателя NULL до назначения ссылки. Но я не знаю ни о каких компиляторах, которые будут генерировать какие-либо ошибки при этом условии - ошибка распространяется дальше в коде. Вот что делает эту проблему настолько коварной. В большинстве случаев, если вы разыменовываете указатель NULL, вы зависаете прямо в этом месте, и для его выяснения не требуется много отладки.

Мой пример выше короткий и надуманный. Вот более реальный пример.

class MyClass
{
    ...
    virtual void DoSomething(int,int,int,int,int);
};

void Foo(const MyClass & bar)
{
    ...
    bar.DoSomething(i1,i2,i3,i4,i5);  // crash occurs here due to memory access violation - obvious why?
}

MyClass * GetInstance()
{
    if (somecondition)
        return NULL;
    ...
}

MyClass * p = GetInstance();
Foo(*p);

Я хочу повторить, что единственный способ получить нулевую ссылку - это искаженный код, и как только вы его получите, вы получите неопределенное поведение. Он никогда не имеет смысла проверить ссылки нуля; например, вы можете попробовать, if(&bar==NULL)...но компилятор может оптимизировать оператор из существования! Допустимая ссылка никогда не может быть NULL, так что, с точки зрения компилятора, сравнение всегда ложно, и предложение свободно исключать ifкак мертвый код - это сущность неопределенного поведения.

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

template<typename T>
T& deref(T* p)
{
    if (p == NULL)
        throw std::invalid_argument(std::string("NULL reference"));
    return *p;
}

MyClass * p = GetInstance();
Foo(deref(p));

Более старый взгляд на эту проблему от кого-то с лучшими навыками письма, см. Null References от Джима Хислопа и Херба Саттера.

Еще один пример опасности разыменования нулевого указателя см. В разделе « Неопределенное поведение при попытке переноса кода на другую платформу » Раймонда Чена.

123
8.01.2018 22:57:42
Данный код содержит неопределенное поведение. Технически, вы ничего не можете сделать с нулевым указателем, кроме как установить его и сравнить. Как только ваша программа вызывает неопределенное поведение, она может делать все что угодно, в том числе работать правильно, пока вы не дадите демонстрацию большому боссу.
KeithB 12.09.2008 16:00:14
Марк имеет действительный аргумент. Аргумент, что указатель может быть НЕДЕЙСТВИТЕЛЬНЫМ, и поэтому вы должны проверить его, также не является реальным: если вы говорите, что функция требует ненулевого значения, то вызывающая сторона должна это сделать. поэтому, если вызывающий не делает этого, он вызывает неопределенное поведение. точно так же, как Марк сделал с плохой ссылкой
Johannes Schaub - litb 27.02.2009 21:14:41
Описание ошибочное. Этот код может создавать или не создавать ссылку на NULL. Его поведение не определено. Это может создать совершенно правильную ссылку. Он может вообще не создавать никаких ссылок.
David Schwartz 20.08.2011 11:41:33
@ Дэвид Шварц, если бы я говорил о том, как все должно работать в соответствии со стандартом, вы были бы правы. Но это не то , о чем я говорю - я говорю о фактическом наблюдаемом поведении с очень популярным компилятором и об экстраполировании, основываясь на моих знаниях типичных компиляторов и архитектур ЦП, на то, что, вероятно, произойдет. Если вы считаете, что ссылки превосходят указатели, потому что они безопаснее, и не считаете, что ссылки могут быть плохими, однажды вы будете озадачены простой проблемой, как и я.
Mark Ransom 22.08.2011 02:11:48
Разыменование нулевого указателя неверно. Любая программа, которая делает это, даже для инициализации ссылки, неверна. Если вы инициализируете ссылку из указателя, вы всегда должны проверять, что указатель действителен. Даже если это успешно, базовый объект может быть удален в любое время, оставляя ссылку для ссылки на несуществующий объект, верно? То, что вы говорите, это хороший материал. Я думаю, что реальная проблема здесь в том, что ссылка не должна проверяться на «нулевое значение», когда вы ее видите, и указатель должен быть как минимум утвержден.
t0rakka 22.03.2017 14:07:22

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

std::string s1 = "123";
std::string s2 = "456";

std::string s3_copy = s1 + s2;
const std::string& s3_reference = s1 + s2;

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

Если вы попробуете это без, constон не сможет скомпилироваться. Вы не можете привязать неконстантную ссылку к временному объекту и не можете взять его адрес в этом отношении.

189
8.10.2010 16:52:17
но какой вариант использования для этого?
Ahmad Mushtaq 22.10.2009 14:10:33
Что ж, s3_copy создаст временный файл и затем скопирует его в s3_copy, тогда как s3_reference напрямую использует временный. Затем, чтобы быть по-настоящему педантичным, вам нужно взглянуть на оптимизацию возвращаемого значения, при которой компилятору разрешается исключать конструкцию копирования в первом случае.
Matt Price 22.10.2009 18:14:56
@digitalSurgeon: магия там довольно мощная. Время жизни объекта продлевается фактом const &привязки, и только когда ссылка выходит из области видимости, вызывается деструктор фактического ссылочного типа (по сравнению с ссылочным типом, который может быть базовым). Поскольку это ссылка, в промежутке между ними не будет никакого среза.
David Rodríguez - dribeas 14.01.2010 17:06:23
Обновление для C ++ 11: последнее предложение следует читать : «Вы не может связать неконстантную ссылку именующей к временному» , потому что вы можете связать неконстантную RValue ссылку на временный, и он имеет то же пожизненные удлинители поведения.
Oktalist 10.11.2013 20:14:26
@AhmadMushtaq: ключевое использование этого - производные классы . Если не задействовано наследование, вы также можете использовать семантику значений, которая будет дешевой или бесплатной из-за конструкции RVO / move. Но если у вас есть Animal x = fast ? getHare() : getTortoise()то xстолкнется с классической проблемой нарезки, пока Animal& x = ...будет работать правильно.
Arthur Tacca 2.11.2017 11:04:03

Вы забыли самую важную часть:

членский доступ с указателями использует ->
членский доступ с использованием ссылок.

foo.barэто явно превосходил foo->barтаким же образом , что VI является явно превосходит Emacs :-)

114
7.07.2015 21:21:27
@Orion Edwards> членский доступ с использованием указателей ->> членский доступ с использованием ссылок. Это не на 100% верно. Вы можете иметь ссылку на указатель. В этом случае вы получите доступ к элементам указателя без ссылки, используя -> struct Node {Node * next; }; Узел * первый; // p - ссылка на указатель void foo (Node * & p) {p-> next = first; } Node * bar = новый узел; Foo (бар); - ОП: Вы знакомы с понятиями rvalues ​​и lvalues?
user6105 12.09.2008 12:57:12
Умные указатели имеют оба. (методы в классе интеллектуальных указателей) и -> (методы в базовом типе).
JBRWilkinson 9.04.2014 09:11:06
@ user6105 Заявление Ориона Эдвардса на самом деле на 100% верно. msgstr "Доступ к членам указателя []], на который нет ссылок . Указатель не имеет членов. У объекта, на который ссылается указатель, есть члены, и доступ к ним - именно то, что ->обеспечивает ссылки на указатели, так же как и с самим указателем.
Max Truxa 15.04.2015 17:37:03
почему это так .и ->имеет отношение к vi и emacs :)
artm 21.08.2016 00:45:28
@artM - это была шутка и, вероятно, не имеет смысла для не носителей английского языка. Мои извенения. Объяснить, лучше ли vi, чем emacs, совершенно субъективно. Некоторые люди думают, что vi намного лучше, а другие думают с точностью до наоборот. Точно так же я думаю, что использовать .лучше, чем использовать ->, но, как и vi против emacs, это совершенно субъективно, и вы ничего не можете доказать
Orion Edwards 25.08.2016 21:41:40

Я использую ссылки, если мне не нужен ни один из них:

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

  • Вы можете сделать арифметику на указатель. Например,p += offset;

15
7.07.2015 21:22:16
Вы можете написать, &r + offsetгде rбыло объявлено в качестве ссылки
M.M 23.02.2017 05:13:46

Еще одно интересное использование ссылок - предоставление аргумента по умолчанию определенного пользователем типа:

class UDT
{
public:
   UDT() : val_d(33) {};
   UDT(int val) : val_d(val) {};
   virtual ~UDT() {};
private:
   int val_d;
};

class UDT_Derived : public UDT
{
public:
   UDT_Derived() : UDT() {};
   virtual ~UDT_Derived() {};
};

class Behavior
{
public:
   Behavior(
      const UDT &udt = UDT()
   )  {};
};

int main()
{
   Behavior b; // take default

   UDT u(88);
   Behavior c(u);

   UDT_Derived ud;
   Behavior d(ud);

   return 1;
}

Вариант по умолчанию использует аспект ссылок bind const на временную ссылку.

10
12.09.2008 17:59:56

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

С другой стороны, одно существенное различие между ссылками и указателями заключается в том, что временные ссылки, назначенные ссылкам const, остаются в силе до тех пор, пока ссылка на const не выйдет из области действия.

Например:

class scope_test
{
public:
    ~scope_test() { printf("scope_test done!\n"); }
};

...

{
    const scope_test &test= scope_test();
    printf("in scope\n");
}

напечатает:

in scope
scope_test done!

Это языковой механизм, который позволяет ScopeGuard работать.

19
3.07.2012 14:41:02
Вы не можете взять адрес ссылки, но это не значит, что они физически не занимают места. Запретить оптимизацию они наверняка могут.
Lightness Races in Orbit 24.04.2011 16:27:32
Влияние, несмотря на то, что «ссылка на стек не занимает места вообще» является явно ложным.
Lightness Races in Orbit 25.04.2011 23:09:47
@ Томалак, ну, это зависит также от компилятора. Но да, говорить, что это немного сбивает с толку. Я полагаю, было бы менее запутанным просто удалить это.
MSN 26.04.2011 21:52:17
В любом конкретном случае это может или не может. Так что «это не так», поскольку категорическое утверждение неверно. Это то, что я говорю. :) [Я не могу вспомнить, что стандарт говорит по этому вопросу; правила референтных членов могут распространять общее правило «ссылки могут занимать место», но у меня нет своей копии стандарта со мной здесь, на пляже: D]
Lightness Races in Orbit 26.04.2011 22:22:05

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

Компилятор хранит «ссылки» на переменные, связывая имя с адресом памяти; это его задача при компиляции переводить любое имя переменной в адрес памяти.

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

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

Теперь немного объяснения реального кода:

int a = 0;
int& b = a;

Здесь вы не создаете другую переменную, которая указывает на a; вы просто добавляете другое имя в содержимое памяти, содержащее значение a. Эта память теперь имеет два названия, aи b, и она может быть решена с помощью либо именем.

void increment(int& n)
{
    n = n + 1;
}

int a;
increment(a);

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

Теперь могут быть случаи, когда ваш компилятор может не узнать ссылку при компиляции, как при использовании переменной extern. Таким образом, ссылка может быть или не быть реализована как указатель в базовом коде. Но в приведенных мною примерах это, скорее всего, не будет реализовано с помощью указателя.

66
7.07.2015 21:25:53
Ссылка - это ссылка на l-значение, необязательно на переменную. Из-за этого он намного ближе к указателю, чем к реальному псевдониму (конструкции времени компиляции). Примеры выражений, на которые можно ссылаться: * p или даже * p ++
user3458 2.03.2009 16:27:28
Правильно, я просто указал на тот факт, что ссылка не всегда помещает новую переменную в стек, как это делает новый указатель.
Vincent Robert 3.03.2009 20:36:46
@VincentRobert: Он будет действовать так же, как указатель ... если функция встроена, и ссылка, и указатель будут оптимизированы. Если есть вызов функции, адрес объекта должен быть передан функции.
Ben Voigt 13.02.2012 23:08:55
int * p = NULL; int & r = * p; ссылка, указывающая на NULL; if (r) {} -> boOm;)
sree 4.10.2015 12:37:47
Этот фокус на этапе компиляции кажется приятным, пока вы не вспомните, что ссылки могут передаваться во время выполнения, после чего статический псевдоним выходит из окна. (И затем ссылки обычно реализуются как указатели, но стандарт не требует этот метод.)
underscore_d 11.10.2015 17:35:14

Что такое справочник по C ++ ( для программистов на C )

Ссылки можно рассматривать как постоянный указатель (не следует путать с указателем на значение константы!) С автоматической косвенностью, то есть компилятор будет применять *оператор для вас.

Все ссылки должны быть инициализированы ненулевым значением, иначе компиляция не удастся. Невозможно получить адрес ссылки - оператор адреса будет возвращать адрес ссылочного значения вместо этого - также невозможно выполнить арифметику для ссылок.

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

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

Рассмотрим следующее утверждение из C ++ FAQ :

Несмотря на то, что ссылка часто реализуется с использованием адреса на базовом ассемблере, не думайте о ссылке как о забавно выглядящем указателе на объект. Ссылка - это объект. Это не указатель на объект и не копия объекта. Он является объектом.

Но если ссылка действительно была объектом, как могли быть висячие ссылки? В неуправляемых языках невозможно, чтобы ссылки были более «безопасными», чем указатели - как правило, просто не существует способа надежного псевдонима значений за пределами границ области действия!

Почему я считаю ссылки на C ++ полезными

Исходя из фона C, ссылки на C ++ может выглядеть несколько глупой концепции, но все же следует использовать их вместо указателей , где это возможно: Автоматическая косвенность является удобным, а также ссылки становятся особенно полезными при работе с RAII - но не из - за какой - либо предполагаемой безопасности преимущество, а скорее потому, что они делают написание идиоматического кода менее неудобным.

RAII является одним из центральных понятий C ++, но он взаимодействует нетривиально с копированием семантики. Передача объектов по ссылке позволяет избежать этих проблем, поскольку копирование не требуется. Если бы ссылки на язык отсутствовали, вам пришлось бы вместо этого использовать указатели, которые более громоздки в использовании, что нарушало бы принцип проектирования языка, согласно которому наилучшее решение должно быть проще, чем альтернативы.

375
7.07.2015 21:27:53
@kriss: Нет, вы также можете получить зависшую ссылку, вернув автоматическую переменную по ссылке.
Ben Voigt 2.11.2010 06:14:59
@kriss: компилятору практически невозможно обнаружить в общем случае. Рассмотрим функцию-член, которая возвращает ссылку на переменную-член класса: это безопасно и не должно быть запрещено компилятором. Затем вызывающая сторона, у которой есть автоматический экземпляр этого класса, вызывает эту функцию-член и возвращает ссылку. Presto: свисающая ссылка. И да, это вызовет проблемы, @kriss: это моя точка зрения. Многие люди утверждают, что преимущество ссылок над указателями в том, что ссылки всегда действительны, но это не так.
Ben Voigt 2.11.2010 13:15:28
@kriss: Нет, ссылка на объект с автоматической продолжительностью хранения очень отличается от временного объекта. В любом случае, я просто привел контрпример к вашему утверждению, что вы можете получить недопустимую ссылку, только разыменовав недопустимый указатель. Кристоф прав: ссылки не более безопасны, чем указатели, программа, которая использует ссылки исключительно, может нарушить безопасность типов.
Ben Voigt 2.11.2010 15:15:07
Ссылки не являются своего рода указателем. Это новое имя для существующего объекта.
catphive 20.07.2011 01:28:00
@catphive: true, если вы используете семантику языка, не true, если вы действительно смотрите на реализацию; C ++ - гораздо более «волшебный» язык, чем C, и если вы удалите магию из ссылок, вы получите указатель
Christoph 23.07.2011 09:07:46

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

void increment(int *ptrint) { (*ptrint)++; }
void increment(int &refint) { refint++; }
void incptrtest()
{
    int testptr=0;
    increment(&testptr);
}
void increftest()
{
    int testref=0;
    increment(testref);
}

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

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

12
15.10.2009 01:57:37

Другое отличие состоит в том, что вы можете иметь указатели на тип void (а это означает указатель на что-либо), но ссылки на void запрещены.

int a;
void * p = &a; // ok
void & p = a;  //  forbidden

Я не могу сказать, что я действительно доволен этой конкретной разницей. Я бы предпочел, чтобы это было разрешено со смысловой ссылкой на что-либо с адресом и в остальном такое же поведение для ссылок. Это позволило бы определить некоторые эквиваленты функций библиотеки C, таких как memcpy, используя ссылки.

13
29.04.2015 15:00:54

Хотя ссылки и указатели используются для косвенного доступа к другому значению, между ссылками и указателями есть два важных различия. Во-первых, ссылка всегда ссылается на объект: определение ссылки без инициализации является ошибкой. Поведение присваивания является вторым важным отличием: назначение ссылки изменяет объект, к которому привязана ссылка; он не перепривязывает ссылку на другой объект. После инициализации ссылка всегда ссылается на один и тот же базовый объект.

Рассмотрим эти два фрагмента программы. В первом мы присваиваем один указатель другому:

int ival = 1024, ival2 = 2048;
int *pi = &ival, *pi2 = &ival2;
pi = pi2;    // pi now points to ival2

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

int &ri = ival, &ri2 = ival2;
ri = ri2;    // assigns ival2 to ival

Это назначение изменяет ival, значение, на которое ссылается ri, а не саму ссылку. После назначения две ссылки по-прежнему ссылаются на свои исходные объекты, и значение этих объектов теперь также остается неизменным.

34
23.10.2015 23:16:52
«ссылка всегда ссылается на объект» - это просто ложь
Ben Voigt 21.07.2017 18:29:56

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

С уважением, & rzej

14
6.02.2012 08:59:29
Ссылки и указатели являются ручками. Они оба дают вам семантику, где ваш объект передается по ссылке, но дескриптор копируется. Нет разницы. (Существуют и другие способы иметь дескрипторы, например, ключ для поиска в словаре)
Ben Voigt 7.11.2013 17:16:16
Я тоже раньше так думал. Но посмотрите связанную статью, объясняющую, почему это не так.
Andrzej 12.11.2013 09:08:25
@Andrzj: Это просто очень длинная версия одного предложения в моем комментарии: дескриптор скопирован.
Ben Voigt 12.11.2013 15:10:26
Мне нужно больше объяснений по этому поводу "Ручка скопирована". Я понимаю некоторую основную идею, но я думаю, что физически ссылка и указатель указывают место в памяти переменной. Это как псевдоним, который хранит значение переменной и обновляет ее, когда значение переменной изменяется или что-то еще? Я новичок, и, пожалуйста, не отмечайте это как глупый вопрос.
Asim 11.12.2013 23:13:44
@Andrzej Ложь. В обоих случаях происходит передача по значению. Ссылка передается по значению, а указатель передается по значению. Сказать иначе смущает новичков.
Miles Rout 27.04.2014 09:28:30

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

    void fun(int &a, int &b); // A common usage of references.
    int a = 0;
    int &b = a; // b is an alias for a. Not so common to use. 
26
1.01.2013 17:45:46

Ссылка не является другим именем, данным некоторой памяти. Это неизменный указатель, который автоматически разыменовывается при использовании. В основном это сводится к:

int& j = i;

Это внутренне становится

int* const j = &i;
18
10.01.2017 01:46:19
Это не то, что говорит стандарт C ++, и компилятору не требуется реализовывать ссылки так, как описано в вашем ответе.
jogojapan 26.02.2013 05:22:44
@jogojapan: Любой способ, допустимый для компилятора C ++ для реализации ссылки, также является допустимым способом реализации constуказателя. Эта гибкость не доказывает, что существует различие между ссылкой и указателем.
Ben Voigt 26.08.2015 23:00:21
@BenVoigt Может быть верно, что любая действительная реализация одного также является допустимой реализацией другого, но это не следует очевидным образом из определений этих двух понятий. Хороший ответ начался бы с определений и продемонстрировал, почему утверждение о том, что эти два понятия в конечном итоге одинаковы, верно. Этот ответ, кажется, является своего рода комментарием к некоторым другим ответам.
jogojapan 26.08.2015 23:41:55
Ссылка - это другое имя, данное объекту. Компилятору разрешено иметь любую реализацию, если вы не можете заметить разницу, это называется правилом «как будто». Важной частью здесь является то, что вы не можете отличить. Если вы можете обнаружить, что указатель не имеет хранилища, компилятор ошибся. Если вы можете обнаружить, что ссылка не имеет хранилища, компилятор по-прежнему соответствует.
sp2danny 6.09.2017 14:52:31

Эта программа может помочь в понимании ответа на вопрос. Это простая программа со ссылкой «j» и указателем «ptr», указывающим на переменную «x».

#include<iostream>

using namespace std;

int main()
{
int *ptr=0, x=9; // pointer and variable declaration
ptr=&x; // pointer to variable "x"
int & j=x; // reference declaration; reference to variable "x"

cout << "x=" << x << endl;

cout << "&x=" << &x << endl;

cout << "j=" << j << endl;

cout << "&j=" << &j << endl;

cout << "*ptr=" << *ptr << endl;

cout << "ptr=" << ptr << endl;

cout << "&ptr=" << &ptr << endl;
    getch();
}

Запустите программу и посмотрите на вывод, и вы поймете.

Кроме того, уделите 10 минут и посмотрите это видео: https://www.youtube.com/watch?v=rlJrrGV0iOg

10
15.03.2013 03:11:50

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

  • Ссылки составлены таким образом, что компилятору существенно легче отслеживать, какие ссылочные псевдонимы какие переменные. Две важные особенности очень важны: нет «ссылочной арифметики» и нет переназначения ссылок. Это позволяет компилятору выяснить, какие ссылки ссылаются на какие переменные во время компиляции.
  • Ссылки могут ссылаться на переменные, которые не имеют адресов памяти, например, те, которые компилятор выбирает для включения в регистры. Если вы берете адрес локальной переменной, компилятору будет очень трудно поместить его в регистр.

В качестве примера:

void maybeModify(int& x); // may modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // This function is designed to do something particularly troublesome
    // for optimizers. It will constantly call maybeModify on array[0] while
    // adding array[1] to array[2]..array[size-1]. There's no real reason to
    // do this, other than to demonstrate the power of references.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(array[0]);
        array[i] += array[1];
    }
}

Оптимизирующий компилятор может понять, что мы обращаемся к [0] и [1] довольно много. Очень хотелось бы оптимизировать алгоритм для:

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Do the same thing as above, but instead of accessing array[1]
    // all the time, access it once and store the result in a register,
    // which is much faster to do arithmetic with.
    register int a0 = a[0];
    register int a1 = a[1]; // access a[1] once
    for (int i = 2; i < (int)size; i++) {
        maybeModify(a0); // Give maybeModify a reference to a register
        array[i] += a1;  // Use the saved register value over and over
    }
    a[0] = a0; // Store the modified a[0] back into the array
}

Для такой оптимизации необходимо доказать, что ничто не может изменить массив [1] во время вызова. Это довольно легко сделать. i никогда не меньше 2, поэтому массив [i] никогда не может ссылаться на массив [1]. MaybeModify () получает a0 в качестве ссылки (псевдоним массива [0]). Поскольку здесь нет "ссылочной" арифметики, компилятору нужно только доказать, что MaybeModify никогда не получает адрес x, и он доказал, что ничего не меняет массив [1].

Это также должно доказать, что нет никакого способа, которым будущий вызов мог бы прочитать / записать [0], пока у нас есть временная регистровая копия этого в a0. Это часто бывает тривиально доказать, потому что во многих случаях очевидно, что ссылка никогда не сохраняется в постоянной структуре, такой как экземпляр класса.

Теперь сделайте то же самое с указателями

void maybeModify(int* x); // May modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Same operation, only now with pointers, making the
    // optimization trickier.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(&(array[0]));
        array[i] += array[1];
    }
}

Поведение такое же; только теперь гораздо сложнее доказать, что MaybeModify никогда не модифицирует массив [1], потому что мы уже дали ему указатель; кошка вышла из сумки. Теперь нужно сделать гораздо более сложное доказательство: статический анализ MaybeModify, чтобы доказать, что он никогда не записывает в & x + 1. Он также должен доказать, что он никогда не сохраняет указатель, который может ссылаться на массив [0], который просто как сложно.

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

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

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

F createF(int argument);

void extending()
{
    const F& ref = createF(5);
    std::cout << ref.getArgument() << std::endl;
};

Обычно временные объекты, такие как созданный при вызове createF(5), уничтожаются в конце выражения. Однако, связывая этот объект со ссылкой, refC ++ продлит срок жизни этого временного объекта до тех пор, пока он refне выйдет из области видимости.

72
8.10.2018 21:51:07
Правда, тело должно быть видимым. Однако определить, что maybeModifyне требуется адрес чего-либо связанного, xсущественно проще, чем доказать, что куча арифметики указателей не происходит.
Cort Ammon 11.09.2013 04:27:01
Я полагаю, что оптимизатор уже делает то, что «куча арифметики указателей не происходит» проверяет множество других причин.
Ben Voigt 11.09.2013 04:32:58
«Ссылки очень похожи на указатели» - семантически, в соответствующих контекстах - но с точки зрения сгенерированного кода, только в некоторых реализациях, а не через какое-либо определение / требование. Я знаю, что вы указали на это, и я не согласен с любым вашим постом в практическом плане, но у нас уже слишком много проблем с людьми, которые слишком много читают в кратких описаниях, таких как «ссылки похожи / обычно реализуются как указатели» ,
underscore_d 11.10.2015 17:31:38
У меня есть ощущение, что кто-то ошибочно пометил как устаревший комментарий в соответствии с тем void maybeModify(int& x) { 1[&x]++; }, что обсуждают другие комментарии выше
Ben Voigt 3.12.2015 23:28:49

Это основано на учебнике . То, что написано, делает это более понятным:

>>> The address that locates a variable within memory is
    what we call a reference to that variable. (5th paragraph at page 63)

>>> The variable that stores the reference to another
    variable is what we call a pointer. (3rd paragraph at page 64)

Просто чтобы запомнить это,

>>> reference stands for memory location
>>> pointer is a reference container (Maybe because we will use it for
several times, it is better to remember that reference.)

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

Посмотрите на следующее утверждение,

int Tom(0);
int & alias_Tom = Tom;

alias_Tomможно понимать как alias of a variable(отличается от typedef, который есть alias of a type) Tom. Также можно забыть, что терминология такого утверждения заключается в создании ссылки на Tom.

19
7.07.2015 21:35:57
И если у класса есть ссылочная переменная, он должен быть инициализирован либо nullptr, либо действительным объектом в списке инициализации.
Misgevolution 15.06.2015 17:32:10
Формулировка в этом ответе слишком запутанна, чтобы иметь реальный смысл. Кроме того, @Misgevolution, вы серьезно рекомендуете читателям инициализировать ссылку с nullptr? Вы на самом деле читали какую-либо другую часть этой темы, или ...?
underscore_d 11.10.2015 17:27:40
Мой плохой, извините за ту глупость, которую я сказал. Я должно быть был лишен сна к тому времени. 'initialize with nullptr' совершенно неверно.
Misgevolution 11.10.2015 18:09:41

Риск добавления к путанице, я хочу добавить некоторые входные данные, я уверен, что это в основном зависит от того, как компилятор реализует ссылки, но в случае gcc идея, что ссылка может указывать только на переменную в стеке на самом деле не правильно, возьмите это, например:

#include <iostream>
int main(int argc, char** argv) {
    // Create a string on the heap
    std::string *str_ptr = new std::string("THIS IS A STRING");
    // Dereference the string on the heap, and assign it to the reference
    std::string &str_ref = *str_ptr;
    // Not even a compiler warning! At least with gcc
    // Now lets try to print it's value!
    std::cout << str_ref << std::endl;
    // It works! Now lets print and compare actual memory addresses
    std::cout << str_ptr << " : " << &str_ref << std::endl;
    // Exactly the same, now remember to free the memory on the heap
    delete str_ptr;
}

Что выводит это:

THIS IS A STRING
0xbb2070 : 0xbb2070

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

int main(int argc, char** argv) {
    // In the actual new declaration let immediately de-reference and assign it to the reference
    std::string &str_ref = *(new std::string("THIS IS A STRING"));
    // Once again, it works! (at least in gcc)
    std::cout << str_ref;
    // Once again it prints fine, however we have no pointer to the heap allocation, right? So how do we free the space we just ignorantly created?
    delete &str_ref;
    /*And, it works, because we are taking the memory address that the reference is
    storing, and deleting it, which is all a pointer is doing, just we have to specify
    the address with '&' whereas a pointer does that implicitly, this is sort of like
    calling delete &(*str_ptr); (which also compiles and runs fine).*/
}

Что выводит это:

THIS IS A STRING

Поэтому ссылка является указателем под капотом, они оба просто хранят адрес памяти, где адрес, на который указывает адрес, не имеет значения, как вы думаете, что произойдет, если я вызову std :: cout << str_ref; ПОСЛЕ вызова delete & str_ref? Что ж, очевидно, что он хорошо компилируется, но вызывает ошибку сегментации во время выполнения, потому что он больше не указывает на допустимую переменную, у нас по существу есть неработающая ссылка, которая все еще существует (пока она не выпадает из области видимости), но бесполезна.

Другими словами, ссылка - это не что иное, как указатель, у которого абстрагирована механика указателя, что делает его более безопасным и простым в использовании (без случайной математики указателя, без перемешивания '.' И '->' и т. Д.), Предполагая, что вы не пытайтесь глупости, как мои примеры выше;)

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

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

Помните, что приведенные выше примеры - это просто примеры, демонстрирующие, что такое ссылка, вы никогда не захотите использовать ссылку таким образом! Для правильного использования ссылки здесь уже есть множество ответов, которые бьют по голове

13
14.10.2014 21:38:00

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

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

Помимо этого, есть, конечно, некоторые практические различия между указателями и ссылками. Синтаксис их использования, очевидно, отличается, и вы не можете «переместить» ссылки, иметь ссылки на ничто или иметь указатели на ссылки.

31
29.10.2014 17:17:23

Может быть, некоторые метафоры помогут; В контексте вашего рабочего стола экрана -

  • Ссылка требует от вас указать фактическое окно.
  • Указатель требует расположения части пространства на экране, которая, как вы уверены, будет содержать ноль или более экземпляров этого типа окна.
8
27.12.2014 13:00:18

Ссылка на указатель возможна в C ++, но обратное невозможно, то есть указатель на ссылку невозможен. Ссылка на указатель обеспечивает более чистый синтаксис для изменения указателя. Посмотрите на этот пример:

#include<iostream>
using namespace std;

void swap(char * &str1, char * &str2)
{
  char *temp = str1;
  str1 = str2;
  str2 = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap(str1, str2);
  cout<<"str1 is "<<str1<<endl;
  cout<<"str2 is "<<str2<<endl;
  return 0;
}

И рассмотрите версию C вышеупомянутой программы. В C вы должны использовать указатель на указатель (множественное косвенное обращение), и это приводит к путанице, и программа может выглядеть сложной.

#include<stdio.h>
/* Swaps strings by swapping pointers */
void swap1(char **str1_ptr, char **str2_ptr)
{
  char *temp = *str1_ptr;
  *str1_ptr = *str2_ptr;
  *str2_ptr = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap1(&str1, &str2);
  printf("str1 is %s, str2 is %s", str1, str2);
  return 0;
}

Посетите следующую для получения дополнительной информации о ссылке на указатель:

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

#include <iostream>
using namespace std;

int main()
{
   int x = 10;
   int *ptr = &x;
   int &*ptr1 = ptr;
}
16
8.07.2015 09:06:24

Разница заключается в том, что непостоянная переменная-указатель (не путать с указателем на постоянную) может быть изменена в течение некоторого времени во время выполнения программы, требует использования семантики указателя (&, *), а ссылки могут быть установлены после инициализации только (поэтому вы можете установить их только в списке инициализатора конструктора, но никак иначе) и использовать обычную семантику доступа к значениям. В основном ссылки были введены для поддержки перегрузки операторов, как я читал в одной очень старой книге. Как кто-то заявил в этой теме - указатель может быть установлен в 0 или любое другое значение, которое вы хотите. 0 (NULL, nullptr) означает, что указатель инициализируется ничем. Ошибка разыменования нулевого указателя. Но на самом деле указатель может содержать значение, которое не указывает на какое-то правильное расположение в памяти. Ссылки, в свою очередь, стараются не позволять пользователю инициализировать ссылку на что-то, на что нельзя ссылаться из-за того, что вы всегда предоставляете ей правильное значение. Хотя существует множество способов инициализировать ссылочную переменную в неправильном месте памяти, лучше не вдаваться в детали. На уровне машины и указатель, и ссылка работают равномерно - с помощью указателей. Допустим, в основных ссылках приведены синтаксические сахара. Rvalue ссылки отличаются от этого - они, естественно, объекты стека / кучи. Хотя существует множество способов инициализировать ссылочную переменную в неправильном месте памяти, лучше не вдаваться в детали. На уровне машины и указатель, и ссылка работают равномерно - с помощью указателей. Допустим, в основных ссылках приведены синтаксические сахара. Rvalue ссылки отличаются от этого - они, естественно, объекты стека / кучи. Хотя существует множество способов инициализировать ссылочную переменную в неправильном месте памяти, лучше не вдаваться в детали. На уровне машины и указатель, и ссылка работают равномерно - с помощью указателей. Допустим, в основных ссылках приведены синтаксические сахара. Rvalue ссылки отличаются от этого - они, естественно, объекты стека / кучи.

5
24.04.2016 13:27:53

Разница между указателем и ссылкой

Указатель может быть инициализирован в 0, а ссылка - нет. Фактически, ссылка должна также ссылаться на объект, но указатель может быть нулевым указателем:

int* p = 0;

Но мы не можем иметь, int& p = 0;а также int& p=5 ;.

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

Int x = 0;
Int y = 5;
Int& p = x;
Int& p1 = y;

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

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

Int a = 6, b = 5;
Int& rf = a;

Cout << rf << endl; // The result we will get is 6, because rf is referencing to the value of a.

rf = b;
cout << a << endl; // The result will be 5 because the value of b now will be stored into the address of a so the former value of a will be erased

Еще один момент: когда у нас есть шаблон, такой как шаблон STL, такой шаблон класса всегда будет возвращать ссылку, а не указатель, чтобы облегчить чтение или присвоение нового значения с помощью оператора []:

Std ::vector<int>v(10); // Initialize a vector with 10 elements
V[5] = 5; // Writing the value 5 into the 6 element of our vector, so if the returned type of operator [] was a pointer and not a reference we should write this *v[5]=5, by making a reference we overwrite the element by using the assignment "="
6
8.02.2017 13:43:19
Мы еще можем иметь const int& i = 0.
Revolver_Ocelot 6.01.2017 14:24:37
В этом случае ссылка будет использоваться только в read, мы не можем изменить эту ссылку const, даже используя const_cast, потому что const_cast принимает только указатель, а не ссылку.
dhokar.w 6.01.2017 14:37:44
const_cast работает со ссылками довольно хорошо: coliru.stacked-crooked.com/a/eebb454ab2cfd570
Revolver_Ocelot 6.01.2017 16:52:27
вы делаете приведение к ссылке, а не приведение ссылки, попробуйте это; const int & i =; const_cast <INT> (я); я пытаюсь выбросить константу ссылки, чтобы сделать возможным запись и присвоение новой ценности ссылке, но это невозможно. пожалуйста, сосредоточьтесь!
dhokar.w 6.01.2017 19:38:23

Я чувствую, что есть еще один момент, который здесь не освещался.

В отличие от указателей, ссылки синтаксически эквивалентны объекту, на который они ссылаются, то есть любая операция, которая может быть применена к объекту, работает для ссылки и с точно таким же синтаксисом (за исключением, конечно, инициализации).

Хотя это может показаться поверхностным, я считаю, что это свойство имеет решающее значение для ряда функций C ++, например:

  • Шаблоны . Поскольку параметры шаблона имеют тип утка, синтаксические свойства типа - это все, что имеет значение, поэтому часто один и тот же шаблон может использоваться с обоими Tи T&.
    (или std::reference_wrapper<T>который все еще полагается на неявное приведение к T&)
    Шаблоны, которые охватывают оба T&и T&&даже более распространены.

  • Lvalues . Рассмотрим оператор str[0] = 'X';без ссылок, он будет работать только для c-strings ( char* str). Возвращение символа по ссылке позволяет пользовательским классам иметь одинаковые обозначения.

  • Копировать конструкторы . Синтаксически имеет смысл передавать объекты для копирования конструкторов, а не указатели на объекты. Но у конструктора копирования просто нет возможности получить объект по значению - это приведет к рекурсивному вызову того же конструктора копирования. Это оставляет ссылки как единственный вариант здесь.

  • Операторские перегрузки . С помощью ссылок можно ввести косвенное обращение к вызову оператора, скажем, operator+(const T& a, const T& b)с сохранением той же записи инфикса. Это также работает для обычных перегруженных функций.

Эти пункты дают значительную часть C ++ и стандартной библиотеки, так что это довольно важное свойство ссылок.

10
6.07.2017 19:48:46
« неявное приведение » приведение - это синтаксическая конструкция, она существует в грамматике; актерский состав всегда явный
curiousguy 22.10.2017 23:58:15

Я всегда решаю по этому правилу из C ++ Core Guidelines:

Предпочитайте T *, а не T &, когда «без аргумента» является допустимым

2
19.10.2017 11:50:17
Использование перегруженных функций, которые не принимают указатели вместо разрешения nullptr, или использование терминальных объектов, является гораздо лучшим решением, чем использование в nullptrкачестве аргументов.
Clearer 19.12.2017 09:01:34
@Clearer Возможно, он чище, но иногда вам просто нужно быстро передать указатели, и могут быть случаи, когда вам все равно, является ли указатель нулевым или нет.
Hitokage 19.12.2017 10:02:06

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

void fn1(std::string s);
void fn2(const std::string& s);
void fn3(std::string& s);
void fn4(std::string* s);

void bar() {
    std::string x;
    fn1(x);  // Cannot modify x
    fn2(x);  // Cannot modify x (without const_cast)
    fn3(x);  // CAN modify x!
    fn4(&x); // Can modify x (but is obvious about it)
}

Вернувшись в C, вызов, который выглядит так, fn(x)может быть передан только по значению, поэтому он определенно не может быть изменен x; чтобы изменить аргумент, вам нужно передать указатель fn(&x). Таким образом, если аргумент не предшествовал, &вы знали, что он не будет изменен. (Обратное, &значит измененное, не соответствует действительности, потому что иногда вам придется передавать большие структуры только для чтения по constуказателю.)

Некоторые утверждают, что это такая полезная функция при чтении кода, что параметры указателя всегда следует использовать для изменяемых параметров, а не для constссылок, даже если функция никогда не ожидает a nullptr. То есть эти люди утверждают, что подписи функций, как fn3()указано выше, не должны быть разрешены. Руководящие принципы стиля Google C ++ являются примером этого.

9
25.06.2018 18:58:31

У меня есть аналогия для ссылок и указателей, думаю, что ссылки - это другое имя объекта, а указатели - адрес объекта.

// receives an alias of an int, an address of an int and an int value
public void my_function(int& a,int* b,int c){
    int d = 1; // declares an integer named d
    int &e = d; // declares that e is an alias of d
    // using either d or e will yield the same result as d and e name the same object
    int *f = e; // invalid, you are trying to place an object in an address
    // imagine writting your name in an address field 
    int *g = f; // writes an address to an address
    g = &d; // &d means get me the address of the object named d you could also
    // use &e as it is an alias of d and write it on g, which is an address so it's ok
}
2
12.11.2017 00:05:42