“const T&arg”与“T arg”

如果你有一个函数,例如一个int ,那么声明这个函数的好方法是什么,为什么比另一个好呢?

 void myFunction (const int &myArgument); 

要么

 void myFunction (int myArgument); 

使用const T & arg如果sizeof(T)>sizeof(void*)并使用T arg if sizeof(T) <= sizeof(void*)

他们做不同的事情。 const T&使函数对variables进行引用 。 另一方面, T arg 将调用对象的拷贝构造函数并传递拷贝 。 如果复制构造函数不可访问(例如它是private ), T arg将不起作用:

 class Demo { public: Demo() {} private: Demo(const Demo& t) { } }; void foo(Demo t) { } int main() { Demo t; foo(t); // error: cannot copy `t`. return 0; } 

对于像原始types这样的小数值 (所有事物都是对象的内容,而不是实际的参照标识,也就是说,它不是句柄或什么的), T arg通常是首选。 对于不能复制和/或保留参考标识的大型对象和对象而言(无论大小如何)都很重要,因此传递参考是首选。

T arg另一个优点是既然是副本,被调用者不能恶意地改变原来的值。 它可以像任何局部variables一样自由地变异variables来完成它的工作。

采取从移动构造函数 。 我喜欢简单的规则

  1. 如果函数试图改变参数作为一个副作用,通过引用/指向一个非const对象。 例:

     void Transmogrify(Widget& toChange); void Increment(int* pToBump); 
  2. 如果函数没有修改它的参数,并且参数是基本types的话,那就把它作为值。 例:

     double Cube(double value); 
  3. 除此以外

    3.1。 如果这个函数总是在里面创build一个参数的副本,那就把它作为值。

    3.2。 如果该函数从来没有创build它的参数的副本,请参考const。

    3.3。 加我 :如果function有时做一个副本,然后决定直觉:如果复制几乎总是完成,然后采取的价值。 如果复制一半的时间完成,请按照安全的方式,并参考const。

在你的情况下,你应该按值取int,因为你不打算修改参数,而参数是原始types的。 我认为“原始types”作为一个非类types或没有用户定义的复制构造函数和sizeof(T)只有几个字节的types。

有一个stream行的build议,指出传递的方法(“通过值”与“通过const引用”)应根据您要传递的types的实际大小来select。 即使在这个讨论中,你也有一个标记为“正确”的答案,正是这个答案。

实际上,根据types的大小做出决定不仅是不正确的,这是一个主要的 ,相当公然的devise错误,显示出严重缺乏对良好编程实践的直觉/理解。

基于对象的实际实现相关物理尺寸的决定必须尽可能地留给编译器。 试图通过对传递方法进行硬编码来将代码“定制”到这些大小,对于100个中的99个来说,是一种完全适得其反的浪费。(是的,对于C ++语言来说,编译器不是有足够的自由来交替使用这些方法 – 在一般情况下,它们在C ++中并不是真正的可互换的。虽然,如果需要的话,可以通过模板元编程来实现一个合适的基于尺寸的[半自动]传递方法;但这是一个不同的故事)。

当您“手工”编写代码时,select传递方法的更有意义的标准可能听起来如下:

  1. 当你传递一个primefaces的,单一的,不可分割的实体时,比如一个单一的任何types的非聚合值 – 一个数字,一个指针,一个迭代器,都希望通过“按值”传递。 请注意,例如,迭代器是逻辑级别的单一值。 所以,不pipe实际大小是否大于sizeof(void *),都希望按值传递迭代器。 (STL实现就是这样,BTW)。

  2. 在传递任何types的聚合值时,最好传递“by const引用”。 即在逻辑层次上明显暴露“复合”性质的值,即使其大小不大于sizeof(void *)也是如此。

两者之间的分离并不总是很清楚,但总是有这样的build议。 而且,分离成“primefaces”和“复合”实体可能取决于你的devise的具体细节,所以这个决定实际上可能会因devise而异。

请注意,这个规则可能会产生与本次讨论中提到的所谓的“正确的”基于尺寸的方法不同的决定。

作为一个例子,你可以观察到,基于大小的方法会build议你手动硬编码不同types的迭代器的传递方法,具体取决于它们的物理尺寸。 这使得基于尺寸的方法如何变得特别明显。

再一次,好的编程实践从中获得的基本原则之一是避免根据平台的物理特性做出决定(尽可能多)。 相反,您的决定必须基于程序中实体的逻辑和概念属性(尽可能)。 在这里,“通过价值”或“通过引用”传递的问题也不例外。


在C ++ 11中,将语义移入语言引起了不同parameter passing方法的相对优先级的显着改变。 在某些情况下,通过价值来传递复杂的对象也许是完全可行的

应该将C ++ 11中的所有/大多数setter函数写成接受通用引用的函数模板吗?

与stream行和长期信念相反,即使传递大对象,传递const引用也不一定更快。 你可能想阅读Dave Abrahams最近关于这个主题的文章 。

编辑:(主要是回应杰夫·哈代的评论):在最大数量的情况下,传递const引用可能是“最安全”的select,但这并不意味着它总是最好的做法。 但是,为了理解这里讨论的内容,你确实需要仔细阅读Dave的整篇文章,因为它是相当技术性的,其结论背后的推理并不总是显而易见的(你需要理解做出明智select的理由)。

通常对于内置types,您可以通过值传递。 他们是小型的。

对于用户定义的types(或模板,当你不会传递什么)更喜欢const&。 引用的大小可能小于types的大小。 它不会招致额外的副本(没有调用复制构造函数)。

那么,是的…关于效率的其他答案是真实的。 但是这里还有其他的东西是重要的 – 按值传递一个类创build一个副本,因此调用复制构造函数。 如果你在那里做花哨的东西,这是使用引用的另一个原因。

对const T的引用在int,double等标量types的情况下是不值得的打字工作。经验法则是应该通过ref-to-const接受类types。 但是对于迭代器(可能是类types),我们经常发生exception。

在通用的代码中,你应该大部分时间写“T const”来保证安全。 还有boost可以用来select最有希望的parameter passingtypes的调用特征 。 它基本上使用ref-to-const作为类的types,并为标量types传递值,据我所知。

但也有一些情况下,您可能希望按值接受参数,而不pipe创build副本的花费多less。 见Dave的文章“想要速度?使用价值传递!” 。

对于像int,double和char *这样的简单types,按值传递它是有意义的。 对于更复杂的types,我使用const T&,除非有特定的原因不。

传递4 – 8字节参数的成本尽可能低。 你不通过参考购买任何东西。 对于较大的types,通过价值传递可能是昂贵的。

对int来说不会有任何区别,因为当你使用一个引用时,内存地址仍然需要传递,而内存地址(void *)通常是一个整数的大小。

对于包含大量数据的types来说,它变得更有效率,因为它避免了复制数据所带来的巨大开销。

那么两者之间的差异对于整数并不意味着什么。

但是,当使用较大的结构(或对象)时,您使用的第一个方法(通过const引用传递)可让您访问结构而无需复制结构。 第二种情况是通过值传递将实例化一个新的结构,将具有与参数相同的值。

在这两种情况下,你都可以在调用者看到这个

 myFunct(item); 

对于调用者,item不会被myFunct改变,但是通过引用不会产生创build副本的代价。

对于在C ++中通过引用/值传递的类似问题,有一个非常好的答案

它们之间的区别是传递一个int(被复制),一个使用现有的int。 由于它是一个const引用,它不会被改变,所以它的工作原理是一样的。 这里最大的区别是函数可以在本地修改int的值,而不是const引用。 (我想有些白痴可以用const_cast<>做同样的事情,或者至less尝试一下。)对于较大的对象,我可以想到两个不同之处。

首先,一些对象根本无法被复制, auto_ptr<>和包含它们的对象就是一个明显的例子。

其次,对于大而复杂的对象,通过const引用传递比复制传递要快。 这通常不是什么大事,但是通过const引用传递对象是一个有用的习惯。

要么工作正常。 不要浪费你的时间担心这个东西。

唯一可能有所作为的是types是一个大的结构体,这可能是昂贵的传递堆栈。 在这种情况下,将arg作为指针或引用传递(稍微)更有效。

当您传递对象时会出现问题。 如果你通过值传递,复制构造函数将被调用。 如果你还没有实现一个,那么该对象的浅拷贝将被传递给该函数。

为什么这是一个问题? 如果你有指向dynamic分配内存的指针,当拷贝的析构函数被调用时(当对象离开函数的作用域时),这个指针可以被释放。 那么,当你打电话给你的析构函数,你会有一个双重的自由。

道德:写你的拷贝构造函数。