【问题标题】:Use const wherever possible in C++?在 C++ 中尽可能使用 const 吗?
【发布时间】:2014-04-12 16:57:30
【问题描述】:

正如Effective C++一书中所述:“尽可能使用 const。”,人们会假设这个定义:Vec3f operator+(Vec3f &other); 最好定义为 Vec3f operator+(const Vec3f &other) const;,甚至更好地定义为 const Vec3f operator+(const Vec3f &other) const; .

或带有 5 个 const 关键字的示例:const int*const Foo(const int*const&)const;

当然,你应该只在可以有一个的地方包含 const。我问的是尽可能使用它们是一种好习惯吗?虽然它确实为您提供了更多错误安全代码,但它可能会变得非常混乱。或者,例如,您是否应该忽略指针和引用 const(除非您真的需要它),而只在类型本身上使用它,如 const int* Foo(const int* parameter)const;,这样它最终不会太混乱?

附加信息: http://duramecho.com/ComputerInformation/WhyHowCppConst.html

提前致谢!

【问题讨论】:

  • 在返回类型上放置一个顶级 const 在 C++11 中失去了它的价值,但除此之外,是的,const-correctness 有帮助。
  • 指针和引用 const 在作为函数参数传递时比值 const 更重要。通过值传递时,您传递的是副本,与通过指针或引用传递时不同。
  • 如果函数没有修改它们,你应该通过 const 引用传递函数参数,看起来凌乱是无关紧要的。成员函数如果不修改对象就应该是 const,看起来乱七八糟是无关紧要的。
  • @PetrBudnik, const 关于按值传递的参数被完全忽略,所以它不仅不重要重要,而且不重要
  • @JonathanWakely “忽略”是什么意思?尝试编译 void foo(int const a) { a += 1; }...与引用或指针不同。因此,不在函数声明中使用它并不是什么大不了的事。但是如果你使用它,它不会被忽略,即使是复制也会被编译器强制执行。

标签: c++ pointers reference constants keyword


【解决方案1】:

尽可能使用const 通常是一件好事,但这是一个糟糕的措辞。您应该使用const在任何可能且有意义的地方

最重要的是(它可能很少会打开额外的优化机会)它是一种记录您不修改某些内容的意图的方法。

在您给出的成员operator+ 的具体示例中,最好的解决方案不是制作所有内容const,而是一个独立的operator+,它基于成员operator+= 并按值接受一个参数像这样:

T operator+(T one, T const& two) const { return one += two; }

此解决方案适用于出现在加号两侧的T,它允许链接,它不复制代码,并且它允许编译器执行最大优化。
operator+ 的语义要求制作一个副本,因此您也可以让编译器制作它(另一个将被优化)。

可以将one 制作成const&,但是您必须手动制作副本,这可能不是最佳的(而且难以理解)。

【讨论】:

    【解决方案2】:

    C++ FAQ:

    如果您发现普通类型安全可以帮助您使系统正确(它 做;特别是在大型系统中),您会发现 const 正确性 也有帮助。

    当您想确保不会意外或有意更改变量时,您应该使用const。一些常量(全局和静态类、字符串和整数,但不是具有重要构造函数的变量)可以放置在可执行文件的只读部分中,因此如果您尝试写入它会导致分段错误。

    您应该明确地使用const 作为遵循此原则的函数以及函数参数的说明符。如果您不必更改实际参数,请将其设为const。这不会限制此类函数的可能用法,而是扩展它们,因为现在它们可能用于const 参数和const 对象。

    在声明中

    const int* foo(const int* const&) const;
    

    每个const 都有不同的含义,是的,显然应该使用它如果需要

    总结

    使用const 可以提高程序的类型安全性。

    C++ FAQ:

    [18.3] 我应该尝试“早点”还是“晚点”让事情 const 正确?

    在非常、非常、非常开始。

    【讨论】:

      【解决方案3】:

      当使用const 时,你是在告诉编译器一些事情。

      然后它可以使用该信息来检查您是否在做您不应该做的事情,并使其能够优化代码。

      所以当使用const 时,它会变得更聪明。

      【讨论】:

      • 使用const 几乎不会帮助优化,如果编译器可以判断您是否正在修改对象,那么它可以执行相同的优化而不需要const。见gotw.ca/gotw/081.htm
      • 一旦你开始使用 const 它也可能导致不必要的临时创建和销毁。例如,当函数不修改对象时,将 const 对象传递给非成本参数。您需要始终如一地使用它才能获得好处。
      • @QuentinUK 我不这么认为。让我们考虑一下这种情况:如果你按值传递,如果它是一个左值,无论它是否是 const ,都会生成一个副本。但是,如果您通过引用传递,则根本无法传递您的对象,因为编译器假定可以修改该对象。所以是的,如果你仍然想传递你的对象,你将不得不创建一个副本(明确地)。但这不是错误,而是它的设计。
      • 我想我已经看到了这一点,因为 VS2010 的“功能”,stackoverflow.com/questions/7189420/…
      【解决方案4】:

      我认为尽可能使用const 是一个好方法。正如其他答案所指出的,它为您的代码添加了隐式文档并提高了类型安全性。

      很高兴知道您可以调用const 函数而不必担心它们是否会更改对象的内部状态,并且传递const 指针或对其他对象的引用将使其免受封装破坏。它还允许拥有const 引用的对象从该引用指向的任何内容中获得最大价值。

      此外,从一开始就应用const 是一个好主意,因为以后添加可能会很痛苦。例如,如果你有一个类:

      class A
      {
      public:
      
        //...
      
        int getCount()
        {
          return m_count;
        }
      
      private:
        int m_count;
      }
      

      还有一个:

      class B
      {
      public:
      
        //...
      
        int getInternalCount()
        {
          return m_a->getCount();
        }
      
      private:
        A* m_a;
      }
      

      而你想做的事:

      void foo( const B* b )
      {
        int count = b->getInternalCount();
      
        //-- Do something
      }
      

      这是一个完全有效的用例,但您必须将const 添加到多个位置以支持它(B::getInternalCount()A::getCount())。如果您从一开始就这样做,实际上使用const 非常容易,并且它使设计对其他开发人员更加明显。如果您自己工作,它仍然有很大帮助,但并不那么令人担忧。不过,在团队中工作时,我发现当我们开始执行 const 并改进我们的一些遗留代码以更加遵守它时,它真的很有帮助。

      长话短说:我建议始终使用它。

      【讨论】:

        【解决方案5】:

        const 的最初目的是处理当您使用magic numbers 和一般硬编码值时出现的问题。

        有点愚蠢,但风景如画的例子说明了这一点。这样的代码容易出错且不方便。

        c1 = 3.14159 * diameter1;
        c2 = 3.14159 * diameter2;
        

        您最好将 Pi 定义为常数。

        const double PI = 3.14159;
        c1 = PI * diameter1;
        c2 = PI * diameter2;
        

        与变量相比,常量具有“特权”。在您的示例中,通过 const 引用传递其他向量是有意义的,因为由于此函数将接受临时(右值)作为其参数。

        Vec3f operator+(const Vec3f &other) const;
        
        // Would not be possible with Vec3f operator+(Vec3f &other) const;
        Vec3f v = u + Vec3f(1, 0, 0);
        

        使用constexpr,您可以在上下文中使用常量,例如静态数组分配

        constexpr std::size_t LENGTH = 10;
        int arr[LENGTH];
        

        所以这并不是为了防止你不小心设置了一些随机值(你必须是非常愚蠢的程序员才会犯这样的错误)。尽可能使用它是没有意义的,但只有在你想指定一个常量时才有意义。

        这不是我第一次看到“Effective C++”中的奇怪陈述。

        【讨论】:

          猜你喜欢
          • 2014-11-18
          • 1970-01-01
          • 2013-01-10
          • 1970-01-01
          • 1970-01-01
          • 2013-02-11
          • 2013-12-20
          相关资源
          最近更新 更多