我正在阅读C ++ Primer Plus(第6版)这本书,我遇到了一些让我感到困惑的事情,所以当我试图解释时请耐心等待......
如果我有一个原型如此的函数:
void cube(double x);
和原型的另一个函数看起来像这样:
void cube(const double &x);
两者有什么区别?对于第一个函数原型,值通过值传递,这意味着它将被复制,因此不会被函数更改。对于第二个原型,值通过引用传递,但它是一个常量引用,因此C ++将创建一个匿名临时变量,并将参数的值赋给临时变量,从而模仿pass by value。所以,实质上两个函数原型之间确实没有区别,对吧?那么(const double& x)的重点是什么?
答案 0 :(得分:4)
对于第二个原型,值是通过引用传递的,但它是一个常量引用,因此 C ++将创建一个匿名临时变量,并将参数的值赋给临时变量,从而模仿按值传递。
虽然C ++会在某些情况下执行此操作。例如,如果传递一个返回double
的表达式,C ++将创建一个临时的:
double v = 123.456;
cube(5*v+321.0123);
然而,它不一定会那样做。例如,如果您拨打此电话
double v = 123.456;
cube(v);
C ++会将对v
的引用直接传递给cube()
函数。在这种情况下,v
的并发修改将是"可见"在cube()
函数运行时运行。{/ p>
所以,实质上两个函数原型之间确实没有区别,对吗?
没错,两者之间没有太大区别。假设double
占用与double
的引用相同的空间量,则性能也没有差异。
那么
(const double &x)
有什么意义?
虽然传递double by值或通过常量引用之间几乎没有区别,但在处理其他类型(例如std::string
)时可能会有相当大的差异。当您将算法编码为模板时,即当您必须编写
void cube(const T& v);
,您的T
可以是任何类型。常量引用方法允许您控制正在进行的复制量,因为从特定对象大小开始,传递引用变得比传递副本便宜得多。
答案 1 :(得分:4)
对于以下两个原型
void cube(double x);
和
void cube(const double &x);
从调用者和被调用者的角度来看,行为将是相同的,因为它们都不允许修改传播给调用者。但是,在后者(const引用)示例中,x将通过引用而不是值传递。如果double足够大,或者它是一个更大的数据类型(例如struct),那么通过引用传递将避免复制大量数据(因为只传递了指针)。
二阶性能影响(例如,对数据副本权衡的缓存影响)都非常依赖于实现,并且不仅取决于处理器/缓存架构和编译器,还取决于结构和运行时使用该计划的案例。
答案 2 :(得分:1)
对于原始类型,使用const&
无法获得任何结果。 const&
传统上用于大型对象,以避免可能昂贵和不必要的复制。
如果类型不支持复制但您想要禁止修改引用的对象,则还需要const&
。
随着C ++ 11的出现,移动语义质疑“按值传递小对象,通过const引用传递大对象”的传统智慧就我而言可以看出,C ++专家社区尚未就此主题达成新的共识。例如,请参阅How true is "Want Speed? Pass by value"。
答案 3 :(得分:1)
嗯,有一些区别:
void cube(double x)
,您在函数中制作变量x的本地副本,并且可以在不更改值的情况下更改其值原来的x。因此,在本地函数void cube
中,您可以像使用序数变量一样与x进行交互。void cube(const double& x)
,您只是传递一个指针(在c ++中,引用是一个指针,但略有另一种使用语法),const
在这里表示您无法更改此地址中变量的值(指针指向该值)。因此,在本地函数void cube
中,您应该像使用常量变量一样与x进行交互。性能差异如何?
使用double
,性能没有差别,因为double
占用64位,而double
的引用需要32位或64位,差别不大。但是,假设你有一个结构:
struct some_very_big_struct {
...
}
其中sizeof(some_very_big_struct)
是2^10
个字节,因此制作此结构的副本需要花费很多时间和额外的内存,因此在这种情况下传递引用是最佳选择。
答案 4 :(得分:1)
根据[dcl.init.ref] #5.1
中的规则,该标准明确要求将参数绑定到参数,而不是(仅作为示例)绑定到某个副本的引用。差异是可以检测到的,所以没有"好像"规则适用(在所有情况下)。
double a;
void foo(const double &x) { assert(&a == &x); }
void bar() { foo(a); }
也有实际的区别。在const double &x
的情况下,名称x
只是可以通过其他左值访问的一些双精度的别名,这对优化有很多影响(如寄存器保存/恢复,指令重新排序,等),例如:
double a;
void foo(const double &x) {
... = x;
a = bar(); // here the compiler must assume that the assignment
// may modify x, the two statements can't be reordered
}
void foo(double x) {
... = x;
a = bar(); // here the compiler knows that the assignment
// cannot modify x, and the two statements can be
// reordered
}
答案 5 :(得分:-2)
尽管读/写操作会降低它的速度,但参考/指针在32位机器上只有32位,其中double是64位。对于32位字长,这种情况不太好。
真的没有意义。优化编译器可能会对它们进行相同处理并生成相同的代码。
编辑:为了解释自己,我应该强调“可能”。严格的标准合规性目前是判断编译器是否确实是C ++编译器的一种坏方法 - 编译器经常滥用/忽略标准,支持优化和语法糖。
至于(性能)差异,我试图说测量将双倍(超过字大小)传递给函数VS从指针/引用(字大小)获取的成本的成本 - 并且在大多数情况下这种情况可以忽略不计。您传递的值越大,从指针/引用获取的速度就越快 Casey Muratori提到了这一点,并鼓励将结构略微超过单词大小。