'const'在类的函数声明中的含义是什么?

时间:2009-04-15 13:27:37

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

这些声明中const的含义是什么? const让我感到困惑。

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

11 个答案:

答案 0 :(得分:857)

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方法中,您可以更改实例成员,这在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++;
        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 << "The MyClass instance has been invoked " << ccc.GetInvocations() << " times" << endl;
}

将输出

Foo
Foo const
The MyClass instance has been invoked 2 times

答案 1 :(得分:175)

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

const foobar fb;
fb.foo();

是合法的。

有关详细信息,请参阅How many and which are the uses of “const” in C++?

答案 2 :(得分:43)

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

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

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

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

答案 3 :(得分:22)

这些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了解更多信息

答案 4 :(得分:10)

布莱尔的回答是正确的。

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

如果希望对象记住特定方法的调用次数,而不影响该方法的“逻辑”常量,则可能需要使用此(例如)。

答案 5 :(得分:8)

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

  

类的非const成员函数中this指针的类型   X是X * const。也就是说,它是一个指向非常数X的常量指针   (参见Const Pointers和Pointers to Const [7,21])。因为对象   这引用的不是const,它可以被修改。的类型   这个在类X的const成员函数中是const X * const。那   是,它是一个常量X的常量指针。因为对象是   这指的是const,它不能被修改。那就是   const和非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;
};

答案 6 :(得分:6)

当你在方法签名中使用const时(就像你说的:const char* foo() const;),你告诉编译器this指向的内存不能通过这种方法改变(这里是foo

答案 7 :(得分:2)

我要补充以下几点。

您还可以将其设置为const &const &&

所以

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
}

随时改善答案。我不是专家

答案 8 :(得分:2)

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

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

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

答案 9 :(得分:1)

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

答案 10 :(得分:1)

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

什么是“ const成员函数”?

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

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

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没有运行时空间或速度损失,您无需编写测试用例即可在运行时进行检查。

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