const 正确性如何帮助编写更好的程序?

Posted

技术标签:

【中文标题】const 正确性如何帮助编写更好的程序?【英文标题】:How does const correctness help write better programs? 【发布时间】:2009-04-13 09:38:37 【问题描述】:

这个问题来自一个 C# 人问 C++ 人。 (我对 C 有一定了解,但对 C++ 有一些基本了解)。

所有使用 C# 的 C++ 开发人员都说他们错过了 const 的正确性,这对我来说似乎很奇怪。在 .Net 中,为了禁止更改内容,您需要创建不可变对象或具有只读属性/字段的对象,或传递对象的副本(默认情况下复制结构)。

C++ 的 const 正确性是一种创建不可变/只读对象的机制,还是还有更多?如果目的是创建不可变/只读对象,则可以在 .Net 等环境中完成相同的操作。

【问题讨论】:

【参考方案1】:

FAQ 中有一个专门讨论Const Correctness 的部分。尽情享受吧!

【讨论】:

我已经阅读了您链接的常见问题解答,阅读后我意识到,基于指针的语言需要 const 正确性,该语言没有自动内存管理并且不需要(或根本不需要) ) 在 C# 等基于引用的语言中(采用其他方法来强制执行只读行为) 正如我在分析器中指出的那样 - 问题不是强制执行,您的代码含义很清楚 @Pop Catalin:再看一遍。 const 正确性与指针/引用方法无关,而是为函数提供额外的不变量。 @Pop Catalin:除了人们指出的内容之外,您可能还对我针对 SO ***.com/questions/655027/…> 的另一个问题发表的帖子感兴趣。希望它能帮助您理解。【参考方案2】:

C++ 中的 const 正确性本质上只是一种准确表达您的意思的方式:

void foo( const sometype & t ) 
   ...

说“我不会更改 t 引用的对象,如果我尝试更改,请编译器先生警告我”。

它不是一种安全特性,也不是一种(可靠地)创建只读对象的方法,因为使用 C 风格的转换很容易颠覆。

【讨论】:

您也可以使用更安全的 const_cast 绕过 const。就个人而言,我会在我的代码中彻底禁止 C 风格的强制转换。 const_cast 是不是不能使用。仅作为写得不好的旧图书馆的安全网。如果您需要它,这是一个糟糕设计的明显症状,并且在某些情况下使用它是不安全的。【参考方案3】:

我敢打赌,这是一个心态问题。我正在与长期使用 C++ 的人一起工作,并注意到他们似乎在用 C++ 思考(当然他们会这样做!)。他们需要时间才能开始看到他们已经熟悉的答案的多个答案,以获得像 const 正确性这样的“标准”答案。给他们时间,并尝试温和地教育他们。

话虽如此,我确实喜欢 C++ 'const' 的保证方式。它主要是向接口的用户保证对象、指针后面的数据或其他任何东西都不会被弄乱。我会将此视为 const 正确性的 #1 值。 #2 值是它可以允许编译器优化的值。

当然,问题是,如果整个 sw 堆栈没有从头开始构建时考虑到 const。

我确信 C# 有自己的一套范式来做同样的事情。这说明了为什么“每年学习一种新的(计算或自然)语言”如此重要。只是想通过其他方式来看待世界并解决问题。

【讨论】:

【参考方案4】:

通过 const-correctness,您可以将可变对象公开为只读而无需对其进行复制并浪费宝贵的内存(尤其是与集合相关)。我知道,桌面开发人员有这种CPU 和内存便宜的理念,但它在嵌入式世界中仍然很重要。

最重要的是,如果您在 C++ 中添加内存所有权的复杂性,最好不要复制包含或引用其他对象的非平凡对象,因此有一种方法可以将现有对象公开为只读.

【讨论】:

【参考方案5】:

优秀的 Scott Meyer 的书 Effective C++ 专门针对这个问题发表了一篇文章:

第 3 项:尽可能使用 const。

它真的启发了你:-)

【讨论】:

【参考方案6】:

真正的答案,即使如果你没有使用它真的很难掌握,是你失去了表现力。该语言具有您无法在 C# 中表达的概念,它们具有意义,它们是设计的一部分,但在转换为代码时丢失了。

这不是一个答案,而是一个例子:

假设您有一个容器,该容器按所存储元素的字段排序。那些物体真的很大。您需要为读者提供对数据的访问权限(考虑在 UI 中显示信息)。

现在,在 C#/Java 中,您可以采用以下两种方式之一:要么制作副本供调用者使用(保证数据不会更改,但效率低下),要么返回对内部持有的引用对象(只是希望调用者不会通过设置器更改您的数据)。

如果用户获取引用并通过它更改用作索引的字段,那么您的容器不变量将被破坏。

在 C++ 中,您可以返回一个常量引用/指针,编译器将禁止在您返回的实例上调用 setter 方法(变异方法)。您将获得用户不会更改的安全性 (*) 和调用的效率。

前面没有提到的第三个选项是使对象不可变,但这会禁用任何地方的更改。将不允许对对象进行完全受控的更改,唯一可能的更改是创建一个执行更改的新元素。这相当于时间和空间。

【讨论】:

【参考方案7】:

C++ 提供了许多可以让你的代码混乱的方法。我认为 const 正确性是对代码施加约束的第一种有效方法,以控制“副作用”(不需要的更改)。

将参数标记为 const(通常是对 const 对象的引用或指向 const 对象的指针),将确保传递的对象无法更改,因此您没有该函数/方法的“副作用”。

将方法标记为 const 将保证该方法不会改变它所使用的对象的状态。如果是这样,编译器会产生错误。

标记一个 const 数据成员将保证该数据成员只能在构造函数的初始化列表中被初始化,不能被改变。

此外,智能编译器可以使用常量作为各种性能优化的提示。

当然,您可以覆盖常量。常量是编译时检查。默认情况下你不能打破规则,但你可以使用强制转换(例如 const_cast)使 const 对象成为非 const 对象,以便可以更改它。

【讨论】:

【参考方案8】:

仅在编写代码时寻求编译器的帮助就足以让我提倡 const 正确性。但是今天还有一个额外的优势:当您知道我们的对象可以在哪里更改以及在哪里不能更改时,多线程代码通常更容易编写。

【讨论】:

【参考方案9】:

我认为遗憾的是 C# 不像 C++ 那样支持 const 正确性。我传递给函数的所有参数中有 95% 是常量值,const 特性保证您通过引用传递的数据在调用后不会被修改。 “const”关键字提供了经过编译器检查的文档,这对大型程序有很大帮助。

【讨论】:

以上是关于const 正确性如何帮助编写更好的程序?的主要内容,如果未能解决你的问题,请参考以下文章

如何编写更好,更清晰的银行帐户代码版本? [关闭]

Swift 3 如何正确编写完成处理程序块

如何在 C# 中实现 const 正确性? [复制]

编写减速器的更好方法?

是否值得插入`const`-正确性

*日历程序*无法为此日历程序编写循环以正确显示