卖我的正确性

时间:2008-09-25 23:34:21

标签: c++ const-correctness

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

  1. 似乎每当我将某些内容标记为const时,我都会收到错误,并且必须将其他某些函数更改为const。然后这导致我必须在其他地方更改另一个功能。这是通过经验变得更容易的事情吗?

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

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

16 个答案:

答案 0 :(得分:148)

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

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

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

    if( x = y ) // whoops, meant if( x == y )
    
  4. 同时,编译器可以生成更高效的代码,因为它确切地知道变量/函数的状态。如果你正在编写严格的C ++代码,这很好。

    你是正确的,因为一致地使用const-correctness可能很困难,但结束代码更简洁,更安全。当你进行大量的C ++开发时,这种好处很快就会显现出来。

答案 1 :(得分:119)

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

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

答案 2 :(得分:60)

  

似乎每次我都会标记   作为const的东西,我得到一个错误   必须改变一些其他功能   某处也是const。然后这个   导致我不得不改变另一个   在其他地方运作。这是   让事情变得更轻松的事情   经验?

从经验来看,这是一个完整的神话。当非const-correct与const-correct代码坐在一起时,肯定会发生这种情况。如果你从一开始就设计const-correct,那绝不应该是一个问题。如果你制作一些const,然后其他东西不能编译,编译器会告诉你一些非常重要的东西,你应该花点时间来解决它正确

答案 3 :(得分:29)

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

答案 4 :(得分:24)

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

答案 5 :(得分:21)

const是您作为开发人员所做的一项承诺,并且在执行过程中征求了编译器的帮助。

我的正确理由:

  • 它向您的函数的客户传达您不会更改变量或对象
  • 通过const引用接受参数可以提高通过引用传递的效率和传递值的安全性。
  • 将接口编写为const正确将使客户端能够使用它们。如果您编写接口以接受非const引用,那么使用const的客户端将需要抛弃constness以便与您合作。如果您的接口接受非const char *,并且您的客户端使用std :: strings,那么这尤其令人讨厌,因为您只能从它们获取const char *。
  • 使用const将使编译器保持诚实,这样你就不会错误地改变那些不应该改变的东西。

答案 6 :(得分:18)

我的理念是,如果您打算使用一种挑剔的语言进行编译时检查,那么就可以充分利用它。 const是一种编译器强制执行的方式来传达你 的意思 ...它比评论或doxygen更好。你付出了代价,为什么不拿出价值?

答案 7 :(得分:18)

没有const的C ++编程就像没有安全带的驾驶一样。

每次踏入汽车时都要放上安全带是很痛苦的,365天中的安全带会安全到达。

唯一的区别是当你遇到汽车问题时你会立即感受到它,而对于没有常规编程的编程你可能需要搜索导致该崩溃的两周时间才发现你无意中搞砸了您通过非const引用传递的函数参数以提高效率。

答案 8 :(得分:15)

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

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

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

答案 9 :(得分:11)

const正确性是从一开始就真正需要的东西之一。正如您所发现的那样,稍后添加它会非常困难,尤其是当您添加的新函数与已存在的旧非常量函数之间存在很多依赖关系时。

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

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

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

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

答案 10 :(得分:10)

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;
}

答案 11 :(得分:5)

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

C ++为您提供了一种方法来保护自己免于意外地做一些您本来不应该做的事情。从技术上讲,无论如何你都可以绕过它,但你必须投入额外的工作来拍摄自己。

答案 12 :(得分:4)

关于c ++中的const,有一篇很好的文章here。这是一个非常直接的意见,但希望它有所帮助。

答案 13 :(得分:3)

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

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

答案 14 :(得分:1)

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

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

例如,想象一个简单的数据库引擎......它有模式对象,表,字段等。用户可能有一个'const Table'指针意味着它们不允许修改表模式本身......但是那么操纵与表相关的数据呢?如果Insert()方法被标记为const,那么在内部它必须将const-ness转移到实际操作数据库。如果它没有标记为const,那么它不会防止调用AddField方法。

也许答案是根据常规要求将课程分开,但这往往会使设计更加复杂,因为它带来了好处。

答案 15 :(得分:1)

您可以根据以下代码

为编译器提供带有const的提示....
#include <string>

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

}
void x( std::string& x)
{
}
void main()
{
    f("blah");
    x("blah");   // won't compile...
}