在以下C ++函数中:
void MyFunction(int age, House &purchased_house)
{
...
}
void MyFunction(const int age, House &purchased_house)
{
...
}
哪个更好?
在两者中,'age'按值传递。我想知道'const'关键字是否有必要:对我来说似乎是多余的,但也很有用(作为额外的指示,变量不会改变)。
有没有人对上述哪种(如果有的话)更好有任何意见?
答案 0 :(得分:63)
首先,它只是一个实现细节,如果你把 const 放在那里,不要把它放在声明集(标题)中。只将它放在实现文件中:
// header
void MyFunction(int age, House &purchased_house);
// .cpp file
void MyFunction(const int age, House &purchased_house);
{
...
}
定义中的参数是否为const纯粹是一个实现细节,不应该是接口的一部分。
我经常没见过这种事情,我也不这样做。拥有参数const会让我更加困惑而不是帮助,因为我会立即将模式匹配 - 将其失败为“const int& age”:)这当然与使用 const 完全不同另一个层面:
// const is a *good* thing here, and should not be removed,
// and it is part of the interface
void MyFunction(const string &name, House &purchased_house);
{
...
}
在这种情况下,const将影响函数是否可以更改调用者的参数。应尽可能经常使用此意义的Const,因为它可以帮助确保程序的正确性并改进代码的自我记录。
答案 1 :(得分:41)
我建议阅读Herb Sutter. Exceptional C++。有一章“Const-Correctness”。
“实际上,对于编译器,无论是否在值参数前面包含此const,函数签名都是相同的。”
这意味着这个签名
void print(int number);
实际上与此相同:
void print(int const number);
因此,对于编译器,声明函数的方式没有区别。并且你不能通过将const关键字放在pass参数值前面来重载它。
进一步阅读,Herb Sutter建议:
“避免在函数声明中使用const pass-by-value参数。如果不修改参数const仍然在同一函数的定义中。”
他建议避免这个:
void print(int const number);
因为这个const令人困惑,冗长和多余。
但是在定义中,你应该这样做(如果你不改变参数):
void print(int const number)
{
// I don't want to change the number accidentally here
...
}
所以,你会确定即使在1000行函数的主体之后你总是没有触及数字。编译器将禁止您将该数字作为非const引用传递给另一个函数。
答案 2 :(得分:23)
任何程序员都应该熟悉传递引用和传递值之间的区别,正如任何程序员应该理解'const'一样。
答案 3 :(得分:9)
您不仅表示它不会改变,它将阻止您的代码认为它可以并且它将在编译时被捕获。
答案 4 :(得分:8)
请参阅Meyers和'Effective C ++',并使用const自由,特别是使用pass-by-reference语义。
在这种原子变量的情况下,效率并不重要,但代码清晰度仍然有益。
答案 5 :(得分:7)
好吧,正如其他人已经说过的那样,从C ++语言的角度来看,上面的const
对函数签名没有影响,即函数类型保持不变,无论const
是什么有没有。它在抽象C ++语言层面的唯一影响是你不能在函数体内修改这个参数。
但是,在较低级别,应用于参数值的const
修饰符可以具有一些优化优势,给定足够聪明的编译器。考虑具有相同参数集的两个函数(为简单起见)
int foo(const int a, const double b, const long c) {
/* whatever */
}
void bar(const int a, const double b, const long c) {
/* whatever */
}
让我们说代码中的某处,如下所示
foo(x, d, m);
bar(x, d, m);
通常,编译器在调用函数之前使用参数准备堆栈帧。在这种情况下,堆栈通常会准备两次:每次调用一次。但是聪明的编译器可能会意识到,由于这些函数不会更改其本地参数值(使用const
声明),因此为第一次调用准备的参数集可以安全地重用于第二次调用。因此它只能准备一次堆栈。
这是一个相当罕见且模糊的优化,只有在调用点(相同的转换单元或高级全局优化编译器)知道函数的定义时才能工作,但有时它可能是值得一提。
说它“毫无价值”或“没有效果”是不正确的,即使使用典型的编译器也可能是这种情况。
值得一提的另一个考虑因素是性质不同。那里有编码标准,要求编码人员不要改变初始参数值,如“不要将参数用作普通局部变量,参数值应在整个函数中保持不变”。这有点意义,因为有时它可以更容易地确定函数最初给出的参数值(在调试器中,在函数体内)。为了帮助实施此编码标准,人们可能会在参数值上使用const
说明符。值得与否是一个不同的问题......
答案 6 :(得分:4)
使用const
的一个好处是,您不会在age
的中间意外更改MyFunction
的值(如果您忘记它没有通过引用传递)。一个“缺点”是您无法使用age
等代码回收foo.process(++age);
。
答案 7 :(得分:4)
你是对的,“const int age”的唯一目的是年龄不能改变。然而,对于大多数程序员来说,这可能是非常混乱的。因此,如果这些方法在代码中没有被广泛使用,我建议省略const。
答案 8 :(得分:4)
第二种变体更好。在第一个中你可以意外地改变变量年龄。
答案 9 :(得分:4)
如果(并且仅当)您将在任何其他不会被修改的局部变量上使用它,您应该对参数使用const
:
const int age_last_year = age - YEAR;
在可能的情况下标记局部变量const有时很方便,因为这意味着您可以查看声明并知道该值,而无需考虑中间代码。您可以在将来轻松更改它(并确保您没有破坏函数中的代码,如果它现在代表稍微不同的变量,可能会更改变量的名称,这是可更改的,而不是之前的不变的东西)。
与此相反,它确实使代码更加冗长,而在一个简短的函数中,几乎总是非常明显的是哪些变量发生了变化,哪些变量没有变化。
所以,要么:
void MyFunction(const int age, House &purchased_house)
{
const int age_last_year = age - YEAR;
}
或:
void MyFunction(int age, House &purchased_house)
{
int age_last_year = age - YEAR;
}
答案 10 :(得分:4)
以下是一些很棒的文章和一本书,我发现了解释使用const的优点:
Effective C ++
我可以向您提出以下格言吗?
如果一个对象/变量可以被限定为常量,那么它应该是。在最坏的情况下,它不会花费任何成本。最好的情况是,您将在代码中记录对象/变量的角色,这将使编译器能够更好地优化您的代码。
有些编译器忽略了使用'const'来利用优化的潜力,这导致许多专家在可以使用时忽略了按值使用常量参数。这种做法要求更严格,但不会有害。在最坏的情况下,你不会失去任何东西,但是你也没有获得任何东西,最好的是,你从这种方法中获胜。
对于那些似乎不了解const在函数/方法的by参数中的效用的人......这里有一个简短的例子来解释原因:
的.cpp
void WriteSequence(int const *, const int);
int main()
{
int Array[] = { 2, 3, 4, 10, 12 };
WriteSequence(Array, 5);
}
#include <iostream>
using std::cout;
using std::endl;
void WriteSequence(int const *Array, const int MAX)
{
for (int const * i = Array; i != Array + MAX; ++i)
cout << *i << endl;
}
如果我在int MAX前面删除了const并且我在这里写了MAX + 1会怎么样?
void WriteSequence(int Array[], int MAX)
{
MAX += MAX;
for (int * i = Array; i != Array + MAX; ++i)
cout << *i << endl;
}
你的程序会崩溃!现在,为什么会有人写“MAX + = MAX;” ?也许人为错误,也许程序员当天感觉不舒服,或者程序员根本不知道如何编写C / C ++代码。如果你有const,代码甚至都没有编译!
安全代码是一个很好的代码,当你拥有它时,不需要花费任何东西来添加“const”!
以下是针对一个非常相似问题的不同帖子的答案:
“const在论证时毫无意义 因为你不会通过值传递 修改调用者的对象。“
错误。
这是关于自我记录代码的 和你的假设。
如果您的代码有很多人在工作 它和你的功能 非平凡,那么你应该标记 “const”任何和你的一切 能够。在写工业实力的时候 代码,你应该总是假设 你的同事是精神病患者 以任何方式帮助你 (特别是因为它经常是你自己 在将来)。
此外,正如有人提到的那样 之前,它可能有助于编译器 稍微优化一下(尽管它是一个 远射)。
以下是链接:answer。
答案 11 :(得分:1)
具有通过值const传递的基本类型几乎毫无价值。将const传递给函数通常可以作为与调用者的契约,即函数不会更改该值。在这种情况下,因为int是按值传递的,所以函数不能进行任何在函数外部可见的更改。
另一方面,如果不对对象进行任何更改,则rreferences和非平凡对象类型应始终使用const。从理论上讲,这可能会带来一些优化,但最重要的是我上面提到的合同。缺点当然是,它可以使您的界面更大,并且改进现有系统(或使用不使用const的第三方API)是一件困难的事情。