我应该在哪里更喜欢按引用传递或按值传递?

Posted

技术标签:

【中文标题】我应该在哪里更喜欢按引用传递或按值传递?【英文标题】:Where should I prefer pass-by-reference or pass-by-value? 【发布时间】:2011-06-26 13:25:47 【问题描述】:

在什么情况下我应该更喜欢按引用传递?按值传递?

【问题讨论】:

【参考方案1】:

在四种主要情况下,您应该使用按引用传递而不是按值传递:

    如果您正在调用需要修改其参数的函数,请使用按引用传递或按指针传递。否则,您将获得参数的副本。 如果您调用的函数需要将大对象作为参数,请通过 const 引用传递它,以避免对该对象进行不必要的复制并降低效率。 如果您编写的复制或移动构造函数根据定义必须采用引用,请使用按引用传递。 如果您正在编写一个想要对多态类进行操作的函数,请使用按引用传递或按指针传递以避免切片。

【讨论】:

在案例 #1 中,如果您不想要 NULL 参数,则首选通过引用传递,因此我不会完全使用“可互换” @Chris Becke- 你能详细说明你的答案吗?我几乎不同意你的所有断言,所以我想听听你对此的看法。 @Chris 2 如何违反封装?如果有别的,1 是违反封装的(如代替someFunc(obj),最好使用obj.someFunc())但有很多时候1 仍然是首选,例如void someFunc(Obj obj) this.registerSomeFunc(); obj.someFunc(); 无论如何,2 都不过必需,除了最琐碎的对象之外的所有对象。 @Chris Becke-您确实对对象的大小有所了解。但是,我的后续问题是:鉴于在 C++ 中所有参数默认按值传递,由于复制,这可能比按引用传递的成本要高得多,您建议通过引用传递什么替代方案避免不必要的性能损失?同样的论点是否不适用于指针传递?另外,对于您的最后两点,为什么按指针传递是更好的选择?是否有一个特殊的原因表明它是一种比通过引用传递更优雅的方法?最后,1 如何违反最小惊喜? @Chris:我时不时听到pointers 的这种说法,但坦率地说,我并没有留下深刻的印象。您将呼叫者的“通知”交换为可能的nullptr 问题。我更喜欢保护后者,并给函数一个足够描述性的名称,以便它表达可能的修改......如果有的话。【参考方案2】:

有几个考虑因素,包括:

性能

按值传递会复制数据,因此按值传递大型数据结构会抑制性能。通过引用传递只传递对数据的引用(基本上是地址)。对于大型数据结构,这可以大大提高性能。对于较小的数据结构(如 int),按引用传递会降低性能。

修改

按值传递复制数据,因此如果目标代码修改该副本,它不会影响原始代码。通过引用传递仅传递数据的地址,因此对该引用所做的修改将对调用代码“可见”。

【讨论】:

为什么将引用小数据结构作为int 传递会抑制性能? @Kyle_the_hacker:我想是参考地点。【参考方案3】:

是的。

通过值传递诸如原生类型之类的足够小以至于直接传递它们是有效的。否则使用传递 (const) 参考。

困难的部分是编写一个可以适用于其中任何一个的模板(在这种情况下,您通常希望使用按引用传递——按值传递大对象的潜在惩罚比传递的潜在惩罚要糟糕得多按值传递时的引用是首选)。

编辑:当然,这是假设所需语义允许其中任何一个的情况 - 显然,如果您正在使用诸如多态对象之类的东西,则不涉及真正的“偏好”,因为您必须 使用指针或引用来获得正确的行为。

【讨论】:

【参考方案4】:

由于其他人已经很好地回答了你的问题,我想补充一点:

如果类没有public复制构造函数,那么你没有选择传值;你必须通过引用传递(或者你可以传递指针)。

以下程序无法编译:

class A

public:
     A()
private:
     A(const A&) 
;

//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 A&)' 是私有的 prog.cpp:18:错误:在此上下文中 prog.cpp:18: 错误:初始化“void f(A)”的参数 1

在 ideone 看到自己:http://www.ideone.com/b2WLi

但是一旦你通过引用传递函数f,那么它编译得很好:http://www.ideone.com/i6XXB

【讨论】:

【参考方案5】:

您已使用 C 和 C++ 标记了您的问题。

因此,我建议您考虑在支持此功能的 C++ 中使用按引用传递,并且您不要考虑在不支持此功能的 C 中使用它。

【讨论】:

【参考方案6】:

这是简单的规则:

pass by reference when the value is large.

其他答案都很棒。只是试图让这个最简单。

【讨论】:

【参考方案7】:

引用传递只能在以下情况下调用:

传递引用比传递值更有效,因为它不复制参数。形参是实参的别名。被调用函数读取或写入形参时,实际上是读取或写入实参本身。

按引用传递和按值传递的区别在于,在被调用函数中对通过引用传入的参数所做的修改在调用函数中生效,而在被调用函数中对按值传递的参数所做的修改函数不能影响调用函数。

如果要修改调用函数中的参数值,请使用传递引用。否则,使用按值传递来传递参数。

引用传递和指针传递的区别是

指针可以为 NULL 或重新分配,而引用不能。 如果 NULL 是一个有效的参数值或者如果您想重新分配指针,请使用 pass-by-pointer。 否则,使用常量或非常量引用来传递参数。

【讨论】:

【参考方案8】:

虽然指针是引用,但c++中的“引用”通常是指标记SomeType&的参数的做法。

你不应该这样做。唯一合适的地方是作为实现各种预定义运算符所需的魔术语法。否则:

你不应该通过引用传递参数 - 通过指针传递,否则你几乎不可能进行代码审查。通过引用传递使得无法通过检查调用来判断哪些参数可以更改。

您也不应该通过引用传递参数。同样,这意味着您正在执行元优化。您应该始终只按值传递,否则您会在窥视对象内部、检查其实现并决定出于某种原因首选按引用传递。

任何 c++ 类都应该实现所有需要按值传递的复制和赋值构造函数和重载。否则它就没有完成它的工作,将程序员从类的实现细节中抽象出来。

【讨论】:

1) 即使您确实按值传递,也无法通过查看函数调用来判断。 2)那么这个怎么样。总是通过引用传递,除非你需要一个副本,否则你会犯有窥视对象内部,检查它的实现并决定出于某种原因首选传递值。 因为按值传递是任何以函数调用形式编写的编程语言的默认语义。 谁在乎它是否是默认值?它很慢。如果您正在编写一个位 blit 函数,您会认真地按值传递位图吗?或者,如果您编写了一个函数来告诉您数据库是否包含某个条目,您会按值传递整个数据库? 如果我有一个位图类,它将包含一个指向位图位的指针。传递 Bitmap 类将调用复制指针的 O(1) 成本。 那么,您的复制构造函数实际上不会复制?

以上是关于我应该在哪里更喜欢按引用传递或按值传递?的主要内容,如果未能解决你的问题,请参考以下文章

按值传递或按引用传递

R按值传递或按引用传递[重复]

java:按值传递或按引用传递[重复]

第7章 按值传递或按引用传递:7.5 处理返回值

第7章 按值传递或按引用传递:7.6 推荐的模板参数声明方法

第7章 按值传递或按引用传递:7.3 使用std::ref()和std::cref()