一.引用
c++11新增加了一种右值引用,这种主要用于内置类。我们这里都指的是左值引用。简单理解:当一个对象被用作右值的时候用的是对象的值(内容),用作左值时用的是对象的身份(内存中的位置)。
引用相当于为对象起了另一个名字,通过声明&d来定义引用,其中d是声明的变量。
int val=10;
int &refval=val;
注意:引用必须被初始化 . int &val; 就是错误的.
变量初始化时,初始化的值会被拷贝到新对象中,定义这个对象的一个引用只是把它和原本的初始值绑在一起,不是把初始值拷贝给对象,且无法令引用再重新绑定到另一个对象。所以可以通过引用修改原本的对象。
用引用作为初始值实际就是用引用绑定的对象作为初始值。下面两种初始化都是正确的:
refval3绑定到了变量val上,refval2=10
int refval2=refval;
int &refval3=refval;
但是由于引用本身不是一个对象,所以不能定义引用的引用。
所有引用都要和绑定的对象严格匹配,不过有两种例外情况:
- 1.初始化常量引用时允许用任意表达式作为初始值 例如:const int & r1=42;
- 2.可以将基类的指针或引用绑定到派生类对象上。
int val=10;
double &val2=val;//错误,类型不一致
二.指针
指针(pointer)是“指向( point to)”另外一种类型的复合类型。与引用类似,指针也实现了对其他对象的间接访问。然而指针与引用相比又有很多不同点。
- 其一,指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。
- 其二,指针无须在定义时赋初值。和其他内置类型一样,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值。
指针的定义:
定义指针类型的方法将声明符写成*d的形式,其中d是变量名。如果在一条语句中定义了几个指针变量,每个变量前面都必须有符号*:
int *ip1,*ip2;
用指针获取对象地址
int ival=42;
int *p=&ival;
这里把p定义为一个指向 int 的指针,随后初始化p令其指向名为ival的int对象。因为引用不是对象,没有实际地址,所以不能定义指向引用的指针。
指针类型要和它指向的对象类型严格匹配。
下面的pd和pd2都指向的是dval;
double dval;
double *pd=&dval;
double *pd2=pd;
int *i=pd; //错误,类型不匹配
但有两种例外:
- 1.允许一个指向常量的指针指向一个非常量对象。
- 2.可以将基类指针绑定到派生类对象上。
指针值的4种状态:
1.指向一个对象。
2.指向紧邻对象所占空间的下一个位置。
3.空指针,意味着指针没有指向任何对象。
4.无效指针,也就是上述情况之外的其他值。
利用指针访问对象
通过解引用符(*)访问对象。
int ival=4;
int *p=&ival;
cout<< *p;
&和*操作符能作为声明也能作为表达式的运算符。

空指针
生成空指针三个方法:
int *p1 = nullptr;
int *p2 = 0;
int *p3 = NULL;/需要首先#include cstdlib
指针用于条件表达式和参与运算
如果指针的值是0,条件取false。任何非0指针对应的条件值都是true。
int ival = 1024;
int *pi = 0;//pi合法,是一个空指针
int *pi2 = &ival;//pi2是一个合法的指针,存放着ival的地址
if (pi)//pi的值是0,因此条件的值是false
//...
if (pi2)//pi2指向ival,因此它的值不是0,条件的值是true
/ / ...
对于两个类型相同的合法指针,可以用相等操作符(==)或不相等操作符(!=)来比较它们,比较的结果是布尔类型。如果两个指针存放的地址值相同,则它们相等;反之它们不相等。这里两个指针存放的地址值相同(两个指针相等)有三种可能:它们都为空、都指向同一个对象,或者都指向了同一个对象的下一地址。需要注意的是,一个指针指向某对象,同时另一个指针指向另外对象的下一地址,此时也有可能出现这两个指针值相同的情况,即指针相等。
void *指针
void是一种特殊的指针类型,可用于存放任意对象的地址。一个void指针存放着一个地址,这一点和其他指针类似。不同的是,我们对该地址中到底是个什么类型的对象并不了解。
double i=3.14;
void *pv=&i;
复合类型声明
变量的定义包括一个基本数据类型和一组声明符。在同一条定义语句中,虽然基本数据类型只有一个,但是声明符的形式却可以不同。也就是说,一条定义语句可能定义出不同类型的变量:
//i是一个int型的数,p是一个int型指针,r是一个int型引用
int i =1024,*p = &i, &r = i;
p1是指向int的指针,p2是int
int* p1,p2;
指向指针的指针
通过*的个数可以区分指针的级别。也就是说,**表示指向指针的指针,***表示指向指针的指针的指针,以此类推:
int ival = 1024;
int *pi = &ival;
int **ppi = π.
pi指向一个int型的数.ppi指向一个int型的指针

指向指针的引用
注意:引用本身不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用
int i =42;
int *p; p是一个int型指针
int *&r= p; r是一个对指针p的引用
r = &i ; r引用了一个指针,因此给r赋值&i就是令p指向i
*r = 0 ; 解引用r得到i,也就是p指向的对象,将i的值改为0
要理解r的类型到底是什么,最简单的办法是从右向左阅读r的定义。离变量名最近的符号(此例中是&r的符号&)对变量的类型有最直接的影响,因此r是一个引用。声明符的其余部分用以确定r引用的类型是什么,此例中的符号*说明r引用的是一个指针。最后,声明的基本数据类型部分指出r引用的是一个int指针。
三.const限定符
const定义的变量是不能被修改的,只能在const类型的对象上执行不改变其内容的操作。
在不改变const对象的操作中还有一种是初始化,如果利用一个对象去初始化另外一个对象,则它们是不是const都无关紧要
int i = 42;
const int ci = i;//正确:i的值被拷贝给了ci
int j = ci;/ /正确: ci的值被拷贝给了j
默认状态下,const对象仅在文件内有效
如果程序包含多个文件,则每个用了const对象的文件都必须得能访问到它的初始值才行。所以必须在每一个用到变量的文件中都有对它的定义。为了支持这一用法,同时避免对同一变量的重复定义,默认情况下,const对象被设定为仅在文件内有效。当多个文件中出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。
解决的办法是,对于const变量不管是声明还是定义都添加extern关键字,这样只需定义一次就可以实现多个文件之间共享。
// file_1.cc定义并初始化了一个常量,该常量能被其他文件访问extern const int bufSize = fcn () ;
// file_l.h头文件
extern const int bufSize; //与file_1.cc中定义的bufSize是同一个
对const的引用------常量引用
把引用绑定到const对象上称为对常量的引用(reference to const)。与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象:
const int ci =1024;
const int &rl = ci;//正确:引用及其对应的对象都是常量
rl =42;//错误:r1是对常量的引用
int &r2 = ci;//错误:试图让一个非常量引用指向一个常量对象
初始化
允许将常量引用绑定到非常量对象
double dval = 3.14;
const int &ri = dval;
此处ri引用了一个int型的数。对ri的操作应该是整数运算,但dval却是一个双精度浮点数而非整数。因此为了确保让ri绑定一个整数,编译器把上述代码变成了如下形式:
const int temp = dval;//由双精度浮点数生成一个临时的整型常量
const int &ri = temp;// 让ri绑定这个临时量
在这种情况下,ri 绑定了一个临时量( temporary)对象。
常量引用仅对引用可参与的操作做出了限定,对于引用的对象本身是不是一个常量未作限定。因为对象也可能是个非常量,所以允许通过其他途径改变它的值
int i = 42;
int &r1 = i ;//引用ri绑定对象i
const int &r2 = i;// r2也绑定对象i,但是不允许通过r2修改i的值
r1 = 0 ;//r1并非常量,i的值修改为0
r2 = 0;//错误:r2是一个常量引用
四.指针和const
常量指针和指向常量的指针
指向常量的指针是令指针指向常量,不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针。
允许令一个指向常量的指针指向一个非常量对象。
const double pi = 3.14;// pi是个常量,它的值不能改变
double *ptr = π//错误:ptr是一个普通指针
const double *cptr = π//正确:cptr可以指向一个双精度常量
* cptr = 42;//错误:不能给*cptr赋值
和常量引用一样,指向常量的指针也没有规定其所指的对象必须是一个常量。所谓指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其他途径改变。
double dval = 3.14;// dval是一个双精度浮点数,它的值可以改变
cptr = &dval;//正确:但是不能通过cptr改变dval的值
常量指针是把指针本身定为常量。常量指针(const pointer)必须初始化,而且一旦初始化完成,则它的值(也就是存放在指针中的那个地址)就不能再改变了。把*放在const关键字之前用以说明指针是一个常量,这样的书写形式隐含着一层意味,即不变的是指针本身的值而非指向的那个值。
int errNumb = 0;
int *const curErr = &errNumb;//curErr将一直指向errNumb
const double pi = 3.14159;
const double *const pip = π// pip是一个指向常量对象的常量指针
指针本身是一个常量并不意味着不能通过指针修改其所指对象的值,能否这样做完全依赖于所指对象的类型。
*pip = 2.72;//错误:pip是一个指向常量的指针
*curErr = 0;//正确:把curErr所指的对象的值重置
顶层const和底层const
用名词顶层const( top-level const)表示指针本身是个常量,而用名词底层const (low-level const)表示指针所指的对象是一个常量。
指针类型既可以是顶层const也可以是底层const
int i= 0 ;
int *const pl = &i;//不能改变p1的值,这是一个顶层const
const int ci = 42;//不能改变ci的值,这是一个顶层const
const int *p2 = &ci;//允许改变p2的值,这是一个底层 const
const int *const p3 = p2;//靠右的const是顶层const,靠左的是const
const int &r = ci;//用于声明引用的const都是底层 const
当执行对象的铂贝操作时,常量是顶层const还是底层const区别明显
1.顶层const不受影响
i = ci;//正确:拷贝ci的值,ci是一个顶层const,对此操作无影响
p2 = p3;//正确:p2和p3指向的对象类型相同,p3顶层 const的部分不影响
2.底层const限制
当执行对象的拷贝操作时,拷入和拷出的对象必须具有相同的底层 const资格,或者两个对象的数据类型必须能够转换。一般来说,非常量可以转换成常量,反之则不行:
int *p= p3;//错误:p3包含底层const的定义,而p没有
p2 = p3;//正确:p2和p3都是底层const
p2=&i ;//正确:int*能转换成const int*
int &r = ci;//错误:普通的int&不能绑定到int常量上
const int &r2 = i;//正确:const int&可以绑定到一个普通int 上
本文详细讲解了C++11中的右值引用概念,以及指针的定义、初始化、不同类型指针的使用,常量限定符和const引用的区别,以及指针与const的结合。重点阐述了引用与指针的不同之处,如引用不可再绑定,而指针可以。
2618

被折叠的 条评论
为什么被折叠?



