传递(int x)和(const int &x)之间的区别
Difference between passing (int x) and (const int &x)
我正在阅读《c++入门+(第6版)》这本书,我遇到了一些让我感到困惑的东西,所以请原谅我,我试图解释…
如果我有一个函数,它的原型是这样的:
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)
的意义是什么?
虽然通过值传递double类型和通过常量引用传递double类型没有什么区别,但是在处理其他类型(如std::string
)时可能会有很大的区别。当你将算法编码为模板时,通过常量引用获取形参变得非常有用,例如,当你必须写
void cube(const T& v);
和T
可以是任何类型。恒定引用方法允许您控制正在进行的复制的数量,因为从某个对象大小开始,传递引用比传递副本要便宜得多。
对于以下两个原型
void cube(double x);
和
void cube(const double &x);
,从调用者和被调用者的角度来看,行为将是相同的,因为两者都不允许将修改传播到调用者。然而,在后一种(const引用)示例中,x将通过引用而不是通过值传递。如果double足够大,或者它是一个更大的数据类型(例如,struct),通过引用传递将避免复制大量数据(因为只传递指针)。
二级性能影响(例如,对缓存的影响与数据副本的权衡)都非常依赖于实现,不仅取决于处理器/缓存体系结构和编译器,还取决于程序的结构和运行时用例。
对于基本类型,使用const&
不会获得任何好处。const&
通常用于大对象,以避免可能昂贵和不必要的复制。
如果类型不支持复制,你还需要const&
,如果你想禁止修改被引用的对象。
随着c++ 11的到来,移动语义质疑了"按值传递小对象,按常量引用传递大对象"的传统智慧据我所知,c++专家社区在这个话题上还没有达成新的共识。例如,看看"想要速度"有多正确?传递value"。
根据[dcl.init.ref] #5.1
中的规则,标准明确要求将引用形参绑定到实参,而不是将引用绑定到某个副本(仅举个例子)。差异是可检测的,因此没有"as if"规则适用(在所有情况下)。
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
}
嗯,有一些区别:
- 使用
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:
struct some_very_big_struct {
...
}
,其中sizeof(some_very_big_struct)
是2^10
字节,所以复制这个结构体需要花费很多时间和额外的内存,所以在这种情况下,传递一个引用是最好的选择。
虽然读/写操作会减慢速度,但在32位机器上,引用/指针只有32位,而双精度对象是64位。这在32位的字长下不能很好地播放。
这没有意义,真的。一个优化的编译器可能会将它们视为相同的,并生成相同的代码。
编辑:为了解释我自己,我应该强调"可能"。严格的标准遵从性目前是判断编译器是否确实是c++编译器的一个坏方法——编译器经常滥用/忽略标准,而倾向于优化和语法糖。
至于(性能)差异,我想说的是测量传递双精度对象(单词大小)给函数的成本与从指针/引用(单词大小)获取的成本——在大多数情况下,它可以忽略不计。你传递的值越大,从指针/引用中获取的速度就会越快。
Casey Muratori提到了这一点,并鼓励传递稍微大于单词大小的结构。
- int(c) 和 c-'0' 之间的区别。C++
- 向量 <int> a {N, 0} 和 int arr a[N] = {0} 的时间复杂度有什么区别
- 结构体 S { int align; } 之间的区别;(struct 关键字后的名称)和 struct { int al
- 在 C 和 C++ 中作为函数参数,int **a 和 int a[][] 之间有什么确切的区别
- 在 C/C++ 中将数组作为形式参数作为 int arr[] 和 int arr[N] 传递的区别
- int x_ 和 int x 在C++中有什么区别
- 无符号长整型和无符号 int 之间有什么区别,这 2 种类型应该如何在 c# 中封送?
- int (%d) 和 %.0lf 之间有什么区别?
- 打印一个带有静态 int 的函数,有一个 std::cout 和多个 std::cout 有什么区别?
- 有什么区别 - 常量 int x = 5000;和常量整数 x = 50'00;在C++?
- 杀死幻数:"const int" vs "constexpr int"(或者最后没有区别)
- uniform_int_distribution <>和 <int>有什么区别?
- “int*[1]”和“int(*)[1]”有什么区别
- std::vector<int> 和 std::vector* 有什么区别<int>?
- "auto x = vector<int>()"和"vector<int> x"有什么区别?
- int '*p = new int (5);' 和 'int *p = new int[5];' 有什么区别?
- C++ 中的 "int a, b =0" 和 "int a =0; int b = 0" 有什么区别?
- const int const&和const int&in C++有什么区别?
- "long int a=2"和"int a=2L"有什么区别?
- "long"和"long int"以及"long long"与"long long int"有什么区别