const的理解和应用
const的理解和应用
一.理解
const是一种语义上的约束,可以通知编译器和其他程序员某个值要保持不变。尽量使用const可以帮助在编译的时候发现错误,使程序更容易懂。
二.用法
const修饰的变量必须在定义的同时初始化(理解为只读的变量,定义时候不初始化,其他地方肯定不可以修改)
a.对于指针
const char *p = "hello"; // 非const指针, const数据
char * const p = "hello"; // const指针, 非const数据
const char * const p = "hello"; // const指针,const数据
const修饰的是紧跟其后的变量。简单记忆:const出现在*号的左边,表示指针指向的数据为常量;出现在右边,表示指针本身为常量(const靠近指针);两边都有,表示二者都是常量。
const char* p 等同于char const *p
b.对于函数
函数的参数表里,形参定义为const类型,表示这个函数不会修改实参的值,当这个形参是个引用的时候,这个非常有用(使程序更容易理解和应用)。
void Fun(const T *const ) //C中表示指针指向的位置和指向的内容都不被修改
void Fun(const T& ) //C++
上面两个形参是等价的.
关于const形参和非const形参:
在C++两种传值方式中,以值传递,const变量和非const变量可以互相传递,因为他们实际是传递一个拷贝,不会威胁原来的数据属性。
而引用传值(传地址)会威胁到原有数据属性(违反const语义)。
所以对以引用传递来说:一个const类型的实参是不能传递给非const类型的形参的(但是GCC,仅仅是给出警告信息,提示discard qualifiers),反过来可以。
例如:
1.void fun ( const char *p );
void fun1(char * p) ;
char *cp = "abc";
const char *cp1="abc";
fun ( cp ) ;//合法
fun1( cp1); //语义上,因为fun1内部可能会修改const类型的cp1,这是不合理的(GCC会警告)
2.实参char **argv 与形参 const char **p 不相容
为啥不相容?我的理解是实参char **argv传给形参const char**p并无 语义和语法上的问题,不相容
仅仅是因为ANSI C标准的规定。
上面的是我自己的理解,官方的解释是:
参数传递过程类似于赋值。合法的赋值,必须满足下列条件之一:
两个操作数都是指向有限定符或无限定符的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。
而
char * 是指向char的指针
const char* 指向的char数据具有const修饰符
他们都指向char,类型相容,修饰符多的做左值,
所以根据上面的原则,const char*只能做左值,也就是形参
对于char **argv和const char **p
const char*本身并没有限定符,只是它指向的内容有限定符
char**argv是指向 char*类型的指针(poitter to poitter to char),const char**p是指向 const char*类型的指针(poitter to poitter to const char)
char*和const char*是都没有限定符的,即argv和p所指向的类型都没有限定符,那么(char*)和(const char*)是相容的类型么?问你的编译器吧,我GCC认为是不相容的- -
c.对于类
1.类的成员函数,被修饰成const类型(const放在参数表后面)表示:
2.这个成员函数不会修改类的成员变量的值;
3.这个const成员函数只能被const类型的对象(或者指向const类型对象的指针)所调用;
4.编译器还会仅仅根据是否有const来判断是不是函数重载(原因见上一条);
mutable关键字:
有种情况是,当类里const函数要修改一些为实现类的机制而定义的变量的时候,这个变量需要用mutable
来声明。
比如iterator 里的_next。_length,_beg_pos才是数列的抽象属性,改变这些,形同改变数列的性质。_next,并不属于数列的属性,
所以将其声明为mutable并不会破坏class object的常数性.
const和非const之间的转换,只能通过const_cast转换(c++)
三.其他
const int Max=100;
int Array[Max];
C,编不过,C++可以编过。说明在C中const类型的Max仍然为变量(定义数组时候必须制定),C++被扩展。
同理在C中 case后也不能const类型的常量(case后只能跟常量,const的修饰,把其理解为只读的变量)
总结:C中定义常量只能用#define,const修饰表示只读;
C++中常量定义,可以用const,也可以用#define;
在C++编程中定义常量要尽量用const来定义常量,好处是:
编译器可以做类型检查,宏常量无类型,而且宏替换会意想不到的错误(当然也可以利用这种特性,写出有意思的代码)
和#define的区别(C语言)
1.const定义的只读变量从汇编的角度来看,只是给除了对应的内存地址,#define给出的是立即数。所以在程序运行中,const变量只有一份拷贝,而#define定义的宏常量在内存中有若干哥拷贝。
2.#define宏是在预编译阶段替换,const变量在编译的时候确定其值
当编译器不允许在类中进行常量的初始化的时候,可以借用enum
class GamePlayer {
private:
enum { NUM_TURNS = 5 } // "the enum hack" — makes
// NUM_TURNS a symbolic name
// for 5
int scores[NUM_TURNS];// fine
};