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

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

问题描述:

我正在阅读C++ Primer Plus(第6版)一书,我遇到了一些让我感到困惑的东西,所以请耐心等待我试着解释...传递(int x)和(const int x)之间的区别

如果我有它的原型看起来像这样的功能:

void cube(double x); 

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

void cube(const double &x); 

是什么之间的区别二?对于第一个函数原型,该值通过值传递,这意味着它将被复制并因此不被该函数改变。对于第二个原型,值是通过引用传递的,但是它是一个常量引用,因此C++将创建一个匿名临时变量并将该参数的值分配给临时变量,从而模仿按值传递。所以,从本质上来说,这两个函数原型真的没有区别,对吧?那么(const double & x)是什么意思呢?

+0

它不会创建临时变量,您将传递双倍地址,除非调用者被麻醉,否则它不能为空(如果您尝试解引用空引用,您将得到异常抛出)以及你可以使用。而不是 - >。此外,你不能改变储存在x – Creris

+0

内的内容我喜欢你如何描述,详细地说,两者之间的差异。然后你说:“所以两者没有区别,对吧?”你的描述也不太准确:在这里没有“匿名临时”。 –

对于第二个原型,该值是通过引用传递但它是一个恒定的基准所以C++将创建匿名临时变量和由值参数的值分配给临时变量从而模拟通。

虽然C++会在某些情况下做到这一点。例如,如果你通过返回double表达,C++会创建一个临时的:

double v = 123.456; 
cube(5*v+321.0123); 

但是,它不一定会做到这一点。例如,如果您拨打这个电话

double v = 123.456; 
cube(v); 

C++将传递一个参考v直接向cube()功能。在这种情况下,v的同时修改在运行时对cube()功能是“可见的”。

所以,实质上这两个函数原型之间确实没有区别,对吧?

没错,两者没有太大的区别。假设double所占空间与double相同,则性能也不会有差异。

那么(const double &x)的要点是什么呢?

虽然经过双通过值或恒定参考之间的差别不大,你可以与其他类型的交易时,如std::string得到相当大的差异。通过不断参考拍摄参数时,你必须写

void cube(const T& v); 

当你的代码的算法为模板,即你的T可以是任何类型变得非常有用。常量引用方法允许您控制正在进行的复制量,因为从某个对象大小开始,传递引用变得比传递副本便宜得多。

+0

我对这个回应有点困惑。如果按值传递,函数将接收它允许编辑的变量的副本。如果通过常量引用传递,而不管引擎盖下发生了什么,语义上该函数将接收对其无法修改的对象的引用。这似乎是一个很大的差异。我是否错过了这一观点,而问题是真正关注价值是如何传递的? –

+0

很好解释。非常感谢你。 – Jonathan

+0

有一个重要的区别:如果通过const引用传递,调用者可以在代码运行时修改引用(例如,从另一个线程或者代码调用另一个函数时)。这改变了你的函数内的值。按价值传递给你一个无人修改的副本。 –

尽管读/写操作会减慢速度,但32位机器上的参考/指针仅为32位,其中双位为64位。这与32位字大小无关。

这是没有道理的,真的。优化编译器可能会将它们视为相同并生成相同的代码。

编辑:为了解释我自己,我应该强调“可能”。

严格的标准符合性目前是一种判断编译器是否确实是C++编译器的不好方法 - 编译器经常滥用/忽略标准而偏爱优化和语法糖。 (性能)的差异,我试图说测量传递一个双(超过字的大小)的函数VS的成本从指针/参考(字大小)获取成本 - 并在大多数情况下它可以忽略不计。您传递的值越大,从指针/引用获取的速度将越快。
凯西Muratori提到这一点,并鼓励通过结构稍微超过字的大小。

+2

任何将它们视为相同并生成相同代码的编译器都不是C++编译器。 – hvd

+1

你似乎对此事有一些认识,但我认为你只是在这里猜测。 –

对于以下两种原型

void cube(double x); 

void cube(const double &x); 

行为将是从呼叫者的,所述的角度与被呼叫者相同也不会允许修改传播到呼叫者。然而,在后者(const引用)示例中,x将通过引用而不是按值传递。如果double足够大,或者它是一个更大的数据类型(例如,struct),通过引用传递将避免复制大量数据(因为只传递指针)。

二阶性能影响(例如,对缓存权衡影响数据副本的影响)都与实现有关,并且不仅取决于处理器/缓存架构和编译器,还取决于结构和运行时该程序的用例。

对于原始类型,您不会通过使用const&获得任何收益。 const&传统上用于大对象以避免可能的昂贵和不必要的复制。

如果类型不支持复制,您还需要const&但是您不希望修改引用的对象。

随着C++ 11,移动语义出现问题传统智慧“以const引用传递的价值小物件,大物”据我所看到的,C++专家社区有没有但在这个话题上达成了新的共识。例如,请参阅How true is "Want Speed? Pass by value"

好,有一定的区别:

  • 随着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字节,所以使得这个结构的副本,需要真正大量的时间和更多的内存,所以在这种情况下,通过一个参考是最好的选择。

+0

大小是实现定义的。例如,使用Visual Studio编译x86 sizeof(double)== 8和sizeof(double *)== 4 – Creris

+0

@Creris哦,我假定OP使用的是64位系统。编辑,谢谢^^ – FalconUA

根据[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 
    }