传递(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)是什么意思呢?
对于第二个原型,该值是通过引用传递但它是一个恒定的基准所以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
可以是任何类型变得非常有用。常量引用方法允许您控制正在进行的复制量,因为从某个对象大小开始,传递引用变得比传递副本便宜得多。
我对这个回应有点困惑。如果按值传递,函数将接收它允许编辑的变量的副本。如果通过常量引用传递,而不管引擎盖下发生了什么,语义上该函数将接收对其无法修改的对象的引用。这似乎是一个很大的差异。我是否错过了这一观点,而问题是真正关注价值是如何传递的? –
很好解释。非常感谢你。 – Jonathan
有一个重要的区别:如果通过const引用传递,调用者可以在代码运行时修改引用(例如,从另一个线程或者代码调用另一个函数时)。这改变了你的函数内的值。按价值传递给你一个无人修改的副本。 –
尽管读/写操作会减慢速度,但32位机器上的参考/指针仅为32位,其中双位为64位。这与32位字大小无关。
这是没有道理的,真的。优化编译器可能会将它们视为相同并生成相同的代码。
编辑:为了解释我自己,我应该强调“可能”。
严格的标准符合性目前是一种判断编译器是否确实是C++编译器的不好方法 - 编译器经常滥用/忽略标准而偏爱优化和语法糖。 (性能)的差异,我试图说测量传递一个双(超过字的大小)的函数VS的成本从指针/参考(字大小)获取成本 - 并在大多数情况下它可以忽略不计。您传递的值越大,从指针/引用获取的速度将越快。
凯西Muratori提到这一点,并鼓励通过结构稍微超过字的大小。
任何将它们视为相同并生成相同代码的编译器都不是C++编译器。 – hvd
你似乎对此事有一些认识,但我认为你只是在这里猜测。 –
对于以下两种原型
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
字节,所以使得这个结构的副本,需要真正大量的时间和更多的内存,所以在这种情况下,通过一个参考是最好的选择。
根据[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
}
它不会创建临时变量,您将传递双倍地址,除非调用者被麻醉,否则它不能为空(如果您尝试解引用空引用,您将得到异常抛出)以及你可以使用。而不是 - >。此外,你不能改变储存在x – Creris
内的内容我喜欢你如何描述,详细地说,两者之间的差异。然后你说:“所以两者没有区别,对吧?”你的描述也不太准确:在这里没有“匿名临时”。 –