c++中新增了一种复合类型---引用变量,引用是已定义的变量的别名。例如,如果将b作为a变量的引用,则可以交替使用rats和rodents来表示该变量。那么这种别名有什么作用呢?引用变量的主要用途是用作函数的形参。通过将引用变量用作参数,函数将使用原始数据,而不是其副本。这样除指针之外,引用也为函数处理大型结构提供了一种非常方便的途径,同时对于设计类来说,引用也是必不可少的。

1、创建引用变量

作用:给变量起别名

创建引用变量的格式

(typename&) type_name
数据类型& 别名 = 原名
typename:引用变量类型
type_name:引用变量名

c和c++中使用&符号来指示变量的地址。c++给&符号赋予了另一个含义,将其用来声明引用。

int rats = 10;

int& rodents = a;

其中&不是地址运算符,而是类型标识符的一部分。

就像声明中的char*指的是指向char的指针一样,int&指得是指向int的引用

上述引用声明rats和rodents互换 ----- 它们指向相同的值和内存单元

对于c语言用户而言,首次接触到引用时可能也会有些困惑,因为指向用户很自然的会想到指针,但它们之间还是有区别的。

int rats = 101;

int& rodents = rats;

int* prats = &rats;

表达式rodents和prats都可以和rats互换,而表达式&rodent和prats都可以同&rats互换。

从这一点来说,引用看上去很像伪装表示的指针(其中,*接触引用运算符被隐式理解)

//firstref.cpp
#include <iostream>
using namespace std;int main(void)
{int rats = 101;int& rodents = rats;int* prats = &rats;cout << "rat:" << rats << "\t" << "&rat:" << &rats << endl;cout << "rodents:" << rodents << "\t" << "&rodents:" << &rodents << endl;cout << "*prat:" << *prats << "\t" << "&prats:" << &prats << endl;return 0;}

引用和指针除了表示法不同外,还有其他的差别。差别之一是,必须在声明引用时将其初始化,而不能像指针一样,先声明,再赋值

int rat;

int& rodent;

rodent = rat; //error

注意:必须在声明引用变量时进行初始化

引用更接近const指针,必须在创建时进行初始化,一旦于某个变量关联起来,就将一致效忠于它。

int& rodents = rats;

实质上是下述代码的伪装表示:

int *const pr = &rats;

引用的本质是指针常量

指针常量是指针不可改向,也就是为什么引用不可更改

引用不能反复赋值,可以通过初始化声明来设置引用,但不能通过赋值来赋值来设置

引用和指针的区别

1、引用不占用内存空间,指针占用内存空间

2、引用是原来变量的一个别名,跟原来变量实质使用一个东西,指针是一个变量,存储的是一个地址

3、引用只能是一级,但是指针可以有多级指针(**p)

4、引用在定义的时候必须初始化,指针可以不用初始化

5、引用不能是空,指针可以是空(NULL)

6、引用在被初始化后就不能被修改,但是指针可以修改指向内存空间

7、在使用sizeof()函数后返回值可能不同,sizeof(a)和sizeof(p) 即两个都是用sizeof测量后,引用随数据类型不同返回值不同,但是指针的值不会改变

8、自增以后的值不同:a++相当于val++ p++代表p指向val后面的那个内存空间

9、引用在作为函数参数时,可以不用检查;但是指针在作为函数参数的时候需要检查是否为空

2、将引用用作函数参数

引用经常被i用做函数参数,使得函数中的变量名成为调用程序中的变量的别名。这种传递参数的方法称为按引用传递。按引用传递允许被调用的函数能够访问调用函数中的变量,c++新增的这项特性是对c语言的超越,c语言只能按值传递,按值传递导致被调用函数使用调用程序的值的拷贝,当然,c语言也允许避开按值传递的限制,采用按指针传递方式。

//swaps.cpp
#include <iostream>
using namespace std;void swap_val(int a,int b)
{a = a + b;b = a - b;a = a - b;cout << "swap_val:\ta : " << a << "\t" << "b : " << b << endl;
}void swap_ptr(int* a,int* b)
{*a = *a + *b;*b = *a - *b;*a = *a - *b;cout << "swap_ptr:\ta : " << *a << "\t" << "b : " << *b << endl;
}void swap_rats(int& a,int& b)
{a = a + b;b = a - b;a = a - b;cout << "swap_rats:\ta : " << a << "\t" << "b : " << b << endl;
}int main(void)
{int a = 10;int b = 20;swap_val(a,b);cout << "a : " << a << "\t" << "b : " << b << endl; swap_ptr(&a,&b);cout << "a : " << a << "\t" << "b : " << b << endl; swap_rats(a,b);cout << "a : " << a << "\t" << "b : " << b << endl; return 0;
}

作用:函数传参时,可以利用引用的技术让形参修饰实参

优点:可以简化指针修改实参

引用作为函数的返回值:

返回引用的函数实际上时被引用的变量的别名(可作为左值及逆行赋值操作)

返回引用的时最重要的一点是应避免返回函数终止时不再存在的内存单元引用

第一次访问的时候可以访问到,那是因为编译器做了保留,但第二次访问的时候就会报错,因为临时变量存放在栈区,函数运行完之后回收栈区资源,临时变量的内存空间已经被释放,此时再访问已经是非法访问空间,编译器会报错。

3、引用的属性和特别之处

先引入一段代码

//cubes.cpp
#include <iostream>
using namespace std;double cube_val(double a)
{a *= a*a;return a;
}double cube_rats(double& ra)
{ra *= ra * ra;return ra;
}int main()
{double a = 3.0;double a1 = 3.0;double& b = a1;cout << cube_val(a);cout << " = cube of " << a << endl;cout << cube_rats(b);cout << " = cube of " << a1 << endl;return 0;
}

cube_rats函数修改了传进去的值,而cube_val并没有。这提醒我们为何通常按按值传递。如果程序员的意图是让函数使用传递给它的信息,而不对这些信息进行修改,同时又想使用引用,则应使用常量引用。

临时变量,引用参数和const

如果实参与引用参数不匹配,c++将生成临时变量。当前,仅当参数为const引用时,c++才允许这样做,但以前不是这样。

首先,什么时候将创建临时变量呢?

如果引用参数是const,则编译器将在下面两种情况下生成临时变量:

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

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

左值参数是可被引用的数据对象,例如,变量,数组元素,结构成员,引用和接触引用的指针都是左值。非左值包括字面常量(用引号括起的字符串除外,它们由地址表示)和包含多项的表达式。

常规变量和const变量都可视为左值,因为可通过地址访问它们,但常规变量属于可修改的左值,而const变量属于不可修改的左值

const int& ref = 10;(临时变量)

编译器将代码修改

int temp = 10;

int& ref = &temp;

如果接收引用参数的函数的意图是修改作为参数传递的变量,则创建临时变量将阻止这种意图的实现。解决方法是,禁止创建临时变量,现在的c++标准正是这样做的。

如果声明将引用指定为const,c++将在必要是生成临时变量。实际上,对于形参为const引用的c++函数,如果实参不匹配,则其行为类似于按值传递,为确保原始数据不被修改,将使用临时变量来存储值。

注意:如果函数调用的参数不是左值或与相应的const引用参数的类型不匹配,则c++将创建类型正确的匿名变量,将函数调用的参数的值传递给该匿名变量,并让参数来引用该变量

应尽可能使用const

将引用参数声明为常量数据的引用的理由由三个:

1、使用const可以避免无意中修改数据的编程错误

2、使用const是函数能够处理const和非const实参,否则将只能接收非const数据

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

c++11新增了另一种引用 --- 右值引用(rvalue reference),这种引用可指向右值,是使用&&声明的:

double&& rref = std::sqrt(9.0);

double j = 15.00;

double&& jred = 2.0*j+18.5;

新增右值引用的主要目的是,让库设计人员能够提供有些操作的更有效实现。

以前的引用(使用&声明的引用)现在称为左值引用。

4、何时使用引用参数

使用引用参数的主要原因有两个:

1、程序员能够修改调用函数中的数据对象

2、通过传递引用而不是整个数据对象,可以提高程序的运行速度

对于使用传递的值而不作修改的函数

1、如果数据对象很小,如内置数据类型或小型结构,则按值传递

2、如果数据对象是数组,则使用指针,因为是唯一的选择,并将指针声明为指向const的指针

3、如果数据对象是较大的结构,则使用const指针或const引用,以提高程序的效率,这样可以节省复制结构所需的时间和空间

4、如果数据对象是类对象,则使用const引用,类设计的语义常常要求使用引用 传递类对象参数的标准方式是按引用传递

对于修改调用参数中的数据的函数

1、如果数据对象格式是内置数据类型,则使用指针

2、如果数组对象是数组,则只能使用指针

3、如果数据对象是结构,则使用指针或引用

4、如果数据对象是类对象,则使用引用

5、默认参数

概念:在声明函数的时候可以赋予函数参数默认值。所谓默认值就是在调用时,可以不写某些参数的值,编译器会自动把默认值传递给调用语句中(默认参数指的是当函数调用中省略了实参时自动使用的一个值)

如何使用默认值呢?必须通过函数原型

1、如果函数的声明和定义是分开的,必须在声明的时候设置默认值,不可以在定义的时候设置默认值

2、将变量作引用类型参数的默认值,这时变量必须时已经声明且是全局变量

3、若给某一个参数设置了默认值,那么在参数表中其后所有的参数都必须也设置默认值(如果多参数默认,必须满足从右往左连续默认,不能中间空)

//Default_parameters.cpp
#include <iostream>
using namespace std;const int& ra = 100;// void show(int a,int b)
// void show(int a,int b = 200)
void show(int a = 100,int b = 200)
{cout << "a:" << a << "\tb:" << b << endl;
}//变量作引用类型参数的默认值,这时变量必须时已经声明且是全局变量
//void show1(int& a)
void show1(int& a = ra)
{cout << "a:" << a << endl;
}//如果多参数默认,必须满足从右往左连续默认,不能中间空
void show2(int a = 10,int b,int c = 30)
{}int main(void)
{int a = 10;int b = 20;// show(a,b);// show(a);//show();//show1(ra);show1();return 0;
}

6、占位参数

占位参数:跟默认参数不同,在函数定义时,形参只写类型,不写形参变量名

语法:
返回值类型 函数名(type) //type --- int char
{//占用参数的使用场景比较少,只在运算符重载中可以应用    //带占位参数的函数调用时,要传入对应类型的参数值
}

c++中的引用,默认参数,占位参数相关推荐

  1. c/c++教程 - 2.3 函数的提高 函数的形参列表默认值 占位参数 函数重载

    3. 函数的提高 目录 3. 函数的提高 3.1 函数的默认参数 3.2 函数占位参数 3.3 函数重载 (重要) 相关教程 3.1 函数的默认参数 C++中,函数的形参列表中的形参是可以有默认值的. ...

  2. php中函数的默认值,参数的顺序

    形式参数可以在定义时候给参数赋一个默认值,默认值不能是一个变量,可以是常量,当调用函数时,如果没有参数,函数就会按照默认值处理,如果传递了参数,就按照传递的参数处理 <?phpheader('c ...

  3. 【C++】引用、内联函数、函数重载、函数默认参数(缺省参数)与占位参数、extern “C“ 浅析

    端午继续~         C++对C的扩展 目录 引用 普通变量名引用 对数组的引用 对指针的引用 对函数的引用 引用作为函数的参数 引用作为函数的返回类型 常引用 内联函数 函数重载 函数的默认参 ...

  4. 内联函数、默认参数和函数占位参数

    内联函数 定义:内联函数从源代码层看,有函数的结构,而在编译后,却不具备函数的性质.内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处.编译时,类似宏替换,使用函数体替换调用处的 ...

  5. 函数默认形参与占位参数

    一.默认参数 1.默认参数的基本概念 默认参数就是我们可以给函数的形参设定一个初始值. 我们调用函数的时候,如果我们传了实参,那么就用我们传的实参,如果我们没有传实参的话,就用函数形参的初始值. in ...

  6. C++ 函数参数的默认值和占位参数

    3.2函数占位参数 C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置 语法:返回值类型 函数名(数据类型){} 在现阶段函数的占位参数存在意义不大,但是后面的课程中会用到该 ...

  7. 【C++笔记】函数的用法:函数的默认参数、占位参数、函数重载

    函数的默认参数 在C++中,函数的形参列表中的形参是可以有默认值的. 语法:返回值类型 函数名 (参数=默认值){} int func(int a, int b = 20, int c = 30){r ...

  8. C++ 函数默认参数和占位参数

    函数默认值 函数的默认参数从左向右,如果一个参数设置了默认参数,那么这个参数之后的参数都必须设置默认参数. 如果函数声明和函数定义分开写,函数声明和函数定义不能同时设置默认参数. c++在声明函数时, ...

  9. python中怎么设置默认值_在Python中设置应该是列表的参数的默认值的最佳实践?...

    我有一个将列表作为参数的python函数.如果我将参数的默认值设置为如下空列表: def func(items=[]): print items 皮林特会告诉我"危险的默认值[]作为参数&q ...

最新文章

  1. Google Play应用描述格式
  2. Loadrunner的Tuxedo脚本分析,基本流程和基本函数介绍
  3. STL常用容器大致对比
  4. 累积分布函数_正态累积分布函数的上下界和两个近似初等函数
  5. 初识爬虫的那天,我选择了Java!
  6. android7.1 repo,RK3399 Android 7.1 删除repo后编译报错
  7. InitInstance函数
  8. Android应用开发--MP3音乐播放器代码实现(一),学Android看这就完事了
  9. 2022微软苏州Software Engineer校招暑期实习生一面
  10. 计算机分数转化成小数,分数换算成小数的方法(分数转换小数必背表)
  11. 安卓ios和java如何选_在android和iOS之间选择novi编程
  12. 三星手机「我的文件」应用闪退问题的解决方法
  13. GPS RTK测量定位原理
  14. threejs+angular 实现面积测量
  15. 网页设计作业——小米商城官网首页(1页) HTML+CSS+JavaScript web期末作业设计网页_清新淡雅个人网页大学生网页设计作业成品
  16. 化工厂人员定位保障安全管理
  17. 华为高清会议摄像机预置位的使用方法
  18. ubuntu将cuda卸载干净
  19. python练习题——for循环之“一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?”
  20. pytorch测试报错:RuntimeError: cuda runtime error (10) : invalid device ordinal at torch/csrc/cuda/Module

热门文章

  1. python递归全排列_python递归全排列实现方法
  2. 数字+文化 跨界融合为文化产业注入新活力
  3. jieba关键词提取的源码解析
  4. python codecs.open_Python: open和codecs.open
  5. PS把图片处理成可爱蜡笔画教程
  6. HTML5新增标签以及扩展属性
  7. win10/win11快速安装node并配置环境变量
  8. 【计算机前沿知识】知识图谱
  9. 计算机组成原理(七)——I/O系统
  10. MyBatisPlus多表联查