Подтвердить что ты не робот

Передача как const и по ссылке - стоит?

Возможный дубликат:
Как передать объекты в функции на С++?

В моей игре я делаю чрезмерное использование математических векторов и перегрузку их операторов.

class Vector
{
   float x, y;
};

В основном это касается моего класса Vector (методы исключены).

Теперь я не являюсь экспертом в С++, и я видел и читал о передаче как const и передаче по ссылке.

Итак, где различия производительности в приведенном ниже примере кода?

Float RandomCalcuation( Vector a, Vector b )
{
    return a.x * b.x / b.x - a.x * RANDOM_CONSTANT;
}

// versus..

Float RandomCalcuation( Vector& a, Vector& b )
{
    return a.x * b.x / b.x - a.x * RANDOM_CONSTANT;
}

// versus..

Float RandomCalcuation( const Vector& a, const Vector& b )
{
    return a.x * b.x / b.x - a.x * RANDOM_CONSTANT;
}
  • Какую из трех я должен использовать и почему?
  • Какие преимущества для процесса оптимизации компилятора имеют каждый из параметров?

  • Когда и где я должен быть особенно осторожен?

4b9b3361

Ответ 1

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

Если вы рассматриваете эти три функции:

void VersionOne(Vector v);
void VersionTwo(Vector& v);
void VersionThree(const Vector& v);

Между ними есть тонкие различия. Первая функция, например, будет вызывать конструктор копирования при передаче в Vector, чтобы у него была собственная локальная копия Vector. Если ваш конструктор копий занимает некоторое время для выполнения или много ресурсов выделяет и освобождает, это может быть медленным, но вы можете внести любые изменения, которые вы хотите в параметр, не рискуя любыми изменениями, распространяющимися обратно на вызывающего. В конце функции также будет вызов деструктора, поскольку аргумент очищается, и если это слишком большая стоимость, может быть целесообразно избежать этой настройки. Тем не менее, для небольших объектов это вполне приемлемо.

Вторая версия этой функции занимает Vector по ссылке, что означает, что функция может вносить любые изменения, которые она хочет в Vector, и изменения будут распространяться обратно на вызывающего. Всякий раз, когда вы видите функцию, которая принимает аргумент с помощью ссылки <const, как эта функция VersionTwo, вы должны предположить, что она будет изменять аргумент, так как если бы он не вносил никаких изменений, это было бы взято с помощью ссылки const. Вы, скорее всего, возьмете значение по ссылке, если вам нужно внести изменения в Vector; например, путем его поворота, масштабирования и т.д. Один из возможных решений заключается в том, что Vector не будет скопирован, когда он будет передан в эту функцию, и поэтому вы избежите обращения к конструктору и деструктору копии. Это может иметь последствия для вашей программы, хотя, если вы думаете, что вам следует перейти с помощью ссылки const. Следует отметить, что после ссылки очень похоже на следование указателю (фактически, большинство реализаций ссылок просто обрабатывают их так, как если бы они были автоматически-разыменованными указателями), поэтому при каждом доступе к данные через ссылку. Только профилирование может рассказать вам, является ли это серьезной проблемой, и я бы не стал беспокоиться об этом, если у вас не было определенной причины думать, что это было ошибкой.

Конечная версия этой функции принимает ссылку Vector на const, которая, подобно передаче по обычной ссылке, позволяет избежать копирования. Однако при использовании ссылки Vector на const вам запрещается вносить какие-либо изменения в Vector внутри функции, поэтому клиенты могут предположить, что параметр Vector не будет изменен. (Да, технически он может быть изменен, если он плохо написан или имеет членов данных mutable, но на этот раз мы проигнорируем это. Это важная идея высокого уровня). Этот вариант был бы хорош, если бы вы захотели проверить значение в функции без копирования и без его изменения.

Существует еще одна разница между передачей по ссылке и pass-by- const -reference, а также поведение функции на rvalues. Если у вас есть временный объект Vector - либо вы создали его явно, написав Vector(), либо выполнив некоторую математическую операцию на нем, как запись v1 + v2 - тогда вы не сможете передать этот временный Vector в функцию, которая принимает свой параметр по ссылке, поскольку ссылки могут привязываться только к lvalues. Идея состоит в том, что если у вас есть такая функция:

void DoSomething(Vector& v) {
     v.x = 0.0f;
}

Тогда нет смысла писать

DoSomething(v1 + v2);

Так как это изменило бы поле x временного выражения. Чтобы предотвратить это, компилятор откажется от компиляции этого кода.

Однако С++ делает исключение и позволяет передавать rvalues ​​в функции, которые принимают свой аргумент с помощью ссылки const, потому что, интуитивно, вы не сможете изменять объект с помощью ссылки const. Таким образом, этот код совершенно легален:

void DoSomething(const Vector& v) {
    cout << v.x << endl;
}

DoSomething(v1 + v2);

Итак, суммируем -

  • Передача по значению и прохождение const -reference подразумевают похожие вещи - вы хотите иметь возможность смотреть на значение, не изменяя его.
  • В любое время, когда вы могли использовать pass-by-value, вместо этого вы могли бы использовать pass-by- const -reference, не влияя на правильность программы. Тем не менее, есть компромисс между производительностью между ссылкой и ссылкой на стоимость копирования и разрушения параметра.
  • Передача-не- const -reference должна использоваться для указания "Я хочу изменить аргумент".
  • Вы не можете передавать значения r в функции, которые принимают свои аргументы с помощью ссылки < const.

Надеюсь, это поможет!

Ответ 2

Вы хотите скопировать объекты, переданные функции? Если вы передаете объекты "Vector a" и "Vector b" по значению, вам придется иметь дело с накладными расходами на их копирование. Для небольших структур накладные расходы незначительны.

Если вы передаете объекты по ссылке или указателем, у вас нет этих накладных расходов. Однако передача указателем или ссылкой потенциально допускает модификацию объекта:

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

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

Ответ 3

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

Ответ 4

Вы только передаете ссылку на не-const, если хотите изменить аргументы и попросите клиента наблюдать за этими изменениями.

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

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

Ответ 5

Возможно, теперь все изменилось, но несколько лет назад я провел несколько тестов с несколькими компиляторами С++ и разными вариантами (например, операторы-члены или не-члены, operator+, определенные в терминах operator+= или нет, передаются по значению или по ref и т.п.).

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

Кроме того, к сожалению, в то время я никогда не мог получить ту же производительность, что и разворачиваемый вручную C-код, выполняющий одни и те же вычисления. Закрыть да... но C все еще несколько быстрее.

Ответ 6

Короткий ответ заключается в том, что на практике будет мало различий.

Теперь для долгого ответа:

Будет разница в производительности между первой и второй версиями, и может быть разница между вторым и третьим.

Когда вы вызываете первую версию (Vector a, Vector b), копия каждого из аргументов будет сделана в стеке. Это включает в себя выделение памяти стека и копирование полей-членов класса Vector.

Вторая версия не будет копировать объекты Vector. Вместо этого память будет выделена для двух ссылок (возможно, 4 или 8 байт в зависимости от вашего компьютера) и заполняется адресом объектов-векторов-вызывающих. Это немного меньше распределения памяти и еще несколько записей.

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

В вашем случае класс Vector настолько мал, что вряд ли будет какая-либо практическая разница, если вы не совершаете огромное количество вызовов. Гораздо важнее понять различную семантику между вашей первой и второй версиями. В первой версии изменения на a и b не влияют на представление вызывающего объекта этих объектов. Во второй версии (со ссылками) изменения в a и b do влияют на представление вызывающего абонента.

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

В вашем случае я бы предложил использовать версию 3, потому что "const" показывает ваше намерение, а передача по ссылке удаляет ненужную копию.

EDIT: templatetypedef сказал, что лучше.