类的函数声明中“ const”最后的含义?


727

const这样的声明中的含义是什么?该const混淆了我。

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

Answers:


951

当您将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;
    }

这不是完全正确的,因为您可以将成员标记为mutableconst然后方法可以对其进行更改。它主要用于内部柜台和东西。解决方案将是以下代码。

#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


47

const预选赛的手段,该方法可以在任何值调用foobar。当您考虑在const对象上调用非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);

3
我以为答案是关于其他const方法的,而不是关于const对象的。
Mykola Golubyev 09年

感谢“ const尽管,其背后的想法是标记不会改变类内部状态的方法”。那才是我真正想要的。
kovac

1
@JaredPar是否意味着表示只读操作的任何成员函数都应标记为const
kovac

26

这些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
}

阅读以获得更多信息


1
const没有提及可变的成员函数问题充其量是不完整的。
IInspectable

13

布莱尔的答案已成定局。

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

例如,如果您希望对象记住某个特定方法被调用了多少次,同时又不影响该方法的“逻辑”常数,则可能需要使用此方法。


10

一个const成员函数的意义C ++常识:基本中级设计给出了明确的解释:

X类的非常量成员函数中的this指针的类型为X * const。也就是说,它是指向非常量X的常量指针(请参见常量指针和指向常量的指针[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;
};

this不是const。无法修改的原因是它是prvalue。
布莱恩

7

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


6

我想补充以下几点。

您也可以 成为一个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
}

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


1
*this即使成员函数经过rvalue-ref限定并在rvalue上调用,它始终是一个左值。实例
HolyBlackCat

1
是的,那么我应该如何改善当前答案?
coder3101 '19

我的意思是在该区块中的注释中写些什么,以证明这种行为是正确的
coder3101

更新。可以吗?
coder3101


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,您无需编写测试用例即可在运行时进行检查。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.