那时有位听众看了我以后写的一则该文如是说const,问他不讨厌用const,用了惧怕错,不必也能写流程,干脆就不必了。
《操作符其本质探究
https://blog.csdn.net/super828/article/details/80439812
一时之间我也没归纳const的益处,接着我就找搜寻归纳了下。
const巨作
动画版:Rahul Singh 译者:zhigang
[译者者注]很多地方性按书名说明难通,译者者依照她们的认知作了适度修正。如有欠妥含意,请知会[email protected]或参照书名。
书名源自www.codeproject.com
概要
当我她们写流程须要加进const的这时候,或是是读自己的标识符遇到const的这时候,我经常会停下想一会。很多程序员从不必const,理据是即便不行const她们也那么回来了。责任编辑仅对const的用语稍加深入探讨,期望能对提升应用软件的源标识符产品质量略有协助。
常表达式
表达式用const润色,其值严禁被发生改变。任何人发生改变此表达式的标识符单厢造成校对严重错误。Const加在正则表达式其间均可。
比如
void main(void) { const int i = 10; //i,j都用于常表达式 int const j = 20; i = 15; //严重错误,常表达式不能发生改变 j = 25; //严重错误,常表达式不能发生改变 }常操作符
Const跟操作符一起使用的这时候有两种方法。
const可用来限制操作符不可变。也就是说操作符指向的内存地址不可变,但可以随意发生改变该地址指向的内存的内容。
void main(void) { char* const str = “Hello, World”; //常操作符,指向字符串 *str = M; //可以发生改变字符串内容 str = “Bye, World”; //严重错误,如能发生改变常操作符指向的内存地址 }const也可用来限制操作符指向的内存不可变,但操作符指向的内存地址可变。
void main(void) { const char* str = “Hello, World”; //操作符,指向字符串常量 *str = M; //严重错误,不能发生改变字符串内容 str = “Bye, World”; //修正操作符使其指向另一个字符串 *str = M; //严重错误,仍不能发生改变字符串内容 }看完上面的两个例子,是不是糊涂了?告诉你一个诀窍,在第一个例子中,const用来润色操作符str,str不可变(也就是指向字符的常操作符);第二个例子中,const用来润色char*,字符串char*不可变(也就是指向字符串常量的操作符)。
这两种方式可以组合起来采用,使操作符和内存内容都不可变。
void main(void) { const char* const str = “Hello, World”; //指向字符串常量的常操作符 *str = M; //严重错误,不能发生改变字符串内容 str = “Bye, World”; //严重错误,不能发生改变操作符指向的地址 }Const和引用
引用实际上就是表达式的别名,这里有几条规则:
声明表达式时必须初始化
一经初始化,引用不能在指向其它表达式。
任何人对引用的发生改变都将发生改变原表达式。
引用和表达式本身指向同一内存地址。
下面的例子演示了以上的规则:
void main(void) { int i = 10; //i和j是int型表达式 int j = 20; int &r = i; //r 是表达式i的引用 int &s; //严重错误,声明引用时必须初始化 i = 15; //i 和 r 都等于15 i++; //i 和 r都等于16 r = 18; //i 和r 都等于18 printf(“Address of i=%u, Address of r=%u”,&i,&r); //内存地址相同 r = j; //i 和 r都等于20,但r不是j的引用 r++; //i 和 r 都等于21, j 仍等于20 }用const润色引用,使应用不可修正,但这并不耽误引用反映任何人对表达式的修正。Const加在正则表达式其间均可。
比如:
void main(void) { int i = 10; int j = 100; const int &r = i; int const &s = j; r = 20; //错,不能发生改变内容 s = 50; //错,不能发生改变内容 i = 15; // i和r 都等于15 j = 25; // j和s 都等于25 }Const和成员函数
声明成员函数时,末尾加const润色,表示在成员函数内不得发生改变该对象的任何人数据。这种模式常被用来表示对象数据只读的访问模式。
比如:
class MyClass { char *str =”Hello, World”; MyClass() { //void constructor } ~MyClass() { //destructor } char ValueAt(int pos) const //const method is an accessor method { if(pos >= 12) return 0; *str = M; //严重错误,严禁修正该对象 return str[pos]; //return the value at position pos } }Const和重载
重载函数的这时候也可以采用const,考虑下面的标识符:
class MyClass { char *str =”Hello, World”; MyClass() { //void constructor } ~MyClass() { //destructor } char ValueAt(int pos) const //const method is an accessor method { if(pos >= 12) return 0; return str[pos]; //return the value at position pos } char& ValueAt(int pos) //通过返回引用设置内存内容 { if(pos >= 12) return NULL; return str[pos]; } }在上面的例子中,ValueAt是被重载的。Const实际上是函数参数的一部分,在第一个成员函数中它限制这个函数不能发生改变对象的数据,而第二个则没。这个例子只是用来说明const可以用来重载函数,没甚么实用意义。
实际上我们须要一个新版本的GetValue。如果GetValue被用在operator=的右边,它就会充当一个表达式;如果GetValue被用于一元操作符,那么返回的引用可以被修正。这种用语常用来重载操作符。String类的operator[]是个很好的例子。(这一段译得很烂,书名如下:In reality due to the beauty of references just the second definition of GetValue is actually required. If the GetValue method is used on the the right side of an = operator then it will act as an accessor, while if it is used as an l-value (left hand side value) then the returned reference will be modified and the method will be used as setter. This is frequently done when overloading operators. The [] operator in String classes is a good example.)
class MyClass { char *str =”Hello, World”; MyClass() { //void constructor } ~MyClass() { //destructor } char& operator[](int pos) //通过返回引用可用来更改内存内容 { if(pos >= 12) return NULL; return str[pos]; } } void main(void) { MyClass m; char ch = m[0]; //ch 等于 H m[0] = M; //m的成员str变成:Mello, World }Const的担心
C/C++中,数据传递给函数的方式默认的是值传递,也就是说当参数传递给函数时会造成一个该参数的拷贝,这样该函数内任何人对该参数的发生改变都不会扩展到此函数以外。每次调用该函数单厢造成一个拷贝,效率不高,尤其是函数调用的次数很高的这时候。
比如:
class MyClass { public: int x; char ValueAt(int pos) const //const method is an accessor method { if(pos >= 12) return 0; return str[pos]; //return the value at position pos } MyClass() { //void constructor } ~MyClass() { //destructor } MyFunc(int y) //值传递 { y = 20; x = y; //x 和 y 都等于 20. } } void main(void) { MyClass m; int z = 10; m.MyFunc(z); printf(“z=%d, MyClass.x=%d”,z,m.x); //z 不变, x 等于20. }通过上面的例子可以看出,z没发生变化,因为MyFunc()操作的是z的拷贝。为了提升效率,我们可以在传递参数的这时候,不采用值传递的方式,而采用引用传递。这样传递给函数的是该参数的引用,而不再是该参数的拷贝。然而问题是如果在函数内部发生改变了参数,这种发生改变会扩展到函数的外部,有可能会导致严重错误。在参数前加const润色保证该参数在函数内部不会被发生改变。
class MyClass { public: int x; MyClass() { //void constructor } ~MyClass() { //destructor } int MyFunc(const int& y) //引用传递, 没任何人拷贝 { y =20; //严重错误,不能修正常表达式 x = y } } void main(void) { MyClass m; int z = 10; m.MyFunc(z); printf(“z=%d, MyClass.x=%d”,z,m.x); //z不变, x等于10. }如此,const通过这种简单安全机制使你写不出那种说不定是甚么这时候就会掉过头来咬你一口的标识符。你应该尽可能的采用const引用,通过声明你的函数参数为常表达式(任何人可能的地方性)或是定义那种const method,你就可以非常有效确立这样一种概念:本成员函数不会发生改变任何人函数参数,或是不会发生改变任何人该对象的数据。别的流程员在采用你提供的成员函数的这时候,不会担心她们的数据被改得一塌糊涂。