C++详细分析讲解引用的概念与使用

目录
  • 1.引用的概念
  • 2.引用的格式
  • 3.引用的特性
  • 4.取别名原则
  • 5.引用的使用场景
    • 做参数
    • 做返回值
      • int&Count()的讲解
      • 传值传引用效率比较
  • 6.引用和指针的不同点

1.引用的概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

2.引用的格式

类型 & 引用变量名 ( 对象名 ) = 引用实体;

举例如下:

注意:引用类型必须和引用实体是同种类型的

3.引用的特性

(1). 引用在 定义时必须初始化

例如:int& d; 这样就是没有初始化是错的

(2). 一个变量可以有多个引用

int a = 10;
    int& b = a;
    int& c = b;
    int* p = &b;    //p是指针

(3). 引用一旦引用一个实体,再不能引用其他实体

4.取别名原则

对原引用变量,权限只能缩小,即 可读可写(普通类型) 可以改成 只读(const);不能放大:,即 只读 不能改成 可读可写的

例子1:权限的放大,不能把const给非const

例子2:权限的缩小 非const 既可以给非const,也可以给const:

例子3:权限缩小和放大规则:适用于引用和指针间

例子4: 权限不适用于普通赋值:

难点:隐式类型转换的引用

整形e能否做双精度浮点型d的别名呢?

直接赋值是不行的,需要加上const才正确。即:const int& e = d; 正确

解释:隐式类型转换的普通赋值的情况,上面的例子:int f=d;从语法上看把8字节的d不能直接给4字节的f,因为浮点数和整形的存储形式就不一样,没办法直接截取,所以d需要先把整数部分给一个4字节的临时变量,再把临时变量给f

再看引用:int& e=d; 临时变量具有常性,所以这里把临时变量给引用e的时候,相当于是把自带const的临时变量赋值给非const的e,把只读的给可读可写的是放大了权限,所以错误;必须改成const int& e=d;才正确!

5.引用的使用场景

做参数

(1)传参:实参给形参传值和传地址都需要传一份值/地址的拷贝,引用传参可以减少拷贝,提高效率

void Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}
void Swap(double& x, double& y)
{
	double tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int a = 0, b = 1;
	swap(a, b);
	double c = 1.1, d = 2.2;
	swap(c, d);
	return 0;
}

(2)作输出型参数:

leetcode上的题往往有输出型参数,在c++中就可以用引用代替更加方便

做返回值

int&Count()的讲解

传值返回:会有一个拷贝

传引用返回:没有这个拷贝了,函数返回的直接就是返回变量的别名

int& Count()
{
	int n = 0;
	n++;
	return n;
}
int main()
{
	int ret = Count();
	return 0;
}

首先我们来看普通的传值返回:普通的传值返回需要把返回值n给一个函数类型int的临时变量(函数类型就是返回值类型),再把临时变量给ret。

为什么设计一个临时变量,直接把n给ret不行吗?

答:不行,因为当函数Count里执行完各种代码后,返回n,等出了Count函数的作用域后n就会销毁,所以不能直接把n给ret,需要一个临时变量。

如何证明返回时存在临时变量呢?:如果你用int& ret 接收,写成int& ret = Count(); 发现无法运行,因为临时变量有常性,所以需要写成const int& ret = Count();才能通过。

此时再看传引用返回:

当用引用接收引用返回时:这里ret和n的地址一样,也就意味着ret其实就是n的别名。但是因为n出作用域不会立即被覆盖,所以第一次通过ret可以打印是1,当打印第二次时,因为前面已经调用过一次打印函数,已 "销毁" 的Count函数栈帧在此时被打印函数覆盖,再打印ret就会是随机数了!

即:如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已 经还给系统了,则必须使用传值返回。

用static修饰n后:用static静态变量使n只初始化一次且改变其生命周期,把n放进了静态区,这样n就一直存在,就可以通过ret找到n了,再怎么打印ret都是1.

传值传引用效率比较

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是 当参数或者返回值类型非常大时,效率就更低。 总结:传值和指针在作为传参以及返回值类型上效率相差很大 。

6.引用和指针的不同点

1. 引用 在定义时 必须初始化 ,指针没有要求

2. 引用 在初始化时引用一个实体后,就 不能再引用其他实体 ,而指针可以在任何时候指向任何一个同类型 实体

3. 没有 NULL 引用 ,但有 NULL 指针

4. 在 sizeof 中含义不同 : 引用 结果为 引用类型的大小 ,但 指针 始终是 地址空间所占字节个数 (32 位平台下占 4 个字节 )

5. 引用自加即引用的实体增加 1 ,指针自加即指针向后偏移一个类型的大小

6. 有多级指针,但是没有多级引用

7. 访问实体方式不同, 指针需要显式解引用,引用编译器自己处理

8. 引用比指针使用起来相对更安全

引用和指针在语法上是不一样的,但是实际上从反汇编的代码上我们能看到引用和指针的底层实现是一样的!

这就好比保时捷的卡宴和大众的途锐汽车,他们的三大件底盘,发动机,变速箱都是一样的,但是他们的品牌不一样,价格不同

到此这篇关于C++详细分析讲解引用的概念与使用的文章就介绍到这了,更多相关C++引用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++简明分析讲解布尔类型及引用

    目录 一.C++中的布尔类型 二.C++中的三目运算符 三.C++中的引用 四.总结 一.C++中的布尔类型 C++在C语言的基本类型系统之上增加了bool C++中的bool可取的值只有true和 false 理论上bool只占用一个字节 C++编译器会将非0值转换为true ,0值转换为false 注意: true代表真值,编译器内部用1来表示 false代表非真值,编译器内部用0来表示 下面看一下这段代码,加深一下对bool类型的理解. #include <stdio.h> int ma

  • C++中引用和const关键字介绍

    目录 引用 常引用 const关键字用法 1)定义常量 2)定义常量指针 3)定义常引用 总结 引用 下面的写法定义了一个引用,并将其初始化为引用某个变量.类型名 & 引用名 = 某变量名; int n = 4; int & r = n; // r 引用了n,r的类型是 int & 某个变量的引用,等价于这个变量,相当于该变量的一个别名. int n = 7; int & r = n; // r 引用了n,r和n就是一回事 r = 4; cout << r; /

  • C++精要分析右值引用与完美转发的应用

    目录 区分左值与右值 右值引用 移动语义 完美转发 结语 区分左值与右值 在C++面试的时候,有一个看起来似乎挺简单的问题,却总可以挖出坑来,就是问:“如何区分左值与右值?” 如果面试者自信地回答:“简单来说,等号左边的就是左值,等号右边的就是右值.” 那么好了,手写一道面试题继续提问. int a=1; int b=a; 问:a和b各是左值还是右值? b是左值没有疑问,但如果说a在上面是左值,在下面是右值的,那就要面壁思过了.C++从来就不是一门可以浅尝辄止的编程语言,要学好它真的需要不断地去

  • C++详细分析引用的使用及其底层原理

    目录 引用 引用的注意事项 引用做参数 实参传值和传引用的优劣 引用做函数返回值 传引用返回 引用的权限 引用经典笔试题 产生临时变量的情况 关于右值 引用的底层原理 引用 引用不是定义一个新变量,而是给已存在的变量取了一个外号,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间. 举个形象的例子,鲁智深又被叫做"花和尚",这里的花和尚和鲁智深都是同一个人,花和尚就是鲁智深的引用,说白了引用其实就是取外号. 引用的注意事项 1.引用必须初始化 int main() {

  • C++中引用的相关知识点小结

    目录 引用的概念 引用特性 常引用 使用场景 引用和指针的区别 总结 引用的概念 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间. 比如:李逵,在家称为"铁牛",江湖上人称"黑旋风".那么这里的“铁牛”.“黑旋风”就称李逵的引用. 在程序中呢,引用的用法如下: 类型& 引用变量名(对象名) = 引用实体: 举个例子: void TestRef() { int a = 10; int&

  • c++超细致讲解引用

    C和C++禁止在函数调用时直接传递数组的内容,而是强制传递数组指针,而对于结构体和对象没有这种限制,调用函数时既可以传递指针,也可以直接传递内容:为了提高效率,我曾建议传递指针,这样做在大部分情况下并没有什么不妥. 但是在 C++ 中,我们有了一种比指针更加便捷的传递聚合类型数据的方式,那就是引用 在 C/C++ 中,我们将 char.int.float 等由语言本身支持的类型称为基本类型,将数组.结构体.类(对象)等由基本类型组合而成的类型称为聚合类型(在讲解结构体时也曾使用复杂类型.构造类型

  • C++深入探究引用的本质与意义

    目录 一.引用的意义 二.特殊的引用 三.引用是否占用存储空间 四.引用的本质 五.引用的注意事项 六.小结 一.引用的意义 引用作为变量别名而存在,因此在一些场合可以代替指针 引用相对于指针来说具有更好的可读性和实用性 下面通过代码来进行说明,在C语言中,可以这么写: #include <stdio.h> void swap(int* a, int* b) { int t = *a; *a = *b; *b = t; } int main() { int a = 1; int b = 2;

  • C++详细分析讲解引用的概念与使用

    目录 1.引用的概念 2.引用的格式 3.引用的特性 4.取别名原则 5.引用的使用场景 做参数 做返回值 int&Count()的讲解 传值传引用效率比较 6.引用和指针的不同点 1.引用的概念 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间. 2.引用的格式 类型 & 引用变量名 ( 对象名 ) = 引用实体: 举例如下: 注意:引用类型必须和引用实体是同种类型的 3.引用的特性 (1). 引用在 定义时必须初

  • Java详细分析讲解自动装箱自动拆箱与Integer缓存的使用

    目录 1. 前言 2. 包装类 3. 自动装箱与自动拆箱 4. Interger缓存 5. 回答题目 1. 前言 自动装箱和自动拆箱是什么?Integer缓存是什么?它们之间有什么关系? 先来看一道题目. Integer a = new Integer(1); Integer b = new Integer(1); System.out.println(a==b); Integer c = 1; Integer d = 1; System.out.println(c==d); Integer e

  • Java详细分析讲解泛型

    目录 1.泛型概念 2.泛型的使用 2.1泛型类语法 2.2泛型方法语法 2.3泛型接口语法 2.4泛型在main方法中的使用 3.擦除机制 4.泛型的上界 5.通配符 5.1通配符的上界 5.2通配符的下界 6.包装类 6.1装箱和拆箱 1.泛型概念 泛型就是将类型参数化 所谓类型参数化就是将类型定义成参数的形式,然后在使用此类型的时候的时候再传入具体的类型 到这我们可以看出来:泛型在定义的时候是不知道具体类型的,需要在使用的时候传入具体的类型,泛型可以用在类.接口和方法中,这样做的好处是一个

  • Java超详细分析讲解哈希表

    目录 哈希表概念 哈希函数的构造 平均数取中法 折叠法 保留余数法 哈希冲突问题以及解决方法 开放地址法 再哈希函数法 公共溢出区法 链式地址法 哈希表的填充因子 代码实现 哈希函数 添加数据 删除数据 判断哈希表是否为空 遍历哈希表 获得哈希表已存键值对个数 哈希表概念 散列表,又称为哈希表(Hash table),采用散列技术将记录存储在一块连续的存储空间中. 在散列表中,我们通过某个函数f,使得存储位置 = f(关键字),这样我们可以不需要比较关键字就可获得需要的记录的存储位置. 散列技术

  • C++ Boost Foreach超详细分析讲解

    目录 一.说明 二.示例代码 2.1 最简单的代码 2.2 使用BOOST_FOREACH与BOOST_REVERSE_FOREACH 三.BOOST_FOREACH特点和应用范围 四.遍历循环控制 一.说明 Boost.Foreach Boost.Foreach 提供了一个宏来模拟 C++11 中基于范围的 for 循环.您可以使用在 boost/foreach.hpp 中定义的宏 BOOST_FOREACH 来迭代序列而不使用迭代器.如果你的开发环境支持C++11,可以忽略Boost.For

  • JavaScript自动内存管理与垃圾回收策略详细分析讲解

    目录 自动内存管理 垃圾回收策略 标记清理策略 引用计数策略 内存管理技巧 解除引用 const和let变量声明 自动内存管理 JavaScript编程语言通过自动内存管理实现内存分配和闲置资源回收. 简单来讲就是:只要确定某个变量X不会再被使用了,就将变量X占用的内存进行释放.这种判断是周期性执行的,即:垃圾回收程序隔一定时间就会自动执行一次,以释放某些不必要的内存开支. JavaScript垃圾回收过程中的难点在于:如何正确判定一块内存是否还有用? 垃圾回收策略 在C/C++程序中,我们记忆

  • Java 栈与队列超详细分析讲解

    目录 一.栈(Stack) 1.什么是栈? 2.栈的常见方法 3.自己实现一个栈(底层用一个数组实现) 二.队列(Queue) 1.什么是队列? 2.队列的常见方法 3.队列的实现(单链表实现) 4.循环队列 一.栈(Stack) 1.什么是栈? 栈其实就是一种数据结构 - 先进后出(先入栈的数据后出来,最先入栈的数据会被压入栈底) 什么是java虚拟机栈? java虚拟机栈只是JVM当中的一块内存,该内存一般用来存放 例如:局部变量当调用函数时,我们会为函数开辟一块内存,叫做 栈帧,在 jav

  • C语言 超详细总结讲解二叉树的概念与使用

    目录 1.二叉树的概念及结构 2.二叉树链式结构的实现 1.二叉树的概念及结构 ①概念:一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成. ②二叉树的特点: 每个结点最多有两棵子树,即二叉树不存在度大于2的结点.(度最多为2) 二叉树的子树有左右之分,其子树的次序不能颠倒. ③现实中的二叉树: 当一名普通的人看到这样一颗树,可能会想:好标准的一棵树 当一个程序猿看到这样一棵树,可能会想:好像数据结构中的二叉树,并且还是颗满二叉树 ④数据结

  • C语言详细分析讲解struct与union使用方法

    目录 一.struct 的小秘密 二.结构体与柔性数组 三.C语言中的 union 四.小结 一.struct 的小秘密 C语言中的 struct 可以看作变量的集合 struct 的问题:空结构体占用多大内存?下面编写程序看一下吧: #include <stdio.h> struct TS { }; int main() { struct TS t1; struct TS t2; printf("sizeof(struct TS) = %d\n", sizeof(stru

  • C++详细分析讲解函数参数的扩展

    目录 一.函数参数的默认值 二.函数占位参数 三.小结 一.函数参数的默认值 C++ 中可以在函数声明时为参数提供一个默认值 当函数调用时没有提供参数的值,则使用默认值 参数的默认值必须在函数声明中指定 下面看一段代码: #include <stdio.h> int mul(int x = 0); int main(int argc, char *argv[]) { printf("%d\n", mul()); printf("%d\n", mul(-1

随机推荐