【问题标题】:What are the semantics of a const member function?const 成员函数的语义是什么?
【发布时间】:2010-09-11 01:16:43
【问题描述】:

我知道该函数不允许更改对象的状态,但我想我在某处读到允许编译器假设如果使用相同的参数调用该函数,它将返回相同的值并且因此可以重用缓存的值(如果可用)。例如

class object
{
    int get_value(int n) const
    {
        ...
    }

...


object x;

int a = x.get_value(1);
    ...
int b = x.get_value(1);

然后编译器可以优化第二次调用,或者使用寄存器中的值或者简单地做b = a;

这是真的吗?

【问题讨论】:

    标签: c++ methods constants


    【解决方案1】:

    const 是关于程序语义而不是关于实现细节。当成员函数const 不改变对象的可见状态时,您应该标记它,并且应该可以在本身为const 的对象上调用。在X 类的const 成员函数中,this 的类型是X const *:指向常量X 对象的指针。因此,所有成员变量实际上都是该成员函数中的constmutable 除外)。如果你有一个const 对象,你只能在它上面调用const 成员函数。

    您可以使用mutable 指示成员变量即使在const 成员函数内也可能发生变化。这通常用于标识用于缓存结果的变量,或者用于不影响实际可观察​​状态的变量,例如互斥体(您仍然需要在const 成员函数中锁定互斥体)或使用计数器。

    class X
    {
        int data;
        mutable boost::mutex m;
    public:
        void set_data(int i)
        {
            boost::lock_guard<boost::mutex> lk(m);
            data=i;
        }
        int get_data() const // we want to be able to get the data on a const object
        {
            boost::lock_guard<boost::mutex> lk(m); // this requires m to be non-const
            return data;
        }
    };
    

    如果您通过指针而不是直接保存数据(包括智能指针,例如std::auto_ptrboost::shared_ptr),则指针在const 成员函数中变为const,但不是指向的数据,所以你可以修改指向的数据。

    至于缓存:通常编译器不能这样做,因为状态可能会在调用之间发生变化(尤其是在我使用互斥锁的多线程示例中)。但是,如果定义是内联的,那么编译器可以将代码拉入调用函数并优化它在那里可以看到的内容。这可能会导致函数有效地只被调用一次。

    C++ Standard (C++0x) 的下一个版本将有一个新的关键字 constexpr。标记为constexpr 的函数返回一个常量值,因此可以缓存结果。在这样的函数中你可以做的事情是有限制的(以便编译器可以验证这个事实)。

    【讨论】:

    • 你成就了我的一天,先生。我不知道我一直需要的是mutable。非常感谢。
    【解决方案2】:

    成员变量上的关键字mutable 允许const 函数改变手头对象的状态。

    不,它不会缓存数据(至少不是所有调用),因为以下代码是一个有效的 const 函数,会随时间变化:

    int something() const { return m_pSomeObject->NextValue(); }
    

    请注意,指针可以是 const,尽管指向的对象不是 const,因此对 SomeObject 上的 NextValue 的调用可能会也可能不会改变它自己的内部状态。这会导致函数在每次调用时返回不同的值。

    但是,我无法回答编译器如何使用 const 方法。我听说它可以优化某些东西,但我必须查一下才能确定。

    【讨论】:

      【解决方案3】:

      没有。

      一个 const 方法是一种不改变对象状态(即它的字段)的方法,但你不能假设给定相同的输入,一个 const 方法的返回值是确定的。换句话说,const 关键字并不意味着该函数是一对一的。例如,返回当前时间的方法是 const 方法,但它的返回值在调用之间会发生变化。

      【讨论】:

        【解决方案4】:

        成员函数上的 const 关键字将 this 参数标记为常量。该函数仍然可以静音全局数据(因此无法缓存),但不能静音对象数据(允许调用 const 对象)。

        【讨论】:

        • 其实,如果你有一个成员标记为 mutable,那么 const 函数仍然可以修改它。这主要用于缓存最后的结果。 :-)
        【解决方案5】:

        在这种情况下,const 成员函数意味着this 也被视为const 指针。实际上,这意味着您不能在 const 成员函数中修改 this 的状态。

        对于无副作用的函数(即,您想要实现的),GCC 有一个名为 pure 的“函数属性”(您可以通过说 __attribute__((pure)) 来使用它):http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

        【讨论】:

        • 我并不是要实现无副作用的功能。我只是想了解将函数设为 const 的所有含义。
        【解决方案6】:

        我对此表示怀疑,该函数仍然可以调用一个改变世界状态且不违反 const 的全局函数。

        【讨论】:

          【解决方案7】:

          除了成员函数可以修改全局数据这一事实之外,成员函数还可以修改相关对象的显式声明的可变成员。

          【讨论】:

            【解决方案8】:

            Corey 是正确的,但请记住,任何标记为 mutable 的成员变量都可以在 const 成员函数中进行修改。

            这也意味着这些函数可以从其他 const 函数调用,或者通过其他 const 引用。


            编辑:妈的,被打了9秒……9!!! :)

            【讨论】:

            • 西部最快的枪,多少? :-P
            • 嘿,我的回复比你早 9 秒多。 :P
            【解决方案9】:

            const 方法也可以修改静态局部变量。例如,以下是完全合法的(并且重复调用 bar() 将返回递增的值 - 而不是缓存的 0):

            class Foo
            {
            public:
                int bar() const
                {
                    static int x = 0;
                    return x++;
                }
            };
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-05-02
              • 1970-01-01
              • 2017-03-01
              • 1970-01-01
              • 1970-01-01
              • 2013-10-16
              相关资源
              最近更新 更多