C++引用

简介

引用为对象起了另外一个名字,引用类型引用另外一种类型。通过将声明符写成&d的形式来定义引用类型,其中d是声明的变量名。
因为引用本身不是一个对象,所以不能定义引用的引用。
指向指针的引用
引用本身不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用。

定义
int a=10,b=20;
double c = 3.14;
int d[10] = { 0,1,2,3,4,5,6,7,8,9 };

int &rx1 = 30;      //错误:引用类型的初始值必须是一个对象
int &rx2 = c;       //错误:此处的引用类型必须是int型对象
int &rx3 = a;       //正确:定义引用rx3,将rx3绑定到a,rx3是a的别名
&rx4 = b;           //错误:表达式必须是可修改的左值
int &rx5 = rx3;     //正确:将rx5绑定到a
int &&rx6 = rx3;    //错误:无法将右值声明绑定到左值,&&为右值引用而不是对引用的引用
typedef int& rint;
rint &rx7 = rx3;    //错误:rx7为未声明的标识符,即无法定义对引用的引用
int &rx8[10] =/* ?*/;   //错误:不存在引用的数组
int(&rx9)[10] = d;  //正确:rx9引用一个含有10个整数的数组
说明:
  • &在此不是求地址运算,而是起标识作用。
  • 类型标识符是指目标变量的类型。
  • 定义引用时,程序吧引用和它的初始值绑定在一起,而不是拷贝给引用。
  • 因为无法令引用重新绑定到另一个对象,声明引用时,必须同时对其进行初始化。
  • 引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
    rx3=1; 等价于 a=1;
  • 无法将引用绑定到不同类型的对象上,也无法绑定到字面值上。
  • 声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&rx3与&a相等。
  • 由于引用不是数据类型,指示一个别名,所以无法建立引用的数组,但是可以建立对数组的引用。

使用

const的引用

与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象。

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

Int i=1;
const int &r1=I;
const int &r2=1;//r2如果是非常量则不能这么引用
const int &r3=r1*2;

对const的引用可能引用一个并非const的对象。
常量引用对引用可参与的操作做出了限定,对于引用对象本身是不是一个常量未做限定。也就是可以通过直接改变所引用对象的值来改变引用的值。

  • 引用没有自身,只能是别人(引用是别名)。所以引用只能是底层const。
  • 作为只能是底层const的引用,它无法修改它所指向的对象的内容,但同时由于引用本身的特性限制了它一旦绑定了一个值再无法修改,所以它也无法修改它所指向的对象。这就是引用作为底层const的同时还保留了顶层const的特质的原因。
decltype

decltype处理顶层const和引用的方式与auto有些许不同。如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用在内)。
需要指出的是,引用从来都作为其所指对象的同义词出现,只有用在decltype处例外。
decltype和引用
如果decltype使用的表达式不是一个变量,则decltype返回表达式结果对应的类型。
如果表达式的内容是解引用操作,则decltype将得到引用类型。
如果decltype使用的是一个不加括号的变量,则得到的结果就是该变量的类型;如果给变量加上了一层或多层括号,编译器就会把它当成是一个表达式。变量是一种可以作为赋值语句左值的特殊表达式,所以这样的decltype就会得到引用类型。

vector操作

如果对于vector对象v,v[n] 返回v中第n个位置上元素的引用。

迭代器

*iter返回迭代器iter所指元素的引用。

使用范围for语句处理多维数组

size_t cnt=0;
for(auto &row:ia)
    for(auto &col:row){
        col=cnt;
        ++cnt;
}

上面的例子中,因为要改变数组元素的值,所以我们选用引用类型作为循环控制变量,但其实还有一个深层次的原因促使我们这么做,例如如下循环:

for(const auto &row:ia)
    for(auto col:row)
        cout<<col<<endl;

这个循环我们将外层循环的循环控制变量声明成了引用类型,这是为了避免数组被自动转成指针。假设不用引用类型,则循环如下形式:

for(auto row:ia)
    for(auto col:row)

程序将无法通过编译:row是指针,当然无法进行遍历了。

隐式类型转换

数组转换成指针:当数组被用作decltype关键字的参数,或者作为取地址符(&),sizeof及typeid等运算符的运算对象时,转换不会发生。如果用一个引用来初始化数组,转换也不会发生。
转换成常量:如果T是一种类型,我们就能将指向T的指针或引用分别转换成指向const T的指针或引用。相反的转换并不存在,因为它试图删除掉底层const。

传引用参数

使用引用避免拷贝
拷贝大的类类型对象或者容器对象比较低效,甚至有的类类型(包括IO类型在内)根本就不支持拷贝操作。当某种类型不支持拷贝操作时,函数只能通过引用形参访问该类型的对象。
尽量使用常量引用
我们不能把const对象、字面值或者需要类型转换的对象传递给普通的引用形参。所以为了使用更加灵活,形参尽量使用常量引用而非普通引用。
数组的引用形参
在形参是数组的引用中,维度是数组引用类型的一部分,所以数组的引用形参可以用来限制传入数组实参的长度。

函数重载

如果形参是某种类型的指针或引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载,此时的const是底层的。

函数的返回值

不要返回局部对象的引用或指针。
函数完成后,它所占用的存储空间也随之被释放掉。因此,函数终止意味着局部变量的引用将指向不再有效的内存区域。
引用返回左值
调用一个返回引用的函数将得到左值,其他返回类型得到右值。我们能为返回类型是非常量引用的函数的结果赋值。
返回*this的成员函数
返回*this的函数是返回的引用,是左值的。意味着这些函数返回的是对象本身而非对象的副本。比如move和set两个函数都返回*this,则我们可以将他们连起来这么写:

myScreen.move(4.0).set(‘#’);

加入当初我们另一的返回类型不是引用,则move的返回值将是*this的副本,因此调用set只能改变临时副本,而不能改变myScreen的值。
从const成员函数返回*this
一个const成员函数如果以引用的形式返回*this,那么它的返回类型将是常量引用。

转载请注明出处:http://blog.csdn.net/ylbs110/article/details/50866656

总结

  • 在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不如意的问题。
  • 用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,且通过const的使用,保证了引用传递的安全性。
  • 引用与指针的区别是,指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
  • 使用引用的时机。流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。

推荐阅读:http://www.cnblogs.com/Mr-xu/archive/2012/08/07/2626973.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值