C++中const和引用修饰变量和函数的总结
来源:互联网 发布:网络推广课程 编辑:程序博客网 时间:2024/04/30 16:31
一、对于修饰变量的用法
对于const和&的基础用法就不说了。下面说点之前有误区和容易错的地方
const修饰变量的误区
关于const类型,这里有一个我之前的误区,我以为const定义的时候只能用常量初始化,但是实际上是没有这个规定的。const定义的变量可以用各种const,非const对其进行初始化,甚至可以是函数的返回值之类的。
const int a = 5; //在编译的时候进行初始化,即类似于#define,进行全文替换const int b = size(); //在执行的时候初始化int n =7;const int c = n; //在执行的时候初始化
这个其实非常显然啦,因为在函数传参的时候,就能看出来,形参可以是const的,但是传进去的实参可以是非const的。[1]
对于这个问题就可以看出中文各种博客上面的说法有多垃圾了,就我看过的80%的博客都说const必须要用常量初始化,同时在编译时就完成了初始化。以后技术问题少看中文博客,之前被误导了那么久。
常量指针和指向常量的指针
指向常量的指针
const int *p,表示指向常量的指针,按照之前上篇文章中的方法C++
中复合类型声明的理解从右到左看,p是一个指针,指向const
int,表示的意义就是不能通过p来修改p指向的值,而p本身所指向的地址是可以修改的。(此处也可以写成int const *p是一样的)
常量指针
int *const p,表示p本身是一个const,所以p所指的地址不会变,但是地址里的值是可以修改的。
有个需要注意的地方是:常量引用。
const int i1 = 1024;const int &ci1 = i1;int i2 = 2048;const int &ci2 = i2; //允许将const int&绑定到一个普通int上,其实常量引用可以用任何值进行初始化
其实常量引用这个说法是一个非常不严谨的说法,因为C++中本身就不允许随便改变引用所绑定的对象。所以所有引用都是常量。而常量引用类比于上面指针的说法,应该是对常量的引用。
还要记住一点就是常量引用可以用任何值进行初始化,即可以用右值对其初始化。
int i = 1024; const int &ri = i*2;
对于这一点,可以用复制构造函数去理解,在C++11标准的右值引用出来之前,就是用常量引用来延长一个右值的生命周期的。其在计算机内的实现可以认为是这样的:
const int tmp = i*2;const int &ri = tmp;
即ri绑定的是一个临时变量。在C最早版本的时候,引用时可以绑定左值和右值的,只不过如果出现上面这种情况,ri绑定一个临时量,如果ri不是const的话,其修改也会导致临时量的修改,而此时临时值已经被销毁了。所以ri必须是const的。
顶层const与底层const
这里有一个顶层const和底层const的区别,正常情况下,const类型的变量可以给任何变量赋值和初始化的。但是指针就不一样了,指针本身是个对象,而其可以指向另外一个对象,所以指针本身是否是const和其所指的是不是一个常量就是两个独立的问题。
顶层const是指const修饰变量本身,即变量/对象本身是;而底层const则表示这个变量所指向/引用的值不可变,即不能通过这个变量修改其值,只有对常量的引用和指向常量的指针是底层const
int i=0;int *const r3 = &i;const int r1 = i;
以上例子是顶层const即对象本身是const的。而
const int *r4 = &i;const int &r5 = i;
则是底层const,其所指的对象是常量,即不能通过自己修改原来数的值
对于底层const而言,底层const只能赋值给同样是底层的const,即int* r6 = r4会报错。
注意下面一个问题
const int m = 0; //m本身是const类型的值const int* m2 = &m; //所以&m是一个const int*类型的,即常量取地址得到的是一个底层constint* const m3 = &m; //所以第二行没有错,第三行会报错:const int i = 0; //i是底层constint &i2 = i; //报错,因为i2不是底层const
第一个报的错误就是 “const int *” 类型的值不能用于初始化 “int *const” 类型的实体
第二个报的错误就是将 “int &” 类型的引用绑定到 “const int” 类型的初始值设定项时,限定符被丢弃
二、对于修饰函数的用法
引用限定符
先看一个神奇的语句
string s1 = "hello ";string s2 = "world!";s1+s2 = "wow!";string("adfasdf") = "123"; //注意此时一定要加string,因为C++对于常量字符串是用const char*数组保存的,
以上这三句话是可以顺利运行的,原因在于s1+s2得到的是一个右值,而string类型的对象,会重载=运算符: string &operator=(const string&)
而=运算符可以作为一个函数进行调用,而对对象的函数进行调用时是不在乎其是左值还是右值,如(s1+s2).size()肯定是可以运行的,所以上面的那个=号也是可以运行的。
还有一点需要注意就是=号返回的是一个左值,所以如果对于连等的情况a1 = a2 = a3,其从右向左一次调用=号,等价于a1 = (a2 = a3),而此时调用的是=是复制运算符,而不是移动复制运算法符,因为返回的是左值。
所以本质上来说,因为函数只能限制传入的参数类型,和返回的类型,并不能限制this本身的类型导致的。
但是现在新标准为了填旧标准的这个坑,就加入了引用限定符(reference qualifier)虽然string这个类为了像前兼容,上述写法还是可以用的,但是对于我们自己写的对象来说可以避免这种情况了。跟const类似,引用限定符是加在参数列表之后的:
Foo &Foo::operator=(const Foo&) &; //this此时只能是可修改的左值Foo &Foo::operator=(const Foo&) &&; //this此时只能是右值
在函数声明结尾加入&和&&来限制this本身。所以此时再重载=运算符,其后加上&,就能避免之前的那种情况了。
还有几点需要注意:
- const和&都只能用来修饰类的非static成员函数,在函数的声明和定义中都要出现
- 一个函数可以右const和&同时修饰,但是此时const必须加在&之前
const限定符
这个其实没啥说的,就是在把const加在参数列表之后。
- 常量函数只能调用常量函数。
- 常量对象只能调用其中的常量函数
这里加一个const和#define用法上的区别
- const便于进行类型检查:const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误
可以节省空间,避免不必要的内存分配:const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象
#define
一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝更多const的用法总结详见这篇文章:关于C++ const 的全面总结
- C++中const和引用修饰变量和函数的总结
- [C++] C++中const修饰指针,变量, 函数参数和函数返回值的用法总结
- c语言变量和函数声明的修饰符static,extern,#define,const
- 修改C中const修饰的变量
- C语言:变量和函数引用的总结
- C/C++中static 修饰变量和修饰函数时的不同
- C/C++中static 修饰变量和修饰函数时的不同
- const修饰指针和引用的用法
- const修饰指针和引用的用法
- const修饰指针和引用的用法
- const修饰指针和引用的用法
- const修饰指针和引用的用法
- C/C++中关于地址、指针和引用变量的学习笔记(六) : const和void
- const修饰变量的总结
- c中const和c++中const的学习总结
- C++中有关const修饰指针和变量
- 黑马程序员—C学习笔记—static和const修饰的全局与局部变量
- c语言中const修饰指针变量
- Android中使用GridView和ImageViewSwitcher实现电子相册简单功能
- KeyListener的一些问题
- Handler的正确使用,使用静态内部类+虚引用,解决Context泄漏
- 线程分离
- 每日总结 (2016-12-05)
- C++中const和引用修饰变量和函数的总结
- UNIX系统编程复习笔记 一
- 商人小鑫
- 大学感悟【八】
- xutils
- eclipse Mac安装了tomcat7.0,启动后在window-> preferences选项中找不到tomcat项
- TODO:字节的那点事Go篇
- Android点击展示/收起更多详情+动画效果 Value
- 《编程之美》-- 让CPU占用率听你指挥