刚才在看《C 语言嵌入式系统编程修炼之三:内存操作》的时候再次看到了关键字const的重要性;
以前在很多地方看到过const关键字,但是终究没有认真的看过其具体含义,后面看到多了,也看到很多人说这是一个非常重要的的知识,一定要理解。我以前对const的理解是常量的意思,可是在(不知道哪里了),说如果一个面试官问你对const关键字的理解,你答是常量的话,那面试官肯定觉得是跟一个行外人打交道,即便是说是只读的还能接受呀……呵呵……看来自己学了那么久,也就是个行外人而已,这次再次看到,所以就的求个究竟了:
关键字const

const 意味着"只读"。区别如下代码的功能非常重要,也是老生长叹,如果你还不知道它们的区别,而且已经在程序界摸爬滚打多年,那只能说这是一个悲哀:界摸爬滚打多年,那只能说这是一个悲哀:
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
(1)关键字const 的作用是为给读你代码的人传达非常有用的信息。例如,在函数的形参前添加const 关键字意味着
这个参数在函数体内不会被修改,属于\"输入参数\"。在有多个形参的时候,函数的调用者可以凭借参数前是否有const 关
键字,清晰的辨别哪些是输入参数,哪些是可能的输出参数。
(2)合理地使用关键字const 可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改,这样
可以减少bug 的出现。
const 在C++语言中则包含了更丰富的含义,而在C 语言中仅意味着:\"只能读的普通变量\",可以称其为\"不能改变的
变量\"(这个说法似乎很拗口,但却最准确的表达了C 语言中const 的本质),在编译阶段需要的常数仍然只能以#define
宏定义!故在C 语言中如下程序是非法的:
const int SIZE = 10;
char a[SIZE]; /* 非法:编译阶段不能用到变量 */
下面是我在网上找到的一篇认为说得比较好的。是一位网友在csnd论坛上回答令一位网友的问题时对const关键字的阐述。如下:
(另外一篇对const很详细的解释的博客http://19880512.blog.51cto.com/936364/306027
关键字const

说到const在C++中的作用,只要有一定经验的程序员就不会仅仅想到它是用来标识常量。关于const的作用在很多书上都有讲解,在这里说到const可能并没有什么新的东西,只能说是一起来复习一下const。现总结const的主要作用如下(当然,应该还有很多吧):

1. 代替#define。

讲到用const来代替#define,为什么要这样做呢,当然是const比#define更好啦,一个很明显区别就是const有数据类型,可以进行类型安全检查,而#define只是简单的替换,并这个功能。所以我们就尽量使用

const double pi = 3.1415926;

来代替这样的语句:

#define pi 3.1415926;//最好用const来定义

而且#define的定义在进入编译器前会被替换掉,这样,当涉及到这个常量的编译错误时,报告是不是pi而是3.1415926这个常量,这样就带来的调试上的麻烦。

用cosnt来代替#define还有一个好处就是减少不必要的内存分配,例如:

#define PI 3.1415926 //常量宏

const doulbe Pi=3.1415926; //此时并未将Pi放入ROM中

......

double i=Pi; //此时为Pi分配内存,以后不再分配!

double I=PI; //编译期间进行宏替换,分配内存

double j=Pi; //没有内存分配

double J=PI; //再进行宏替换,又一次分配内存!

const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝换,又一次分配内存!

2. 使某个对象(变量)、值,指针,引用不能被修改。

相信这一点是大家最常见,用它来定义常量,使之具有不可变性。

对于用来修饰对象(变量),非常好理解,也是经常使用const的作用之一。但对于指针,可能就不好那好理解了,幸运的是Meyers大师给了我们一个很好的方法(在 < <EC.Iitem21> > 中)如果const在*号的左边,则说明数据是常量也就是指针所指向的那个对象为常量,如果const在*号的右边,则说明指针自身是一个常量,看看下的代码的解释:

int myvar = 500; //变量b不能被修改,因为b已用const修饰

const int* p1 = &myvar; //指针p可以被修改,但myvar(*p)是不能被修改的。

int const *p2 = &myvar; //(同语句const int* p = myvar意思一样)。

int* const p3 = &myvar;//指针p不能被修改,但myvar(*p)是可以被修改的。

const int* const p4 = &myvar; //指针p和myvar(*p)都不能被修改。

同样const可也用来修饰引用,加在数据类型前后都可以.

int var1 ;

double var2 ;

const int &r = var1;//r不能被修改,var1可以修改

double const &s = var2;//s不能被修改,var2可以修改

3. 使类的静态对象在类内部可以初始化

当我们要在类的内部定义某个常量时,就可能用const来修饰,否则的话,就不能初始化,看看下面的代码

class print

{

private:

//不能写成static int count = 10;

static const int count = 10;

string info[count];

};

const int print::count;

当然出也可以通过类构造函数的初始化成员列表来初始化cosnt常量:

class A

{

public:

A(int i=0):count(i) {}

private:

const int count;

};

(题外话)当然,对于很久以前的编译器(1995年以前)是不认可这种代码的,当然要达到这种效果可以采用借助为menu来解决,代码像这样:

class print

{

menu{count = 10 };//借助枚举来得到一个初始值

string info[count];

};

4. 修饰函数参数和返回值,对于类的成员函数可以确定不能修改类的数据成员.

用const用修饰函数参数是一种常见的行为,见下面代码:

const bigint operator+(const bigint& bigvar1, const bigint& bigvar2)

{ return bigvar1.value+bigvar2.value; }

由于bigvar1和bigvar2 都是类类型,为了提高传值的效率,所以就用对象的引用,但是这样就有一个问题,我们不能阻止在函数内部修改bigvar1和bigvar2的值,解决办法只能用const,这就是我们在非内部数据类型的函数参数传递中常用的一种”const引用传递”,它可得到高效率,同时阻止函数内部对对象进行修改。

再看看对象前面的const(bigint前面) 它的作用是不允许下面的代码存在: (a+b) = c //a,b,c都是bigint类型

上面的代码很显然是没有意义,它对一个运算结果进行赋值,我们就应该加上const来阻止这样的代码发生。

对于类的成员函数,若不会修改数据成员,我们都应该有const来声明,若我编写这样的函数时不小心修改了类的数据成员或调用非const的成员函数,编译器就会给予相关的提示。看看下面的代码:

class myprint

{

public:

myprint():printcount(0){};

void print();

int getcount() const;

private:

myprint(const myprint&);

myprint& operator=(const myprint&);

int printcount;

};

void myprint::print(){}

int myprint::getcount() const

{

++printcount; //错误,以const成员函数不能修改类的数据成员

print(); //错误,以const成员函数不能调用非const成员函数

return printcount;

}

用const来修饰成员函数实际上修饰的是this指针,所以静态成员函数不能声明为const,原因很简单:静态成员函数没有this指针。这里要注意const成员函数中关键字const是放在成员函数的最后面。

有时候我的确要在const成员函数中修改类的内部数据员,这时应该怎么办呢,幸运的是标准c++提供的关键字mutable来达到,只要在类数据成员前加上关键字mutable:

class myprint
{

public:

……

int getcount() const;

private:

……

mutable int printcount;

};

……

int myprint::getcount() const

{

++printcount;//正确,因为printcount有关键字mutable修饰

……

return printcount;

}

当然,还有其它方法(使用const_cast或通过指针间接来修改或通过成员函数的this指针)同样能达到修改的目的,但最好是用mutable。

5. 用const来修饰重载的类的成员函数。

有时候我们要为类提供成员函数的const重载版本,以适应不同情况的需要,例如看看SGI STL关于stack中的一段代码:

class stack

{

……

public:

reference top() { return _M_c.back(); }

const_reference top() const { return _M_c.back(); }

……

}

这里就为stack提供为返回栈顶值的两个版本。例如:

top = mystack.top() //调用const_reference top() const

而下面的代码:

mystack.top() = myvar //调用reference top()

其实 const的作用应该不止上面说的那些,但可以说一些常用的功能都提到了。在合适使用cosnt的地方,应该尽量使,使程序变得简单,清晰.

最后,可以看看这个函数中所有const的意义:

const char* const foo(char const * const str) const

第一个const表示返回类型为const,也就是不能把此函数的返回值当作左值来使用。

第二个const表示指针的不可变性,但在这是可以省略,因为返类型已经是const。

第三个cosnt表示str的常量性,也就其内容是不能改变,可以写在其前面的char的前面。

第四个cosnt表示str的指针的常量性,也就是此指针不能指向别的地址。

第五个cosnt表示此函数的常量性(前提是类的成员函数),不能修改所在类的数据成员。