zoukankan      html  css  js  c++  java
  • c++ const 成员函数和mutable

      常量(const)成员函数:承诺不会修改对象的状态

    double Student_info::grade() const {...}   // 成员函数版
    double grade(const Student_info&) {...}   // 旧版
     
      对常量对象只能调用常量成员函数,而不能调用非常量成员函数。常成员函数不能更新类的成员变量,也不能调用该类中没有用const修饰的成员函数,只能调用常成员函数和常数据成员。
      任何不会修改数据成员的函数都应该声明为const 类型。如果在编写const 成员函数时,不慎修改了数据成员,或者调用了其它非const 成员函数,编译器将指出错误,这无疑会提高程序的健壮性。
    以下程序中,类stack 的成员函数GetCount 仅用于计数,从逻辑上讲GetCount 应当为const 函数。编译器将指出GetCount 函数中的错误。
    class Stack
    {
    public:
    void Push(int elem);
    int Pop(void);
    int GetCount(void) const; // const 成员函数
    private:
    int m_num;
    int m_data[100];
    };
    int Stack::GetCount(void) const
    {
    ++ m_num; // 编译错误,企图修改数据成员m_num
    Pop(); // 编译错误,企图调用非const 函数
    return m_num;
    }

    const 成员函数的声明看起来怪怪的:const 关键字只能放在函数声明的尾部,大

    概是因为其它地方都已经被占用了。
     

    有时候要在const 成员函数中调用非const 成员函数,怎么办?

    当然有不少办法,个人觉得,最简单的方法可以这样:

    写一个全局函数,非const 成员函数的类指针作为参数,然后在这个全局函数中调用想要调用的非const 成员函数。如,

    static void get_xxx(Student *s){s->get_xxx();}

     

    什么时候函数写成const常量成员函数?

     如果函数会改变一个对象的状态,那它就应该成为这个对象的成员。

     
    如果函数不会改变一个对象的状态,那么需要从函数是做什么的,用户可能希望以怎样的方式调用它等方面来考虑如何取舍。(需要经验的积累)
     const修饰函数时:
    class Text
    {
    public:
        const std::size_t length() const; 
        // 返回文本的长度
        // 第一个 const 表示 函数返回一个常量,不可作为左值使用
        // 第二个 const 表示 函数不修改 Text 类中的数据成员(除了 static 和 mutable 修饰过的)
    private:
        char* data;
    };
    mutable关键字

      mutable的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词。

      在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中

      我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰。

      下面是一个小例子: 

    class ClxTest
    {
     public:
      void Output() const;
    };

    void ClxTest::Output() const
    {
     cout << "Output for test!" << endl;
    }

    void OutputTest(const ClxTest& lx)
    {
     lx.Output();
    }

      类ClxTest的成员函数Output是用来输出的,不会修改类的状态,所以被声明为const的。

      函数OutputTest也是用来输出的,里面调用了对象lx的Output输出方法,为了防止在函数中调用其他成员函数修改任何成员变量,所以参数也被const修饰。

      如果现在,我们要增添一个功能:计算每个对象的输出次数。如果用来计数的变量是普通的变量的话,那么在const成员函数Output里面是不能修改该变量的值的;而该变量跟对象的状态无关,所以应该为了修改该变量而去掉Output的const属性。这个时候,就该我们的mutable出场了——只要用mutalbe来修饰这个变量,所有问题就迎刃而解了。

      下面是修改过的代码:

    class ClxTest
    {
     public:
      ClxTest();
      ~ClxTest();

      void Output() const;
      int GetOutputTimes() const;

     private:
      mutable int m_iTimes;
    };

    ClxTest::ClxTest()
    {
     m_iTimes = 0;
    }

    ClxTest::~ClxTest()
    {}

    void ClxTest::Output() const
    {
     cout << "Output for test!" << endl;
     m_iTimes++;
    }

    int ClxTest::GetOutputTimes() const
    {
     return m_iTimes;
    }

    void OutputTest(const ClxTest& lx)
    {
     cout << lx.GetOutputTimes() << endl;
     lx.Output();
     cout << lx.GetOutputTimes() << endl;
    }

      计数器m_iTimes被mutable修饰,那么它就可以突破const的限制,在被const修饰的函数里面也能被修改。
     
    说下class中const,static ,const static初始化问题:

     const定义的常量在超出其作用域之后其空间会被释放,而static定义的静态常量在函数执行后不会释放其存储空间

          static表示的是静态的。类的静态成员函数、静态成员变量是和类相关的,而不是和类的具体对象相关的。即使没有具体对象,也能调用类的静态成员函数和成员变量。一般类的静态函数几乎就是一个全局函数,只不过它的作用域限于包含它的文件中。

          在C++中,static静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体的外部,通常在类的实现文件中初始化,如:double Account::Rate=2.25;static关键字只能用于类定义体内部的声明中,定义时不能标示为static

          在C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。

          const数据成员 只在某个对象生存期内是常量,而对于整个类而言却是可变的因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为类的对象没被创建时,编译器不知道const数据成员的值是什么。

          const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现,或者static const。

    #include<iostream>
    using namespace std;
    
    class A{
    private:
    
        static int a;//只是声明,没有定义
        int b;
        const int c;
        const static int d=4;//可以直接初始化,或者在类外:const int A::d=0;//注意:给静态成员变量赋值时,不需要加static修饰符。但要加const
         
    public:
    
        A():c(3){ b=2; }     //{  c =3;}错误,表达式必须是可修改的左值
        void output() const{
            a++;//ok,b++是错误的,: 由于正在通过常量对象访问“b”,因此无法对其进行修改
            
            cout<<a<<ends<<b<<ends<<c<<ends<<d<<endl;
        }
    };
    int A::a=1;
    
    int main()
    {
        A a;
        a.output();
    }

    参考了:

    http://blog.csdn.net/yjkwf/article/details/6067267

  • 相关阅读:
    力扣(LeetCode) 14. 最长公共前缀
    力扣(LeetCode)965. 单值二叉树
    力扣(LeetCode)258. 各位相加
    力扣(LeetCode)389. 找不同
    PTA 阶乘之和取模
    A. Sea Battle
    PTA 二叉树路径
    PTA 重构二叉树
    PTA 笛卡尔树
    绿豆蛙的归宿
  • 原文地址:https://www.cnblogs.com/youxin/p/2503931.html
Copyright © 2011-2022 走看看