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

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

В чем разница между указателем на ссылку, ссылкой на указатель и указателем на указатель на С++?

Где лучше выбирать другого?

4b9b3361

Ответ 1

Во-первых, ссылка на указатель похожа на ссылку на любую другую переменную:

void fun(int*& ref_to_ptr)
{
    ref_to_ptr = 0; // set the "passed" pointer to 0
    // if the pointer is not passed by ref,
    // then only the copy(parameter) you received is set to 0,
    // but the original pointer(outside the function) is not affected.
}

В С++ указатель на ссылку является незаконным, потому что - не похоже на указатель - ссылка - это просто концепция, которая позволяет программисту создавать псевдонимы чего-то другого. Указатель - это место в памяти, у которого есть адрес чего-то другого, но ссылка НЕ.

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

int x;
int& rx = x; // from now on, rx is just like x.
// Unlike pointers, refs are not real objects in memory.
int* p = &x; // Ok
int* pr = ℞ // OK! but remember that rx is just x!
// i.e. rx is not something that exists alone, it has to refer to something else.
if( p == pr ) // true!
{ ... }

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

Ответ 2

Указатель на указатель

A указатель в С++ - это просто значение, которое хранит ячейку памяти (обычно это 32-битное значение).

Скажем, у вас было целочисленное значение ввода пользователем (78 == 0x4E в шестнадцатеричном формате).

Он будет храниться в памяти аналогичным образом (я намеренно упрощаю вещи для этого примера):

Memory address    Value
0x12345678        0x0000004E

Если вы хотите создать "указатель" на это значение, это будет выглядеть так в памяти:

Memory address    Value
0x22334455        0x12345678

В адресе памяти 0x22334455 теперь у вас есть "указатель" , значение которого 0x12345678 или адрес памяти, где хранится значение целочисленного значения пользователя (0x4E).

Скажем, вы хотели создать "указатель" на это значение указателя. Это будет выглядеть так:

Memory address    Value
0x11335577        0x22334455

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

Указатели могут создаваться так неопределенно - ключ запоминает, что указатель - это просто другое значение, которое компилятор интерпретирует как место памяти (и он предоставляет различные семантики доступа, такие как * и ->, которые являются особыми для типы "указатель" ).

Ссылка на указатель

A reference можно рассматривать как представление или псевдоним на другом реальном объекте. Когда вы создаете ссылку на указатель под названием myReference, вы просто определяете новое имя под названием myReference, которое может использоваться для доступа к указателю, который вы ранее определили в памяти.

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

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

Указатель на ссылку

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

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

Примечание о параметрах

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

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

Например:

void myMethod(int myValue)
{
    // NOTE: This change will be lost to the caller!
    myValue = 5;
}

void myMethod2(int* myValue)
{
    // Correct way of modifying pointer parameter value
    *myValue = 5;
}

void myMethod3(int& myValue)
{
    // Correct way of modifying reference parameter value
    myValue = 5;
}

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

void myMethod4(int* myValue)
{
    // Warning: You will lose the address of the allocated
    // memory when you return!
    myValue = new int[5];
}

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

void myMethod5(int** myValue)
{
    // Correct way of allocating memory in a method
    // via pointer-to-pointer
    *myValue = new int[5];
}

void myMethod6(int*& myValue)
{
    // Correct way of allocating memory in a method
    // via reference-to-pointer
    myValue = new int[5];
}

В этих нижних 2 примерах код, вызывающий myMethod5 и myMethod6, корректно получит адрес памяти вновь выделенной памяти с помощью указателя или ссылки параметра myValue.

Ответ 3

Нет указаний на ссылку.

Ответ 4

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

Вам не нужны ссылки. Вы всегда можете использовать указатели. Однако иногда код может быть проще читать с ними.

Типичным примером для начинающих является связанный список. Представьте, что у вас есть переменная, называемая "список", которая содержит указатель на первый. Если вы хотите добавить что-то в голову, вам нужно будет дать вашему add() двойной указатель, так как он должен иметь возможность изменять "head". Однако вместо этого вы можете использовать ссылку на указатель. Здесь мы хотим использовать указатели в самом списке, так как мы будем их мутировать, но функция add() будет более понятной, если мы передадим ссылку на головку списка вместо двойного указателя.

Это просто выбор стиля. Если вы работаете над более крупным проектом, вы должны пойти со стилем проекта. Если нет, вы можете использовать все, что вы считаете предпочтительным. Однако вы должны быть комфортно использовать все стили, если вы даже надеетесь быть умеренно успешным программистом на С++.

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

Ответ 5

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

struct contains_ref
{
     int& ref;
     contains_ref(int& target) : ref(target) {}
};

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

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


1 Фактически это является следствием того, что С++ не предоставляет никакого синтаксиса для обращения к самой ссылке, а не к ее цели. Все операторы, включая оператор присваивания, просто применяются к цели.

Ответ 6

Просто попробуйте сами убедиться, что у вас есть. Образец программы просто печатает значение для int и адресов разных объектов:

#include<stdio.h>

int main(){
  int myInt ;
  int *ptr_to_myInt = &myInt;
  int *ptr_to_myInt_ref = ptr_to_myInt;

  myInt = 42;

  printf("myInt is %d\n",myInt);
  printf("ptr_to_myInt is %x\n",ptr_to_myInt);
  printf("ptr_to_myInt_ref is %x\n",ptr_to_myInt_ref);
  printf("&ptr_to_myInt is %x\n",&ptr_to_myInt);

  return 0;
}

Вывод:

myInt is 42
ptr_to_myInt is bffff858
ptr_to_myInt_ref is bffff858
&ptr_to_myInt is bffff854

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

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