在C ++中传递值或传递常量引用会更好吗?
在C ++中传递值或传递常量引用会更好吗?
我想知道哪种更好的做法。 我意识到通过常量引用应该提供更好的性能,因为你没有制作变量的副本。
对于迭代器和函数对象 (lambdas,派生自std::*_function
类),除了内建类型( char
, int
, double
等)之外,对于所有类型使用const ref,通常建议使用最佳做法1 )。
在移动语义存在之前尤其如此。 原因很简单:如果按值传递,必须创建一个对象的副本,除了非常小的对象外,这通常比传递引用更昂贵。
使用C ++ 11,我们获得了移动语义。 简而言之,移动语义允许在某些情况下,对象可以“按值”传递而不需要复制它。 特别是当你传递的对象是右值时,就是这种情况。
移动物体本身至少与通过参考一样昂贵。 但是,在很多情况下,函数会在内部复制一个对象 - 即它将获得参数的所有权
在这些情况下,我们有以下(简化)权衡:
“按值传递”仍然会导致对象被复制,除非对象是右值。 在右值的情况下,可以移动对象,以便第二种情况突然不再是“复制,然后移动”,而是“移动,然后(可能)再次移动”。
对于实现合适的移动构造函数(如向量,字符串...)的大对象,第二种情况比第一种效率高得多。 因此, 如果函数拥有参数的所有权,并且对象类型支持高效移动 ,则建议使用值传递 。
历史笔记:
实际上,任何现代编译器都应该能够计算出传值时的代价是昂贵的,并且如果可能的话,隐式地将调用转换为使用const ref。
理论上。 在实践中,编译器不能总是在不破坏函数的二进制接口的情况下改变它。 在某些特殊情况下(函数内联时),如果编译器可以发现原始对象不会通过函数中的操作进行更改,则实际上将复制副本。
但是总的来说,编译器无法确定这一点,并且C ++中移动语义的出现使得这种优化更不相关。
1例如Scott Meyers,Effective C ++。
2这对于对象构造函数来说尤其如此,这可能需要参数并将它们内部存储为构造对象状态的一部分。
编辑:由cave-next上的Dave Abrahams撰写的新文章:
想要速度? 按价值传递。
按值传递复制价格低廉的结构时,编译器可能会认为这些对象不是别名(不是相同的对象)。 使用传递引用编译器不能总是假设。 简单的例子:
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&),编译器就不可能假设这一点。 因为gi可能会被f-> i替代,并且必须具有值7.因此编译器将不得不从内存中重新获取gi的新值。
对于更多实用的规则,这里有一些在Move Constructors文章中发现的一系列规则(强烈推荐阅读)。
上面的“Primitive”意味着基本上只有几个字节长的小数据类型,并且不是多态的(迭代器,函数对象等等)或者复制成本高。 在那篇文章中,还有一条规则。 这个想法是,有时候想要做一个副本(如果参数不能被修改),有时候不想要(如果参数本来就是临时的,那么在函数中想要使用参数本身) , 例如)。 该文件详细解释了如何做到这一点。 在C ++ 1x中,该技术可以与语言支持一起使用。 在此之前,我会遵循上述规则。
例子:为了使字符串大写并返回大写版本,应该总是按值传递:无论如何,必须获取它的副本(不能直接更改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 */
}
取决于类型。 您将增加必须作参考和取消引用的小额开销。 对于大小等于或小于使用默认复制ctor的指针的类型,按值传递可能会更快。
链接地址: http://www.djcxy.com/p/73007.html上一篇: Is it better in C++ to pass by value or pass by constant reference?
下一篇: Different types of *