具体解释C++引用——带你走进引用的世界
一、介绍引用
首先说引用是什么,大家能够记住,引用就是一个别名,比方小王有个绰号叫小狗。他的妈妈喊小狗回家吃饭。那就是在喊小王回家吃饭。
接下来我们用两行代码来声明一个引用(就拿小王和小狗来说吧):
int xiaoW;
int &xiaoG=xiaoW;
上面就是一个引用,说明几点要注意的地方:
1.&不是取地址符。而是引用运算符;
2.xiaoG是xiaoW的别名,所以这两个变量的值和地址都是一样的;
3.引用仅仅能初始化。而不能先声明再赋值,由于引用就相当于一个常量;
4.在声明一个引用时必须同一时候对其进行初始化。
引用是很忠心的,假设你定义了一个变量的别名。那么这个别名永远属于这个变量。它的地址始终和变量的地址同样,当改变别名的值也会改变这个变量的值。
相同,对对象的引用也与对变量的引用相同。但须要注意的是不能对类引用,由于类是一种类型,没有内存地址。
关于引用的介绍就到这里。以下我们来学习一下引用的使用。
二、三种传參方式的比較
函数的传參方式有三种:按值传递、按地址传递及按别名传递(按别名传递也是按地址传递的一种。这里我为了方便解说把它单列出来)。接下来我们用三个程序来对三种方式作下比較。程序的功能是交换两个变量的值。
1.按值传递。
#include<iostream>
using namespace std;
void swap(int i,int j)
{
cout<<"交换前的i:"<<i<<" "<<"j:"<<j<<endl;
int temp;
temp=i;
i=j;
j=temp;
cout<<"交换后的i:"<<i<<" "<<"j:"<<j<<endl;
}
int main()
{
int a=1,b=2;
cout<<"交换前的a:"<<a<<" "<<"b:"<<b<<endl;
swap(a,b);
cout<<"交换后的a:"<<a<<" "<<"b:"<<b<<endl;
return 0;
}
程序的执行截图例如以下:
从结果我们能够看出,变量a和b的值分别传给i和j,函数内的i和j的值是交换了的。而a和b的值并未交换。这是怎么回事呢。
这是由于,当把a和b的值传递到函数中时,这种传递方式就是按值传递。编译器会自己主动在栈中创建a和b的副本。然后再将a和b的副本传递给函数。这样在函数中交换的是a和b的副本,而不是a和b本身。解决这种问题就要引入指针了。
2.按地址传递。
#include<iostream>
using namespace std;
void swap(int *i,int *j)
{
cout<<"交换前的i:"<<*i<<" "<<"j:"<<*j<<endl;
int temp;
temp=*i;
*i=*j;
*j=temp;
cout<<"交换后的i:"<<*i<<" "<<"j:"<<*j<<endl;
}
int main()
{
int a=1,b=2;
cout<<"交换前的a:"<<a<<" "<<"b:"<<b<<endl;
swap(&a,&b);
cout<<"交换后的a:"<<a<<" "<<"b:"<<b<<endl;
return 0;
}
程序的执行截图例如以下:
能够看到,这样就能够交换a和b的值了。由于程序传递的是a和b的地址,所以在函数中能够通过地址直接訪问a和b,交换它们的值。
指针尽管能够实现交换的功能,可是使用起来比較麻烦,又不易阅读,使用引用看起来就会直观得多。
3.按别名传递
#include<iostream>
using namespace std;
void swap(int &i,int &j)
{
cout<<"交换前的i:"<<i<<" "<<"j:"<<j<<endl;
int temp;
temp=i;
i=j;
j=temp;
cout<<"交换后的i:"<<i<<" "<<"j:"<<j<<endl;
}
int main()
{
int a=1,b=2;
cout<<"交换前的a:"<<a<<" "<<"b:"<<b<<endl;
swap(a,b);
cout<<"交换后的a:"<<a<<" "<<"b:"<<b<<endl;
return 0;
}
程序的执行截图例如以下:
结果也是成功交换,程序看起来易懂,并且操作也方便不少。
三、传递对象
对象和变量一样能够作为參数传递。而不一样的是。对象可能包括有大量的数据,那么用上述的三种方式分别传递对象是什么样子呢。
1.按值传递对象
当按值传递的參数是一个对象时,编译器相同会建立一个对象的副本,函数中返回对象时,相同会建立对象的副本,当该对象的数据非常多时。那对内存的消耗就非常大了,我们先来看按值传递的程序。
#include<iostream>
using namespace std;
class A
{
public:
A(){cout<<"构造函数被调用\n"<<endl;}
A(A&a){cout<<"拷贝构造函数被调用\n"<<endl;}
~A(){cout<<"析构函数被调用\n"<<endl;}
private:
int x;
};
A func(A a)
{
return a;
}
int main()
{
A a;
func(a);
return 0;
}
程序执行结果截图例如以下:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMTQyMTYwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
由程序执行结果能够看出。执行了一次构造函数,两次复制构造函数,三次析构函数,接下来我们对比程序结果和代码分析一下(此处知识对新手十分重要,大家务必牢记!
),main函数中创建了一个对象a。所以调用了构造函数。再将a传递到func函数中,这时会自己主动调用复制构造函数来创建对象a的副本。也就是说传进func函数的是a的副本而不是a本身。func函数的返回值是对象,返回的方式也是按值返回,此时又自己主动调用复制构造函数创建返回值的副本,所以我们看到调用一次构造函数和两次复制构造函数,对应也会调用三次析构函数释放内存。
2.按地址传递对象
从按值传递对象的程序结果能够看出,这样的方式的系统开销是很大的,而按地址传递则攻克了这一问题。
#include<iostream>
using namespace std;
class A
{
public:
A(){cout<<"构造函数被调用\n"<<endl;}
A(A&a){cout<<"拷贝构造函数被调用\n"<<endl;}
~A(){cout<<"析构函数被调用\n"<<endl;}
private:
int x;
};
A func(A *a)
{
return *a;
}
int main()
{
A a;
func(&a);
return 0;
}
程序执行结果截图例如以下:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMTQyMTYwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
能够看出。上面的程序避免了两次复制构造函数的调用,由于在将对象a传递给func函数时传进的是地址,func函数返回的相同也是个地址。
3.按别名传递对象
#include<iostream>
using namespace std;
class A
{
public:
A(){cout<<"构造函数被调用\n"<<endl;}
A(A&a){cout<<"拷贝构造函数被调用\n"<<endl;}
~A(){cout<<"析构函数被调用\n"<<endl;}
private:
int x;
};
A &func(A &a)
{
return a;
}
int main()
{
A a;
func(a);
return 0;
}
程序执行结果截图例如以下:
能够看出,按别名传递对象与按指针传递有相同的效果。而实现起来又简单的多,这里给新手朋友们做出个小提示,就是func函数返回的类型是别名。假设想在main函数中接收这个别名,相同也要用引用类型的数据接收,比方A &aa=func(a),而不能用一个A类型的对象接收。这点大家要注意。
四、究竟该用指针还是引用
既然引用能够实现指针的功能。又比指针使用方便,那是不是仅仅用引用就能够了呢。答案是否定的。由于指针的某些功能引用是无法实现的,先说一下引用和指针的差别。
1.指针能够为空。而引用不能为空;
2.指针能够被赋值,而引用不可改变;
3.在堆中创建一块内存区域时,必须使用指针来指向这块区域。否则是无法訪问的。而不能用引用来指向
五、使用引用需慎重
引用是不能为空的,否则是得不到正确的结果。我们通过以下的程序来感受一下。
#include<iostream>
using namespace std;
class A
{
public:
A(int i){x=i;}
int get(){return j;}
private:
int j;
};
A &func()
{
A a(100);
return a;
}
int main()
{
A&aa=func();
cout<<aa.get()<<endl;
return 0;
}
程序执行结果截图例如以下:
从执行结果能够看出。我们并没有得到正确的j值(100),这是为什么呢,原因是。func函数中的a是一个局部对象,在func函数执行后,a就没有了,所以func函数返回的是一个并不存在的对象的别名。因此无法正确得到j的值,解决问题非常easy,仅仅要将func函数的引用类型返回值改为A类型。就能够的到正确的结果,由于这样会创建一个对象a的副本。
六、寄语
我刚開始学习C++时对引用就特别头疼,但仅仅要清晰的了解了它的工作方式,学习起来还是比較简单的,所以我以我的亲身体验写出这篇博文。以供刚開始学习的人朋友參考。当然,引用的知识比較混乱并且极easy与指针混淆,须要在应用的过程中才干逐渐掌握,所以建议朋友们还是去多读多写代码。祝朋友们都能学到足够的知识。在实践中进步。成为合格的互联网人才。
因为我也是刚入门不久,所以纰漏之处在所难免,请各位多包涵。假设对本文有不论什么意见或建议,欢迎与我qq交流,谢谢!
具体解释C++引用——带你走进引用的世界的更多相关文章
- 小丁带你走进git的世界二-工作区暂存区分支
小丁带你走进git的世界二-工作区暂存区分支 一.Git基本工作流程 1.初始化一个仓库 git init git clone git仓库分为两种情况: 第一种是在现有项目或目录下导入所有文件到 ...
- 小丁带你走进git的世界三-撤销修改
一.撤销指令 git checkout还原工作区的功能 git reset 还原暂存区的功能 git clean 还没有被添加进暂存区的文件也就是git还没有跟踪的文件可以使用这个命令清除他们 g ...
- 带你走进rsync的世界
导读 Rsync(remote synchronize)是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件,也可以使用 Rsync 同步本地硬盘中的不同目录.rsync共有3种使用方 ...
- 小丁带你走进git的世界三-撤销修改(转)
一.撤销指令 git checkout还原工作区的功能 git reset 还原暂存区的功能 git clean 还没有被添加进暂存区的文件也就是git还没有跟踪的文件可以使用这个命令清除他们 g ...
- 小丁带你走进git的世界四-重写历史记录
一.git对象文件创建 开篇先补充一个知识点,就是比如我建立一个文件之后,使用git add就会生成一个git对象,但是git对象生成后可以在.git/objects里面对应,首先我们来初始化一个仓库 ...
- 一秒钟带你走进P图世界-----(python)PIL库的使用
python-----PIL库的使用 一.什么是PIL库 1.PIL(Python Image Library)库是python语言的第三方库,具有强大的图像处理能力,不仅包含了丰富的像素.色彩操作功 ...
- 人人都可以写的一个Python可视化小程序,带你走进编程的世界
当年的PHP号称是最好的编程语言,今天的Python就是最简单的编程语言,一个小小的程序,寥寥几行代码,带你体验一下编程的乐趣. 最简单的编程语言 今天要介绍的小工具是Python环境安装好之后,自带 ...
- 瞄一眼,带你走进SparkSQL的世界
本文由 网易云发布. 作者:范欣欣(本篇文章仅限知乎内部分享,如需转载,请取得作者同意授权.) 最近想来,大数据相关技术与传统型数据库技术很多都是相互融合.互相借鉴的.传统型数据库强势在于其久经考验 ...
- Java第一天,带你走进编程的世界,我的第一个程序
要想彻底了解Java是什么,我就得首先了解编程语言的发展史.编程语言最初的形势是"0101......"数据编程,也就是机器语言.机器语言可以说是一种几乎没有人能够看懂的编程语言, ...
随机推荐
- POI 导入excel数据自己主动封装成model对象--代码分析
上完代码后,对代码进行基本的分析: 1.主要使用反射api将数数据注入javabean对象 2.代码中的日志信息级别为debug级别 3.获取ExcelImport对象后须要调用init()方法初始化 ...
- 安卓更新Toast流程图
今天照着书写了个程序为了理解更深刻特意画了一个流程图分享给大家 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc29uZ2p1bnlhbg==/font/5 ...
- GIT GUI简易教程
GIT GUI简易教程 前言 之前一直想一篇这样的东西,因为最初接触时,我也认真看了廖雪峰的教程,但是似乎我觉得讲得有点多,而且还是会给我带来很多多余且重复的操作负担,所以我希望能压缩一下它在我工作中 ...
- VBA 第一天
公司实习第一天,excel搞不定啊,学点VBA留着用: 录制宏: 点击录制宏按钮以后,在这段期间你做的每一个操作都会被记录下来,直到你点击停止录制按钮才能够停下,停下来后在此期间每一个操作都会以宏代码 ...
- Redis常用命令速查 <第二篇>【转】
一.Key Key命令速查: 命令 说明 DEL 删除给定的一个或多个 key,不存在的 key 会被忽略,返回值:被删除 key 的数量 DUMP 序列化给定 key,返回被序列化的值,使用 RES ...
- xBIM 高级01 IFC多模型合并
系列目录 [已更新最新开发文章,点击查看详细] 多模型合并可以实现以下功能: 覆盖多个模型以表现得像一个模型 统一访问数据,就像它是单个模型一样 只读.要修改模型的内容,您必须使用特定模型 不 ...
- BZOJ 2588 主席树
思路: 主席树 做完BZOJ 3123 觉得这是道水啊-- 然后狂RE 狂MLE 要来数据 忘把deep[1]设成1了----------. 啊wocccccccccccccccc //By Siri ...
- C# 比较两个数据的不同
string[] arrRate = new string[] { "op1010", "op1020", "op1030", " ...
- SpringMVC(三) RESTful架构和文件上传下载
RESTful架构 REST全名为:Representational State Transfer.资源表现层状态转化.是目前最流行的一种互联网软件架构. 它结构清晰.符合标准.易于理解.扩展方便,所 ...
- AndroidStudio EventBus报错解决方法its super classes have no public methods with the @Subscribe
首先说明,以前我用eventBus的jar包写得项目demo,前几天就写了一个EventBus的实例,这次我没用jar包,直接用gradle引用的,可是demo写完了,报错: its super cl ...