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

вопрос создан в Sun, Jun 17, 2018 12:00 AM

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

Но в чем различия?

Резюме из ответов и ссылок ниже:

  1. Указатель может быть переназначен любое количество раз, в то время как ссылка не может быть переназначена после привязки.
  2. Указатели не могут указывать нигде (NULL), тогда как ссылка всегда ссылается на объект.
  3. Вы не можете использовать адрес ссылки, как вы можете использовать указатели.
  4. Нет "ссылочной арифметики" (но вы можете взять адрес объекта, на который указывает ссылка, и сделать арифметику указателя на нем, как в &obj + 5).

Чтобы уточнить неправильное представление:

  

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

int &ri = i;
     

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

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

Как правило,

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

Интересное чтение:

2974
  1. Я думаю, что точка 2 должна быть «Указатель может быть НЕДЕЙСТВИТЕЛЕН, а ссылка - нет. Только некорректный код может создать нулевую ссылку, а его поведение не определено».
    2010-10-08 17: 21: 24Z
  2. Указатели - это просто другой тип объекта, и, как и любой объект в C ++, они могут быть переменными. Ссылки, с другой стороны, никогда не являются объектами, только переменными.
    2012-06-16 10: 14: 02Z
  3. Это компилируется без предупреждений: int &x = *(int*)0; на gcc. Ссылка действительно может указывать на NULL.
    2012-08-13 09: 00: 00Z
  4. ссылка на псевдоним переменной
    2013-12-23 08: 53: 04Z
  5. Мне нравится, как самое первое предложение является полной ошибкой. Ссылки имеют свою семантику.
    2014-06-01 01: 58: 45Z
30 ответов                              30                         
  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), тогда как ссылка разделяет тот же адрес памяти (с исходной переменной), но также занимает некоторое место в стеке. Поскольку ссылка имеет тот же адрес, что и сама исходная переменная, можно с уверенностью рассматривать ссылку как другое имя для той же переменной. Примечание. То, на что указывает указатель, может быть в стеке или куче. Так же ссылка. Мое утверждение в этом утверждении не в том, что указатель должен указывать на стек. Указатель - это просто переменная, которая содержит адрес памяти. Эта переменная находится в стеке. Поскольку ссылка имеет свое место наk, и поскольку адрес совпадает с переменной, на которую он ссылается. Подробнее о стеке против кучи . Это означает, что существует реальный адрес ссылки, которую компилятор вам не скажет.

    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. Указатель - это переменная, которая содержит адрес памяти. Независимо от того, как реализована ссылка, ссылка имеет тот же адрес памяти, что и элемент, на который она ссылается.

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

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

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

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

1545
2018-01-26 22: 58: 15Z
  1. ... но разыменование NULL не определено. Например, вы не можете проверить, имеет ли ссылка значение NULL (например, & ref == NULL).
    2008-09-11 22: 07: 03Z
  2. Номер 2 является не истиной. Ссылки - это не просто «другое имя для той же переменной». Ссылки могут передаваться функциям, храниться в классах и т. Д. Способом, очень похожим на указатели. Они существуют независимо от переменных, на которые они указывают.
    2008-09-12 23: 37: 54Z
  3. Брайан, стек не имеет значения. Ссылки и указатели не должны занимать место в стеке. Они оба могут быть размещены в куче.
    2008-09-19 04: 33: 38Z
  4. Брайан, тот факт, что переменная (в данном случае указатель или ссылка) требует пробела, не означает, что она требует места в стеке. Указатели и ссылки могут не только указывать на кучу, но на самом деле они могут быть размещены в куче.
    2008-09-19 15: 26: 29Z
  5. еще одна важная разница: ссылки не могут быть вставлены в массив
    2009-02-27 21: 10: 17Z

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

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

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

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

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

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

  

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

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

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

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

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

    
346
2015-07-07 21: 27: 53Z
  1. @ kriss: Нет, вы также можете получить зависшую ссылку, вернув автоматическую переменную по ссылке.
    2010-11-02 06: 14: 59Z
  2. @ kriss: компилятору практически невозможно обнаружить в общем случае. Рассмотрим функцию-член, которая возвращает ссылку на переменную-член класса: это безопасно и не должно быть запрещено компилятором. Затем вызывающая сторона, у которой есть автоматический экземпляр этого класса, вызывает эту функцию-член и возвращает ссылку. Presto: свисающая ссылка. И да, это вызовет проблемы, @kriss: это моя точка зрения. Многие люди утверждают, что преимущество ссылок над указателями в том, что ссылки всегда действительны, но это не так.
    2010-11-02 13: 15: 28Z
  3. @ kriss: Нет, ссылка на объект с автоматической продолжительностью хранения очень отличается от временного объекта. В любом случае, я просто привел контрпример к вашему утверждению, что вы можете получить недопустимую ссылку, только разыменовав недопустимый указатель. Кристоф прав: ссылки не более безопасны, чем указатели, программа, которая использует исключительно ссылки, может нарушить безопасность типов.
    2010-11-02 15: 15: 07Z
  4. Ссылки не являются своего рода указателем. Это новое имя для существующего объекта.
    2011-07-20 01: 28: 00Z
  5. @ catphive: true, если вы используете семантику языка, не true, если вы действительно смотрите на реализацию; C ++ - гораздо более «волшебный» язык, чем C, и если вы удалите магию из ссылок, вы получите указатель
    2011-07-23 09: 07: 46Z

Если вы хотите быть по-настоящему педантичным, есть одна вещь, которую вы можете сделать со ссылкой, которую вы не можете сделать с указателем: продлить время жизни временного объекта. В 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, он не сможет скомпилироваться. Вы не можете привязать неконстантную ссылку к временному объекту, и при этом вы не можете взять его адрес в этом отношении.

    
172
2010-10-08 16: 52: 17Z
  1. но каков вариант использования для этого?
    2009-10-22 14: 10: 33Z
  2. Что ж, s3_copy создаст временный файл, а затем скопирует его в s3_copy, тогда как s3_reference напрямую использует временный. Затем, чтобы быть по-настоящему педантичным, вам нужно взглянуть на оптимизацию возвращаемого значения, при которой компилятору разрешается исключать конструкцию копирования в первом случае.
    2009-10-22 18: 14: 56Z
  3. @ digitalSurgeon: магия там довольно мощная. Время жизни объекта увеличивается за счет факта привязки const & и только тогда, когда ссылка выходит из области видимости, деструктор ссылочного типа actual (по сравнению с ссылочным типом, который может быть базовым) называется. Поскольку это ссылка, в промежутках между ними не будет никакого среза.
    2010-01-14 17: 06: 23Z
  4. Обновление для C ++ 11: в последнем предложении должно быть написано «Невозможно связать неконстантную ссылку lvalue с временной», потому что вы можете привязать неконстантную ссылку rvalue к временному, и он будет иметь такое же поведение, продлевающее жизнь.
    2013-11-10 20: 14: 26Z
  5. @ AhmadMushtaq: ключом использования этого является производные классы . Если не задействовано наследование, вы также можете использовать семантику значений, которая будет дешевой или бесплатной из-за конструкции RVO /move. Но если у вас есть Animal x = fast ? getHare() : getTortoise(), то x столкнется с классической проблемой нарезки, а Animal& x = ... будет работать правильно.
    2017-11-02 11: 04: 03Z

Вопреки распространенному мнению, существует ссылка на 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Ссылки от Джима Хислопа и Херба Саттера.

Еще один пример опасности разыменования нулевого указателя см. в разоблачении неопределенное поведение при попытке переноса кода на другую платформу от Raymond Chen.

    
116
2018-01-08 22: 57: 42Z
  1. Данный код содержит неопределенное поведение. Технически, вы ничего не можете сделать с нулевым указателем, кроме как установить его и сравнить. Как только ваша программа вызывает неопределенное поведение, она может делать все что угодно, в том числе работать правильно, пока вы не дадите демонстрацию большому боссу.
    2008-09-12 16: 00: 14Z
  2. Метка
    имеет действительный аргумент. Аргумент, что указатель может быть НЕДЕЙСТВИТЕЛЕН, и вы должны проверить его, также не является реальным: если вы говорите, что функция требует не-НЕДЕЙСТВИТЕЛЬНЫЙ, то вызывающая сторона должна сделать это. поэтому, если вызывающий не делает этого, он вызывает неопределенное поведение. точно так же, как Марк сделал с плохой ссылкой
    2009-02-27 21: 14: 41Z
  3. Описание является ошибочным. Этот код может создавать или не создавать ссылку на NULL. Его поведение не определено. Это может создать совершенно правильную ссылку. Может вообще не создать ссылку.
    2011-08-20 11: 41: 33Z
  4. @ Дэвид Шварц, если бы я говорил о том, как все должно работать в соответствии со стандартом, вы были бы правы. Но это не то, о чем я говорю - я говорю о фактическом наблюдаемом поведении с очень популярным компилятором и об экстраполировании, основываясь на моих знаниях типичных компиляторов и архитектур ЦП, на то, что, вероятно, будет случиться. Если вы считаете, что ссылки превосходят указатели, потому что они безопаснее, и не считаете, что ссылки могут быть плохими, однажды вы будете озадачены простой проблемой, как и я.
    2011-08-22 02: 11: 48Z
  5. Неверная разыменование нулевого указателя. Любая программа, которая делает это, даже для инициализации ссылки, неверна. Если вы инициализируете ссылку из указателя, вы всегда должны проверять, что указатель действителен. Даже если это удастся, базовый объект может быть удален в любое время, оставляя ссылку для ссылки на несуществующий объект, верно? То, что вы говорите, это хороший материал. Я думаю, что реальная проблема заключается в том, что ссылка не должна проверяться на «нулевое значение», когда вы ее видите, и указатель должен быть как минимум утвержден.
    2017-03-22 14: 07: 22Z

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

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

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

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

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

    
114
2018-04-12 15: 59: 44Z
  1. Я думаю, что это лучший ответ на сегодняшний день. Другие говорят о ссылках и указателях, как будто они разные звери, а затем рассказывают, как они отличаются по поведению. Это не облегчает имхо. Я всегда понимал ссылки как T* const с другим синтаксическим сахаром (что исключает множество * и &из вашего кода).
    2017-01-10 01: 34: 05Z
  2. "Цель константного указателя можно заменить, взяв его адрес и используя константное приведение." Это неопределенное поведение. Увидеть stackoverflow.com/questions/25209838 /… для подробностей.
    2018-06-22 04: 51: 40Z
  3. Попытка изменить или референт ссылки, или значение константного указателя (или любого константного скаляра) является равенством недопустимым. Что вы можете сделать: удалить квалификацию const, которая была добавлена ​​неявным преобразованием: int i; int const *pci = &i; /* implicit conv to const int* */ int *pi = const_cast<int*>(pci); в порядке.
    2018-06-29 16: 00: 18Z
  4. Разница здесь в UB по сравнению с буквально невозможной. В C ++ нет синтаксиса, который позволял бы вам изменять ориентиры.
    2018-06-29 16: 03: 01Z
  5. Не невозможно, сложнее, вы можете просто получить доступ к области памяти указателя, который моделирует эту ссылку, и изменить ее содержимое. Это, безусловно, можно сделать.
    2018-11-17 21: 39: 21Z

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

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

foo.bar явно явно превосходит foo->bar так же, как и vi явно превосходит Emacs : -)

    
107
2015-07-07 21: 21: 27Z
  1. @ Orion Edwards > членский доступ с указателями использует - > > членский доступ со ссылками использует. Это не на 100% верно. Вы можете иметь ссылку на указатель. В этом случае вы получите доступ к элементам указателя, на который нет ссылок, используя - > struct Node {Node * next; }; Узел * первый; //p - ссылка на указатель void foo (Node * & p) {p- > next = first; } Node * bar = новый узел; Foo (бар); - OP: Вы знакомы с понятиями rvalues ​​и lvalues?
    2008-09-12 12: 57: 12Z
  2. Умные указатели имеют оба. (методы класса интеллектуальных указателей) и - > (методы базового типа).
    2014-04-09 09: 11: 06Z
  3. @ user6105 Заявление Ориона Эдвардса на самом деле на 100% верно. "Доступ к членам []] указателя, на который нет ссылок. Указатель не имеет членов. Объект, на который ссылается указатель, имеет члены, и доступ к ним - это именно то, что -> предоставляет для ссылок на указатели, так же как и для самого указателя.
    2015-04-15 17: 37: 03Z
  4. почему . и -> как-то связаны с vi и emacs:)
    2016-08-21 00: 45: 28Z
  5. @ artM - это была шутка, и, вероятно, она не имеет смысла для не носителей английского языка. Мои извенения. Объяснить, лучше ли vi, чем emacs, совершенно субъективно. Некоторые люди думают, что vi намного лучше, а другие думают с точностью до наоборот. Точно так же я думаю, что использование . лучше, чем использование ->, но, как и vi против emacs, оно полностью субъективно, и вы ничего не можете доказать
    2016-08-25 21: 41: 40Z

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

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

Как пример:

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].

Это также должно доказать, что нет никаких способов, которыми будущий вызов мог бы прочитать /записать a [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), уничтожаются в конце выражения. Однако, связав этот объект со ссылкой ref, C ++ продлит срок службы этого временного объекта до тех пор, пока ref не выйдет за пределы области.

    
65
2018-10-08 21: 51: 07Z
  1. Правда, тело должно быть видимым. Однако определить, что maybeModify не использует адрес чего-либо, связанного с x, значительно проще, чем доказать, что связка арифметики указателей не происходит.
    2013-09-11 04: 27: 01Z
  2. Я полагаю, что оптимизатор уже проверяет, что "куча арифметики указателей не встречается", проверяет множество других причин.
    2013-09-11 04: 32: 58Z
  3. «Ссылки очень похожи на указатели» - семантически, в соответствующих контекстах - но с точки зрения сгенерированного кода, только в некоторых реализациях, а не через какое-либо определение /требование. Я знаю, что вы указали на это, и я не согласен ни с одним из ваших постов в практическом плане, но у нас уже слишком много проблем с людьми, слишком много читающими краткие описания, такие как «ссылки похожи /обычно реализуются как указатели» .
    2015-10-11 17: 31: 38Z
  4. У меня есть ощущение, что кто-то ошибочно пометил как устаревший комментарий в соответствии с void maybeModify(int& x) { 1[&x]++; }, который обсуждают другие комментарии выше
    2015-12-03 23: 28: 49Z

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

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

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

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

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

int a = 0;
int& b = a;

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

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

int a;
increment(a);

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

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

    
63
2015-07-07 21: 25: 53Z
  1. Ссылка - это ссылка на l-значение, необязательно на переменную. Из-за этого он намного ближе к указателю, чем к реальному псевдониму (конструкции времени компиляции). Примеры выражений, на которые можно ссылаться: * p или даже * p ++
    2009-03-02 16: 27: 28Z
  2. Правильно, я просто указал на тот факт, что ссылка не всегда может помещать новую переменную в стек, как будет новый указатель.
    2009-03-03 20: 36: 46Z
  3. @ VincentRobert: Он будет действовать так же, как указатель ... если функция встроена, и ссылка, и указатель будут оптимизированы. Если есть вызов функции, адрес объекта нужно будет передать в функцию.
    2012-02-13 23: 08: 55Z
  4. int * p = NULL; int &r = * p; ссылка, указывающая на NULL; if (r) {} - > boOm;)
    2015-10-04 12: 37: 47Z
  5. Этот фокус на этапе компиляции кажется приятным, пока вы не вспомните, что ссылки могут передаваться во время выполнения, после чего статический псевдоним выходит из окна. (Кроме того, ссылки обычно реализуются как указатели, но стандарт не требует этого метода.)
    2015-10-11 17: 35: 14Z

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

    
38
2014-06-01 22: 37: 05Z
  1. См. ответ Марка Рэнсома для контрпримера. Это наиболее часто утверждаемый миф о ссылках, но это миф. Единственная гарантия, которую вы имеете по стандарту, заключается в том, что у вас сразу же появляется UB, когда у вас есть ссылка NULL. Но это все равно, что сказать: «Эта машина безопасна, она никогда не сможет сойти с дороги. (Мы не несем никакой ответственности за то, что может случиться, если вы все равно уведите ее с дороги. Она может просто взорваться.)»
    2014-06-13 11: 36: 07Z
  2. @ cmaster: В допустимой программе ссылка не может быть нулевой. Но указатель может. Это не миф, это факт.
    2014-08-08 04: 42: 07Z
  3. @ Mehrdad Да, действительные программы остаются в дороге. Но нет никакого барьера трафика, чтобы обеспечить выполнение вашей программы. На больших участках дороги фактически отсутствуют маркировки. Поэтому очень легко сойти с дороги ночью. И для отладки таких ошибок крайне важно, чтобы вы знали , что это может произойти: нулевая ссылка может распространиться до того, как произойдет сбой вашей программы, так же, как и нулевой указатель. И когда это произойдет, у вас есть код, такой как void Foo::bar() { virtual_baz(); }, который segfaults. Если вы не знаете, что ссылки могут быть нулевыми, вы не можете отследить нулевое значение до его источника.
    2014-12-29 10: 43: 30Z
  4. int * p = NULL; int &r = * p; ссылка, указывающая на NULL; if (r) {} - > boOm;) -
    2015-10-04 12: 43: 33Z
  5. @ sree int &r=*p; - неопределенное поведение. В этот момент у вас нет «ссылки, указывающей на NULL», у вас есть программа, которую больше нельзя рассуждать о вообще .
    2017-03-28 18: 46: 25Z

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

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

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, а не саму ссылку. После назначения две ссылки по-прежнему ссылаются на свои исходные объекты, и значение этих объектов теперь также остается неизменным.

    
33
2015-10-23 23: 16: 52Z
  1. "ссылка всегда ссылается на объект", просто полностью ложна
    2017-07-21 18: 29: 56Z

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

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

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

    
28
2014-10-29 17: 17: 23Z

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

    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. 
    
22
2013-01-01 17: 45: 46Z

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

С другой стороны, одно из основных различий между ссылками и указателями заключается в том, что временные ссылки, назначенные ссылкам 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 работать.

    
18
2012-07-03 14: 41: 02Z
  1. Вы не можете взять адрес ссылки, но это не значит, что они физически не занимают места. За исключением оптимизаций, они наверняка могут.
    2011-04-24 16: 27: 32Z
  2. Несмотря на это, "ссылка на стек не занимает места вообще" является явно ложной.
    2011-04-25 23: 09: 47Z
  3. @ Tomalak, ну, это зависит также от компилятора. Но да, говорить это немного сбивает с толку. Я полагаю, было бы менее запутанным просто удалить это.
    2011-04-26 21: 52: 17Z
  4. В любом конкретном случае это может или не может. Так что «это не так», поскольку категорическое утверждение неверно. Это то, что я говорю. :) [Я не могу вспомнить, что стандарт говорит по этому вопросу; правила ведения участников могут распространять общее правило «ссылки могут занимать место», но у меня нет своей копии стандарта здесь, на пляже: D]
    2011-04-26 22: 22: 05Z

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

>>> 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) Tom62. Можно также забыть, что терминология такого утверждения означает создание ссылки 0306 /p11 /11/11/11/115011/11/11/11/11/11/11 /p> 115011/11/11 /p> 00001>.     

18
2015-07-07 21: 35: 57Z
  1. И если у класса есть ссылочная переменная, его следует инициализировать либо нулевым, либо действительным объектом в списке инициализации.
    2015-06-15 17: 32: 10Z
  2. Формулировка в этом ответе слишком запутанная, чтобы его можно было реально использовать. Кроме того, @Misgevolution, вы серьезно рекомендуете читателям инициализировать ссылку с Tom? Вы действительно читали какую-либо другую часть этой темы или ...?
    2015-10-11 17: 27: 40Z
  3. Мой плохой, извините за ту глупость, которую я сказал. Должно быть, я был лишен сна к тому времени. 'initialize with nullptr' совершенно неверно.
    2015-10-11 18: 09: 41Z

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

nullptr

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

int& j = i;
    
18
2017-01-10 01: 46: 19Z
  1. Это не то, что говорит стандарт C ++, и компилятору не требуется реализовывать ссылки способом, описанным в вашем ответе.
    2013-02-26 05: 22: 44Z
  2. @ jogojapan: Любой способ, допустимый для компилятора C ++ для реализации ссылки, также является допустимым способом реализации указателя
    int* const j = &i;
    
    . Такая гибкость не доказывает, что между ссылкой и указателем есть разница.
    2015-08-26 23: 00: 21Z
  3. @ BenVoigt Может быть верно, что любая допустимая реализация одного также является допустимой реализацией другого, но это не следует очевидным образом из определений эти два понятия. Хороший ответ начался бы с определений и продемонстрировал, почему утверждение о том, что эти два понятия в конечном итоге одинаковы, верно. Этот ответ, кажется, является своего рода комментарием к некоторым другим ответам.
    2015-08-26 23: 41: 55Z
  4. Ссылка - это другое имя, данное объекту. Компилятору разрешено иметь любую реализацию, если вы не можете заметить разницу, это называется правилом «как будто». Важной частью здесь является то, что вы не можете увидеть разницу. Если вы можете обнаружить, что указатель не имеет хранилища, компилятор ошибся. Если вы обнаружите, что ссылка не имеет хранилища, компилятор по-прежнему соответствует.
    2017-09-06 14: 52: 31Z

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

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

  • Вы можете выполнять арифметику с указателем. Например, const

15
2015-07-07 21: 22: 16Z
  1. Вы можете написать p += offset;, где &r + offset был объявлен как ссылка
    2017-02-23 05: 13: 46Z

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

r

И рассмотрите версию C вышеупомянутой программы. В 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;
}

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

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

#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;
}
    
15
2015-07-08 09: 06: 24Z

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

С уважением, &Амп; rzej р>     

14
2012-02-06 08: 59: 29Z
  1. Ссылки и указатели являются дескрипторами. Они оба дают вам семантику, в которой ваш объект передается по ссылке, а handle копируется. Нет разницы. (Существуют и другие способы иметь дескрипторы, например, ключ для поиска в словаре)
    2013-11-07 17: 16: 16Z
  2. Я тоже привык думать так. Но см. Связанную статью, объясняющую, почему это не так.
    2013-11-12 09: 08: 25Z
  3. @ Andrzj: Это просто очень длинная версия одного предложения в моем комментарии: Дескриптор скопирован.
    2013-11-12 15: 10: 26Z
  4. Мне нужно больше объяснений по этому поводу "Дескриптор скопирован". Я понимаю некоторую основную идею, но я думаю, что физически ссылка и указатель указывают место в памяти переменной. Это как псевдоним, который хранит значение переменной и обновляет ее, когда значение переменной изменяется или что-то еще? Я новичок, и, пожалуйста, не отмечайте это как глупый вопрос.
    2013-12-11 23: 13: 44Z
  5. @ Andrzej False. В обоих случаях происходит передача по значению. Ссылка передается по значению, а указатель передается по значению. Иначе говоря, это сбивает с толку новичков.
    2014-04-27 09: 28: 30Z

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

#include <iostream>
using namespace std;

int main()
{
   int x = 10;
   int *ptr = &x;
   int &*ptr1 = ptr;
}

Который выводит это:

#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).*/
}

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

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

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

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

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

    
13
2014-10-14 21: 38: 00Z

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

THIS IS A STRING

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

    
12
2015-04-29 15: 00: 54Z

Прямой ответ

Что такое ссылка в C ++? Некоторый конкретный экземпляр типа, который не является типом объекта .

Что такое указатель в C ++? Некоторый конкретный экземпляр типа, который является типом объекта .

Из определения типа объекта в ISO C ++ :

  

Тип object - это тип (возможно, cv -qualified), который не является типом функции, не ссылочным типом и не cv void.

Может быть важно знать, что тип объекта является категорией верхнего уровня юниверса типа в C ++. Справочник также является категорией верхнего уровня. Но указатель - нет.

Указатели и ссылки упоминаются вместе в контексте составного типа эм> . Это в основном связано с природой синтаксиса объявления, унаследованного от (и расширенного) C, который не имеет ссылок. (Кроме того, начиная с C ++ 11 существует более одного вида деклараторов ссылок, в то время как указатели все еще «единичны»:

int a;
void * p = &a; // ok
void & p = a;  //  forbidden
+ & против &&.) Таким образом, в этом контексте составляется язык, специфичный для «расширения» с аналогичным стилем C несколько разумно. (Я по-прежнему буду утверждать, что синтаксис деклараторов тратит впустую синтаксическую выразительность много , расстраивает как пользователей, так и реализации. Таким образом, все они не квалифицированы как встроенные в новом языковом дизайне. Это совершенно другая тема о PL дизайне.)

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

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

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

  • Типы объектов могут иметь квалификаторы верхнего уровня *. Ссылки не могут.
  • Переменные типов объектов занимают память в соответствии с семантикой абстрактной машины . Ссылка не обязательно занимает память (подробности см. В разделе о заблуждениях ниже).
  • ...

Еще несколько специальных правил для ссылок:

  • Составные деклараторы являются более строгими в отношении ссылок.
  • Ссылки могут свернуться .
    • Специальные правила для параметров cv (в качестве «ссылок пересылки»), основанные на свертывании ссылок во время вывода параметров шаблона, позволяют «совершенную пересылку» параметров.
  • Ссылки имеют специальные правила при инициализации. Время жизни переменной, объявленной как ссылочный тип, может отличаться от обычных объектов через расширение.
    • Кстати, несколько других контекстов, таких как инициализация с участием &&, следуют некоторым аналогичным правилам продления срока службы ссылок. Это еще одна банка червей.
  • ...

Заблуждения

Синтаксический сахар

  

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

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

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

C ++ имеет только несколько видов синтаксических сахаров в этом строгом смысле. Один экземпляр (унаследованный от C) - встроенный (не перегруженный) оператор std::initializer_list, который определяется точно так же, как если бы он имел одинаковые семантические свойства для определенных форм сочетания по встроенному оператору унарному [] и двоичному * .

Хранение

  

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

Вышеприведенное утверждение просто неверно. Чтобы избежать таких заблуждений, взгляните на правила ISO C ++:

Из [intro.object] /1 :

  

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

Из [dcl.ref] /4 :

  

Не указано, требуется ли ссылка для хранения.

Обратите внимание, что это семантические свойства.

Прагматика

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

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

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

  • Иногда языковые правила явно требуют использования определенных типов. Если вы хотите использовать эти функции, соблюдайте правила.
    • Конструкторам копирования требуются определенные типы ссылочного типа cv -+ в качестве первого типа параметра. (И обычно это должно быть &.)
    • Для конструкторов перемещения требуются определенные типы ссылочного типа cv -const в качестве первого типа параметра. (И обычно не должно быть определителей.)
    • Для определенных перегрузок операторов требуются ссылочные или не ссылочные типы. Например:
      • Перегружен &&, поскольку специальные функции-члены требуют ссылочных типов, аналогичных 1-му параметру конструкторов копирования /перемещения.
      • Постфикс operator= требует фиктивного ++.
      • ...
  • Если вы знаете, что передача по значению (т. е. использование нереферентных типов) является достаточной, используйте ее напрямую, особенно при использовании реализации, поддерживающей обязательное копирование в C ++ 17. ( Предупреждение . Однако исчерпывающе обоснование необходимости может быть очень сложным ).
  • Если вы хотите использовать некоторые маркеры с правами собственности, используйте умные указатели, такие как int и unique_ptr (или даже сами по себе, если вы хотите, чтобы они были непрозрачными ), а не необработанные указатели.
  • Если вы выполняете некоторые итерации по диапазону, используйте итераторы (или некоторые диапазоны, которые еще не предоставлены стандартной библиотекой) вместо необработанных указателей, если вы не уверены, что необработанные указатели будут работать лучше (например, для меньших зависимостей заголовка) в очень конкретных случаях.
  • Если вы знаете, что передачи по значению достаточно и вам нужна определенная семантика, допускающая обнуляемость, используйте обертку, например shared_ptr, а не необработанные указатели.
  • Если вы знаете, что передача по значению не идеальна по указанным выше причинам и вам не нужна семантика, допускающая обнуляемость, используйте {lvalue, rvalue, forwardiнг} -references.
  • Даже если вам нужна семантика, такая как традиционный указатель, часто есть что-то более подходящее, например, std::optional в Library Fundamental TS.

Единственные исключения нельзя обойти на текущем языке:

  • Когда вы реализуете интеллектуальные указатели выше, вам, возможно, придется иметь дело с необработанными указателями.
  • Для определенных подпрограмм взаимодействия языков требуются указатели, например observer_ptr. (Однако cv -operator new по-прежнему довольно отличается и безопаснее по сравнению с обычными указателями объектов, поскольку он исключает неожиданную арифметику указателей, если вы не полагаетесь на некоторых не соответствующих расширению на void*, таких как GNU.)
  • Указатели на функции могут быть преобразованы из лямбда-выражений без перехватов, а ссылки на функции - нет. Вы должны использовать указатели функций в неуниверсальном коде для таких случаев, даже если вы сознательно не хотите обнуляемых значений.

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

  • Вы должны работать с API старого стиля (C).
  • Вы должны соответствовать требованиям ABI определенных реализаций C ++.
  • Вы должны взаимодействовать во время выполнения с различными языковыми реализациями (включая различные сборки, языковую среду выполнения и FFI некоторых высокоуровневых клиентских языков) на основе предположений о конкретных реализациях.
  • В некоторых крайних случаях вам нужно повысить эффективность перевода (компиляция и компоновка).
  • В некоторых крайних случаях вы должны избегать раздувания символов.

Предостережения в отношении языковой нейтральности

Если вы пришли посмотреть на вопрос через какой-либо результат поиска Google (не относится к C ++) , скорее всего, это не то место.

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

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

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

(Дополнительное примечание: вопрос может быть значимым раньше, чем участвуют любые "C-подобные" языки, например, АЛГОЛЬ 68 против PL /I .)

    
12
2019-03-01 12: 43: 18Z

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

void*

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

    
10
2008-09-12 17: 59: 56Z

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

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;
}

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

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

    
10
2009-10-15 01: 57: 37Z

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

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

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

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

10
2013-03-15 03: 11: 50Z

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

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

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

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

    #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();
    }
    
    , так и с T.
    (или T&, который все еще полагается на неявное приведение до std::reference_wrapper<T>)
    Шаблоны, которые охватывают как T&, так и T&, встречаются еще чаще.
  • Lvalues ​​. Рассмотрим оператор T&& Без ссылок он будет работать только для c-строк (str[0] = 'X';). Возвращение символа по ссылке позволяет пользовательским классам иметь одинаковые обозначения.

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

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

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

    
9
2017-07-06 19: 48: 46Z
  1. " неявное приведение " приведение - это синтаксическая конструкция, она существует в грамматике; приведение всегда явное
    2017-10-22 23: 58: 15Z

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

  • Для ссылки требуется указать фактическое окно.
  • Для указателя необходимо указать место на экране, которое, как вы уверены, будет содержать ноль или более экземпляров этого типа окна.
8
2014-12-27 13: 00: 18Z

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

operator+(const T& a, const T& b)

В C вызов, похожий на

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)
}
, может быть передан только по значению, поэтому он определенно не может изменить fn(x); чтобы изменить аргумент, вам нужно передать указатель x. Таким образом, если аргумент не предшествует fn(&x), вы знали, что он не будет изменен. (Обратное, & означает изменение, не соответствует действительностипотому что иногда вам придется передавать большие структуры только для чтения указателем &.)

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

    
8
2018-06-25 18: 58: 31Z

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

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

fn3()

Но у нас не может быть

int* p = 0;
, а также int& p = 0;.

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

int& p=5 ;

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

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

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

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

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
    
6
2017-02-08 13: 43: 19Z
  1. У нас все еще может быть
    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 "="
    
    .
    2017-01-06 14: 24: 37Z
  2. В этом случае ссылка будет использоваться только при чтении, мы не можем изменить эту ссылку const, даже используя "const_cast", потому что "const_cast" принимает только указатель, а не ссылку.
    2017-01-06 14: 37: 44Z
  3. const_cast хорошо работает со ссылками: coliru.stacked-crooked.com/a/eebb454ab2cfd570
    2017-01-06 16: 52: 27Z
  4. вы делаете приведение к ссылке, а не приведение ссылки, попробуйте это; const int & я =; const_cast &л; INT &GT; (я); я пытаюсь выбросить константу ссылки, чтобы сделать возможной запись и присвоение нового значения ссылке, но это невозможно. пожалуйста, сосредоточьтесь!
    2017-01-06 19: 38: 23Z

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

    
5
2016-04-24 13: 27: 53Z

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

  

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

    
2
2017-10-19 11: 50: 17Z
  1. Использование перегруженных функций, которые не принимают указатели вместо разрешения const int& i = 0, или использование терминальных объектов, является гораздо лучшим решением, вместо того, чтобы разрешить nullptr в качестве аргументов.
    2017-12-19 09: 01: 34Z
  2. @ Clearer Возможно, это чище, но иногда вам просто нужно быстро передать указатели, и могут быть случаи, когда вам все равно, равен ли указатель нулю или нет.
    2017-12-19 10: 02: 06Z
nullptr
источник размещен Вот