1. 引用
引用即绑定到另一个对象上,因此定义了一个引用,对其操作即是对与之绑定的对象进行操作(如为引用赋值,获取引用的值,以引用作为初始值...)。
- 定义引用时是绑定对象,因此必须在定义引用同时必须初始化。
- 引用本身不是对象,只是另一个对象的别名,因此不能定义引用的引用,也不能定义引用的指针。
- 引用本身非对象,因此不能进行赋值和拷贝,一旦定义了引用,其只能绑定这个对象,无法再绑定别的对象。但一个对象可以定义多个引用。
- 引用的类型要与之绑定的对象严格匹配(不支持类型转换),并且引用只能绑定在变量/对象上,不能绑定字面值或者表达式上(即形参为string &str;时,必须使用变量初始化,“hello word”初始化将编译出错)。——两个例外情况
2. 指针
与引用类似,实现对对象的间接访问。
- 指针允许在定义时进行初始化,此时将执行默认初始化。
- 指针本身是对象,因此可以定义指针的引用,可以对指针赋值和拷贝(对指针赋值即是存放新的地址,指向新的对象),因此在其生命周期内可以先后指向不同对象。并且一个对象可以定义多个指针。
- 初始化为空指针,即不指向任何对象。(int *p=0;或者int *p=nullptr;均可以。 但是int i=0; int *p=i; 不是初始化空指针语句。)
- 指针类型和它指向的对象类型必须严格匹配(不支持类型转换),并且指针只能指向变量/对象,不能指向字面值或者表达式。——两个例外情况
指针和引用的本质区别:
引用不是对象,指针本身即是对象。
指针和引用的共同:
都需要类型和对象类型严格匹配(不支持类型转换),并且指针只能指向变量/对象,不能指向字面值或者表达式。同时,也都对常量指针和常量引用出现例外情况,即这两种情况下可以使用一般的初始化。
指针值(前三种有效):
- 1.指向一个对象;
- 2.指向末尾的下一个位置;
- 3.空指针,即指针不指向任何对象;
- 4.无效指针。
注意:
1、试图拷贝或者以其他形式访问一个无效指针将引发错误,访问无效指针的后果无法预计,并且编译器不检查此类错误,在设计程序时,必须清楚所有指针的有效性;
2、第2和3种指针虽然有效,但并不指向任何具体对象,因此访问此类指针的对象也不允许。
因此:建议初始化所有指针,并且尽量在定义了对象之后,再定义对象的指针。如若不清楚指针的指向,可以暂时初始化为nullptr或0 ,此时编译器即可以知道所有指针的指向。
指针作为条件判断:
前提:指针有效。
如if(p)... 空指针,条件为0;其他有效指针,条件为1 。
如if(*p)... 表示指针指向的对象作为条件判断。
比较两个指针是否相等。相等情况有三:都为空指针;都指向同一对象;都指向同一对象的下一个地址。
3. const 限定符
普通类型限定:
const只是个限定符,限定了不能改变其值,此限定只在要改变const变量的值时才起作用。
- const对象一旦创建即不能改变其值,因此const对象必须在定义时初始化(普通初始化——直接、隐式、表达式、常值、变量)
- const限定只在改变其值时限制,对其他操作并不影响。因此和普通初始化相同,可以用变量、常量初始化,也可以进行隐式地类型转换。同时也可以拷贝const对象,并赋值给常量对象或者变量对象(因为对变量的拷贝不会改变对象的值,赋值会改变对象的值)。
- 即使用常量初始值代替const对象
- 多文件使用const对象,需要在声明和定义中都加extern关键字
3.1 const 与引用
const int ci=2017;
const int &ri=ci;
- 对常量的引用可以绑定常量和非常量,但是const限定了 不能通过引用改变其绑定对象的值,但可以通过别的方式改变对象的值(直接给对象赋值,或者定义一个新的普通引用)。
- 当然,无论是加不加const,都可以使用解引用或者用引用直接赋值等普通操作(如 int i1 = 0; const int &ci = i1; int i2 = ci; 可以用常量引用给普通变量赋值)。
引用中的一种例外:
- 对常量的引用,在初始化时允许使用任意表达式作为初始值,只要该表达式能转换成引用的类型(常量int)即可。——也即可以进行普通初始化,即 允许常量引用绑定 非常量的对象、字面值或者一般表达式,且允许发生隐式转换。
- (因为 对常量的引用 只是限定了不能通过引用改变绑定对象,并没有限定对象本身是不是一个常量,也没有限定不能通过其他方式改变对象的值)
注释:
对常量的引用可以普通初始化,可以绑定非常量,但常量对象必须用对常量的引用绑定(但常量对象可以赋值给常量或变量对象)。
3.2 const 与指针
- 指针可以指向常量和非常量,但是const限定 不能通过指针改变其所指对象的值,但可以通过别的方式改变对象的值。
- 当然,无论是加不加const,都可以使用解引用等普通操作,但不能把存放了 指向常量的指针 赋给另一个普通指针(int i = 0; const int *pi1 = &i; int *pi2 = pi1;)。
指针中的一种例外:
- 指向常量的指针,可以指向一个非常量对象。
- (因为 指向常量的指针 只是限定了不能通过指针改变对象的值,但没有规定对象本身是不是一个常量,也没有限定不能通过其他方式改变对象的值)
注释:
指向常量的指针 可以指向非常量(普通初始化),但要存放常量的地址必须使用 指向常量的指针。
指针和引用对于const的区别:常量指针(也是普通类型限定)
(1)指针是对象,引用不是对象,因此允许把指针定义为常量,即常量指针。
- 常量指针必须初始化,初始化完成后,不能改变指针的值,即不能改变指针的指向地址。(和普通限定下的对象一样,可以用常量指针赋值给常量或变量指针,且是普通赋值)
- 变量地址存储为常量指针 int errNumb=0; int *const curErr=&errNumb;
- 常量地址存储为指向常量的常量指针 const double pi=3.14; const double *const pip =π
- 而能不能改变常量指针指向的变量的值,完全依赖于对象的类型。
- 因此引用“ 无论是加不加const,都可以使用用引用实现直接赋值等普通操作,因为此时相当于使用了对象(如 int i1 = 0; const int &ci = i1; int i2 = ci;编译通过, 可以用常量引用给普通变量赋值)。”
- 而指针“ 无论是加不加const,都可以使用解引用等普通操作,但不能把存放了 指向常量的指针 赋给另一个普通指针(int i = 0; const int *pi1 = &i; int *pi2 = pi1;编译出错,不能用 const int * 初始化 int *)。”
注释:
对const的引用和指向常量的指针都是对引用和指针的限定,不能通过引用或指针方式改变对象的值。
而常量指针,const限定的是变量类型,和前边的const限定符一样,即不能改变变量的值,在常量指针下,即是不能改变指针的指向。
3.3 顶层const和底层const:
实际上是对 非常量引用、非常量指针、普通类型限定(包括常量指针)、对const的引用、指向const的指针 初始化和赋值的总结。
但是对于引用和指针的使用,不通过绑定或指针改变对象的值,因此可以进行普通赋值和解引用操作 或 其他改变对象值的操作。(如 const int & ci=i1; int i2=ci; 此处是使用引用操作,而不是绑定常量对象)
- 顶层const:作用于对象本身的 const,即对变量const;对指针本身const(常量指针)
- 底层const:对引用和指针的限定
- 顶层的const作用于对象本身,即是普通的类型限定,因此定义时必须初始化,且支持普通初始化——直接、隐式、表达式、常值、变量,也即支持用顶层const对象初始化。而顶层const当然可以作为被拷贝对象,因为拷贝不会改变对象的值(如 const int i1= 's' ; int i2=i1;)。
- 底层const作用于引用和指针限定,即是 对常量的引用 和 指向常量的指针。因此常量对象只能用 对常量的引用 或者 指向常量的指针 绑定或者存储;而 对常量的引用 和 指向常量的指针 可以 绑定或者指向 非常量对象(即引用和指针的例外情况),但非常量引用或指针不能绑定或指向常量对象(因为引用和指针要求类型严格匹配)。