如果您已经知道变量应该是常量,为什么还要使用关键字const?


70

const当不应该修改变量的值时,我正在阅读的许多书都使用关键字。除了向代码的读者指定如果您修改此变量可能会导致错误的代码(您可以使用注释来做到这一点),为什么还需要将该关键字作为任何编程语言的一部分?在我看来,如果您不想修改变量,那就根本不要。

有人可以帮我澄清一下吗?


42
为什么要使用受保护的或私有的?还是抽象的?还是最终的?
奥利弗·查尔斯沃思

9
请记住,有很多关键字和概念可以帮助程序员,而不是程序。const两者都有。
按键

72
const对于从未犯错误的程序员,省略将非常有效。
基思·汤普森

26
Stroustrup做到最好:编译器不阅读注释。也许您认为自己比编译器更擅长遵循规则。
DanielKO

33
在进行嵌入式系统编程时,const指定符将为编译器和链接器提供将值的地址映射到内存的EPROM部分而不是RAM中的选项。多年前,我在一个针对预分配的静态变量具有此行为的系统上工作(在const引入关键字之前)。
潜行者

Answers:


100

除了指定代码阅读者之外,如果您修改此变量,可能会导致错误(您可以使用注释来执行此操作)

不是“可以”;导致程序错误。

  • C ++编译器将在编译失败和诊断消息(“编译器错误”)的情况下强制执行它,而无需注释;
  • AC编译器将在很大程度上执行它,尽管它的标准库由于诸如的遗留问题而有漏洞strchr,并且它具有一些相当宽松的隐式转换规则,可以使您放弃constness而不很容易地实现它。但是,仅仅因为您获得了成功的编译并不意味着您就没有错误。不幸的是,这确实意味着错误可能是程序中的细微错误,也可能是巨大的异常崩溃。

无论哪种方式,都可以确保您的程序中包含错误

在我看来,如果您不想修改变量,那就根本不要。

好吧,这很好,但是没有人是完美的。程序员会犯错误。这使编译器(永远不会(至少通常不会)犯错误)可以向您指出错误。

当您使用一些数据变量而不是创建它的地方时,它特别有用。距离越远,修改它就越容易,而又不会意识到您不应该这样做。对于大型,复杂的代码库,这只是必须的。

您可以在代码库中获得一种新的可证明性正确性稳定性的度量,并且可以消除造成真正细微和令人讨厌的错误的可能原因。当您知道某些值在编译后不会改变的情况下,对于编译器来说(在某些情况下)还有大量的优化机会

我们可以整天列出这些优势,但是,实际上,直到您开发了这样的代码库,您都不会完全理解它。

实际上,在理想情况下所有变量都const默认为,并且您需要使用关键字mutable对其进行声明才能进行更改。C ++倒退了。


17
我想生活在那个完美的世界里!
船长Obvlious

1
Scala是一个完美的世界:鼓励不变性,默认情况下集合类是不可变的,并且您需要使用特定的关键字var来声明非可变变量。
哇,2013年

21
@ouah:Scala并不是那么完美的世界。它鼓励不变性。Haskell(例如)执行它。
杰里·科芬

22
“在理想情况下,所有变量默认情况下都将是const”:难道它们不被称为不变变量吗?
朱利安·席姆斯(JulianSymes)2013年

2
+1。经过Haskell和并发编程的一些经验之后,我得出了相同的结论:“默认情况下,所有变量都是const”。实际上,如果变量在默认情况下可变,那么从哲学上讲不明智 。如果类的成员默认情况下是公共的这也有同样的哲学问题。
纳瓦兹

41

至少在C ++中,const它的用途不只是向其他程序员记录您的意图。

const还可以告诉编译器一些事情。例如,带有引用的函数,例如:void f(T &t);不能接受临时对象作为其参数。为此,您需要对const参考进行限定,例如:void f(T const &t)

同样,要在const对象上调用成员函数,必须对成员函数进行const限定,例如:void T::foo() const {}

在嵌入式系统中,它const 可能意味着更多,可能会告诉编译器有关对象的位置(将其放入ROM与RAM中)。const本身并不一定足以告诉它“将这个对象放入ROM”,但是它通常仍然是先决条件。

同样(在C ++ 11下)const 告诉编译器线程安全

现在,毫无疑问,您可以定义某种其他语言(以其他方式)与没有const以这些方式使用的C或C ++类似。结果将是一种与两种语言截然不同的语言。在不知道您的意图的情况下,无法说出结果如何,但是最终可能会更接近Java或C#(在两个示例中),它们在某种程度上都与C和C ++相似,但并非如此。一个(即不要const像C和C ++那样使用)。


const我记得,Java根本不使用。(我认为这是一个保留字,但不能用于任何内容。)其final关键字相似但不完全相同。C#具有const,但是不一样。(我认为它基本上只相当于#defines。)
Darrel Hoffman

38

除了在其他答案中已经讨论过的常规编程注意事项之外,与我有关的一件事是态度:

在我看来,如果您不想修改变量,那就根本不要。

一般的经验法则是,在开发阶段花费了20%的代码编写成本。其余80%花费在代码的整个生命周期中,包括对其进行升级,维护等。这意味着除您之外,还有许多其他人将在您的代码上工作。

在开发过程中花费的时间可以避免几年后出现的问题,这是一笔不错的投资。这项工作包括:撰写评论;定义为常数的常数;并编写依赖模糊语言构造的显式代码。

另外,@ worlboss,我听到了相当多的不宽容。正如其他人所评论的那样,碳单元会出错,而硅单元可以帮助避免错误的任何事情都值得赞赏。


9
+1表示大多数人会忘记的80%的维护费用。
Thomas Matthews

那20%似乎很高。如果一开始就做所有正确的事情,可能会占20%。不幸的是,这似乎并不总是发生。
2013年

@ user606723,这是经验法则。如果开发阶段的成本超过计划成本,那么20%的成本确实会增加,占软件整个生命周期成本的一部分。
JackCColeman 2013年

1
@JackCColeman,我是说20%似乎很高。我认为它接近10-15%
user606723

21

它告诉编译器不应修改该变量,因此,如果有人编写修改该变量的代码,则编译器会将其标记为错误。


2
我敢打赌,与性能相关的好处在这个时候也很重要。
克里斯,

17

两个原因:

  1. 编译器强制执行的文档
  2. 编译器优化

这里是一个很好的解释,从伊恩·兰斯·泰勒(谁在工作gccgold链接):

const的第一个含义对程序有实际影响。声明为const的变量可以与未声明为const的变量进行不同的编译。

另一方面,const的第二个含义实际上是编译器强制执行的文档。如果尝试使用const限定指针更改值,则编译器将发出错误,但声明此类指针不会更改生成的代码。


@LightnessRacesinOrbit也许是因为对于API,您肯定const只会使用指针参数
哇,2013年

1
@ouah:什么?不,不是。const实际上在声明中对参数(无论它们是否是指针无关紧要)(语言)忽略了;您在说的是在声明中应用于const基础指针对象类型而不是函数参数类型本身,因为这毫无意义。它仅对const函数定义中的局部性有用。那么,它对API有什么影响呢?
Lightness Races in Orbit

3
[C++11: 8.3.5/3]: [..]生成参数类型列表后,在形成函数类型时,将删除所有修改参数类型的顶级cv限定词[..]
Lightness比赛于

1
[C99: 6.7.5.3/15]: [..](在确定类型兼容性和复合类型时,以函数或数组类型声明的每个参数均被视为具有调整后的类型,以限定类型声明的每个参数均被视为具有其声明类型的非限定版本。)[..]
亮度种族在轨道

1
^的API(函数声明)甚至没有去看到const秒。在功能定义中重新应用无关。
Lightness Races在轨道上进行的比赛

6

这是一个简单的C示例:

void PrintList(const struct List *l);
void SortList(struct List *l);
int  CmpList(const struct List *a, const struct List *b);
void AppendList(struct List *l, struct List *m);
void PushList(struct List *l, struct ListNode *n);
void PopList(struct List *l, struct ListNode *n);

在这里,我们有一小组的功能可以使用某种类型的节点列表。首先,即使不知道函数的名称,我们也可以立即看到哪些函数以某种方式更改了列表,而哪些没有。const就像标准库中的函数一样,这些函数不会更改您的数据,并且不允许您使用它们来更改数据。C编译器尝试保持对const传递给函数的数据的指针的-ness强制。因此,在这种情况下,我可以合理地确定在进行运行时调试时比较两个列表不是使它们混乱的功能,因为我已经保护自己免受意外修改数据的影响。;)


5

您的编译器可以知道变量不会被更改,因此可以进行重大优化:将变量直接存储在可执行操作码中,而不是将其存储在内存中。

例如:您有一个a和b,要添加它们,则使a + b。如果将a声明为const且值为3,程序将改为3 + b,这将节省内存和周期,因为它不需要检索a值。

问题是您的编译器无法事先知道变量是否恒定,它当然可以分析整个代码并检查您是否修改了这些变量,但是并不能百分百确定,因为将来的代码也可以对其进行修改。


4

关键字const对于团队和长期项目非常有用。我会给你几个例子,这些例子应该说明const关键字的值。

可以说现在我正在创建将用于进一步项目的lib。因此,这意味着今天编写的代码需要在几年后得到信任,在这样的一段时间内,我会忘记不应修改哪个变量(同事甚至都不知道可以修改什么,不能修改什么)。因此,此简短示例说明了为什么要使用const

谈论评论,当最后期限到来时,还有很多东西仍然无法在每个功能上发表评论,这只会浪费时间。但是,在某些情况下,注释是必须的,因为第一个问题(截止日期)可能无法读取注释,因为大多数注释无用,但重要的注释也将被跳过。因此,最好使用const关键字,它会导致编译错误并指出问题所在,然后编写和阅读大量注释。


2

这是一个棘手的问题,因为IMO是基于信念的。这些信念之一是,您可以仅添加更多代码就可以保护代码免受某些更改。当然,编译器会使用多余的代码来检查所有内容。

我认为这并不总是正确的,您无法保护自己或自己的开发团队免受代码或仅添加关键字的开发团队的侵害,实际上,有许多语言没有任何const,public,private,protected,internal,int,float, double关键字,但这并不意味着它们不是良好的语言。

某些代码模式也会发生同样的情况,为什么人们浪费太多时间讨论Singletons !?如果您只想拥有一个实例,那么唯一要做的就是创建一个实例,仅此而已。同样的想法无处不在,看看10年前发表的防御性编程文章,再来看看用代码保护代码的想法。

在某些时候,您必须决定要在哪里设置责任,是在开发人员手上还是在编译人员手上。但是,编译器和任何其他工具都无法将代码保存在开发人员手中,因此许多关键字毫无用处,或者只是与其他开发人员进行交流的一种方式。


1

常量变量只允许您编写更具可读性的代码。

const在几乎所有语言中,最常用的用法是允许我们使用名称来引用常量值,因此您可以使用流利的语言告诉其他人该名称的含义,而无需在代码中散布注释并节省时间和精力从读者那里了解参数的类型和参数的特殊性。当然,如果常量值在代码中重复使用,您也会从中受益。好吧,这样的代码可以更具可读性:

processPages(LETTER_PAPER_WIDTH, LETTER_PAPER_HEIGHT);

...比这个:

processPages(215.9, 279.4); // 8.5 x 11 Inches in millimeters for Letter Papers

在上面的示例中,您需要了解每个参数的含义,其单位和解释值的类型,并且还需要针对注释进行验证,因为这样的冗余注释(那些重放编码内容的注释)不是可靠且有用的评论(根据“干净代码:http//goo.gl/5EyY”中的Robert Martin来说,这是不好的评论)。


-2

考虑这样一种情况:您在整个项目中多次使用相同的常量,并且在所有位置都对其进行硬编码。现在突然需要将常量的值更改为另一个值,因此在所有位置进行更改都非常麻烦。

因此,我认为使您的代码更具可维护性绝对是原因之一。


3
-1。虽然您的答案本身是正确的,但与所提问题无关。问题是为什么要使用const修饰符,答案是为什么要在单个位置定义。
Emilio M Bumachar

我承认我错误地提出了其他问题。抱歉,是我的错!
Mohan Mahajan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.