一:什么是引用变量
在自定义函数时,常常需要使用到形参。由于只能在函数体内部使用并且只能由外部实参传递数据给形参,形参也被称为形式参数或者虚拟参数。但是需要注意的一点是,形参是在调用函数时被创建并分配内存,在结束调用时释放内存,并不是真正“虚拟”的。当形参和实参的类型不是指针的时候,形参和实参是不同的变量,在内存中位于不同的位置,形参将实参的内容复制一份,在该函数运行结束的时候形参被释放,而实参内容不会改变。比如一个简单的交换两个变量值的程序:

void Swap(int a,int b)  //a,b作为形参,在函数调用时被创建,在调用结束时释放内存
{   //交换两个参数数值 int temp;temp = a;a = b;b = temp;
}
int main(){int x = 1;int y = 2;Swap(x,y);  //调用函数Swap() cout<<"x="<<x<<" y="<<y;return 0;
}

那么这样的代码能够改变两个变量的数值吗?

可见,对形参进行操作,没有改变原参数的值。同时,return关键字一次只能返回一个数据,通过return简单修改达到目的并不容易。或许可以考虑将变量设为全局变量,或者将原来的数据变为数组从而使用指针对其操作,但是这未免增大了工作量。
这时候可能不免想到,如果能够在调用函数的时候使用原始数据而非其拷贝,那么这个问题就能够得到解决。使用指针是一种方法,因为传递的参数是原始数据存储的地址,对形参的操作也是对实参的操作。但同时,C++也提供了另一种选择——引用变量。引用变量相当于已定义变量的一个别称。比如说一个人叫做张三,他的小名叫做小三,那么张三和小三都指的是同一个人。同样,引用变量和原始变量都指的是同一个内存单元中的数据,对引用变量的操作也是对原始数据的操作。引用不仅给函数处理大型结构提供了一种更便捷的途径,同时,在设计类的时候,引用也是必不可少的。那么接下来,简单介绍引用变量的创建和使用。

二:引用变量的创建和使用
<1>引用变量的创建
在之前的博客指针及动态管理内存相关中,介绍过操作符&,C和C++用其指示变量的地址,而C++还赋予了其另一个含义:用来声明引用。比如用bro作为变量brother的引用:

int brother = 10;
int & bro = brother;

需要注意的是,这里的&不是取地址操作符,而是在声明引用,正如int*是指向int类型变量的指针一样,int&是指向int的引用。在上面的范例中,代表brother和bro指向同样的内存单元和值。可以加上几行代码验证这个观点:

int brother;int & bro = brother;cout<<"address of brother is "<<&brother<<", brother = "<<brother<<endl;  //这里的&是取地址操作符cout<<"address of bro is "<<&bro<<", bro = "<<bro<<endl;

上述代码的运行结果为:

可见,引用变量和原始变量指向的内存单元和值是完全相同的。如果初次接触到引用变量,可能在区分引用和取地址操作符方面有困惑,但实际上二者的区别还是容易看出的:

 int brother;int & bro = brother; //声明引用变量 int * br = &brother; //声明指针

在声明指针时,可以先声明指针,再对指针进行初始化。但是,和const指针一样,引用变量的初始化必须在声明引用变量时进行!

 int a;//这样声明和初始化指针是正确的int * b;b = &a;  //这样声明和初始化引用是错误的!!int & c;c = b;

同时,和const指针一样,引用变量一旦和某个变量相关联,那么就不能改变引用变量的效忠对象。如果在声明int &a = b;以后又写a = c;那么a不会变为c的引用,这种代码只会将c的值赋给a和b。

<2>将引用作为函数参数
在本博客的开篇,引入了交换两个变量数值的范例程序,那么使用引用参数代替形式参数,是不是就能够满足要求了呢?为了感受引用和指针的区别和联系,将同时使用三种方式交换变量:

void Swapv(int a,int b) //按值传参,交换两个参数数值
{   int temp;temp = a;a = b;b = temp;
}
void Swapr(int &a,int &b) //使用引用变量,交换两个参数数值
{   int temp;temp = a;a = b;b = temp;
}
void Swapp(int *a,int *b) //使用指针传参,交换两个参数数值
{   int temp;temp = *a;*a = *b;*b = temp;
}int main(){int x = 1;int y = 2;Swapv(x,y);  //调用函数Swapv() cout<<"x="<<x<<" y="<<y<<endl;Swapr(x,y);  //调用函数Swapr() cout<<"x="<<x<<" y="<<y<<endl;Swapp(&x,&y);  //调用函数Swapp(),使用取地址操作符&取得变量的指针 cout<<"x="<<x<<" y="<<y;return 0;
}

那么这个测试用例的输出结果为:

可见,变量x和y交换了两次,按值传参没有改变两个变量的值,而引用变量和指针传参都成功交换了两个变量的值。

<3>引用的注意点
需要注意的是,传统的按值传参虽然无法直接对原始数据进行操作,但是这样的属性同时也有保护原始数据完整性的作用。比如下面这个函数:

int add(int &a){a += 1;return a;
}
int main(){int x = 1;cout<<x<<" + 1 = "<<add(x);return 0;
}

这样的代码的输出结果为:

之所以产生这样的结果,就是因为没有注意到引用参数无法保证初始数据的私有性产生的错误。

三:将引用运用到结构中
引用非常适合结构和类,尤其是结构在没有类公有函数时,使用引用参数能够很好的对结构进行操作。事实上,引入引用主要是为了类和结构而不是为了作为一种基本内置类型的。那么接下来用一段范例代码演示如何将结构运用到结构中:

using namespace std;
struct Student{  //声明一个结构Student char name[10];int age;
};
//定义一个引用变量传参,返回一个对结构的引用的函数
const Student & add_age(Student &stu){stu.age++;return stu;
}
int main(){Student stu1 = {"Alex",10};  //创建一个结构体实例 cout<<stu1.name<<"'s age is "<<stu1.age<<endl;Student stu2 = add_age(stu1);  //新创建一个结构体实例,将add_age()返回的引用赋给他 cout<<"Now "<<stu2.name<<"'s age is "<<stu2.age<<endl;cout<<"After add age,"<<stu2.name<<"'s age is "<<add_age(stu2).age<<endl;//输出再一次调用add_age()后的原结构体信息 return 0;
}

以上代码的输出结果为:

可知达到了预期的效果。需要注意的是,返回引用的函数实际上是被引用变量的别名,即是说:

const Student & add_age(Student &stu)

这样的函数的返回实际上就是被引用的变量。即是说即使没有上面代码中的新建一个结构,不将add_age()函数返回值赋给其他变量,作为函数参数的引用的被引用对象也被改变了。即是说:

 add_age(stu1);  cout<<"Now "<<stu1.name<<"'s age is "<<stu1.age<<endl;

也能得到和之前一样的结果。同时,需要注意的是,不要让函数返回临时变量的引用,因为临时变量在函数结束调用时会被释放内存,即是说,这样的代码是应当避免写下的:

const Student & add_age(Student &stu){Student stu1;stu1 = stu;return stu1;
}

四:将引用运用到类中
在类中使用引用参数和在结构中使用引用十分相似,所以我将使用和上面类似的一个测试程序来演示如何将引用运用到类中:

#include <iostream>
#include <cstring>
using namespace std;
class Student{  //创建类Student
private:char name[10];int age;
public:Student(char * n,int a);  //构造函数 const Student & add_age(Student &); //接受类的引用,返回改变后类的引用 void show();
};
Student::Student(char * n,int a){ //初始化姓名和年龄 strncpy(name,n,10);age = a;
}
const Student & Student::add_age(Student &stu){stu.age += 1;return stu;
}
void Student::show(){cout<<name<<"'s age is "<<age<<endl;
}
int main(){Student stu1("Alex",20); //创建类实例stu1.show();stu1.add_age(stu1);    //调用stu1的成员函数,将stu1自身的引用作为参数传入cout<<"after add ";stu1.show();return 0;
}

上述代码的执行结果为:

【C++】引用变量相关相关推荐

  1. JavaScript高级day01-AM【WebStrom安装、数据类型分类及判断、数据-内存-变量、引用变量赋值、对象的组成】

    笔记.视频.源码:JavaScript(基础.高级)笔记汇总表[尚硅谷JavaScript全套教程完整版] 目   录 P1 01.尚硅谷_JS高级_准备 07:07 WebStrom 下载及安装 1 ...

  2. 数据库知识:SQLServer变量相关知识介绍

    今天给大家分享SQLServer变量相关介绍,希望对大家能有所帮助! 1.概述 SQLServer变量对应内存中的一个存储空间.它和常量不同,变量的值可以在执行过程中改变. 2.分类 SQLServe ...

  3. [转载] Java静态代码块/构造代码块/构造函数/静态变量/成员变量(相关示例)

    参考链接: Java程序的输出| 构造函数 近期做牛客网的Java开发笔试题,发现这类型的题目较多,很容易混淆,特将相关概念和相关示例整理如下,供大家参考^_^ 1. 静态代码块在类加载时即运行,而且 ...

  4. JS高级程序设计读书笔记(第五章 引用变量)

    第五章 引用变量 Object 创建 Object 实例的方式有两种.第一种是使用 new 操作符后跟 Object 构造函数,另一种方式是使用对象字面量表示法. var person = new O ...

  5. linux快捷键 赋值,2_Shell语言———bash的快捷键、变量声明、引用变量及变量替换...

    一.初识bash Bash是一种解释型语言,但它也提供了交互式接口,支持变量,因此提供了编程环境,所以可以称bash为程序语言. 解释型语言属于弱类型语言,其特点为: 1.它不强制区分变量类型,即无论 ...

  6. 动态改变标题_插入控件-gt;引用变量-gt;实现动态图表纵横筛选

    欢迎关注我的微信公众号:HR爱玩儿Excel和PPT,分享有趣又有逼格的Excel和PPT创意和技巧,emmm...不关注也没有关系...哼 昨晚没睡好,因为睡前老友跟我说了句话: 我们总部做的表没有 ...

  7. php的引用变量与销毁机制

    在php中,符号"&"表示引用. 1.看看不引用的情况是这样子: $a  = "hello world";//定义一个变量,下面赋值给$b $b = $ ...

  8. C++_引用变量探究

    什么是引用 引用变量是已定义变量的别名. 如何定义引用变量: int rats; int & rodents = rats; 其中&不失地址运算符,而是类型标识符的一部分.就行声明ch ...

  9. 【Android RTMP】x264 图像数据编码 ( Camera 图像数据采集 | NV21 图像数据传到 Native 处理 | JNI 传输字节数组 | 局部引用变量处理 | 线程互斥 )

    文章目录 安卓直播推流专栏博客总结 一. NV21 数据传入 Native 层 二. jbyte * 数据类型 ( Java 中的 byte[] 数组传入 JNI 处理方式 ) 三. 局部引用处理 四 ...

  10. dos中定义变量与引用变量以及四则运算

    在dos中使用set定义变量: set  a=8              (注意等号两边没有空格) 引用变量如: echo  %a%        将打印a的值 dos中要使用算术运算,需要使用 s ...

最新文章

  1. SpringBoot BasicService
  2. ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry ’1′ for key
  3. c语言fread读取错误,【求助】C语言fread读取二进制文件时,读取结果全都是零
  4. 2018-2019-1 20189221 《深入理解计算机系统》第 1 周学习总结
  5. centos7防火墙操作
  6. osg中实现HUDAxis功能
  7. LeetCode之Add Two Numbers
  8. 在线ASCII流程图编辑器工具
  9. URAL 1992 CVS 可持久化链栈
  10. 多项目加载顺序修改_面试官:Java 类在 Tomcat 中是如何加载的?
  11. IOS多线程实现多图片下载(一)
  12. php curl 下载文件
  13. 东南大学 软件工程 复试3门科目历年真题 思维导图整理(软件工程/数据库/计算机网络)
  14. cubic 插值和 bicubic,pytorch, opencv和matlab的实现差异
  15. 只需10行代码就能对Excel文件进行批量去重~
  16. 风暴英雄 服务器在哪个文件夹,《风暴英雄》国服官方答疑 玩家最关心的问题都在这里...
  17. Tomcat启动失败 staring tomcat server at localhost has encoutered a problem
  18. svn访问报错500
  19. 谈谈最近管理情绪和时间的心得:真的是破心中贼难
  20. 【033】基于51单片机的步进电机角度测量与速度设定Proteus仿真设计

热门文章

  1. go.js的一些功能
  2. 非计算机java学历低_现在大专非计算机专业0基础学习Java过后找工作好找吗?
  3. 服务器系统共享打不开怎么办,服务器共享打不开
  4. 阿里云1核1G的意思
  5. Python pandas.DataFrame.get_ftype_counts函数方法的使用
  6. 在Mongoose中使用嵌套的populate处理数据
  7. 初学吉他买什么吉他好 什么牌子的吉他性价比高2023
  8. 苹果微信聊天记录如何迁移到新手机?记住这4个步骤
  9. 黑马程序员_样式选择器的种类及用法
  10. Fmoc-Arg(Me, Pbf)-OH 1135616-49-7