C++中的const成员函数介绍

来源:互联网 发布:邢台学院教务处网络 编辑:程序博客网 时间:2024/06/02 21:19
 List {
02private:
03     Node * p_head;
04     int length;
05     ……
06Public:
07     int GetLength () const;
08     bool GetNodeInfo(const int index,Node & buffer) const {…… }
09     bool DeleteNode(const int index);
10     …………
11}

可以看到,在GetLength和GetNodeInfo两个成员函数的参数列表后面出现了一个const。这个const指明了这个函数不会修改该类的任何成员数据的值,称为常量成员函数。
对于const函数的外部定义,也不能忘记书写const限定符,如下面给出GetLength函数(指返回链表的长度)的定义:

1int List::GetLength() const //这里依然不能忘记const
2{
3     Return length;
4}

如果在const成员函数的定义中出现了任何修改对象成员数据的现象,都会在编译时被检查出来,如:

1int List::GetLength() const
2{
3     Return length++; //错误!
4}

const成员函数存在的意义在于它能被const常对象调用。我们都知道,在定义一个对象或者一个变量时,如果在类型前加一个const,如const int x;,则表示定义的量为一个常量,它的值不能被修改。但是创建的对象却可以调用成员函数,调用的成员函数很有可能改变对象的值,比如下面这段程序:

1const List myList;
2myList.DeleteNode(3); //错误,DeleteLength是非const成员函数

显然调用DeleteNode这个成员函数删除一个链表结点后,很有可能改变对象中length(链表长度)这个值,这不符合const对象的规定。但是,如果不允许const对象调用任何成员函数又是非常不合理的。于是,我们把那些肯定不会修改对象的各个属性值的成员函数加上const说明符,这样,在编译时,编译器将对这些const成员函数进行检查,如果确实没有修改对象值的行为,则检验通过。以后,如果一个const常对象调用这些const成员函数的时候,编译器将会允许。比如:

1const List myList;
2myList.GetLength(); //正确,GetLength是const常函数,它返回链表长度,的确没有改变
3//属性值的行为,被检验通过

你可能会问,为什么不在一个const常对象调用成员函数的时候再进行检查呢?如果被调用的函数会改变对象的属性值,则立即打住就是了。这样就不用麻烦地在成员函数后面加const限定符了。然而,这无疑会大大增加编译时间。考虑下面这段代码:

1const List MyList;
2MyList.GetLength();
3……
4MyList.GetLength();
5……

这段代码中,GetLength被调用了两次,但是编译时却也要检查两次,倘使一个成员函数被调用多次,那么他将在每次调用的时候都会被检查。这显然大大不利。而如果在定义类的时候加上const限定符对常函数加以标记,那么编译器只是检查一次就好,在const对象调用成员函数时,const函数将会被直接放行。所以,C++采取了const限定符描述常函数方案而摈弃了后者。所以,即使一个函数没有修改对象值的行为,如果没有加上const限定符说明是常函数,那么const对象依然不能调用它。
然而,有些时候,我们却必须要让const函数具有修改某个成员数据值的能力。比如一些内部的状态量,对外部用户无所谓,但是对整个对象的运行却大有用处,如支持缓存的技术。遇到这种问题,我们可以把一个成员数据定义为mutable(多变的),它表示这个成员变量可以被const成员函数修改却不违法。比如下面定义了一个is_valid类成员:

01class List
02{
03private:
04     ……
05     mutable bool is_valid;
06     ……
07public:
08      bool CheckList() const
09     {
10          if(length >= 1) then return is_valid =true;
11          else return is_valid = false//正确!
12      };

这样,即使像CheckList这样的const成员函数修改它也是合法的。
但需要注意的时,不可滥用mutabe描述符,如果在某个类中只有少数一部分是被允许const常量函数修改的,使用mutable是再合适不过的。如果大部分数据都定义为mutable,那么最好将这些需要修改的数据放入另一个独立的对象里,并间接地访问它。

首先说明一下使用const的好处:
使用const的好处在于它允许指定一种语意上的约束------某种对象不能被修改--------编译器具体来实施这种约束。通过const,你可以通知编译器和其他程序员某个值要保持不变。只要是这种情况,你就要明确地使用const ,因为这样做就可以借助编译器的帮助确保这种约束不被破坏。
(一)
首先解释一下const与指针的关系:
const在指针的声明中有一下三种形式:
const char *p        = "hello";          // 非const指针,
                                         // const数据,就是说p指向的那个内存空间的数据是不可变的,但p还可以指向新的内存地址。

char * const p       = "hello";          // const指针,
                                         // 非const数据,就是说这个指针p一旦赋值或初始化,就不能在指向其他位置了,但其指向的位置的数据值是可变的。

const char * const p = "hello";          // const指针,
                                         // const数据,这个就很明显了,集上述两家之长处(也可能是短处哦,),上述两者都不可变。
一般来说,你可以在头脑里画一条垂直线穿过指针声明中的星号(*)位置,如果const出现在线的左边,指针指向的数据为常量;如果const出现在线的右边,指针本身为常量;如果const在线的两边都出现,二者都是常量。

恩,差点忘了,还有一种形式:
char const * p = "hello"; 
这其实与上边的情形一是一样的,只是由于个人习惯的不同,二者都是对的。

(二)
在一个函数声明中,const可以指的是函数的返回值,或某个参数;对于成员函数,还可以指的是整个函数。
const(1) int fun(int  const(2)& )const(3)
{
       int temp;
       retrun temp;
}
参数的 const属性(上例2处)一般用引用传递,是为了保证该参数在函数中不允许被修改,一旦修改,编译器会报错。

而返回值的const属性(上例1处)是保证函数的返回值不被修改,也许你会质疑这种可能性,但是这种可能性确实存在,
详细情形如下:(摘自effective c++)
const rational operator*(const rational& lhs,
                         const rational& rhs);

很多程序员第一眼看到它会纳闷:为什么operator*的返回结果是一个const对象?因为如果不是这样,用户就可以做下面这样的坏事:

rational a, b, c;

...

(a * b) = c;      // 对a*b的结果赋值

我不知道为什么有些程序员会想到对两个数的运算结果直接赋值,但我却知道:如果a,b和c是固定类型,这样做显然是不合法的。一个好的用户自定义类型的特征是,它会避免那种没道理的与固定类型不兼容的行为。对我来说,对两个数的运算结果赋值是非常没道理的。声明operator*的返回值为const可以防止这种情况,所以这样做才是正确的。

呵呵,象Scott Meyers这样的大师见地就是不一般吧

接下来说明函数的const属性:(上例3处)
当然喽,一般用于成员函数了,它有以下属性:
(1)const对象的访问只能靠const成员函数
(2)const成员函数不被允许修改它所在对象的任何一个数据成员。

(三)尽量使用 const代替define 吧,因为const是类型安全的。
应该使用
const double  pi = 3.1415926;
而不要用#define pi 3.1415926
后者是宏,仅仅是对程序中的pi用3.1415926代替,会让你对于一些编译时的错误很难定位。

0 0
原创粉丝点击