Можем ли мы переназначить ссылку на С++?
Я везде читал, что ссылка должна быть инициализирована тогда и там и не может быть повторно инициализирована снова.
Чтобы проверить мое понимание, я написал следующую небольшую программу. Кажется, мне действительно удалось переназначить ссылку. Может кто-нибудь объяснить мне, что на самом деле происходит в моей программе?
#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) ссылка сохранила тот же самый адрес в памяти, но ссылочные значения (часть вектора) были перераспределены в другом месте. Ссылка я может свисать.