передать по ссылке c ++ - PullRequest
10 голосов
/ 16 февраля 2010

Мой учитель по c ++ сказал мне, что вызов по ссылке должен использоваться только в том случае, если я не собираюсь ничего менять в массивах внутри функции. У меня есть несколько действительно больших векторов, которые я передаю в своей программе. Все векторы будут изменены внутри функций. Мои матрицы имеют размеры около [256*256][256][50] ...

Есть ли какая-то особая причина не использовать здесь ссылку на вызов?

AFAIK вызов по ссылке должен быть намного быстрее и потреблять меньше памяти?

Ответы [ 11 ]

8 голосов
/ 16 февраля 2010

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

Из-за обратной совместимости с C, а также из-за вашей конкретной проблемы: массивы могут быть огромными, массивы никогда не передаются по значению ни в C, ни в C ++. Массив затухает в указатель на первый элемент, поэтому, когда вы пишете:

void foo( type array[100] );

Компилятор фактически обрабатывает:

void foo( type *array );

Независимо от того, каков размер массива (здесь есть две распространенные ошибки: полагая, что array - это массив внутри foo, и полагая, что в нем будет гарантированно 100 элементов.

Теперь в C ++ вы можете передавать массивы по ссылке, но ссылка должна быть конкретного типа массива, который включает размер:

void foo_array( type (&array)[100] );

Забавный синтаксис говорит компилятору, что функция примет массив из 100 элементов типа type. Преимущество в том, что компилятор может выполнить проверку размера за вас:

// assuming 'type' is defined
int main() {
   type array0[99];
   type array1[100];

   foo( array0 );     // compiles, but if size=100 is assumed it will probably break
                      // equivalent to: foo( &array0[0] )
   // foo2( array0 ); // will not compile, size is not 100
   foo2( array1 );    // compiles, size is guaranteed to be 100
}

Теперь проблема заключается в том, что ваша функция будет работать только для массива точно из 100 элементов, и в некоторых случаях вам может потребоваться выполнить одну и ту же операцию с различными размерами массива. Два решения: шаблонная функция в размере массива, которая обеспечит безопасную по размеру реализацию для каждого используемого размера - большее время компиляции и двоичный размер, шаблон компилируется для каждого другого размера - или с использованием пароля - Синтаксис значения по значению, который сделает распад массива - небезопасным по размеру, который должен быть передан как дополнительный аргумент, меньшее время компиляции и двоичный размер. Третий вариант сочетает в себе оба:

void foo( type *array, int size );
template <size_t N>
void foo( type (&array)[N] ) {
   foo( array, N );
}

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

Теперь передача по ссылке очень редко используется с массивами.

5 голосов
/ 16 февраля 2010

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

Его следует использовать, когда вы не изменяете что-либо внутри функции или , вы изменяете вещи и хотите, чтобы изменения были отражены в исходном массиве изменения, которые будут отражены в исходном массиве.

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

4 голосов
/ 16 февраля 2010

Ваш учитель не прав. Если вам нужно изменить массивы, лучше всего использовать ссылку по ссылке. Если вы не хотите, чтобы что-то изменилось, передайте ссылку на констант.

1 голос
/ 16 февраля 2010

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

Теперь, если вызываемый вами метод или функция не изменяет объект, рекомендуется объявить функцию следующим образом:

void some_function(const some_object& o);

Это приведет к ошибке компиляции, если вы попытаетесь изменить состояние объекта внутри тела функции.

Также следует отметить, что массивы всегда передаются по ссылке.

1 голос
/ 16 февраля 2010

Подожди секунду .. Мне страшно, как люди отвечают на этот вопрос. Насколько я помню, массивы всегда передаются по ссылке.

void function(int array[])
{
    std::cout << array[0] << '\n';
}

// somewhere else..
int array[2] = { 1, 2 };
function(array); // No copy happens here; it is passed by reference

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

void function(int &array[]) // error here
{ /* ... */ }

Так что вы имеете в виду?

Кроме того, многие говорят, что вы должны делать это только в том случае, если вы изменяете содержимое массива внутри функции. Тогда как насчет ссылки на const?

void function(const int arr[])
{
    std::cout << arr[0] << '\n';
}

- редактировать

Кто-нибудь, пожалуйста, укажите мне, как не передавать массив по ссылке в C ++?

- редактировать

О, так вы говорите о векторах. Хорошо, тогда эмпирические правила таковы:

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

Я что-то пропустил?

- редактировать

  • В случае простых массивов C рекомендуется передавать их по ссылке (как в void function(int (&array)[100])), когда вы хотите убедиться, что массив имеет заданный определенный размер.

Спасибо, дрибеи.

1 голос
/ 16 февраля 2010

Вы можете перейти по ссылке, если:

  1. вы не будете изменять переданный объект
  2. вы хотите изменить объект и не хотите оставлять старый объект нетронутым

Когда вы передаете что-то по ссылке, в функцию передается только указатель. Если вы передаете весь объект, вам нужно скопировать его, чтобы он занимал больше процессора и памяти.

1 голос
/ 16 февраля 2010

Чтобы предотвратить случайные изменения, используйте pass-by-const-reference;таким образом, по умолчанию * переданный массив не может быть изменен вызываемой функцией.

* Может быть переопределен с помощью const_cast.

0 голосов
/ 09 июня 2016

Передача объекта по ссылке всегда хороший выбор, но мы должны быть осторожны, и сначала мы должны решить, какова наша цель / назначение нашей функции?

Здесь вы должны сделать выбор, будем ли мы только читать данные объекта или изменять их.

Предположим, вы получили интерфейс типа

void increament_value(int& a);

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

поэтому c ++ предоставляет вам функциональность, позволяющую не изменять значение объекта, ссылку на который вы передаете функции, и всегда полезно передать ссылку const объекта, например,

double get_discounted_amount(const double &amount,double discount){
    return (amount*discount/100);
}

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

0 голосов
/ 16 февраля 2010

Наш стиль дома НИКОГДА не передает объект по значению, а всегда передает ссылку или постоянную ссылку. У нас не только есть структуры данных, которые могут содержать 100 МБ данных и передавать по значению, что может стать причиной смерти приложения, но также, если бы мы передавали трехмерные точки и векторы по значению, наши приложения остановились бы.

0 голосов
/ 16 февраля 2010

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

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

...