Передача по ссылке / значению в C ++ - PullRequest
35 голосов
/ 04 января 2009

Я хотел бы уточнить различия между значением и ссылкой.

Я нарисовал картину

enter image description here

Итак, для передачи по значению,

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

Как понимать слова: " Если функция изменяет это значение, изменения появляются также в рамках вызывающей функции как для передачи по значению, так и по ссылке «

Спасибо!

Ответы [ 6 ]

60 голосов
/ 04 января 2009

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

struct Object {
    int i;
};

void sample(Object* o) { // 1
    o->i++;
}

void sample(Object const& o) { // 2
    // nothing useful here :)
}

void sample(Object & o) { // 3
    o.i++;
}

void sample1(Object o) { // 4
    o.i++;
}

int main() {
    Object obj = { 10 };
    Object const obj_c = { 10 };

    sample(&obj); // calls 1
    sample(obj) // calls 3
    sample(obj_c); // calls 2
    sample1(obj); // calls 4
}

Некоторые люди утверждают, что 1 и 3 передаются по ссылке, а 2 - по значению. Другая группа людей говорит, что все, кроме последнего, передаются по ссылке, потому что сам объект не копируется.

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

    sample(&obj);
       // yields a `Object*`. Passes a *pointer* to the object by value. 
       // The caller can change the pointer (the parameter), but that 
       // won't change the temporary pointer created on the call side (the argument). 

    sample(obj)
       // passes the object by *reference*. It denotes the object itself. The callee
       // has got a reference parameter.

    sample(obj_c);
       // also passes *by reference*. the reference parameter references the
       // same object like the argument expression. 

    sample1(obj);
       // pass by value. The parameter object denotes a different object than the 
       // one passed in.

Я голосую за следующее определение:

Аргумент (1.3.1) передается по ссылке, если и только если соответствующий параметр вызываемой функции имеет ссылочный тип и , параметр ссылки привязывается непосредственно к выражению аргумента (8.5.3 / 4). Во всех остальных случаях мы имеем дело с передачей по значению.

Это означает, что следующее передается по значению:

void f1(Object const& o);
f1(Object()); // 1

void f2(int const& i);
f2(42); // 2

void f3(Object o);
f3(Object());     // 3
Object o1; f3(o1); // 4

void f4(Object *o);
Object o1; f4(&o1); // 5

1 передается по значению, поскольку оно не связано напрямую. Реализация может скопировать временную и затем привязать эту временную ссылку. 2 передается по значению, потому что реализация инициализирует временный литерал, а затем привязывается к ссылке. 3 передается по значению, поскольку параметр не имеет ссылочного типа. 4 передается по значению по той же причине. 5 передается по значению, поскольку параметр не имеет ссылочного типа. Следующие случаи передаются по ссылке (по правилам 8.5.3 / 4 и др.):

void f1(Object *& op);
Object a; Object *op1 = &a; f1(op1); // 1

void f2(Object const& op);
Object b; f2(b); // 2

struct A { };
struct B { operator A&() { static A a; return a; } };
void f3(A &);
B b; f3(b); // passes the static a by reference
8 голосов
/ 04 января 2009

При передаче по значению:

void func(Object o);

, а затем позвонив

func(a);

вы создадите Object в стеке, и в реализации func на него будет ссылаться o. Это все еще может быть мелкая копия (внутренние элементы a и o могут указывать на те же данные), поэтому a может быть изменено. Однако, если o является глубокой копией a, то a не изменится.

При передаче по ссылке:

void func2(Object& o);

, а затем позвонить

func2(a);

вы будете давать только новый способ ссылки a. «a» и «o» - это два имени одного и того же объекта. Изменение o внутри func2 сделает эти изменения видимыми для вызывающей стороны, которая знает объект под именем "a".

6 голосов
/ 04 января 2009

Большое спасибо всем за этот вклад!

Я процитировал это предложение из лекционной заметки онлайн: http://www.cs.cornell.edu/courses/cs213/2002fa/lectures/Lecture02/Lecture02.pdf

первая страница 6 слайд

» Пройдите ЗНАЧЕНИЕМ Значение переменной передается функции Если функция изменяет это значение, изменения остаются в пределах объем этой функции.

Пройти по ссылке Ссылка на переменную передается функции Если функция изменяет это значение, изменения появляются также в рамках вызывающей функции.

"

Большое спасибо еще раз!

5 голосов
/ 04 января 2009

Я не уверен, правильно ли я понимаю ваш вопрос. Это немного неясно. Однако, что может вас смущать, так это:

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

  2. При передаче по значению вызывается конструктор копирования. Конструктор копирования по умолчанию будет делать только поверхностное копирование, следовательно, если вызываемая функция изменяет целое число в объекте, это не будет видно вызывающей функции, но если функция изменит структуру данных, на которую указывает указатель внутри объекта , то это будет видно вызывающей стороне из-за мелкой копии.

Возможно, я неправильно понял ваш вопрос, но подумал, что все равно нанесу ему удар.

2 голосов
/ 04 января 2009

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

1 голос
/ 04 января 2009

Мое понимание слов "Если функция изменяет это значение, изменения появляются и в рамках вызывающей функции как для передачи по значению, так и по ссылке", так это то, что они ошибка .

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

Либо вы неправильно набрали слова в кавычках, либо они были извлечены из любого контекста, сделавшего то, что кажется неправильным, верно.

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

...