【问题标题】:Meaning of 'const' last in a function declaration of a class?类的函数声明中最后一个“const”的含义?
【发布时间】:2019-08-03 09:45:00
【问题描述】:

const 在这样的声明中是什么意思? const 让我很困惑。

class foobar
{
  public:
     operator int () const;
     const char* foo() const;
};

【问题讨论】:

    标签: c++ constants declaration c++-faq


    【解决方案1】:

    const 对象中只能调用const 方法。这种方法中的所有字段都被视为const 字段。 最后一期的效果很奇怪:

    • 指针变成了一个常量指针int* const,这与一个指向常量const int*的指针不同。因此,您可以更改指针指向的对象,但不能使指针指向另一个对象。
    • reference 应该成为一个 const 引用,但它始终是一个 const 引用:您不能将它重新初始化到另一个对象。但同样,您可以更改引用所指的对象。

    【讨论】:

      【解决方案2】:

      这里的 const 意味着在那个函数中任何变量的值都不能改变

      class Test{
      private:
          int a;
      public:
          void test()const{
              a = 10;
          }
      };
      

      像这个例子一样,如果你试图在测试函数中改变一个变量的值,你会得到一个错误。

      【讨论】:

      • 这个答案没有增加评分最高的答案。
      【解决方案3】:

      当您将const 关键字添加到方法时,this 指针本质上将成为指向const 对象的指针,因此您无法更改任何成员数据。 (除非你使用mutable,稍后会详细介绍)。

      const 关键字是函数签名的一部分,这意味着您可以实现两种类似的方法,一种在对象为const 时调用,另一种则不是。

      #include <iostream>
      
      class MyClass
      {
      private:
          int counter;
      public:
          void Foo()
          { 
              std::cout << "Foo" << std::endl;    
          }
      
          void Foo() const
          {
              std::cout << "Foo const" << std::endl;
          }
      
      };
      
      int main()
      {
          MyClass cc;
          const MyClass& ccc = cc;
          cc.Foo();
          ccc.Foo();
      }
      

      这将输出

      Foo
      Foo const
      

      在非常量方法中,您可以更改实例成员,这在const 版本中是无法做到的。如果你把上面例子中的方法声明改成下面的代码,你会得到一些错误。

          void Foo()
          {
              counter++; //this works
              std::cout << "Foo" << std::endl;    
          }
      
          void Foo() const
          {
              counter++; //this will not compile
              std::cout << "Foo const" << std::endl;
          }
      

      这并不完全正确,因为您可以将成员标记为mutable,然后const 方法可以更改它。它主要用于内部计数器和东西。解决方案是下面的代码。

      #include <iostream>
      
      class MyClass
      {
      private:
          mutable int counter;
      public:
      
          MyClass() : counter(0) {}
      
          void Foo()
          {
              counter++;
              std::cout << "Foo" << std::endl;    
          }
      
          void Foo() const
          {
              counter++;    // This works because counter is `mutable`
              std::cout << "Foo const" << std::endl;
          }
      
          int GetInvocations() const
          {
              return counter;
          }
      };
      
      int main(void)
      {
          MyClass cc;
          const MyClass& ccc = cc;
          cc.Foo();
          ccc.Foo();
          std::cout << "Foo has been invoked " << ccc.GetInvocations() << " times" << std::endl;
      }
      

      哪个会输出

      Foo
      Foo const
      Foo has been invoked 2 times
      

      【讨论】:

      • 如果我只创建一个 const 方法但没有正常方法,然后使用非常量对象调用该方法,我的代码通常运行良好。它是错误的、有害的还是什么?
      • @KhiemGOM 这完全没问题,对于只读成员来说是很正常的模式。
      【解决方案4】:

      https://isocpp.org/wiki/faq/const-correctness#const-member-fns

      什么是“const成员函数”?

      检查(而不是改变)其对象的成员函数。

      const 成员函数由成员函数的参数列表后面的 const 后缀表示。带有const 后缀的成员函数称为“const 成员函数”或“检查器”。没有const 后缀的成员函数称为“非常量成员函数”或“mutators”。

      class Fred {
      public:
        void inspect() const;   // This member promises NOT to change *this
        void mutate();          // This member function might change *this
      };
      void userCode(Fred& changeable, const Fred& unchangeable)
      {
        changeable.inspect();   // Okay: doesn't change a changeable object
        changeable.mutate();    // Okay: changes a changeable object
        unchangeable.inspect(); // Okay: doesn't change an unchangeable object
        unchangeable.mutate();  // ERROR: attempt to change unchangeable object
      }
      

      尝试调用unchangeable.mutate() 是在编译时捕获的错误。 const 没有运行时空间或速度损失,您无需编写测试用例在运行时对其进行检查。

      inspect() 成员函数后面的 const 应该用来表示该方法不会改变对象的 abstract(客户端可见)状态。这与说该方法不会更改对象结构的“原始位”略有不同。 C++ 编译器不允许采用“按位”解释,除非它们能够解决通常无法解决的别名问题(即,可能存在可能修改对象状态的非常量别名)。这个别名问题的另一个(重要)见解:用指向 const 的指针指向一个对象并不能保证该对象不会改变;它只是保证对象不会通过那个指针改变

      【讨论】:

        【解决方案5】:

        与函数声明一起使用的 const 关键字指定它是 const 成员函数,它将无法更改数据对象的成员。

        【讨论】:

          【解决方案6】:

          我想补充以下几点。

          您也可以将其设为 const &amp;const &amp;&amp;

          所以,

          struct s{
              void val1() const {
               // *this is const here. Hence this function cannot modify any member of *this
              }
              void val2() const & {
              // *this is const& here
              }
              void val3() const && {
              // The object calling this function should be const rvalue only.
              }
              void val4() && {
              // The object calling this function should be rvalue reference only.
              }
          
          };
          
          int main(){
            s a;
            a.val1(); //okay
            a.val2(); //okay
            // a.val3() not okay, a is not rvalue will be okay if called like
            std::move(a).val3(); // okay, move makes it a rvalue
          }
          
          

          请随意改进答案。我不是专家

          【讨论】:

          • *this 始终是左值,即使成员函数是右值引用限定的并且在右值上调用。 Example.
          • 已更新。可以吗?
          【解决方案7】:

          Const 成员函数的含义C++ Common Knowledge: Essential Intermediate Programming 中给出了明确的解释:

          类的非常量成员函数中this指针的类型 X 是 X * 常量。也就是说,它是一个指向非常量 X 的常量指针 (参见 Const 指针和指向 Const 的指针 [7, 21])。因为对象 this 所指的不是 const,可以修改。的类型 类 X 的 const 成员函数中的 this 是 const X * const。那 就是,它是一个指向常量 X 的常量指针。因为对象 this 指的是 const,它不能被修改。那是 const 和 non-const 成员函数的区别。

          所以在你的代码中:

          class foobar
          {
            public:
               operator int () const;
               const char* foo() const;
          };
          

          你可以这样想:

          class foobar
          {
            public:
               operator int (const foobar * const this) const;
               const char* foo(const foobar * const this) const;
          };
          

          【讨论】:

          • this 不是const。不能修改的原因是它是prvalue。
          【解决方案8】:

          const 限定符意味着可以对foobar 的任何值调用方法。当您考虑在 const 对象上调用非常量方法时,差异就出现了。考虑一下您的 foobar 类型是否具有以下额外的方法声明:

          class foobar {
            ...
            const char* bar();
          }
          

          方法bar() 是非常量的,只能从非常量值访问。

          void func1(const foobar& fb1, foobar& fb2) {
            const char* v1 = fb1.bar();  // won't compile
            const char* v2 = fb2.bar();  // works
          }
          

          const 背后的想法是标记不会改变类内部状态的方法。这是一个强大的概念,但在 C++ 中实际上是不可执行的。与其说是保证,不如说是一种承诺。还有一个经常坏的,很容易坏的。

          foobar& fbNonConst = const_cast<foobar&>(fb1);
          

          【讨论】:

          • 我认为答案是关于其他 const 方法而不是关于 const 对象。
          • 感谢“const 背后的想法是标记不会改变类内部状态的方法”。这正是我一直在寻找的。​​span>
          • @JaredPar 这是否意味着任何代表只读操作的成员函数都应标记为const
          【解决方案9】:

          当您在方法签名中使用 const 时(如您所说:const char* foo() const;),您是在告诉编译器 this 指向的内存无法通过此方法更改(此处为 foo )。

          【讨论】:

            【解决方案10】:

            const 意味着该方法承诺不会改变类的任何成员。即使对象本身被标记为const,您也可以执行被标记的对象成员:

            const foobar fb;
            fb.foo();
            

            是合法的。

            更多信息请参见How many and which are the uses of “const” in C++?

            【讨论】:

              【解决方案11】:

              这些 const 意味着如果 'with const' 方法更改内部数据,编译器将出错。

              class A
              {
              public:
                  A():member_()
                  {
                  }
              
                  int hashGetter() const
                  {
                      state_ = 1;
                      return member_;
                  }
                  int goodGetter() const
                  {
                      return member_;
                  }
                  int getter() const
                  {
                      //member_ = 2; // error
                      return member_;
                  }
                  int badGetter()
                  {
                      return member_;
                  }
              private:
                  mutable int state_;
                  int member_;
              };
              

              测试

              int main()
              {
                  const A a1;
                  a1.badGetter(); // doesn't work
                  a1.goodGetter(); // works
                  a1.hashGetter(); // works
              
                  A a2;
                  a2.badGetter(); // works
                  a2.goodGetter(); // works
                  a2.hashGetter(); // works
              }
              

              阅读this了解更多信息

              【讨论】:

              • 关于const 成员函数但未提及mutable 的问题充其量是不完整的。
              【解决方案12】:

              布莱尔的答案是正确的。

              但是请注意,有一个mutable 限定符可以添加到类的数据成员中。任何如此标记的成员都可以const 方法中进行修改,而不会违反const 合约。

              如果您希望一个对象记住某个特定方法被调用了多少次,同时不影响该方法的“逻辑”常量,您可能想要使用它(例如)。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2022-11-25
                • 2012-10-28
                • 2011-07-27
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多