C ++: Как мне решить, передавать ли параметры по ref или по значению? - PullRequest
0 голосов
/ 25 февраля 2012

С C ++, как мне решить, должен ли я передать аргумент по значению или по ссылке / указателю?(скажите мне ответ как для 32, так и для 64-битных). Давайте возьмем A. Является ли 2 32-битных значения меньше или равнозначно работе в качестве указателя на 32-битное значение?Я думаю, что должен передать по значению, но кто-то сказал мне (хотя я не видел доказательств), что процессоры не обрабатывают значения, а не их битовый размер, и поэтому это больше работы.Так что, если бы я передавал их, было бы больше работы, чтобы передать по значению, таким образом, byref быстрее?Наконец я добавил enum.Я думаю, что перечисления всегда должны быть по значению

Примечание. Когда я говорю под ref, я имею в виду константную ссылку или указатель (не могу забыть констант ...)мне ответ, если я нажимал параметры много раз, и код не использует хвостовой вызов?(Допустим, значения не используются до тех пор, пока 4 или около того не вызовет глубину)

Ответы [ 9 ]

4 голосов
/ 25 февраля 2012

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

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

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

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

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

3 голосов
/ 25 февраля 2012

Я передаю все параметры по ссылке для согласованности, включая встроенные функции (конечно, const используется там, где это возможно).

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

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

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

1 голос
/ 25 февраля 2012

Во-первых, ссылки и указатели не совпадают.

Передача по указателю

Передача параметров указателями, если таковые имеются / некоторые из них применяются:

  1. Переданный элемент может быть нулевым.
  2. Ресурс распределяется внутри вызываемой функции, и вызывающая сторона должна отвечать за освобождение такого ресурса. Не забудьте в этом случае предоставить функцию free () для этого ресурса.
  3. Значение имеет тип переменной, как, например, void*. Когда его тип определяется во время выполнения или в зависимости от шаблона использования (или сокрытия реализации - т.е. Win32 HANDLE), такого как аргумент процедуры потока. (Здесь предпочтение отдается шаблонам c ++ и std :: function, и используйте для этой цели указатели, только если ваша среда не разрешает иное.

Передать по ссылке

Передать параметры по ссылке, если они применимы / некоторые из них применимы:

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

Пропускать копию

Передайте копию, если таковые имеются / некоторые из них применимы:

  1. Обычно старайтесь избегать этого.
  2. Если вы хотите оперировать с копией переданного аргумента. то есть вы знаете, что вызываемая функция все равно создаст копию.
  3. С примитивными типами, меньшими, чем размер системного указателя, - поскольку это не делает разницы между производительностью и памятью по сравнению с константной ссылкой.
  4. Это сложно - когда вы знаете, что тип реализует конструктор перемещения (такой как std :: string в C ++ 11). Тогда это выглядит так, как будто вы передаете копию.

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

1 голос
/ 25 февраля 2012

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

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

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

Поэтому попробуйте передать ихпо ссылке.

Надеюсь, что это было полезно для вас.

С уважением, Кен

0 голосов
/ 31 марта 2012

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

0 голосов
/ 25 февраля 2012

Вот правила, которые я использую:

  • для нативных типов:
    • по значению, когда они являются входными аргументами
    • по неконстантной ссылке, когда ониявляются обязательными выходными аргументами
  • для структур или классов:
    • по константной ссылке, когда они являются входными аргументами
    • по неконстантной ссылке, когда они выводятсяаргументы
  • для массивов:
    • по указателю const, когда они являются входными аргументами (const применяется к данным, а не к указателю здесь, т.е. const TYPE *)
    • по указателю, когда они являются выходными аргументами (const применяется к данным, а не указателю)

Я обнаружил, что очень мало раз требуется выполнениеисключение из вышеуказанных правил.Единственное исключение, которое приходит на ум - это аргумент struct или class, который является необязательным, и в этом случае ссылка не будет работать.В этом случае я использую константный указатель (входной) или неконстантный указатель (выходной), так что вы также можете передать 0.

0 голосов
/ 25 февраля 2012

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

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

При этом, если у вас очень большая структура, вывероятно, не хочу делать много копий этого.Это где const ссылки удобны.Имейте в виду, что const в C ++ не строго соблюдается (даже если это считается плохой практикой, вы всегда можете const_cast отказаться от него).Нет никаких оснований для передачи const int& через int, хотя есть причина для передачи const ClassWithManyMembers& через ClassWithManyMembers.

Все перечисленные вами структуры, я бы сказал,хорошо передать по значению, если вы хотите, чтобы они рассматривались как значения.Учтите, что если вы вызываете функцию, которая принимает один параметр типа struct Rectangle{int x, y, w, h}, это то же самое, что независимо передавать эти 4 параметра, что на самом деле не имеет большого значения.Как правило, вам следует больше беспокоиться о работе, которую должен выполнять конструктор копирования - например, передача vector по значению, вероятно, не очень хорошая идея, поскольку ему придется динамически выделять память и выполнять итерацию по списку, размер котороговы не знаете, и вызываете еще много конструкторов копирования.

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

Кроме того, в C ++ 11 введены ссылки на r-значения, которые еще более усложняют ситуацию.Но это другая тема.

0 голосов
/ 25 февраля 2012

Копирование объектов по значению, как правило, плохая идея - больше ЦП для выполнения функции конструктора;больше памяти для фактического объекта.Используйте const для предотвращения изменения объекта функцией.Подпись функции должна сообщать вызывающей стороне, что может произойти с объектом, на который есть ссылка.

Такие вещи, как int, char, pointers, обычно передаются по значению.

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

0 голосов
/ 25 февраля 2012

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

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

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

Преимущество заключается в уменьшении загрузки памяти для больших объектов, передаваемых по ссылке. Для базовых типов данных (например, 32-разрядных или 64-разрядных целых) производительность незначительна.

Как правило, если вы собираетесь работать на C / C ++, вы должны научиться использовать указатели. Передача объектов в качестве параметров почти всегда будет передаваться через указатель (против ссылки). Несколько экземпляров, которые вы обязательно должны использовать, находятся в конструкторе копирования. Вы также захотите использовать его в операторах, но это не обязательно.

...