ChatGPT解决这个技术问题 Extra ChatGPT

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

像这样的声明中的 const 是什么意思? const 让我感到困惑。

class foobar
{
  public:
     operator int () const;
     const char* foo() const;
};
请标记答案。谢谢
我认为这是不可能的@Jesse。询问者的帐户已经干杯多年了。

s
stuckexchange

当您将 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 这完全没问题,对于只读成员来说是非常正常的模式。
g
gnud

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

const foobar fb;
fb.foo();

将是合法的。

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


G
GntS

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 背后的想法是标记不会改变类内部状态的方法”。这真的是我一直在寻找的。
@JaredPar 这是否意味着表示只读操作的任何成员函数都应标记为 const
M
Mykola Golubyev

这些 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 的问题充其量是不完整的。
A
Alnitak

布莱尔的回答是正确的。

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

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


N
Nan Xiao

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

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

我想补充以下几点。

您也可以将其设为 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
}

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


*this 始终是左值,即使成员函数是右值引用限定的并且在右值上调用也是如此。 Example
更新。可以吗?
M
Matrix Buster

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


a
aboloo

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

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

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


这个答案对评分最高的答案没有任何帮助。
C
Chandra Shekhar

与函数声明一起使用的 const 关键字指定它是一个 const 成员函数,并且不能更改对象的数据成员。


C
Community

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

什么是“const 成员函数”?检查(而不是变异)其对象的成员函数。 const 成员函数由成员函数的参数列表后面的 const 后缀指示。带有 const 后缀的成员函数称为“const 成员函数”或“检查器”。没有 const 后缀的成员函数称为“非 const 成员函数”或“mutators”。类弗雷德{公共:无效检查()常量; // 这个成员承诺不会改变 *this void mutate(); // 这个成员函数可能会改变 *this }; void userCode(Fred& changeable, const Fred& unchangeable) { changeable.inspect(); // 好的:不改变可变对象 changeable.mutate(); // 好的:改变一个可变对象 unchangeable.inspect(); // 好的:不改变不可改变的对象 unchangeable.mutate(); // 错误:尝试更改不可更改的对象 } 尝试调用 unchangeable.mutate() 是在编译时捕获的错误。 const 没有运行时空间或速度损失,并且您无需编写测试用例来在运行时检查它。 inspect() 成员函数上的尾随 const 应该用于表示该方法不会更改对象的抽象(客户端可见)状态。这与说该方法不会更改对象结构的“原始位”略有不同。 C++ 编译器不允许采用“按位”解释,除非它们能够解决通常无法解决的别名问题(即,可能存在可能修改对象状态的非常量别名)。这个别名问题的另一个(重要)见解:用指向常量的指针指向一个对象并不能保证该对象不会改变;它只是保证对象不会通过该指针更改。


M
Max Popov

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

指针变为 const 指针 int* const,这与指向 const const int* 的指针不同。因此,您可以更改指针指向的对象,但不能使指针指向另一个对象。

引用应该成为一个常量引用,但它始终是一个常量引用:你不能将它重新初始化到另一个对象。但是您可以再次更改引用所指的对象。