【C++】C++入门(中)--引用

news2024/12/29 11:32:48

目录

一 引用概念

二 引用特性

三 常引用

四 引用使用场景

1 做参数

2. 做返回值

1 例一

2 例二

3 例三

4 例四

五 传值, 传引用效率比较

六  值和引用的作为返回值类型的性能比较

七 引用和指针的区别


一 引用概念

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

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

int main()
{
       int a = 1;
       int b = a;

       int& c = a;
       int& d = a;
       int& e = c;

       cout << &a << endl;
       cout << &c << endl;
       cout << &d << endl;
       cout << &e << endl;
       cout << &b << endl;

       return 0;
}

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

二 引用特性

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

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

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

int main()
{
       int a = 0;

       // 引用必须初始化
       // int& b; 这是错误的

       int& c = a;
       int d = 1;

       // c变成d的别名?还是d赋值给c?--把d赋值给c
       c = d;

       // 一个对象可以有多个别名,可以别名继续取别名
       int& e = a;
       int& f = e;

       return 0;
}

三 常引用

void TestConstRef()
{
    const int a = 10;

    //int& ra = a;   // 该语句编译时会出错,a为常量 权限不能放大
    const int& ra = a;

    // int& b = 10; // 该语句编译时会出错,b为常量
    const int& b = 10;

    double d = 12.34;
    const double& rrd = d;//权限可以缩小

    //int& rd = d; // 该语句编译时会出错,类型不同
    const int& rd = d;//加了了const后 可以隐形类型提升

    int i = 1;
    double j = i;
    const double& rj = i;
}

 四 引用使用场景

1 做参数

// 传参数
void Swap(int* left, int* right)
{
       int temp = *left;
       *left = *right;
       *right = temp;
}

void Swap(int& left, int& right)
{
       int temp = left;
       left = right;
       right = temp;
}

typedef struct SListNode
{
       struct SListNode* next;
       int val;
}SLTNode, * PSLTNode;

//void SListPushBack(PSLTNode& phead, int x)
void SListPushBack(SLTNode*& phead, int x)
{
       if (phead == NULL)
       {
              // ... 
              //phead = newnode;
       }
       else
       {
              // 找尾结点,再链接newnode
              // tail->next = newnode;
       }
}

int main()
{
       int a = 0, b = 2;
       Swap(&a, &b);
       Swap(a, b);

       //PSLTNode plist = NULL;
       SLTNode* plist = NULL;
       SListPushBack(plist, 1);
       SListPushBack(plist, 2);
       SListPushBack(plist, 3);


       return 0;
}

我们可以看到插入的时候我们就没有传地址了. 接收的就是她本身, 只是换取了一个名字

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

2. 做返回值

1 例一

// 出了函数作用域,返回对象就销毁了,不能用引用返回,否则结果是不确定
int& Count()
{
       int n = 0;
       n++;

       return n;
}

int main()
{
       int ret = Count();
       cout << ret << endl;

       cout << ret << endl;

       return 0;
}

 2 例二

// 出了函数作用域,返回对象就销毁了,不能用引用返回,否则结果是不确定
int& Add(int a, int b)
{
       int c = a + b;
       return c;
}

int main()
{
       int& ret = Add(1, 2);//这里ret本质也是c的别名
       cout << "Add(1, 2) is :" << ret << endl;

       Add(3, 4);

       cout << "Add(1, 2) is :" << ret << endl;
       return 0;
}

3 例三

int& Add(int a, int b)
{
       static int c = a + b;//静态变量 只接受一次初始值
       return c;
}


int main()
{
       int& ret = Add(1, 2);
       cout << "Add(1, 2) is :" << ret << endl;

       Add(3, 4);

       cout << "Add(1, 2) is :" << ret << endl;
       return 0;
}

4 例四

typedef struct SeqList
{
       int a[100];
       int size;
}SL;

//void SLModify(SL* ps, int pos, int x)
//{
//     //...
//     assert(ps);
//     assert(pos < ps->size);
//     ps->a[pos] = x;
//}

//引用做返回值:可以修改返回对象
int& SLat(SL* ps, int pos)
{
       assert(ps);
       assert(pos < ps->size);
       
       return ps->a[pos];
}

int main()
{
       SL s;
       //...

       SLat(&s, 3) = 10;

       // 每个位置的值++
       for (size_t i = 0; i < s.size; i++)
       {
              SLat(&s, i)++;
       }

       return 0;
}

这里SLat函数里的ps不是在此函数里定义的, 是在main函数定义传参过来的, 所以出了这个SLat函数, 返回值不会销毁, 所以可以引用返回.

怎样判断?

只要这个对象没有被定义在这个函数里, 即便函数执行结束了, 那么这个对象不会被销毁, 所以它的引用是存在的.

五 传值, 传引用效率比较

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

#include <time.h>
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{
       A a;
       // 以值作为函数参数
       size_t begin1 = clock();
       for (size_t i = 0; i < 10000; ++i)
              TestFunc1(a);
       size_t end1 = clock();
       // 以引用作为函数参数
       size_t begin2 = clock();
       for (size_t i = 0; i < 10000; ++i)
              TestFunc2(a);
       size_t end2 = clock();
       // 分别计算两个函数运行结束后的时间
       cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
       cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

int main()
{
       TestRefAndValue();
}

六  值和引用的作为返回值类型的性能比较

#include <time.h>
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }
void TestReturnByRefOrValue()
{
       // 以值作为函数的返回值类型
       size_t begin1 = clock();
       for (size_t i = 0; i < 100000; ++i)
              TestFunc1();
       size_t end1 = clock();
       // 以引用作为函数的返回值类型
       size_t begin2 = clock();
       for (size_t i = 0; i < 100000; ++i)
              TestFunc2();
       size_t end2 = clock();
       // 计算两个函数运算完成之后的时间
       cout << "TestFunc1 time:" << end1 - begin1 << endl;
       cout << "TestFunc2 time:" << end2 - begin2 << endl;
}
int main()
{
       TestReturnByRefOrValue();
}

引用返回秒杀值返回

七 引用和指针的区别

在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。

指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作

int main()
{
       int a = 10;

       // b是否开空间? -- > 没有
       int& b = a;

       // ptr开了空间
       int* ptr = &a;

       char ch = 'x';
       char& r = ch;
       cout << sizeof(r) << endl;
       return 0;
}

在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

引用表面好像是传值,其本质也是传地址,只是这个工作有编译器来做

引用和指针的不同点:

1. 引用概念上定义一个变量的别名,指针存储一个变量地址。

2. 引用在定义时必须初始化,指针没有要求 引用一旦定义时初始化指定,就不能再修改,指针可以改变指向

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

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

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

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

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

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

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

本节讨论的主要就是引用, 引用的重要性很大, 可以说贯穿了整个C++过程, 只不过现在肯定感受不到的, 要在后面类和对象的学习中, 才能深刻感受到它的作用和魅力, 有些是指针不能替代的. 最后还是那句话, C基础不扎实的看这个会困难, 可以看我之前C语言的博客.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1144223.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频智能视觉分析真的遥不可及吗?有没有那种下载就能用的视频分析服务?

我一直有一个感觉&#xff0c;就是市面上很难找到那么一个带视频算法的软件&#xff0c;能让我们很直观地看到视频分析的效果&#xff0c;大部分都要内置在某种算力硬件上&#xff0c;或者对GPU要求比较严格&#xff0c;很难做到像以前我们做的视频直播软件那样&#xff0c;下载…

Springboot+vue地方废品回收机构管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue地方废品回收机构管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&…

牛客网刷题-(7)

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

RK3568平台 sys虚拟文件系统添加节点

一.常见的linux文件系统 1. EXT2: EXT2是最早的Linux文件系统之一&#xff0c;它被广泛应用于Linux操作系统中。它支持大小为16TB的分区和最大文件大小为2TB。由于其简单性和高可靠性&#xff0c;在很长一段时间内仍被许多用户所选择。 2. EXT3: 2001年&#xff0c;Linux社区…

【100天精通Python】Day72:Python可视化_一文掌握Seaborn库的使用《二》_分类数据可视化,线性模型和参数拟合的可视化,示例+代码

目录 1. 分类数据的可视化 1.1 类别散点图&#xff08;Categorical Scatter Plot&#xff09; 1.2 类别分布图&#xff08;Categorical Distribution Plot&#xff09; 1.3 类别估计图&#xff08;Categorical Estimate Plot&#xff09; 1.4 类别单变量图&#xff08;Cat…

《C和指针》笔记35:结构体

本文整理一下结构体的相关知识&#xff0c;记录是为了更好地加深理解。 1. 结构体声明 下面两个声明语句&#xff1a; struct {int a;char b;float c; } x;struct {int a;char b;float c; } y[20], *z;这两个声明被编译器当作两种截然不同的类型&#xff0c;即使它们的成员列…

数字电路与逻辑设计 之 组合电路的设计(多输出电路,全加器,乘法器)

一些例子 多输出的电路设计 全加器 我们尝试不去弄到最简 乘法器 要分析有几个输入&#xff0c;几个输出

代码签名证书续费

代码签名证书的有效周期是1-3年&#xff0c;这种情况下证书到期了就要重新申请办理&#xff0c;最开始同样的申请验证步骤还要再走一遍&#xff0c;尤其是Ukey还是要CA机构重新颁发&#xff0c;还是要等待快递配送。OV代码签名证书、EV代码签名证书目前行业内统一采取Ukey存储&…

链动2+1全新9.0版本 无限链动收益

一个平台能否长期存活取决于它是否有一个支撑其持续发展的商业模式。蜂群精选深谙用户心理&#xff0c;对链动21模式进行改造&#xff0c;创新出一种同时具备裂变能力和高效吸引用户留存的新玩法。 链动21模式在整个架构上都是完整的&#xff0c;可以说是一个非常出色的营销模式…

【JAVA】我们该如何规避代码中可能出现的错误?(三)

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️初识JAVA】 文章目录 前言throws/throw 关键字throw 关键字throws 关键字 finally关键字 前言 异常是程序中的一些错误&#xff0c;但并不是所有的错误都是异常&#xff0c;并且错误00有时候是可以避…

统计学习方法 决策树

文章目录 统计学习方法 决策树决策树模型与学习特征选择决策树的生成ID3 算法C4.5 的生成算法 决策树的剪枝CART 算法CART 回归树的生成CART 分类树的生成CART 剪枝 统计学习方法 决策树 阅读李航的《统计学习方法》时&#xff0c;关于决策树的笔记。 决策树模型与学习 决策…

C++学习笔记之四(标准库、标准模板库、vector类)

C 1、C标准库2、C标准模板库2.1、vector2.1.1、vector与array2.1.2、vector与函数对象2.1.3、vector与迭代器2.1.4、vector与算法 1、C标准库 C C C标准库指的是标准程序库( S t a n d a r d Standard Standard L i b a r a y Libaray Libaray)&#xff0c;它定义了十个大类…

编码,解码

一.标准ASCll字符集 标准ASCll 字符集使用一个字节存储一个字符&#xff0c;首尾是0 二.GBK字符集 GBK中一个中文字符编码成两个字节的形式存储&#xff0c;一个英文字母编码成一个字节的形式存储 对于 汉字中夹英文的&#xff0c;GBK规定&#xff1a;汉字的第一个字节的第一位…

MySQL安装多个实例——批处理脚本一键配置MySQL服务

1.下载mysql的免安装压缩包 官网&#xff1a;https://downloads.mysql.com/archives/community/ 2.解压并新增批处理脚本 echo off chcp 65001 setlocal enabledelayedexpansionecho MySQL版本为8.0.34REM 使用set /p命令获取用户输入的端口号 set /p "port请输入端口号…

C++标准模板(STL)- 类型支持 (类型特性,is_pointer,is_lvalue_reference,is_rvalue_reference)

类型特性 类型特性定义一个编译时基于模板的结构&#xff0c;以查询或修改类型的属性。 试图特化定义于 <type_traits> 头文件的模板导致未定义行为&#xff0c;除了 std::common_type 可依照其所描述特化。 定义于<type_traits>头文件的模板可以用不完整类型实…

【git】git使用教程

1、版本管理工具 如果有一个软件能记录我们对文档的所有修改&#xff0c;所有版本&#xff0c;这类软件我们一般叫做版本控制工具。 特性“ 能够记录历史版本&#xff0c;回退历史版本团队开发&#xff0c;方便代码合并 2、版本管理工具介绍 svn、git svn是集中式版本控制…

mac版本 Adobe总是弹窗提示验证问题如何解决

来自&#xff1a; mac软件下载macsc站 mac电脑使用过程中总是弹出Adobe 的弹窗提示&#xff0c;尤其是打开Adobe的软件&#xff0c;更是频繁的弹出提示&#xff1a; Your Adobe app is not genuine. Adobe reserves the right to disable this software after a 0 grace period…

Ubuntu 22.04 开机闪logo后卡在/dev/sda3: clean

环境 Vmware 17.0.0&#xff0c;CPU 2&#xff0c;内存4G&#xff0c;硬盘50G Ubuntu 22.04 问题描述 开机 --> 显示两行代码 --> 显示ubuntu logo --> 左上显示两个代码卡住不动 原因分析 1、网上大多说显卡驱动&#xff0c;最近没安装相关软件&#xff0c;也没…

ARM汇编指令之数据操作指令

数据搬移指令&#xff1a;立即数&#xff1a;在待判断的32位数&#xff08;以十六进制展开&#xff09;中&#xff0c;寻找一个0~255&#xff08;即0x00~0xff&#xff09;之间的数值&#xff0c;然后将这个数值循环右移偶数个位置&#xff0c;可以得到待判断的数&#xff0c;即…

Vue $nextTick

我们用一个例子来说明$nextTick的作用&#xff1a; 我们用一个变量showIpt来控制input框的显示和隐藏&#xff0c;默认是隐藏。 我们点击一个按钮后显示这个输入框的同时&#xff0c;input还要自动获取焦点。 但是我们点击按钮过后并没有生效。 为什么&#xff1f;this.show…