在c++中,是通过值传递更好,还是通过引用到const传递更好?
我不知道哪种做法更好。我意识到,通过引用传递到const应该在程序中提供更好的性能,因为您没有对变量进行复制。
在c++中,是通过值传递更好,还是通过引用到const传递更好?
我不知道哪种做法更好。我意识到,通过引用传递到const应该在程序中提供更好的性能,因为您没有对变量进行复制。
当前回答
编辑:戴夫·亚伯拉罕斯关于cpp-next的新文章:想要速度?按值传递。
对于复制容易的结构体,通过值传递还有一个额外的好处,即编译器可能假定对象没有别名(不是相同的对象)。使用引用传递,编译器不能总是这样假设。简单的例子:
foo * f;
void bar(foo g) {
g.i = 10;
f->i = 2;
g.i += 5;
}
编译器可以将其优化为
g.i = 15;
f->i = 2;
因为它知道f和g不在同一个位置。如果g是一个引用(foo &),编译器不可能假设。因为g.i可以用f->i作为别名,所以它的值必须是7。因此编译器必须从内存中重新获取g.i的新值。
对于更实用的规则,可以在Move Constructors文章(强烈推荐阅读)中找到一组很好的规则。
如果函数打算将实参作为副作用更改,则通过非const引用获取它。 如果函数没有修改其实参,且实参为基本类型,则按值获取它。 否则通过const引用获取,以下情况除外 如果函数无论如何都需要复制const引用,则按值获取。
"Primitive" above means basically small data types that are a few bytes long and aren't polymorphic (iterators, function objects, etc...) or expensive to copy. In that paper, there is one other rule. The idea is that sometimes one wants to make a copy (in case the argument can't be modified), and sometimes one doesn't want (in case one wants to use the argument itself in the function if the argument was a temporary anyway, for example). The paper explains in detail how that can be done. In C++1x that technique can be used natively with language support. Until then, i would go with the above rules.
示例:要使字符串大写并返回大写版本,应该始终通过value传递:无论如何都必须获取它的副本(不能直接更改const引用)-所以最好让它对调用者尽可能透明,并尽早复制,以便调用者尽可能优化-如论文中所述:
my::string uppercase(my::string s) { /* change s and return it */ }
然而,如果你不需要改变形参,可以引用const:
bool all_uppercase(my::string const& s) {
/* check to see whether any character is uppercase */
}
但是,如果形参的目的是向实参中写入内容,则通过非const引用传递
bool try_parse(T text, my::string &out) {
/* try to parse, write result into out */
}
其他回答
对于所有类型,除了内置类型(char, int, double等),迭代器和函数对象(lambdas,从std::*_function派生的类),通常推荐使用const ref。
在move语义出现之前尤其如此。原因很简单:如果通过值传递,则必须创建对象的副本,并且除了非常小的对象外,这总是比传递引用更昂贵。
在c++ 11中,我们获得了move语义。简而言之,move语义允许在某些情况下,对象可以“按值”传递,而无需复制它。特别是当你传递的对象是右值的时候。
就其本身而言,移动对象的代价至少与通过引用传递相同。然而,在许多情况下,函数无论如何都会在内部复制一个对象——即它将获得参数的所有权
在这些情况下,我们有以下(简化的)权衡:
我们可以通过引用传递对象,然后在内部复制。 我们可以按值传递对象。
“按值传递”仍然会导致对象被复制,除非该对象是右值。在右值的情况下,对象可以被移动,因此第二种情况突然不再是“复制,然后移动”,而是“移动,然后(可能)再次移动”。
对于实现了适当的move构造函数的大型对象(如向量、字符串……),第二种情况比第一种情况效率高得多。因此,如果函数拥有参数的所有权,并且对象类型支持有效移动,建议使用按值传递。
历史笔记:
事实上,任何现代编译器都应该能够计算出什么时候传递值是昂贵的,并在可能的情况下隐式地将调用转换为使用const ref。
理论上是这样。在实践中,编译器不能总是在不破坏函数的二进制接口的情况下更改此值。在一些特殊的情况下(当函数是内联的),如果编译器能够判断出原始对象不会通过函数中的操作被改变,副本实际上会被省略。
但一般来说,编译器不能确定这一点,而c++中move语义的出现使这种优化变得不那么重要。
例如,在Scott Meyers的《Effective c++》中。
2对于对象构造函数来说尤其如此,它可以接受参数并将它们存储在内部,作为构造对象状态的一部分。
简单的区别:-在函数中,我们有输入和输出参数,所以如果你传递的输入和输出参数相同,那么使用引用调用,否则,如果输入和输出参数不同,那么最好使用值调用。
示例无效金额(int账户,int存款,int总数)
输入参数:账户、存款 输出参数:total
输入和输出是不同的使用值调用
无效金额(int total, int deposit)
投入总保证金 输出总
根据经验,value用于非类类型,const引用用于类。 如果一个类非常小,最好是通过值传递,但区别很小。你真正想要避免的是按值传递一些巨大的类,并将其全部复制——如果你传递的是一个std::vector,其中包含相当多的元素,这将产生巨大的差异。
通过引用传递比通过值传递更好。我在Leetcode上解lcs问题。它显示了TLE的传递值,但接受代码的传递引用。我花了30分钟才弄明白。
编辑:戴夫·亚伯拉罕斯关于cpp-next的新文章:想要速度?按值传递。
对于复制容易的结构体,通过值传递还有一个额外的好处,即编译器可能假定对象没有别名(不是相同的对象)。使用引用传递,编译器不能总是这样假设。简单的例子:
foo * f;
void bar(foo g) {
g.i = 10;
f->i = 2;
g.i += 5;
}
编译器可以将其优化为
g.i = 15;
f->i = 2;
因为它知道f和g不在同一个位置。如果g是一个引用(foo &),编译器不可能假设。因为g.i可以用f->i作为别名,所以它的值必须是7。因此编译器必须从内存中重新获取g.i的新值。
对于更实用的规则,可以在Move Constructors文章(强烈推荐阅读)中找到一组很好的规则。
如果函数打算将实参作为副作用更改,则通过非const引用获取它。 如果函数没有修改其实参,且实参为基本类型,则按值获取它。 否则通过const引用获取,以下情况除外 如果函数无论如何都需要复制const引用,则按值获取。
"Primitive" above means basically small data types that are a few bytes long and aren't polymorphic (iterators, function objects, etc...) or expensive to copy. In that paper, there is one other rule. The idea is that sometimes one wants to make a copy (in case the argument can't be modified), and sometimes one doesn't want (in case one wants to use the argument itself in the function if the argument was a temporary anyway, for example). The paper explains in detail how that can be done. In C++1x that technique can be used natively with language support. Until then, i would go with the above rules.
示例:要使字符串大写并返回大写版本,应该始终通过value传递:无论如何都必须获取它的副本(不能直接更改const引用)-所以最好让它对调用者尽可能透明,并尽早复制,以便调用者尽可能优化-如论文中所述:
my::string uppercase(my::string s) { /* change s and return it */ }
然而,如果你不需要改变形参,可以引用const:
bool all_uppercase(my::string const& s) {
/* check to see whether any character is uppercase */
}
但是,如果形参的目的是向实参中写入内容,则通过非const引用传递
bool try_parse(T text, my::string &out) {
/* try to parse, write result into out */
}