传递(int x)和(const int& x)之间的区别

时间:2015-07-19 15:54:13

标签: c++

我正在阅读C ++ Primer Plus(第6版)这本书,我遇到了一些让我感到困惑的事情,所以当我试图解释时请耐心等待......

如果我有一个原型如此的函数:

void cube(double x);

和原型的另一个函数看起来像这样:

void cube(const double &x);

两者有什么区别?对于第一个函数原型,值通过值传递,这意味着它将被复制,因此不会被函数更改。对于第二个原型,值通过引用传递,但它是一个常量引用,因此C ++将创建一个匿名临时变量,并将参数的值赋给临时变量,从而模仿pass by value。所以,实质上两个函数原型之间确实没有区别,对吧?那么(const double& x)的重点是什么?

6 个答案:

答案 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提到了这一点,并鼓励将结构略微超过单词大小。