Можем ли мы переназначить ссылку на С++?

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

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

#include <iostream>
#include <stdio.h>
#include <conio.h>

using namespace std;

int main()
{
    int i = 5, j = 9;

    int &ri = i;
    cout << " ri is : " << ri  <<"\n";

    i = 10;
    cout << " ri is : " << ri  << "\n";

    ri = j; // >>> Is this not reassigning the reference? <<<
    cout << " ri is : " << ri  <<"\n";

    getch();
    return 0;
}

Код компилируется отлично, а вывод - так, как я ожидаю:

ri is : 5
ri is : 10
ri is : 9

Ответ 1

ri = j; // >>> Is this not reassigning the reference? <<<

Нет, ri по-прежнему ссылается на i - вы можете доказать это, напечатав &ri и &i и увидев, что они являются одним и тем же адресом.

Что вы сделали, это изменить i через ссылку ri. Распечатайте i после, и вы увидите это.

Кроме того, для сравнения, если вы создаете const int &cri = i;, это не позволит вам назначить это.

Ответ 2

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

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

В вашем примере, когда вы выполняете int &ri = i;, ri привязан к i для своего срока службы. Когда вы выполняете ri = j;, вы просто присваиваете значение j ri. ri по-прежнему остается ссылкой на i! И это приводит к тому же результату, как если бы вы вместо этого написали i = j;

Если вы хорошо понимаете указатели, тогда всегда думайте о ссылке как о аналогичной интерпретации T* const, где T - любой тип.

Ответ 3

Когда вы назначаете что-то ссылку, вы фактически присваиваете значение объекту, к которому привязана ссылка. Итак:

ri=j;

имеет тот же эффект, что и

i = j;

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

Ответ 4

Вы не переназначаете ссылку при выполнении ri = j;. Фактически вы назначаете j в i. Попробуйте выполнить печать i после строки, и вы увидите, что значение i изменилось.

Ответ 5

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

using namespace std;
vector<int>myints;

auto &i = myints.emplace_back();   // allocate and reference new int in vector
auto myintsaddr = &myints; auto myintfrontaddr = &myints.front(); // for future reference
i = 1;                             // assign a value to the new int through reference
cout << hex << "address of i: 0x" << &i << " equals " << "address of 
myints.back(): 0x" << &myints.back() << '.' << endl;  // check reference as expected
i = myints.emplace_back();     // allocate new int in vector and assign to old reference variable
i = 2;                         // give another value to i
cout << "i=" << i << ", myints={" << myints[0] << ", "<< myints[1] << '}' << endl; // any change to potentially referenced objects?
cout << hex << "&i: 0x" << &i << " unequal to " << "&myints.back(): 0x" << &myints.back() << " as well as &myints.front(): 0x" << &myints.front() << endl;
cout << "Myints " << (myintsaddr== &myints?"not ":"") << "relocated from " << myintsaddr << " to " << &myints << endl;
cout << "Myints front() " << (myintfrontaddr == &myints.front() ? "not " : "") << "relocated from " << myintfrontaddr << " to " << &myints.front() << endl;

Вывод:

address of i: 0x0063C1A0 equals address of myints.back(): 0x0063C1A0.
i=2, myints={1, 0}
&i: 0x0063C1A0 unequal to &myints.back(): 0x0063F00C as well as &myints.front(): 0x0063F008
Myints not relocated from 0039FE48 to 0039FE48
Myints front() relocated from 0063C1A0 to 0063F008

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