解析“const”
文章目录
const的用法
修饰变量
修饰常量
与变量不同,有const修饰的量是常量,常量的值不可改变,在定义的时候必须初始化,使用未初始化的常量是错误的。
int a=3;
const int b=a;
修饰引用
指向常量的引用
const引用是指–指向const的引用,其实这样说也不恰当,和const指针一样,const引用可以与常量绑定,也可以与变量绑定,只是不能通过这个const引用来改变绑定对象的值,和const指针类似。
const int &ra=a;
a=5;//正确
ra=4;//错误,不能通过引用改变a的值
非const引用不能与const对象绑定,因为常量a的值不可改变,但却可以通过非const引用来改变常量a的值,这样做是错误的,如下:
const int a=10;
int &ra=a;//错误,非const引用不能绑定到const对象
**由于引用本身不是对象,所以其初始化只是将它绑定到初始值对象上,**而且引用一旦初始化完成,引用将始终和它的初始值对象绑定在一起。引用的类型必须与所引用的对象类型一致,但是有例外情况:在初始化常量引用时可以用任意的表达式作为初始值,只要表达式的结果能转换成引用的类型。
|
|
若改成
|
|
就可以正常运行
引用在内部存放的是一个对象的地址,它是该对象的别名。对于不可寻址的值,如文字常量,以及不同类型的对象,编译器为了实现引用,必须生成一个临时对象,引用实际上指向该对象,但用户不能访问它。
例如:
double dval = 23;
const int &ri = dval;
编译器将其转换为:
int tmp = dval; // double -> int
const int &ri = tmp;
指向指针的引用
引用的对象可以为指针
int *pa = &a;
int *&rd = pa;
但是引用的对象不能为地址,如果一定要让引用绑定地址,需要让引用指向的对象和引用本身都为const
int a = 1;
int *&ra = &a; //错误,因为ra为非常量引用,而a的地址值不是对象,编译器会产生临时对象存储a的地址值
const int * const &rc = &a; //正确,rc为绑定到一个指向int常量的指针的常量引用
修饰指针
|
|
const在谁后面谁就不可修改,const在最前面则将其后移一位即可,二者等效
修饰函数的参数
-
如果输入参数采用“指针传递”,那么加const修饰可以防止意外地改动该指针。
void StringCopy(char *strDestination, const char *strSource);
-
如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const 修饰。
不要将函数void Func1(int x) 写成void Func1(const int x)。
-
对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。
例如:将void Func(A a) 改为void Func(const A &a)。
因为函数体内将产生A 类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间;“引用传递”仅借用一下参数的别名而已,不需要产生临时对象。
-
对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。
例如:void Func(int x) 不应该改为void Func(const int &x)。
因为内部数据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当。
修饰函数的返回值
-
如果给以“指针传递”方式的函数返回值加const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
例如:函数const char * GetString(void); 正确的用法是: const char *str = GetString(); //写为char *str = GetString();将出现编译错误
-
如果函数返回值采用“值传递”方式,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。
例如:不要把函数int GetInt(void) 写成const int GetInt(void)。
-
函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数
修饰函数的定义体
任何不会修改数据成员的函数都应该声明为const 类型。如果在编写const 成员函数时,不慎修改了数据成员,或者调用了其它非const 成员函数,编译器将指出错误,这无疑会提高程序的健壮性。例如:
|
|
const关键字所起作用的本质,就是把隐藏着的默认的this指针参数,改成const类型。也就是说:假如void foo( )函数被编译器改写为 void foo(T pThis),则void foo( ) const将会被改写为void foo(const T pThis) 。
在函数末尾添加一个const,就相当于在隐藏的this参数类型前加一个const
const成员函数
const成员函数的调用
尽管函数名和参数列表都相同,char get( ) const成员函数是可以与char get( )并存的,可以形成重载!
我们假设调用语句为cs.get(),如果cs为non-const对象,则调用foo()。如果cs为const对象,则调用foo()const。
|
|
假如没有提供get()const,则const cs调用get()将会报错。但假如是没有提供foo(),则non-const obj调用foo()const是完全没有问题的。也就是说,non-const对象可以调用const函数,但const对象不能调用non-const函数.
原因在于底层const和顶层const。当对象为const,调用函数时会传入一个const this指针,而函数的形参为非const指针,所以无法正确传入。而当对象为非const,调用函数时传入的非const指针可以被转换为const指针,所以能够正确传入。
小结
类中二函数都存在的情况下:
-
const对象默认调用const成员函数,非const对象默认调用非const成员函数;
-
若非const对象想调用const成员函数,则需显式转化,如(const Student&)obj.getAge();
-
若const对象想调用非const成员函数,同理const_cast(constObj).getAge();(注意:constObj要加括号)
类中只有一函数存在的情况下:
-
非const对象可以调用const成员函数或非const成员函数;
-
const对象只能调用const成员函数,直接调用非const函数时编译器会报错;
在const成员函数里面调用非const成员函数
直接在const成员函数里面调用非const成员函数会报错。
-
向const成员函数内传入一个非const对象指针或引用,然后通过该指针或引用来调用非const成员 数。
-
在const成员函数内部进行this指针的const_cast转换,将其变为非const指针,然后就可以直接调用非const成员函数。
-
使用mutable关键字直接忽略掉const函数的限制。
|
|
|
|
在非const成员函数中调用const成员函数
当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。若要在非const成员函数中调用const成员函数,可以用const_cast将this指针指向的对象转换为const类型,然后进行相应的函数调用。
|
|
const的作用
const意味着“只读”
-
关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。
-
通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
-
合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
转载:
http://blog.csdn.net/lihao21/article/details/8634876
http://blog.csdn.net/mysunshinetbg/article/details/48346195
文章作者 Forz
上次更新 2017-06-23