const 总结(绝对全面)

原文地址为: const 总结(绝对全面)

第一章   概念篇  

 1...  概述

     有时我们需要定义一种变量,它的值不能被改变。例如,用一个变量来表示缓冲区的大小。使用变量的好处是当我们觉得缓冲区大小不再合适时,很容易对其进行调整。另一方面,也应随时警惕防止程序一不小心改变了这个值。为了满足这个要求,可以用关键字const 对变量的类型加以限制:

const int bufSize =300;
这样就把bufSzie定义成了一个常量,任何试图(定义之后)改变bufSize值的行为都将引发错误。

因为const对象一旦创建后其值就不能改变,所以const对象必须初始化

	const int i = getSize();		//正确 运行时初始化
const int j=42; //正确:编译时初始化
const int k; //错误 k是一个未经初始化的常量

   2.. 初始化和const

    与非const类型所能参与的操作相比,const类型的对象能完成其中大部分,主要的限制就是只能在const类型的对象上执行不改变其内容的操作。如普通的算术运算(加减乘除)。在不改变const对象的操作中还有一种是初始化,如果利用一个对象去初始化另一个对象,则它们是不是const都无关紧要:

	int i=42;
const int ci = i; //正确 i的值拷贝给ci
int j=ci; //正确 ci的值拷贝给j

理解:拷贝一个对象的值并不会改变它,一旦拷贝完成,新的对象就和原来的对象没什么关系。

小结:可以用普通变量去初始化

      默认状态下,const对象仅在文件内有效。类似于宏定义,当以编译时初始化的方式定义一个const对象时(如1小节中的变量j),编译器将在编译过程中把用到该变量的地方都替换成对应的值。为了执行这种替换,编译器必须知道变量的初始值。如果程序包含多个文件,则每个用了const对象的文件都必须能访问到它的初始值才行。则:必须在每个用到变量的文件中都有它的定义,所以为支持这一用法,同时避免对同一对象的重复定义,默认情况下,const对象被设定为仅在文件内有效。当多个文件中出现同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。

    当然,另一种将const对象在文件中共享的方法是在某个文件中加上extern关键词定义它,然后在其他文件中也用extern关键词声明此对象:

	// file_1  文件一:定义并初始化
extern const int bufSize =100;
//file_2 文件二:声明
extern const int bufSize;
    3... const的引用

       3.1    可以把引用绑定到const对象上,就像绑定到其他对象上一样,我们称之为对常量的引用: 对常量的引用不能被用作修改它所绑定的对象:

	const int ci=100;
const int &r_ci=ci; //正确 引用r_ci及其绑定的对象都是常量
r_ci =10; //错误 r_ci是对常量的引用(别名) 不能修改常量的值
int &r_ci1 = ci; //错误:试图用一个非常量引用绑定一个常量对象
 这里要注意常量引用的定义,假设上面第四条语句正确,则可以通过r_ci1来改变它所引用对象的值(ci的值),这明显不对。

      3.2    在初始化常量引用时允许用任意表达式作为其初始值,只要该表达式的结果能转换成引用的类型即可。尤其,允许为一个常量引用绑定非常量的对象、字面值,甚至是一个表达式:

	int i =4;
const int &r1 = i; //允许将const int &绑定到普通int对象上
const int &r2 = 42; //r2 是一个常量引用(绑定在字面值上)
const int &r3 = r1*2; //r3 是一个常量引用(绑定在表达式上)
int &r4 = r1*2; //错误:r4是一个普通的非常引用
    要理解这几条语句,需弄清楚当一个常量引用被绑定到另一种类型上时到底发生了什么:

	double dval=3.14;
const int &ri=dval;
此处ri引用了一个int型的数,对ri的操作应该是整数运算,但dval确实一个double型。因此,为确保让ri绑定一个整数,编译器把上述代码变成:

	const int temp = dval;  //由double型生成一个临时的整型常量
const int &ri=temp; //让ri绑定这个临时量

即:常量引用ri实际上是绑定在一个临时量上。程序猿既然让ri引用dval,就肯定像通过ri改变dval的值,否则干啥要给ri赋值呢?所以,既然大家基本上不会想着把引用绑定到临时量是,C++也就把这种行为归为非法。就如上面的:r4。

  还有一点: i 是普通变量,可以任意改变其值,r1是对i的常量引用,不可以通过r1改变i的值。相当于r1在定义时自己给自己加了约束,这不关它本身所绑定的普通对象i什么事。这点需要理解。

小结:可以使用非常量初始化一个底层const对象(指针所指对象是常量),但是反过来不行。

   4.. 指针和const
       4.1   与引用一样,也可以令指针指向常量或非常量。类似于常量引用,指向常量的指针不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针。

	const double pi =3.14;
double *ptr = &pi ; //错误 ptr是一个普通指针
const double *cptr = π //正确 cptr是一个指向double型的常量指针
*cptr = 42; //错误 不能给*cptr赋值
   与常量引用类似,允许指向常量的指针指向一个非常量。所谓指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定该对象的值不能通过其他途径改变。
	double dval = 3,14;	cptr =&dval;		//正确 但是不能通过cptr改变dval的值
      4.2   const指针

      指针是对象而引用不是,因此允许指针本身定为常量(常量指针)常量指针必须初始化,而且一旦初始化完成,则它的值(存放在指针中的那个地址)就不能再改变了。把*放在const关键字之前用以说明指针本身是个常量:

	int errNum =0;
int *const curErr = &errNum; // curErr 一直指向errNum
const double pi =3.14;
const double *const pip =π // pip 是一个指向常量对象的常量指针
  5... 顶层const与底层const

    由于指针是一个对象,所以指针本身是不是常量以及指针所指的对象是不是常量是两个独立的问题。顶层const表示指针本身是个常量;底层const表示指针所指的对象是一个常量。更一般的,底层const可以任意对象为常量;底层const则与指针和引用等复合类型的基本类型有关。

6...  总结:可以使用非常量初始化一个底层const对象(指针所指对象是常量),但是反过来不行(3.2);

              一个普通的引用必须用同类型的对象初始化(3.1);

               可以用非常量对象去初始化顶层const(对象本身是常量)对象,此时初始化工作会忽略顶层const。(2 const和初始化)

第二章 应用篇

 1... const形参和实参

    由第一章得到:可以使用非常量初始化一个底层const对象,但是反过来不行;同时一个普通的引用必须用同类型的对象初始化。

   把函数不会改变的形参定义为普通的引用会引起一种常见的错误,这么做带给函数的调用者一种误导,即函数可以修改它的实参的值。此外,使用引用而非常量引用也会极大的限制函数所能接受的实参类型:不能把const对象、字面值或者需要普通类型转换的对象传递给普通的引用形参。

   所以:对于不改变形参内容的函数,尽量使用常量引用类型。



转载请注明本文地址: const 总结(绝对全面)

猜你喜欢

转载自blog.csdn.net/kkwant/article/details/80867585