学习C ++时,学习函数概念的第一个函数之一就像
int add(int a, int b)
{
return a+b;
}
现在我想知道:我应该在这里使用const
- 关键字,还是不使用,导致
int add(const int a, const int b)
{
return a+b;
}
但那会有意义吗?它会加速我的程序,做一些其他重要的事情,还是只是增加混乱?
答案 0 :(得分:15)
从来电者的角度,第一个和第二个表单都是相同的。
由于整数按值传递 ,即使函数修改了a
和b
,修改后的值也是原始的副本并且不会被来电者看到。
然而,从功能实施者的角度来看,存在差异。事实上,在第二种形式:
int add(const int a, const int b)
如果您尝试修改函数体内a
和b
的值,则会出现编译错误,因为它们被标记为const
。
相反,如果省略const
,则可以更改这些值
同样,这些修改将不对调用者可见。
答案 1 :(得分:7)
使用int add(const int a, const int b)
意味着您无法修改函数体中的输入参数。
也许编译器可以根据它进行一些优化,因此它应该永远不会比非const
等效的慢。但我从未在实践中观察过这种效应。
传递const
参数还可以提高代码库的稳定性,尤其是在协作项目中。
尽管如此,我发现它太冗长,而且不必要,并会使用int add(int a, int b)
。 非常偶然,对于特别长的函数,我利用了这样一个事实:你可以使用非const
参数声明函数,并且 define 它带有参数const
。
答案 2 :(得分:6)
如果您在const
帮助的代码库中遇到正确性错误,那么请添加const
。
也就是说,您应该考虑相关问题。函数参数的顶级限定符不是函数类型的一部分,因此您的函数类型仍然只是int(int, int)
。 (限定符仅影响函数定义中的参数变量。)这意味着函数的任何声明也会忽略限定符,因此int add(int, int)
和int add(const int, int)
以及{{1} } all声明相同的函数。因此,您必须决定如何编写头文件的策略。现在,您可以采取三个基本立场:
始终符合声明和定义。好处是,这可能使代码保持一致&#34;一致&#34; (在创建实现时考虑复制/粘贴)。缺点是限定符与界面无关,并且完全不可执行(您可以稍后更改定义!),所以最好是它的噪音,最坏的情况是错误的。< / p>
符合定义,但不符合其他声明。好处是,它正确地传达了接口,你仍然可以在定义中进行const检查。缺点是有些人可能会对拼写上的差异感到困惑。 (就像人们可能会混淆int add(int, const int)
类成员可以用static constexpr T foo;
定义。)
不具备资格。好处是它一致,干净,易于记忆和最小化。缺点是您在定义中错过了正确性检查。
没有正确答案。如果您是代码库所有者或项目负责人,则应根据代码库和团队中的最大问题来决定。 (我的个人立场是坚持(3),直到你有充分的理由改变。)
答案 3 :(得分:1)
我觉得每个人都在围绕这部分答案跳舞......
确实,使用const
会使函数无法修改int a
&amp;的价值。 b
在函数内部。这非常有用,因此可以根据需要使用它,编译器允许它。但是,函数调用者永远不会知道对a
&amp;的任何更改。函数完成后b
。即使a
&amp; b
已更改,除定义的函数外,没有人知道其更新的值。
int funcB(int a, int b)
{
a = a+1;
b = b*b;
return a+b;
}
void funcA()
{
int s = 5;
int t = 6;
int result = funcB(s, t);
printf("%f + %f = %f", s,t, result);
}
funcA打印:&#34; 5 + 6 = 42&#34;
通过引用传递值时经常使用 Const
保护,即:
int function(const int &a, const int &b) {}
这会将a
和b
的引用传递给函数(即,不会复制a
和b
,但只传递该变量的内存地址,又名:手柄)。通过引用传递变量时,对变量所做的任何更改都会在函数范围之外被记住,并且可以更改程序的运行方式。这通常是不希望的行为。
因此,如果您从上方返工funcB
并通过引用传递:
int funcB(int &a, int &b)
{
a = a+1;
b = b*b;
return a+b;
}
funcA打印:&#34; 6 + 36 = 42&#34;
如果您将const
正确性添加到funcB
:
int funcB(const int &a, const int &b)
{
a = a+1;
b = b*b;
return a+b;
}
我认为编译器甚至不会让你这样做,因为你明确地试图修改你通过const
保护的值。
另一个时候,使用const可能非常重要的是当你通过指针传递时,而不是引用或复制......
int funcB(int *a, int *b)
{
a = a+1;
b = b*b;
return a+b;
}
除非您是指针专家,否则请避免在没有const预设的情况下传递指针。这个func可能会尝试迭代指针数组的索引,并且你自己打开与运行时内存有关的运行时错误。你可能会意外地从一个完全不同的程序中看到记忆......但可能不会。
最后,由于您只是传递int
,因此没有实际需要通过引用传递(这通常是为了避免将复杂数据添加到内存中,因为每个非引用或由于int
的内存占用量很小,因此非指针传递给函数会将值复制到内存中以供被调用函数使用。除非,您使用内存极其有限的专用硬件,否则它可能会有用;这不适用于过去20年内制造的大多数标准计算机和台式机,或智能手机。
答案 4 :(得分:0)
int add(const int a, const int b)
{
return a+b;
}
这里使用了const,因此该函数不会任何机会修改a和b的原始值。 在上面的例子中它没有意义。但如果它是一个像
的例子int add(const int *a, const int *b)
{
//*a = 10; //This will throw error.
return a+b;
}
在传递对象,数组或此类数据类型的函数中,使用const来避免原始数据结构的突变是一种很好的方法。
答案 5 :(得分:0)
如果你想要真正的const正确,那么你应该添加它,但实际上它所做的只是让你输入和阅读更多。
没有什么会变得更快,虽然它可能意味着你的变量进入内存中的另一个位置,但它在大多数现代机器上都不太可能。
它会阻止你做什么是意外地为它们分配值,但由于它们是函数的本地,它相对来说并不重要。重要的是它们是否是引用,因为这表明你打算让调用者不要改变它们。
答案 6 :(得分:0)
如果您愿意,可以在那里使用const
。
不太可能加速你的程序,因为任何合理的编译器都已经可以看到没有代码路径改变a
或b
的值并进行所需的任何优化。
a
和b
是int
,它们按值传递,因此使const
对此功能的用户没有影响。
唯一可能的优势是您的功能很长且更复杂,并且您希望避免在功能期间更改a
或b
的原始值时可能出现的编程错误。
答案 7 :(得分:0)
如果您将使用const,则无法修改a,b的值。这就是为什么我们不使用const。
答案 8 :(得分:0)
constness的主要目的是提供文档并防止编程错误。 Const允许您向自己和其他人说明不应该更改某些内容。此外,它还有一个额外的好处,即你声明const的任何东西实际上都会使用强有力的方法。将引用参数声明为const引用的函数特别有用:
bool SomeFunction (const myObj& obj);
这里,myObj对象通过引用传递给SomeFunction。为了安全起见,const用于确保SomeFunction不能更改对象 - 毕竟,它只是为了确保对象处于有效状态。这可以防止可能导致损坏对象的愚蠢的编程错误(例如,通过设置类的字段用于测试目的,这可能导致字段永远不会被重置)。此外,通过声明参数const,函数的用户可以确保它们的对象不会被更改,也不必担心进行函数调用可能产生的副作用。
此外,原因是参数的const仅在函数中本地应用,因为它正在处理数据的副本。这意味着函数签名实际上是相同的。尽管如此,这可能是糟糕的风格。我个人倾向于不使用const,除了参考和指针参数。
答案 9 :(得分:0)
作为一个经验法则,我们建议尽可能多地使用const,因为我们得到了编译器的大力帮助,因此每当我们尝试更改常量时,编译器都会捕获错误;避免陷入错误行为是一件好事。
第二件事:a和b按值传递所以它们被创建为局部常量但是只要它们不可更改我们为什么需要副本?
好处是传递一个const引用,这意味着永远不会更改参数并避免复制(按值):
int add(const int& a, const int& b) // now there are no copies of a and b in addition we cannot modify a and be
{
return a+b;
}