151

那么为什么总是建议尽可能多地使用 const 呢?在我看来,使用 const 可能比 C++ 中的帮助更痛苦。但话又说回来,我是从 python 的角度来看的:如果你不想改变某些东西,就不要改变它。话虽如此,这里有几个问题:

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

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

我应该注意到,在这个时间点上,我最关注的是使用 const 来实现正确性和可维护性的好处,尽管了解性能影响也很好。

4

16 回答 16

170

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

简而言之,使用 const 是一种很好的做法,因为...

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

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

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

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

于 2008-09-25T23:40:30.943 回答
136

这是一段带有常见错误的代码, const 正确性可以保护您免受以下错误的影响:

void foo(const int DEFCON)
{
   if (DEFCON = 1)     //< FLAGGED AS COMPILER ERROR! WORLD SAVED!
   {
       fire_missiles();
   }
}
于 2008-09-25T23:37:05.100 回答
67

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

根据经验,这完全是一个神话。当非 const-correct 与 const-correct 代码一起出现时,肯定会发生这种情况。如果您从一开始就设计 const-correct,那么这永远不会成为问题。如果你做了一个 const 的东西,然后其他的东西没有编译,编译器告诉你一些非常重要的东西,你应该花时间正确地修复它。

于 2008-09-26T00:19:29.583 回答
33

如果您严格使用 const,您会惊讶于大多数函数中的实变量很少。通常不超过一个循环计数器。如果你的代码达到了那个点,你会感到内心温暖……编译验证……函数式编程领域就在附近……你现在几乎可以触摸它了……

于 2008-09-27T00:03:11.180 回答
32

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

于 2008-09-25T23:37:59.670 回答
23

const 是您作为开发人员所做的承诺,并在执行时寻求编译器的帮助。

我是 const 正确的原因:

  • 它与您的函数的客户沟通,您不会更改变量或对象
  • 通过 const 引用接受参数为您提供了按引用传递的效率和按值传递的安全性。
  • 将您的接口编写为 const 正确将使客户能够使用它们。如果您编写接口以接受非 const 引用,则使用 const 的客户将需要放弃 const 才能与您合作。如果您的界面接受非 const char*,并且您的客户正在使用 std::strings,这尤其令人讨厌,因为您只能从它们那里获得 const char*。
  • 使用 const 将使编译器保持诚实,这样您就不会错误地更改不应更改的内容。
于 2008-09-26T02:24:55.710 回答
22

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

每次上车都系上安全带很痛苦,365 天中有 364 天你会安全到达。

唯一的区别是,当您的汽车遇到问题时,您会立即感觉到,而在没有 const 的情况下进行编程,您可能不得不搜索两周导致崩溃的原因,结果却发现您无意中弄乱了一个函数参数您通过非常量引用以提高效率。

于 2008-09-26T08:00:52.330 回答
21

我的理念是,如果您要使用带有编译时检查的挑剔语言,而不是充分利用它。 const是一种编译器强制执行的方式来传达您的意思……它比注释或 doxygen 更好。你付出了代价,为什么不得出价值呢?

于 2008-09-26T00:54:20.703 回答
20

对于嵌入式编程,const在声明全局数据结构时明智地使用可以通过使常量数据位于 ROM 或闪存中而无需在引导时复制到 RAM 来节省大量 RAM。

在日常编程中,const谨慎使用可以帮助您避免编写崩溃或行为不可预测的程序,因为它们试图修改字符串文字和其他常量全局数据。

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

于 2008-09-26T03:30:20.890 回答
15

const 正确性是从一开始就真正需要到位的事情之一。正如您所发现的,以后添加它会很痛苦,尤其是当您添加的新函数和已经存在的旧的非常量正确函数之间存在很多依赖关系时。

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

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

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

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

于 2009-12-09T02:40:20.023 回答
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;
}
于 2008-09-25T23:45:13.660 回答
7

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

C++ 为您提供了一种方法来保护自己免于意外地做一些原本不应该做的事情。从技术上讲,无论如何您都可以绕过它,但是您必须付出额外的努力才能射击自己。

于 2008-09-26T03:46:24.283 回答
4

这里有一篇关于 C++ 中 const的好文章。这是一个非常直截了当的意见,但希望它对一些人有所帮助。

于 2008-09-25T23:37:30.607 回答
3

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

是的,随着时间的推移,它确实变得更容易了。

于 2008-09-26T00:07:01.597 回答
2

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

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

例如,想象一个简单的数据库引擎……它有模式对象、表、字段等。用户可能有一个“const Table”指针,这意味着他们不允许修改表模式本身……但是如何操作与表关联的数据?如果 Insert() 方法被标记为 const,那么在内部它必须放弃 const 才能实际操作数据库。如果它没有被标记为 const 那么它就不能防止调用 AddField 方法。

也许答案是根据 const-ness 要求将类拆分,但这往往会使设计变得比我想要的更复杂,因为它带来的好处。

于 2008-09-26T00:01:19.083 回答
1

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

#include <string>

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

}
void x( std::string& x)
{
}
void main()
{
    f("blah");
    x("blah");   // won't compile...
}
于 2008-09-26T03:59:30.647 回答