以const正确性卖给我


133

那么,为什么总是建议尽可能多地使用const呢?在我看来,在C ++中使用const可能比提供帮助更痛苦。但是再说一次,我是从python角度出发的:如果您不希望更改某些内容,请不要更改它。如此说来,这里有几个问题:

  1. 好像每次将某物标记为const时,我都会遇到错误,并且还必须在某个地方更改其他一些函数才能成为const。然后这导致我不得不在其他地方更改另一个功能。有了经验,这会变得更容易吗?

  2. 使用const的好处真的足以弥补麻烦吗?如果您不打算更改对象,为什么不不编写不更改对象的代码呢?

我应该注意,在这个时候,我最专注于将const用于正确性和可维护性的好处,尽管对性能的影响也很了解。


1
回顾了8年,但是..如何将代码加速100倍(现场直播)?
lorro

1
在这里检查我的答案(相关问题,我会说相同的答案):stackoverflow.com/questions/42310477/…顺便说一句:我确实喜欢const
Gines Hidalgo,

Answers:


158

这是有关“常量正确性”的权威文章:https : //isocpp.org/wiki/faq/const-correctness

简而言之,使用const是一个好习惯,因为...

  1. 它可以保护您避免意外更改不希望更改的变量,
  2. 它可以防止您意外分配变量,并且
  3. 编译器可以对其进行优化。例如,您受到保护

    if( x = y ) // whoops, meant if( x == y )

同时,编译器可以生成更有效的代码,因为它确切地知道变量/函数始终处于什么状态。如果您正在编写紧凑的C ++代码,那很好。

您是正确的,因为很难始终如一地使用const-correctness,但是最终代码更简洁,更安全。当您进行大量C ++开发时,这种好处很快就会显现出来。


我一直认为const值可以自由缓存,从而避免了许多并发问题或提高速度。真的吗?
菲尔H

3
当然。 从某种意义上说,const变量可以提高速度,因为编译器可以知道变量的全部意图和用法,因此可以生成更优的代码。您会注意到差异吗?好吧,这在您的应用程序上是有争议的。就并发而言,const变量是只读的,这意味着不需要对这些变量进行排他锁,因为值将始终相同。
Jordan Parmer 2013年

4
从C ++ 11开始,标准库还假定const是线程安全的,并且以这种方式处理您自己的代码可以更轻松地检查可能的多线程问题,并且是为API用户提供API保证的简便方法。
2014年

3
对我来说,const正确性的最大附加价值之一就是,当指针引用的数据永远不会被函数改变(即仅输入)时,您只需查看函数原型即可知道。
cp.engr

1
不要忘记存在逻辑物理上的一致性。包含的任何标记为可变的聚合对象都可以更改,尽管将其标记为隐含意味着它与包含的对象的逻辑常数无关。
阿德里安

128

这是一段带有常见错误的代码,它们的const正确性可以防止您受到以下攻击:

void foo(const int DEFCON)
{
   if (DEFCON = 1)     //< FLAGGED AS COMPILER ERROR! WORLD SAVED!
   {
       fire_missiles();
   }
}

19
因此,我们说const是必需的,因为某些血腥的白痴在C中选择了“ =”作为赋值运算符?;-)
史蒂夫·杰索普

37
当然,大多数现代编译器都会警告有条件的赋值,我们都打开将警告视为错误标志,对:->
Jack Bolding

7
在这个示例上不要太用力:这是一些开发人员宣传的相同示例,以说服我们使用if(0 == i)代替if(i == 0)。最后,Doug的代码模式可以在“ if”语句之外使用。重要的是它以幽默的方式显示const的好处之一。1 +
paercebal

2
一些编译器(和棉绒)已经对此发出警告很长时间了,与const:codepad.org/Nnf5JUXV相比,捕获这种错字更为有用。

7
@Roger,您假设我们生活在一个无污染且无警告的世界中。我的经验是,在很多代码中,警告在噪声中迷失了方向。另外,有很多代码在if表达式中进行赋值,许多人会认为这是一种有效的“好”风格。
Doug T.

62

似乎每次将某物标记为const时,我都会遇到错误,并且还必须在某处更改其他一些函数以使其成为const。然后这导致我不得不在其他地方更改另一个功能。有了经验,这会变得更容易吗?

从经验来看,这完全是神话。当然,当非const正确的代码与const正确的代码一起使用时,会发生这种情况。如果从一开始就设计const-correct,那么这绝对不是问题。如果您使const变为常量,然后又不符合要求,则编译器会告诉您一些非常重要的信息,您应该花一些时间正确地对其进行修复。


7
这帮助我避免了很多错误,因为我忘记了它会修改下面的数据,而我有一个const函数即将调用非const函数。
Mooing Duck 2013年

9
很少有人总是从干净的代码库入手。大多数编码是对遗留代码的维护,其中引用的注释非常准确。在编写新的叶子函数时,我使用了const,但是我质疑是否值得通过不熟悉的代码的六个或十二个调用级别来进行研究。
沃伦·露

3
根据我的经验,这是完全错误的。嵌套点类型为此类问题带来了最大的麻烦,您可以在一个类型中使用多个常量。
Noldorin

4
“如果从一开始就设计const-correct,”实际上您有多少次从一开始就强制执行const-correctness?我们大多数人每天都必须处理大量的API和库,但事实并非如此,您几乎无能为力。因此,我同意OP的观点“似乎每次将某事标记为const时我都会出错” ...
nyholku

@WarrenDew这是一个传递性的论点;如果原始作者使用const得当(即“从头开始”),那么确实不会有问题,这就是重点!
Lightness Races in Orbit

31

最初编写代码时不适合您。它是供其他人(或几个月后您)在类或接口中查看方法声明以查看其作用的。不修改对象是从中收集的重要信息。


1
这只是一种事实。常量也被用作保护,以通过接口等强制执行变量。
Jordan Parmer's

是的,但是您可以通过规范的编码实践和发布者声明来强制实施。真正的好处来自于API中反映的这一点。
Antonio Haley

2
究竟。最好有“ const int Value = 5;” 比具有“ int ConstValue = 5;”。+1。
paercebal

6
正确地输入const-correctness是在最初编写API时。否则,当您进行改型时,您会遇到const中毒的麻烦(这就是为什么将其添加到旧代码中与在新代码中进行完全不同的原因)。
多纳研究员,2010年

@DonalFellows这不是说稍后放入const。就是说,当您在const已经存在的情况下阅读代码时,便会得到好处
Caleth '19

27

如果严格使用const,您会惊讶的是,大多数函数中真正的变量很少。通常只不过是一个循环计数器。如果您的代码达到了这一点,您会感到内在...通过编译进行验证...功能编程领域就在附近...您现在几乎可以触摸它了...


1
由于C ++ 17和constexpr良好,我正在编写编译时间单元测试...越来越近了……
QBziZ

22

const是您作为开发人员所做的承诺,并在执行中争取了编译器的帮助。

我的const正确原因是:

  • 它与您的函数的客户端通信,您将不会更改变量或对象
  • 通过const reference接受参数使您可以高效地进行引用传递,并可以安全地传递值。
  • 以正确的const格式编写接口将使客户端可以使用它们。如果编写接口以接受非const引用,则使用const的客户端将需要放弃constness以便与您一起工作。如果您的接口接受非const char *,并且您的客户端使用的是std :: strings,这尤其令人讨厌,因为您只能从它们中获取const char *。
  • 使用const将使编译器保持诚实,这样您就不会错误地更改了不应更改的内容。

20

我的理念是,如果要在编译时检查中使用精挑细选的语言,而不是对其进行最佳利用。 const是一种编译器强制的方式来传达您的意思 ……比注释或doxygen会更好。您要付出代价,为什么不获取价值呢?


20

没有const的C ++编程就像没有系安全带的情况下开车一样。

每次上车时都要系上安全带,这很痛苦,在365天中有364天可以安全到达。

唯一的区别是,当您遇到汽车故障时,您会立即感觉到它,而在没有const的情况下进行编程,您可能不得不搜索导致崩溃的两周时间,只是发现您无意中弄乱了一个函数参数,您通过非常数参考传递效率。


18

对于嵌入式编程,const在声明全局数据结构时谨慎使用可以使常量数据位于ROM或闪存中而无需在启动时复制到RAM,从而节省了大量RAM。

在日常编程中,const谨慎使用有助于避免编写由于试图修改字符串文字和其他常量全局数据而导致崩溃或行为异常的程序。

在大型项目上与其他程序员合作时,const正确使用有助于防止其他程序员对您造成限制。


13

const帮助您隔离背后“改变事物”的代码。因此,在一个类中,您会将所有不会更改对象状态的方法标记为const。这意味着const该类的实例将不再能够调用任何非const方法。这样,可以避免意外调用可以更改对象的功能。

同样,const它也是重载机制的一部分,因此您可以使用两种具有相同签名的方法,但一种使用带签名的方法const,一种没有签名。一个带有const的称为const引用,另一个称为非const引用。

例:

#include <iostream>

class HelloWorld {
    bool hw_called;

public:
    HelloWorld() : hw_called(false) {}

    void hw() const {
        std::cout << "Hello, world! (const)\n";
        // hw_called = true;  <-- not allowed
    }

    void hw() {
        std::cout << "Hello, world! (non-const)\n";
        hw_called = true;
    }
};

int
main()
{
    HelloWorld hw;
    HelloWorld* phw1(&hw);
    HelloWorld const* phw2(&hw);

    hw.hw();    // calls non-const version
    phw1->hw(); // calls non-const version
    phw2->hw(); // calls const version
    return 0;
}

13

const正确性是一开始就需要真正采取的措施之一。正如您所发现的,以后添加它是一个很大的麻烦,尤其是当您要添加的新函数与已经存在的旧的非const正确函数之间存在很大的依赖性时。

在我编写的许多代码中,确实值得付出努力,因为我们倾向于大量使用组合:

class A { ... }
class B { A m_a; const A& getA() const { return m_a; } };

如果我们没有const正确性,那么您将不得不求助于按值返回复杂对象,以确保自己没有人在背后操纵B类的内部状态。

简而言之,const-correctness是一种防御性编程机制,可以使您免于日后的痛苦。


7

假设您在Python中有一个变量。您知道不应该修改它。如果您不小心怎么办?

C ++为您提供了一种保护自己免遭意外伤害的方法,以防止自己一开始就无法做到。从技术上讲,您还是可以解决它,但是您必须付出额外的努力才能拍摄自己。


4

有一个很好的文章在这里关于常量在C ++。这是一个非常直接的意见,但希望对您有所帮助。


3

当使用“ const”关键字时,您将为类指定另一个接口。有一个包含所有方法的接口,以及一个仅包含const方法的接口。显然,这使您可以限制对某些您不想更改的内容的访问。

是的,随着时间的推移,它变得越来越容易。


2

从理论上讲,我喜欢const正确性。每次我在实践中尝试严格地应用它时,它最终都会崩溃,并且const_cast开始使代码变得丑陋。

也许这只是我使用的设计模式,但是const总是会变得过于宽泛。

例如,假设有一个简单的数据库引擎...它具有架构对象,表,字段等。用户可能具有'const Table'指针,这意味着不允许他们修改表架构本身...但是如何操作与表关联的数据?如果Insert()方法标记为const,则必须在内部将const-ness抛弃以实际操作数据库。如果未标记为const,则它无法防止调用AddField方法。

也许答案是根据稳定性要求拆分类,但这会使设计变得更加复杂,其带来的好处超出了我的期望。


我认为您的示例是过度使用const的情况,但请不要忘记mutable修饰符,该修饰符可以应用于实例变量以删除const-ness。
Zooba

2
除非错误地将Insert()函数何时标记为const?添加事物通常会修改您添加到其中的事物,这意味着它不是const。您真正想要的是一个TableWithConstSchema。
格雷格·罗杰斯

我可以添加另一个类,但我不想纯粹为了const-correctness来使API不必要地复杂。
罗伯·沃克

1

您也可以使用const给编译器提示....按照以下代码

#include <string>

void f(const std::string& s)
{

}
void x( std::string& x)
{
}
void main()
{
    f("blah");
    x("blah");   // won't compile...
}
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.