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



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

有人能帮我澄清一下吗?

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

不是"可能"将导致程序出错。

  • C++编译器将通过编译失败和诊断消息("编译器错误")强制执行它,而不需要注释
  • C编译器将在很大程度上强制执行,尽管其标准库由于遗留问题(如strchr)而存在漏洞,并且它有一些相当宽松的隐式转换规则,可以让您在不容易意识到const的情况下放弃它。然而,仅仅因为你成功地编译了并不意味着你没有错误;不幸的是,确实意味着错误可能是程序中的细微错误,也可能是巨大的、惊人的崩溃

无论哪种方式,您的程序都保证包含错误

在我看来,如果你不想修改一个变量,那就不要。

这一切都很好,但没有人是完美的。程序员会犯错。这允许编译器—从不犯错(至少,通常不会)—把它们指给你看。

当您使用某个数据变量时,它特别有用——在创建它的地方有很多行代码。它离得越远,就越容易在没有意识到你不应该修改它的情况下进行修改。对于大型复杂的代码库来说,这是必须的。

您在代码库中获得了一个新的可证明性正确性定性的度量,以及大量可能导致非常微妙和令人讨厌的错误的原因。编译器(在某些情况下)知道某些值在编译后不会更改时,也有大量的优化机会

我们可以整天列出这些优势,但实际上,在你完成这样的代码库之前,你不会完全开发

事实上,在一个完美的世界中,所有变量默认为const,您需要使用关键字mutable声明它们才能更改它们。C++是向后的

至少在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++有点相似,但不是这个特定的语言(即,不要像C和C++那样使用const)。

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

在我看来,如果你不想修改一个变量不要。

一般的经验法则是,编写代码的成本的20%用于开发阶段。另外80%的时间都花在了代码的升级、维护等方面。这意味着除了你自己,还有很多其他人会处理你的代码。

在多年后避免问题的开发过程中花费的时间是一项很好的投资。这项工作包括:撰写评论;定义作为常量的常量;编写不依赖模糊语言结构的显式代码。

此外,@worloss,我听到了相当多的不容忍。正如其他一些人所评论的那样,碳单元会犯错误,硅单元所能做的任何有助于避免错误的事情都值得赞赏。

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

两个原因:

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

以下是Ian Lance Taylor(曾研究gccgold链接器)的一个很好的解释:

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

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

下面是一个简单的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性。因此,在这种情况下,我可以合理地确定,在我进行运行时调试时,比较两个列表并不是破坏它们的函数,因为我保护自己免受数据意外修改的影响。)

您的编译器可以在知道变量不会更改的情况下进行大规模优化:它不是存储在内存中,而是直接写入可执行操作码。

例如:你有a和b,你想把它们加起来,你就生成了a+b。如果你声明a为const,值为3,程序就会生成3+b,这将节省内存和周期,因为它不需要检索a值。

问题是,你的编译器无法提前知道变量是否是常量,当然它可以分析整个代码并检查你是否修改了这些变量,但它不能100%确定,因为未来的代码也可以修改它。

关键字const对团队和长期项目非常有用。我给你们举几个例子,这些例子应该解释const关键字的价值。

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

谈到评论,当截止日期到来时,有很多东西仍然不起作用,对每个功能进行评论只是浪费时间。然而,在某些情况下,评论是必须的,因为第一个问题(截止日期),评论可能不会被阅读,因为大多数评论都是无用的,但重要的评论也会被跳过。因此,最好使用const关键字,它会给出编译错误并指出问题所在,然后再编写和阅读大量评论。

这是一个棘手的问题,因为IMO是基于信仰的。其中一个信念是,只要添加更多的代码,就可以保护您的代码不受某种更改的影响。当然,编译器会使用额外的代码来检查一切是否正常。

我认为这并不总是正确的,你不能保护你的代码不受你自己或你的开发团队仅仅添加关键字的影响,事实上,有很多语言没有任何const、public、private、protected、internal、int、float、double关键字,这并不意味着它们不是好的语言。

有些代码模式也是如此,为什么人们浪费这么多时间讨论Singleton!?如果你只想有一个实例,你唯一要做的就是只创建一个实例。同样的心态无处不在,看看10年前发表的防御性编程文章,再次提出用代码保护代码的想法。

在某些情况下,您必须决定在哪里设置职责,由开发人员负责还是由编译器负责。然而,无论是编译器还是任何其他工具都无法针对开发人员保存代码,因此许多关键字都毫无价值,或者只是与其他开发人员交流的一种方式。

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

const在几乎所有语言中最常用的是允许我们使用名称来指代常量值,因此您可以使用流利的语言告诉他人这个名称指的是什么,而无需在代码中散布注释,也可以节省读者了解参数类型和参数特殊性的时间和精力。当然,如果您的常量值在代码中得到重用,您也会从中受益。这样的代码可读性更强:

processPages(LETTER_PAPER_WIDTH, LETTER_PAPER_HEIGHT);

比这个:

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

在上面的例子中,你需要了解每个参数是什么,它的单位和解释值的类型,你还需要根据注释来验证它,因为像这样的冗余注释(那些回放编码内容的注释)不是一个可靠且非常有用的注释(这是Robert Martin在《清洁代码》中的一个坏评论:http://goo.gl/5EyY)。

考虑这样一种情况,即在整个项目中多次使用相同的常量,并且在所有地方都对其进行硬编码。现在突然间,你需要将常数的值更改为另一个值,所以在所有位置进行更改会很忙。

因此,我认为让代码更易于维护肯定是原因之一。

最新更新