c++中的引用详解
什么是引用?
引用是C++语言的一个特殊的数据类型描述,用于在程序的不同的部分使用两个以上的变量名指向同一块地址,使得对其中任何一个变量的操作实际上都是对同一地址单元进行的。
使用时的注意事项:
引用的特点:
1.一个变量可取多个别名。
2.引用必须初始化。
3.引用只能在初始化的时候引用一次 ,不能更改为转而引用其他变量。
4.对引用进行操作,实际上就是对被引用的变量进行操作,
5.引用仅是变量的别名,而不是实实在在地定义了一个变量,因此引用本身并不占用内存,而是和目标变量共同指向目标变量的内存地址.声明引用时,目标的存储状态不会改变
6.表达式中的取地址符&不再是取变量的地址,而是用来表示该变量是引用类型的变量。
引用的应用:
1.基础引用:
void TestReference1 ()
{
int a = 1;
int& b = a;
b=2;
cout<<"a:address->" <<&a<< endl;
cout<<"b:address->" <<&b<< endl;
//输出结果a,b都等于2 }
2.const引用:
常引用声明方式:const 类型标识符 &引用名 = 目标变量名;
用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。
#include<iostream.h>
void main(){
int a=1;
int &b=a;
b=2;
cout<<"a="<<a<<endl;//2
int c=1;
const int &d=c;
// d=2;//编译错误 error C2166: l-value specifies const object
c=2;//正确
}
这不光是让代码更健壮,也有其它方面的需求。
【例4】:假设有如下函数声明:
string foo();
void bar(string &s);
那么下面的表达式将是非法的:
bar(foo());
bar("hello world");
原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。
引用型参数应该在能被定义为const的情况下,尽量定义为const 。
3.引用作参数:
1.【值传递】
void Swap (int left, int right) {
int temp = left;
left = right ;
right = temp ;
} 2.【引用传递】
void Swap (int& left, int& right)//使用引用的话,不做临时拷贝,&的使用说明此处只是原参数的另一个名字而已,所以修改时直接在原参数的基础上修改变量值。
{
int temp = left;
right = left ;
left = temp ;
} 3.【指针传递】
void Swap (int* pLeft, int* pRight)//传入的是地址,因为地址是唯一的,所以指针通过地址的访问进而可修改其内容。
{
int temp = *pLeft;
*pLeft = *pRight;
*pRight = temp;
}
//函数参数的传递方式:值传递,地址传递,引用(c++),如何选择呢?
//看是否要修改值,要修改,用指针或者引用,不必修改,则用值传递
//对于指针和引用,看传进来的是不是地址或者时new 出来的,是则选指针,不是则选择引用
由上例可以看出:
(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用\"*指针变量名\"的形式进行运算,这很容易产生错误且程序的阅读性较差;
另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。
4. 引用作为返回值
说明:
(1)以引用返回函数值,定义函数时需要在函数名前加&
(2)用引用返回一个函数值的最大好处是,在内存中不产生被返回值的副本。
以下程序中定义了一个普通的函数fn1(它用返回值的方法返回函数值),另外一个函数fn2,它以引用的方法返回函数值。
#include <iostream>
using namespace std;
float temp;//定义全局变量temp
float fn1(float r);//声明函数fn1
float &fn2(float r);//声明函数fn2 r
float fn1(float r){//定义函数fn1,它以返回值的方法返回函数值
temp=(float)(r*r*3.14);
return temp;
} float &fn2(float r){//定义函数fn2,它以引用方式返回函数值
temp=(float)(r*r*3.14);
return temp;
} int main(){
float e=10.0;
float a=fn1(10.0);//第1种情况,系统生成要返回值的副本(即临时变量)
// float &b=fn1(10.0); //第2种情况,可能会出错(不同 C++系统有不同规定)
/*error: invalid initialization of non-const reference of type 'float&' from an rvalue of type 'float'
*/
//不能从被调函数中返回一个临时变量或局部变量的引用
float c=fn2(10.0);//第3种情况,系统不生成返回值的副本
//可以从被调函数中返回一个全局变量的引用
float &d=fn2(10.0); //第4种情况,系统不生成返回值的副本
e=d; cout<<"a="<<a<<",c="<<c<<",d="<<d<<",e="<<e<<endl;
//a=314,c=314,d=314
return 0;
}
引用作为返回值,必须遵守以下规则:
(1)不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。
(2)不能返回函数内部new分配的内存的引用。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
(3)可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
(4)引用与一些操作符的重载:流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << \"hello\" << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。
引用虽方便,使用须谨慎:
(1)&在这里不是求地址运算,而是起标识作用。
(2)类型标识符是指目标变量的类型。
(3)声明引用时,必须同时对其进行初始化。
(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
(6)不能建立引用的数组。因为数组是一个由若干个元素所组成的集合,所以无法建立一个由引用组成的集合,但是可以建立数组的引用。
(7)引用常见的使用用途:作为函数的参数、函数的返回值。
* 引用和指针的区别和联系(笔试热点)
★不同点:
1.指针的作用有:(1).装地址(2).不同变量访问同一块空间
引用:只可以实现用不同变量访问同一块空间
2.引用定义了就一定要初始化,指针不用
3.引用初始化后就不能再引用其他空间了
4. 没有空的引用,指针可以为空(引用一定要保证引用的时一个合法的存储单元)(在c++中NULL就是0,而c中的NULL是空地址(void*)0)
5. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
6. 指针和引用的自增(++)运算意义不一样;引用是值的增减,指针是地址的偏移
7.从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。
★相同点:两者都是地址的概念,指针指向一块儿内存,其内容为所指内存的地址;引用是某块儿内存的别名。
指针比引用更为灵活,但是其风险也很大。使用指针时一定要检查指针是否为空(NULL),且空间回收后指针最好置零,以免野指针的发生造成内存泄漏等问题。
参考资料:
http://www.cnblogs.com/gw811/archive/2012/10/20/2732687.html(引用与多态)
http://blog.csdn.net/xiao__tian__/article/details/51814617
https://www.cnblogs.com/jycboy/p/5184638.html
http://blog.csdn.net/mysunshinetbg/article/details/48346195
c++中的引用详解的更多相关文章
- php中关于引用(&)详解
php中关于引用(&)详解 php的引用(就是在变量或者函数.对象等前面加上&符号) 在PHP 中引用的意思是:不同的变量名访问同一个变量内容. 与C语言中的指针是有差别的.C语言中的 ...
- JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解
二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...
- AngularJS select中ngOptions用法详解
AngularJS select中ngOptions用法详解 一.用法 ngOption针对不同类型的数据源有不同的用法,主要体现在数组和对象上. 数组: label for value in a ...
- 【转载】C/C++中extern关键字详解
1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说extern ...
- Objective-C中的@Property详解
Objective-C中的@Property详解 @Property (属性) class vairs 这个属性有nonatomic, strong, weak, retain, copy等等 我把它 ...
- WCF中队列服务详解
WCF中队列服务详解 一.引言 在前面的WCF服务中,它都要求服务与客户端两端都必须启动并且运行,从而实现彼此间的交互.然而,还有相当多的情况希望一个面向服务的应用中拥有离线交互的能力.WCF通过服务 ...
- C++引用(&)详解
C++引用详解 引用的概念 引用:就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样. 引用的声明方法:类型标识符 &引用名=目标变量名: 如下:定义引用ra,它是变量a的引 ...
- C++11 左值、右值、右值引用详解
C++11 左值.右值.右值引用详解 左值.右值 在C++11中所有的值必属于左值.右值两者之一,右值又可以细分为纯右值.将亡值. 在C++11中可以取地址的.有名字的就是左值,反之,不能取地址的.没 ...
- PHP之PHP文件引用详解
HP的文件引用涉及到四个函数: 文件引用 1.include()2.include_once()3.require()4.require_once() 这四个函数常常会给PHP初学者造成困扰,总的来说 ...
随机推荐
- Python之路----各类推导式
[每一个元素或者是和元素相关的操作 for 元素 in 可迭代数据类型] #遍历之后挨个处理[满足条件的元素相关的操作 for 元素 in 可迭代数据类型 if 元素相关的条件] #筛选功能 列表推导 ...
- ELK+Kafka学习笔记之FileBeat日志合并配置输出到kafka集群
filebeat.prospectors: - type: log #日志输出类型 enabled: true ...
- Android 拖动条 和 Handle
- 详解 HTML5 中的 WebSocket 及实例代码-做弹幕
原文链接:http://www.php.cn/html5-tutorial-363345.html
- Git 基础笔记整理1
Git 官网:http://git-scm.com/ git教程1:http://www.yiibai.com/git/home.html git教程2 :http://www.liaoxuefeng ...
- Go第四篇之流程控制
流程控制是每种编程语言控制逻辑走向和执行次序的重要部分,流程控制可以说是一门语言的“经脉”. Go 语言的常用流程控制有 if 和 for,而 switch 和 goto 主要是为了简化代码.降低重复 ...
- C#入门经典 第六章 委托
C#入门经典 第六章 6.6 委托的声明非常类似于函数,但不带函数体,且要使用delegate关键字. 委托的声明指定了一个返回类型和一个参数列表. 在定义了委托后,就可以声明该委托类型的变量. 接着 ...
- MYSQL语句:创建、授权、查询、修改、统计分析等 二 用户的创建、权限设置、删除
接着上面一的内容 4.设置更改用户密码 命令格式:SET PASSWORD FOR 'username'@'host'=PASSWORD('newpassword'); 如果是当前登录用户用:SET ...
- Redis集群学习笔记
Redis集群学习笔记 前言 最近有个需求,就是将一个Redis集群中数据转移到某个单机Redis上. 迁移Redis数据的话,如果是单机Redis,有两种方式: a. 执行redis-cli shu ...
- TDD、BDD、DDD
TDDTest-Driven DevelopmentTest-Driven Development (TDD) is a software development technique where au ...