引用,简单粗暴的解释叫做别名,简单粗暴的例子就是,我是熊叫大雄,但是很多时候别人不叫我熊叫大雄,会叫我大雄,粤语地区朋友爱叫我阿雄,有人叫我雄,所以,熊叫大雄这个变量的值是我,雄、大雄、阿雄是熊叫大雄的别名,通过别名叫到的值还是我,晕了没?

这一篇主要讲引用做函数参数,引用还有其它内容,不过我觉得做函数参数比较常用且好理解一点,

1、创建引用

引用的创建形式有点类似指针,但是又有不同,一般这样创建引用:

int dNum1 = 1;
const int dNum2 = 1; int &dNum11 = dNum1;  // OK
int &dNum111;         // ERROR
int &dNum1111 = 1;    // ERROR
int &dNum21 = dNum2;  // ERROR

上面这两句已经表明,引用的创建的时候就必须初始化的,也就是创建一个引用的时候不能够先说有个别名再说这个别名是谁的,必须是先有变量再有该变量引用,而且这个变量还不能是常量,否则也错误

在《C++ Primer Plus》一书有个说法是引用更接近const指针的伪装,例如声明引用的时候就有点像是这样:

int &dNum11 = dNum1;
==>
int * const dNum11 = &dNum1

所以,引用一旦声明之后就将与初始化的变量长伴一生,一生双存,一死双亡,一变俱变。

2、引用做函数参数

我觉得这个是比较能体现引用价值的地方之一。我们知道,在使用带参函数的时候,我们需要考虑的是参数的传入是否是变量本身,因为函数中有些操作可能是要对原变量进行改变的,之前C语言的做法是用指针来传递原变量地址,这样的好处是如果需要将改变传回或者改变原变量则不需要返回值,可以通过传入指针直接改变。那C++提供了引用的新特性,所以修改传入参数的时候就可以这样用了:

void suibianla(int &dInput)    
{
dInput++;
} int main(int argc, char* argv[])
{
int dNum1 = 1;
const int dNum2 = 1; int &dNum11 = dNum1; std::cout << dNum1 << std::endl;
std::cout << dNum11 << std::endl;
suibianla(dNum1);
std::cout << dNum1 << std::endl;
std::cout << dNum11 << std::endl;
system("pause");
return 0;
}

这种方式跟指针做参数的方式有点类似,但是跟指针参数还是有区别的。

引用做参数的一些特点:

1、传递引用是传递原变量,不需要做变量拷贝,普通的变量做函数参数的时候会开辟内存拷贝数值,而传递引用则不需要开辟内存;

2、传递引用限制比传递普通函数严格,比如:

void suiBianLa(int dInput)    
{
std::cout << "dInput 地址 " << &dInput << std::endl;
dInput++;
} void aiZuoNiJiuZuoNi(int &dInput)
{
std::cout << "dInput 地址 " << &dInput << std::endl;
dInput++;
} suiBianLa(dNum1 + 1);        // OK
aiZuoNiJiuZuoNi(dNum1 + 1);  // ERROR

aiZuoNiJiuZuoNi()的参数是引用,suiBianLa()的参数是普通变量,这个时候aiZuoNiJiuZuoNi()的使用是会报错的:

error C2664: “void aiZuoNiJiuZuoNi(int &)”: 无法将参数 1 从“int”转换为“int &”

3、如果既想用引用,又不想原始变量被改变,那么就用常量引用:

void aiZuoNi(const int &dInput)
{
std::cout << "dInput 地址 " << &dInput << std::endl;
dInput++;    // ERROR
}

因为是常量引用,所以不能对输入引用变量做改变的操作。

4、仅当使用常量引用的时候,如果输入参数与引用参数不匹配,才会生成临时变量,这种不匹配体现为:

(1)、实参的类型正确,但不是左值;

(2)、实参的类型不正确,但是可以转换为正确的类型。

有点不好理解,左值是啥?简单粗暴的说就是变量、数组元素、结构成员、引用和接触引用的指针等,非左值有字面常量、包含多项式的表达式等。

void aiZuoNi(const float &fInput)    
{
std::cout << "fInput地址 " << &fInput<< std::endl;
} long lNum = 1L;
float fNum = 1L; aiZuoNi(lNum);        // lNum是long型,fInput是临时变量
aiZuoNi(fNum);        // fInput是fNum,没有临时变量
aiZuoNi(2.0);         // 2.0常量,有临时变量
aiZuoNi(fNum + 1);    // fNum + 1是表达式,有临时变量

同时注意到,aiZuoNi(fNum + 1)在非常量引用的时候是不行的,在常量引用下是可以的。

void aiZuoNi(const float &dInput)
{
std::cout << "dInput =  " << dInput << std::endl;
std::cout << "dInput 地址 " << &dInput << std::endl;
} long lNum = 1L;
float fNum = 1L;
aiZuoNi(lNum);
std::cout << "lNum =  " << lNum << std::endl;
std::cout << "lNum 地址 " << &lNum << std::endl;
aiZuoNi(fNum);
std::cout << "fNum =  " << fNum << std::endl;
std::cout << "fNum 地址 " << &fNum << std::endl;
aiZuoNi(2.0);
aiZuoNi(fNum + 1);

上述代码的运行结果是:

可以看到地址一样的只有aiZuoNi(fNum)调用的情况。

《C++ Primer Plus》给出的三个使用常量引用的理由是:

1、可以避免无意中修改数据的变成错误;

2、能够处理const和非const实参,否则只能使用非const数据,参见aiZuoNiJiuZuoNi(dNum1 + 1); // ERROR

3、使函数能够正确生成并使用临时变量

3、引用和指针的区别

  1. 声明方式不同,引用声明的时候必须初始化定义,指针可以先声明,再定义指向某个地址;

  2. 指针是需要分配内存的,指针本身占用内存来存储变量地址,引用不用,引用是原变量的别名,就还是原变量自己;

  3. 有多级指针,没见过多级引用;

  4. sizeof(引用)得到的是所指向的变量(对象)的大小,而sizeof(指针)得到的是指针本身的大小,而不是指向对象的大小;

  5. 指针自增减是地址的增减。引用的自增减是变量值得自增减;

  6. 函数引用参数是类型安全,指针参数则不是(故常见指针参数需判断指针是否有效);

  7. 引用是从一而终,指针是见异思迁;

假如你不够快乐

也不要把眉头深锁

人生本来短暂

为什么 还要栽培苦涩 

打开尘封的门窗

让阳光雨露洒遍每个角落

走向生命的原野

让风儿熨平前额 

博大可以稀释忧愁

深色能够覆盖浅色

C++基础--引用做函数参数的更多相关文章

  1. go语言基础之结构体做函数参数 值传递和地址传递

    1.结构体做函数参数值传递 示例: package main //必须有个main包 import "fmt" //定义一个结构体类型 type Student struct { ...

  2. go语言基础之map赋值、遍历、删除 、做函数参数

    1.map赋值 示例: package main //必须有个main包 import "fmt" func main() { m1 := map[int]string{1: &q ...

  3. go语言基础之切片做函数参数

    1.切片做函数参数 (备注:用了冒泡排序) 示例: package main //必须有个main包 import "fmt" import "math/rand&quo ...

  4. go语言基础之数组指针做函数参数

    1.数组指针做函数参数 示例: package main //必须有个main包 import "fmt" //p指向实现数组a,它是指向数组,它是数组指针 //*p代表指针所指向 ...

  5. go语言基础之数组做函数参数是值拷贝

    1.数组做函数参数是值拷贝 示例: package main //必须有个main包 import "fmt" //数组做函数参数,它是值传递 //实参数组的每个元素给形参数组拷贝 ...

  6. go语言基础之指针做函数参数用地址传递

    1.指针做函数参数 示例: package main //必须有个main包 import "fmt" func swap(p1, p2 *int) { *p1, *p2 = *p ...

  7. go语言基础之指针做函数参数

    1.指针做函数参数 示例: package main //必须有个main包 import "fmt" func swap(a, b int) { a, b = b, a fmt. ...

  8. Day8 函数指针做函数参数

    课堂笔记 课程回顾         多态 virtual关键字 纯虚函数 virtual func() = 0;         提前布局vptr指针 面向接口编程 延迟绑定 多态的析构函数的虚函数. ...

  9. 3205: 数组做函数参数--数组元素求和1--C语言

    3205: 数组做函数参数--数组元素求和1--C语言 时间限制: 1 Sec  内存限制: 128 MB提交: 178  解决: 139[提交][状态][讨论版][命题人:smallgyy] 题目描 ...

随机推荐

  1. dotnet-千星项目OpenAuthNet基于NetCore21的快速开发框架

    下载

  2. Django(十八)后台管理:列表页选项、编辑页选项、自定义后台页面

    [参考]https://blog.csdn.net/u010132177/article/details/103814357 [参考]https://docs.djangoproject.com/zh ...

  3. Zabbix WebUI 配置监控Zabbix Agent

    Zabbix WebUI 配置监控Zabbix Agent 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.部署zabbix服务 1>.部署zabbix server 和z ...

  4. 快速为Eclipse配置PyDev插件

    想学习Python,查询网络之后发现PyDev是很好的插件,所以就想为Eclipse配置它.结果在整个配置的过程中出现了各种问题,版本问题,重复问题,反正乱七八糟的,本身安装一次的时间就很长,中间出现 ...

  5. Metasploit学习笔记——Web应用渗透技术

    1.命令注入实例分析 对定V公司网站博客系统扫描可以发现,它们安装了zingiri-web-shop这个含有命令注入漏洞的插件,到www.exploit-db.com搜索,可以看到2011.11.13 ...

  6. 我用Python帮朋友做了张猪肉数据分析图,结果。。。

    却发现他是这么拿我当兄弟的 事情的经过是这样的: 我开开心心的去一家烧饼店吃饭 .   ​ 抬头一看,二师兄又涨价了 叹了口气,再这么下去真的要吃不起夹肉的烧饼了 点了两个烧饼一碗馄饨 快吃完的时候, ...

  7. PL/SQL 找到某列都为空的列名

    DECLARE CURSOR temp IS SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE TABLE_NAME=Upper('xxx'); v_num ...

  8. python手动实现深拷贝

    深拷贝是将对象全拷贝,包括嵌套对象 def deepcopy(cls): if isinstance(cls, dict): dct = {} for k, v in cls.items(): dct ...

  9. C# MQTT M2MQTT

    MQTT 入门介绍 MQTT是基于二进制消息的发布/订阅编程模式的消息协议 实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish).代理(Bro ...

  10. <深入理解redis>读书笔记

    chapter2 键管理与数据结构 对大多数redis解决方案而言,键的命名设计至关重要.对于管理来说,内存消耗和redis性能都与数据结构设计相关.所以对开发者而言,最好有数据结构的命名文档规范. ...