Почему функция не может изменить адрес / значение ссылки объекта аргумента?

Я узнал, что в Java и C вы можете назначить указатель, передать указатель методу, следовать за указателем в методе и изменить данные, на которые он указывал. Однако вы не можете изменить место, куда указывает этот указатель.

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

void reassign(string & a);
int main()
{
    string x = "abcd";
    cout <<"x is " << x <<" at " << &x <<endl;    //"x is abcd at 0x7bc7ebd5b720"
    reassign(x);
    cout <<"x is " << x <<" at " << &x <<endl;    //"x is efgh at 0x7bc7ebd5b720"

}

void reassign(string & a)
{
    a = string("efgh");
}

Поскольку «string ()» создает новую строку, почему функция «переназначить» не изменяет адрес исходной строки?


person ladiesman    schedule 20.05.2015    source источник
comment
Почему вы думаете, что создание нового объекта в ссылке подразумевает перемещение объекта, на который ссылаются?   -  person Daniel Kamil Kozar    schedule 20.05.2015
comment
reassign() просто создает новую строку в том же месте памяти, на которое ссылается a. Если вы хотите изменить адрес, вам нужно будет передать либо ссылку на указатель, либо указатель на указатель.   -  person Austin Brunkhorst    schedule 20.05.2015
comment
Фактически вы не печатаете, куда указывает указатель. Вы просто печатаете адрес указателя на то, где находится x.   -  person RealSkeptic    schedule 20.05.2015
comment
Когда вы передаете объект по ссылке, функция может изменять значение объекта, используя эту ссылку. Но он не может переустановить ссылку, чтобы она полностью ссылалась на другой объект.   -  person David Schwartz    schedule 20.05.2015
comment
Ссылки на Java и ссылки на C ++ - разные вещи. Представьте себе стационарный ящик, куда можно складывать вещи. Ссылка на Java будет относиться к предмету внутри коробки, а ссылка на C ++ будет относиться к самой коробке. Другими словами: в Java ссылка всегда относится к одному и тому же объекту, независимо от того, в какое поле вы ее поместили; в C ++ ссылка всегда относится к одному и тому же блоку, независимо от того, что вы в него помещаете.   -  person molbdnilo    schedule 20.05.2015
comment
так разве "string mystr = string("newstring")" не то же самое, что "string mystr = *(new string("newstring"))" с точки зрения динамического распределения памяти?   -  person ladiesman    schedule 20.05.2015


Ответы (2)


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

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

void reassign(string* & a);

int main() {
    string *x = new string("abcd");
    cout <<"x is " << *x <<" at " << x <<endl;    //"x is abcd at 0x95b7008"
    reassign(x);
    cout <<"x is " << *x <<" at " << x <<endl;    //"x is efgh at 0x95b7030"
    delete x;
    return 0;
}

void reassign(string* & a) {
    string *old = a;
    a = new string("efgh");
    delete old;
}

Демо.

person Sergey Kalinichenko    schedule 20.05.2015
comment
Если вы удалите объект до переназначения, вы можете повторно использовать тот же адрес, нарушив демонстрацию. Поэтому я сохранил a в old, переназначил a и только потом удалил old. - person Sergey Kalinichenko; 20.05.2015

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

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

Например, в Java у вас может быть что-то вроде этого:

public static void reassign(String a) {
    a = new String("efgh");
}

public static void main(String[] args) {
    String x = "abcd";
    System.out.printf("x is %s at %x\n", x, System.identityHashCode(x)); // x is abcd at 25154f
    reassign(x);
    System.out.printf("x is %s at %x\n", x, System.identityHashCode(x)); // x is abcd at 25154f
}

Здесь x в main - указатель на объект. a в reassign также является указателем на объект. Присвоение параметру указателя a в reassign не влияет на переданную переменную указателя x (т. Е. Не влияет на то, куда указывает указатель), потому что он передается по значению.

Эквивалент приведенного выше кода на C ++ будет таким:

void reassign(string *a) {
    a = new string("efgh");
}

int main() {
    string *x = new string("abcd");
    cout << "x is " << *x <<" at " << x << endl; // x is abcd at 0x82b1008
    reassign(x);
    cout << "x is " << *x <<" at " << x << endl; // x is abcd at 0x82b1008
    return 0;
}

В дополнение к передаче по значению, показанной выше, C ++ также имеет передачу по ссылке, где присвоение параметру имеет тот же эффект, что и присвоение переданной переменной в вызывающей области. Вот то же самое, что и выше, но с передачей по ссылке:

void reassign(string *&a) {
    a = new string("efgh");
}

int main() {
    string *x = new string("abcd");
    cout << "x is " << *x <<" at " << x << endl; // x is abcd at 0x8673008
    reassign(x);
    cout << "x is " << *x <<" at " << x << endl; // x is efgh at 0x8673030
    return 0;
}

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

person newacct    schedule 25.05.2015