2015-07-19 3 views
2

Я читаю книгу C++ Primer Plus (6-е издание), и я сталкивался с чем-то, что меня смущает, поэтому несите меня, пока я попытаться объяснить ...Разница между передачей (int x) и (const int & x)

Если у меня есть функция, прототип выглядит так:

void cube(double x); 

и другая функция, кто прототип выглядит так:

void cube(const double &x); 

чем разница между два? Для первого прототипа функции значение передается значением, означающим, что оно будет скопировано и таким образом не изменено функцией. Для второго прототипа значение передается по ссылке, но оно является постоянной ссылкой, поэтому C++ создает анонимную временную переменную и присваивает значение аргумента временной переменной, таким образом, имитируя проход по значению. Итак, по сути, нет никакой разницы между двумя прототипами функций, так? Какова точка (const double & x), то?

+0

он не будет создавать временную переменную, вы передадите в адрес этого двойника с гуаратетом, что, если вызывающий не получает наркотики, он не может быть нулевым (вы получите исключение, которое я считаю, если вы попытаетесь разыменовать нулевую ссылку) и что вы можете использовать. вместо ->. Кроме того, вы не можете изменить содержимое, хранящееся внутри x – Creris

+0

Мне нравится, как вы подробно описываете различия между ними. Тогда вы говорите «так что между ними нет разницы, верно?» Также вы не совсем точны в своем описании: здесь нет «анонимного временного». –

ответ

4

Для второго прототипа, значение передается по ссылке, но это постоянная ссылка так C++ создаст анонимный временную переменную и присвоить значение аргумента временной переменной, таким образом, имитирующей проход по значению.

Хотя C++ будет делать это в определенных ситуациях. Например, если вы передаете выражение возвращающегося double, C++ создаст временный:

double v = 123.456; 
cube(5*v+321.0123); 

Однако, это не обязательно делать это. Например, если вы сделаете этот призыв

double v = 123.456; 
cube(v); 

C++ будет передавать ссылку на v непосредственно к функции cube(). В этом случае одновременная модификация v будет «видна» функции cube() во время ее работы.

Итак, по сути, нет никакой разницы между двумя прототипами функций, верно?

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

В чем смысл (const double &x)?

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

void cube(const T& v); 

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

+0

Я немного смущен этим ответом. Если вы передаете по значению, функции получат копию переменной, которую разрешено редактировать. Если вы передаете постоянную ссылку, независимо от того, что происходит под капотом, семантически функция получает ссылку на объект, который он не может изменить. Это кажется большой разницей. Я просто пропущу точку, и вопрос действительно сосредоточен на том, как именно передается значение? –

+0

Очень хорошо объяснено. Большое спасибо. – Jonathan

+0

Существует важная разница. Если вы передаете ссылку const, вызывающий может изменить ссылку во время работы вашего кода (например, из другого потока или когда ваш код вызывает другую функцию). Это изменяет значение внутри вашей функции. Передача по значению дает вам копию, которую никто не может изменить. –

-2

Несмотря на то, что операции чтения/записи замедлят его, ссылка/указатель будет 32 бит на 32-битной машине, где двойной будет 64 бита. Это не играет также с 32-битным размером слова.

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

EDIT: Чтобы объяснить себя, я должен был подчеркнуть «возможно».

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

Что касается отличия (производительности), я пытался сказать, оценивая стоимость передачи двойного (по размеру слова) функции VS стоимости извлечения из указателя/ссылки (размер слова) - и в в большинстве случаев это незначительно. Чем больше значение, которое вы передаете, тем быстрее будет выполняться выбор из указателя/ссылки.
Кейси Муратори упомянул об этом и рекомендует передавать структуры немного над размером слова.

+2

Любой компилятор, который обрабатывает их одинаково и генерирует идентичный код, не является компилятором C++. – hvd

+1

У вас, кажется, есть * кое-какие сведения по этому вопросу, но я думаю, вы просто угадываете здесь. –

4

Для следующих двух прототипов

void cube(double x); 

и

void cube(const double &x); 

поведение будет таким же, с точки зрения вызывающего абонента и вызываемого абонента в том, что ни один не позволит модификации объекта к вызывающему , Однако в последнем примере (const reference) x будет передаваться по ссылке вместо значения. Если double были достаточно большими или если это был более крупный тип данных (например, struct), проходящий по ссылке, то можно было бы избежать копирования большого количества данных (поскольку передается только указатель).

Эффекты производительности второго порядка (например, эффекты на кеш, взвешенные с копией данных), все зависят от реализации и зависят не только от архитектуры процессора и кэша и от компилятора, но и от структуры и времени выполнения использование программы.

1

Для примитивных типов вы ничего не получаете, используя const&. const& традиционно используется для больших объектов, чтобы избежать дорогостоящего и ненужного копирования.

Вам также нужен const&, если тип не поддерживает копирование, но вы хотите запретить модификацию указанного объекта.

С появлением C++ 11, хода семантики вопроса традиционной мудрости «проходят небольшие объекты по значению, большие объекты по константной ссылке» Насколько я могу видеть, C++ экспертного сообщество не имеет но достиг нового консенсуса по этой теме. Например, см. How true is "Want Speed? Pass by value".

1

Ну, есть некоторая разница:

  • С void cube(double x), вы делаете локальную копию переменной х в функции, и вы можете изменить его значение без изменения значения исходного х. Таким образом, в локальной функции void cube вы можете взаимодействовать с x как с порядковой переменной.
  • С void cube(const double& x), вы просто передавая указатель (в C++, ссылка является указателем, но с немного другим синтаксисом использования), и const здесь означает, что вы не можете изменить значения переменного этот адрес (на который указывает указатель). Итак, в локальной функции void cube вам следует взаимодействовать с x как с постоянной переменной.

А как насчет разницы в производительности?

С double нет никакой разницы в производительности, так как double занимает 64 бита, и ссылка на double занимает 32 или 64 бита, нет большой разницы. Но, представьте, у вас есть на структуру:

struct some_very_big_struct { 
    ... 
} 

где sizeof(some_very_big_struct) является 2^10 байт, так что делает копию этой структуры занимает очень много времени и дополнительной памяти, так что в этом случае прохождения ссылки является лучшим выбором.

+0

Размеры определяются реализацией. Например, с Visual Studio при компиляции для x86 sizeof (double) == 8 и sizeof (double *) == 4 – Creris

+0

@Creris О, я предположил, что OP использует 64-битную систему. Отредактировано, спасибо ^^ – FalconUA

1

Стандарт явно требует, в соответствии с правилами в [dcl.init.ref] #5.1, что ссылочный параметр должен быть привязан к аргументу, в противоположность (например,) к ссылке, связанной с какой-либо копией. Разница обнаруживается, поэтому не применяется правило «как будто» (во всех случаях).

double a; 
void foo(const double &x) { assert(&a == &x); } 
void bar() { foo(a); } 

Существует также практическая разница. В случае const double &x имя x является просто псевдонимом для некоторого двойника, к которому можно получить доступ через другие значения l, которые имеют большое значение для оптимизации (например, сохранение/восстановление регистров, переупорядочение команд и т. Д.), Например:

double a; 
    void foo(const double &x) { 
     ... = x; 
     a = bar(); // here the compiler must assume that the assignment 
        // may modify x, the two statements can't be reordered 
    } 

    void foo(double x) { 
     ... = x; 
     a = bar(); // here the compiler knows that the assignment 
        // cannot modify x, and the two statements can be 
        // reordered 
    } 
Смежные вопросы