1、C语言中const与 C++中的const
void main() { const int a = 10; int *p = (int*)&a; *p = 20; printf("%d", a); }
比较上述代码在C中和C++中运行结果:C:打印20;C++:打印0;
由此可见,C语言中的const是个“冒牌货”,C++中
的const是一个真正的常量。
原因:C语言直接为a分配了内存。C语言中的const变量是只读变量,有自己的存储空间。而C++编译器对const的处理是:当碰见常量声明时,在“符号表”(C++编译器专门设置的)中放入常量,在使用a的时候直接在符号表中取值!
那么问题是那又如何解释取地址呢?
A.编译过程中若发现使用常量则直接以符号表中的值替换;
B.编译过程中若发现对const使用了extern(需要在其他文件中使用)
或者 &操作符,则给const常量另外再专门分配存储空间。
如上图所示,对const变量取地址后,为“20”单独专门分配了空间,而与符号表中的a没有关系。
2、const与基础数据类型:
const int bufsize = 512;bufsize的值不能再被修改,任何试图为bufsize赋值的行为都将引发错误。因为const的对象一旦创建后其值不能再改变,所以const对象必须初始化。 int i=12;const int ci=i;还可以利用另外一个对象(是不是const无关紧要)去初始化const对象。。。int j = ci;一旦拷贝完成,新的对象和原来的对象就没有什么关系了。 { int i = 50; const int j = i; //初始化 int k = j; int m = 100; //j = m; //赋值,编译错误 }
默认状态下,const对象仅在文件内有效,当多个文件中出现了同名的const对象,其实等同于在不同文件定义了独立的变量。
如果const对象需要在不同文件共享,那就需要对const对象不管是声明还是定义都添加extern关键字。
注意只能定义一次:extern const int bufsize = 512;一般被包含在头文件。
在使用时直接声明:extern const int bufsize;不能再定义,否则出错。extern就说明了bufsize并不是本文件独有,它的定义在别处。
3、const与引用
void main() { //对常量对象 只能 用 常量引用 const int ci = 100; //常量对象 const int &ri = ci; //正确:引用及其对应的对象都是常量 //ri = 100; //error:ri是对常量的引用 //int &r2 = ci; //error:将一个非常量引用指向一个常量对象,如果正确,那么将可以通过修改r2来改变ci的值,ci显然不能改变。 //常量引用 允许被 绑定到 非常量的对象、 字面值、 一般表达式 ————例外1 int i = 50; const int &CRi1 = i; //非常量的对象 const int &CRi2 = 500; //字面值 const int &CRi3 = i * 2; //一般表达式 int r4 = CRi1 * 2; //正确:CRi1的常量特征仅仅在执行改变的时候才会发生作用,拷贝赋值并不会改变,一旦拷贝完成,新的对象和原来的对象就没有什么关系了。 //int &r5 = CRi1 * 2; //error:将一个非常量引用指向一个常量对象,如果正确,那么将可以通过修改r5来改变CRi1的值,CRi1显然不能改变。 /* * * * * * * * * *一般来说,引用的类型必须与其所引用对象的类型一致。 *当一个常量引用被绑定到另外一种类型上到底发生了什么? * double d = 3.14; const int &ri = d; * const int tmp = d; // 由双精度浮点数生成一个临时的整型常量 const int &ri = d; // 让ri绑定这个临时的常量 * * * * * * * * */ //常量引用仅仅对自己可参与的操作做了限定,如果引用的对象是个非常量,那么允许通过其他途径改变它的值 int j = 10; int &rj = j; //引用rj 绑定 对象j const int &cj = j; //常量引用cj 也绑定 对象j,但是不允许通过cj 去修改j的值 rj = 1000; //通过rj 修改j,j被修改为1000 //cj = 1000; cout << "j: " << j << " ,cj: " << cj << endl; //j: 1000 ,cj: 1000 }
4、const与指针
所谓指向常量 的指针或者引用,不过是指针或引用“自以为是”罢了,它们自己觉得自己指向了常量,所以自觉地不去改变它所指向的对象的值。(但是常量 的指针或者引用 是可以 指向非常量的,而非常量是可以通过其他途径改变的)。
指针是对象,而引用不是!因此,指针本身是不是常量 和 指针所指的是不是一个常量 是两个相互独立的问题!
顶层const——指针本身是个常量!
底层const——指针所指的对象是个常量!
void main() { int i = 10, m = 20; const int ci = 100, cm = 200; //第一、二个意思一样,代表一个 常整形数,任何试图为a、b赋值的行为都将引发错误 const int a = 0; //必须初始化 int const b = 1; //第三个 c是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本身可以修改) const int *c1 = &ci; //int *c2 = &ci; //存放常量的地址,只能使用指向常量的指针 const int *c = &i; //*c = m; //c指向的内存数据是const的(c的自我感觉),也就是不能通过 *c “直接修改”的 c = &m; //c的值是允许修改的(底层const),修改c的指向,让c指向另外一个内存空间 cout << *c << endl; //20 //第四个 d 常指针————指针本身是个常量(指针变量(指向)不能被修改,但是它所指向内存空间可以被修改) int ii = 0; int * const d = ? //d 将一直指向 ii (顶层const),不允许修改 d 的值 *d = 250; //指针本身是个常量,并不意味着不能通过修改指针所指向的值。(依赖于所指向的对象) cout << ii << endl; // ii 被修改成250,(d指向的是一个非常量) //第五个 e 一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改) const int pi = 123; const int * const e = π //e所指的对象值 和 e自己存储的那个地址 都不能改变 }
5、const与成员函数
将const实施与成员函数的目的,是为了确认该成员函数可作用于const对象身上。两个函数如果只是常量性不同,可以被重载。
6、const与define
const修饰的常量编译期间,就确定下来。const常量是由编译器处理的,提供类型检查和作用域检查;宏定义由预处理器处理,单纯的文本替换。
void fun1() { #define a 10 const int b = 20; //#undef a //将宏定义a卸载,限制作用域 } void fun2() { printf("a = %d\n", a); //printf("b = %d\n", b); //const定义的b是有作用域的 }
7、const与constexpr
constexpr:常量表达式,指的是值不会改变并且在编译过程中就能得到计算结果的表达式。由数据类型和初始值共同决定。
const int max = 100; //max是常量表达式 const int limit = max + 1; //limit是常量表达式 int size = 50; //size不是常量表达式,只一个普通的int类型 const int sz = get_size(); //sz本身是个常量,但它的值直到运行时才能确定,所以不是常量表达式 一般说来,如果你认定变量是一个常量表达式,那就把它声明成constexpr类型。 const int sz = get_size(); //只有get_size()是一个constexptr函数时,才是一条正确的声明语句。
8、const与auto
auto一般会忽略掉顶层const,同时底层const则会保留。
void main() { //auto一般会忽略掉顶层const,同时底层const则会保留。 int i = 10; const int ci = i; const int &cr = ci; auto b = ci; //b是一个int(ci的顶层const特性被忽略) auto c = cr; //c是一个int(cr是ci的别名,ci的顶层const特性被忽略) auto d = &i; //d是一个整型指针(整数的地址就是指向整数的指针) auto e = &ci; //e是一个指向整型常量的指针(对常量对象 取地址 是一种底层const) //如果希望推断出的auto类型是一个顶层const,则需明确指出 const auto f = ci; //ci的推演类型是int,f是const //还可以将引用设为auto,原来的初始化规则仍然适用 auto &g = ci; //g是一个常量引用,绑定到ci //auto &h = 42; //error:显然不能为一个字面值绑定非常量引用。(怎么说?h是42的引用?字面量42没有内存空间 没有办法做引用) const auto &j = 42; //正确:可以为常量引用绑定字面值 }
9、const与decltype
decltype处理顶层const和引用的方式与auto不同。如果decltype使用的表达式是一个变量,则decltype返回该变量的类型。
int i = 10; const int ci = i; const int &cr = ci; decltype(ci) x = 0; //x的类型是const int decltype(cr) y = x; //y的类型是const int&,y绑定到x //decltype(cr) z; //error:z是一个引用,必须初始化