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

原文地址:https://www.cnblogs.com/youxin/p/2503931.html