#c #function #pass-by-reference #pass-by-value
#c #функция #передача по ссылке #передача по значению
Вопрос:
При каких обстоятельствах я должен предпочесть передачу по ссылке? Передача по значению?
Ответ №1:
Есть четыре основных случая, когда вы должны использовать передачу по ссылке вместо передачи по значению:
- Если вы вызываете функцию, которой необходимо изменить ее аргументы, используйте передачу по ссылке или передачу по указателю. В противном случае вы получите копию аргумента.
- Если вы вызываете функцию, которой необходимо принять большой объект в качестве параметра, передайте его по ссылке const, чтобы избежать создания ненужной копии этого объекта и значительного снижения эффективности.
- Если вы пишете конструктор копирования или перемещения, который по определению должен принимать ссылку, используйте передачу по ссылке.
- Если вы пишете функцию, которая хочет работать с полиморфным классом, используйте передачу по ссылке или передачу по указателю, чтобы избежать нарезки.
Комментарии:
1. В случае # 1 передача по ссылке предпочтительнее, если вам не нужен аргумент NULL, поэтому я бы точно не использовал «взаимозаменяемо»
2. @Chris Becke — Не могли бы вы подробнее остановиться на своем ответе? Я не согласен почти со всеми вашими утверждениями, поэтому я хотел бы услышать вашу точку зрения на это.
3. @Chris Как 2 нарушает инкапсуляцию? Если что-то еще, 1 является нарушением инкапсуляции (так как вместо
someFunc(obj)
этого было бы лучше использоватьobj.someFunc()
), но есть много случаев, когда 1 по-прежнему предпочтительнее, например,void someFunc(Obj obj) { this.registerSomeFunc(); obj.someFunc(); }
независимо от того, 2 почти требуется для всех, кроме самых тривиальных объектов.4. @Chris Becke — у вас действительно есть замечание относительно размера объекта. Однако мой следующий вопрос заключается в следующем: учитывая, что в C все параметры передаются по значению по умолчанию, что может стоить значительно дороже, чем передача по ссылке из-за копирования, какую альтернативу вы предлагаете передавать по ссылке, чтобы избежать ненужного снижения производительности? И разве тот же аргумент не применим к передаче по указателю? Кроме того, для ваших последних двух пунктов, почему передача по указателю является лучшим выбором? Есть ли особая причина, по которой это более элегантный подход, чем передача по ссылке? Наконец, как 1 нарушает наименьшее удивление?
5. @Chris: Я время от времени слышал этот аргумент
pointers
, но, честно говоря, я не впечатлен. Вы обмениваете «уведомление» вызывающего абонента на возможнуюnullptr
проблему. Я предпочитаю защищаться от последнего и даю функции достаточно описательное имя, чтобы оно выражало возможную модификацию… если есть.
Ответ №2:
Существует несколько соображений, в том числе:
Производительность
Передача по значению копирует данные, поэтому передача больших структур данных по значению может снизить производительность. Передача по ссылке передает только ссылку (в основном адрес) на данные. Для больших структур данных это может значительно повысить производительность. Для небольших структур данных (таких как int) передача по ссылке может снизить производительность.
Изменения
Передача по значению копирует данные, поэтому, если целевой код изменяет эту копию, это не повлияет на оригинал. Передача по ссылке передает только адрес данных, поэтому изменения, внесенные в отношении этой ссылки, будут «видны» вызывающему коду.
Комментарии:
1. Почему передача по ссылке на небольшие структуры данных
int
снижает производительность?2. @Kyle_the_hacker: Я полагаю, местоположение ссылки.
Ответ №3:
вот простое правило:
pass by reference when the value is large.
другие ответы потрясающие. Просто пытаюсь сделать это проще.
Ответ №4:
ДА.
Передача по значению для таких вещей, как собственные типы, которые достаточно малы, чтобы передача их напрямую была эффективной. В противном случае используйте передачу по ссылке ( const
).
Сложная часть заключается в написании шаблона, который мог бы применяться к любому из них (в этом случае вы обычно хотите использовать передачу по ссылке — потенциальный штраф за передачу большого объекта по значению намного хуже, чем потенциальный штраф за передачу по ссылке, когда передача по значению была бы предпочтительнее).
Редактировать: это, конечно, предполагает ситуацию, когда требуемая семантика допускает либо то, либо другое — очевидно, что если вы работаете с чем-то вроде полиморфных объектов, никаких реальных «предпочтений» не требуется, потому что вы должны использовать указатель или ссылку для получения правильного поведения.
Ответ №5:
Вы отметили свой вопрос как C, так и C .
Поэтому я предлагаю вам рассмотреть возможность использования передачи по ссылке в C , который поддерживает эту функцию, и не рассматривать возможность ее использования в C, который не поддерживает эту функцию.
Ответ №6:
Поскольку другие уже достаточно хорошо ответили на ваш вопрос, я хотел бы добавить важный момент:
Если класс не имеет public
конструктора копирования, то у вас нет выбора передавать по значению; вы должны передавать по ссылке (или вы можете передать указатель).
Следующая программа не будет компилироваться:
class A
{
public:
A(){}
private:
A(const Aamp;) {}
};
//source of error : pass by value
void f(A ) {}
int main() {
A a;
f(a);
return 0;
}
Ошибка:
prog.cpp : В функции ‘int main()’:
prog.cpp: 10: ошибка: ‘A::A(const Aamp;)’ является закрытым
prog.cpp: 18: ошибка: в этом контексте
prog.cpp: 18: ошибка: инициализация аргумента 1 ‘void f (A)’
Посмотрите сами на ideone : http://www.ideone.com/b2WLi
Но как только вы заставляете функцию f
передавать по ссылке, она отлично компилируется: http://www.ideone.com/i6XXB
Ответ №7:
передача по ссылке может быть вызвана только в следующих условиях:
Передача по ссылкам более эффективна, чем передача по значению, потому что она не копирует аргументы. Формальный параметр является псевдонимом для аргумента. Когда вызываемая функция считывает или записывает формальный параметр, она фактически считывает или записывает сам аргумент.
Разница между передачей по ссылке и передачей по значению заключается в том, что изменения, внесенные в аргументы, переданные по ссылке в вызываемой функции, оказывают влияние на вызывающую функцию, тогда как изменения, внесенные в аргументы, переданные по значению в вызываемой функции, не могут повлиять на вызывающую функцию.
Используйте передачу по ссылке, если вы хотите изменить значение аргумента в вызывающей функции. В противном случае используйте передачу по значению для передачи аргументов.
Разница между передачей по ссылке и передачей по указателю заключается в
указатели могут быть нулевыми или переназначенными, тогда как ссылки не могут. Используйте передачу по указателю, если NULL является допустимым значением параметра или если вы хотите переназначить указатель. В противном случае используйте постоянные или непостоянные ссылки для передачи аргументов.
Ответ №8:
Хотя указатели являются ссылками, «ссылка» в c обычно относится к практике пометки параметра SomeType amp; .
Чего ты никогда не должен делать. Единственное место, где это уместно, — это как магический синтаксис, необходимый для реализации различных предопределенных операторов. В противном случае:
-
Вы никогда не должны передавать параметры по ссылке — передача по указателю, иначе вы сделаете просмотр кода практически невозможным. Передача по ссылке делает невозможным определение путем проверки вызова, какие параметры, как ожидается, будут изменены.
-
Вы также никогда не должны передавать параметр по ссылке. Опять же, это означает, что вы выполняете мета-оптимизацию. Вы всегда должны просто передавать по значению, в противном случае вы виновны в том, что заглядываете внутрь объекта, изучаете его реализацию и решаете, что передача по ссылке по какой-то причине предпочтительнее.
Любой класс c должен реализовывать все конструкторы копирования и присваивания и перегрузки, необходимые для передачи по значению. В противном случае он не выполнил свою работу по абстрагированию программиста от деталей реализации класса.
Комментарии:
1. 1) Даже если вы передаете по значению, вы не можете определить это, посмотрев на вызов функции. 2) Как насчет этого тогда. Всегда передавайте по ссылке, если только вам не нужна копия, иначе вы будете виновны в том, что заглядываете внутрь объекта, изучаете его реализацию и решаете, что передача по значению по какой-то причине предпочтительнее.
2. Потому что передача по значению является семантикой по умолчанию любого языка программирования, который написан в терминах вызовов функций.
3. Кого волнует, что это значение по умолчанию? Это медленно. Если бы вы писали функцию bit blit, вы бы серьезно передавали растровое изображение по значению? Или, если вы написали функцию, которая сообщает вам, содержит ли база данных определенную запись, вы бы передавали всю базу данных по значению?
4. Если бы у меня был класс Bitmap, он содержал бы указатель на биты bitmap. Передача класса Bitmap привела бы к затратам O (1) на копирование указателя.
5. Итак, ваш конструктор копирования на самом деле не будет копировать?