【C++初探:简单易懂的入门指南】二

【C++初探:简单易懂的入门指南】二

  • 1.引用
    • 1.1引用做函数的参数
    • 1.2 引用做返回值
      • 1.2.1 关于引用做返回值的几点补充
    • 1.3 多引用(对一个变量取多个别名)
    • 1.4 引用类型一致性原则以及权限的问题阐述
    • 1.5引用的效率问题
    • 1.6引用和指针的比较
  • 2.auto关键字
    • 2.1 auto关键字的使用细则
    • 2.2 auto关键字不能使用的场景
  • 3.特殊的for循环(基于范围)
    • 3.1基于范围for的语法
    • 3.2 基于范围for的使用规则

❤️博客主页: 小镇敲码人
🍏 欢迎关注:👍点赞 👂🏽留言 😍收藏
🌞任尔江湖满血骨,我自踏雪寻梅香。 万千浮云遮碧月,独傲天下百坚强。 男儿应有龙腾志,盖世一意转洪荒。 莫使此生无痕度,终归人间一捧黄。🍎🍎🍎
❤️ 什么?你问我答案,少年你看,下一个十年又来了 💞 💞 💞

在这里插入图片描述

1.引用

引用是C++与C不同的点之一,它虽然是给变量取别名,算不上一个新定义的概念,但是它和typedef的区别还是存在的,例如它可以在函数做参数和返回值时使用,但是typedef没有这种功能,&是一个操作符,表示引用,你可以理解为要给一个变量取别名。

1.1引用做函数的参数

下面我们给出一段代码帮助你理解引用的最常见的功能:

#include<iostream>
using namespace std;int Add(int& a, int& b)
{return a + b;
}int main()
{int a = 3;int b = 4;int c = Add(a, b);cout << "a+b = " << c << endl;return 0;
}

运行结果截图:

在这里插入图片描述
上面Add函数的两个参数就是实参ab的别名,就相当于我们人类社会里的绰号,就比如你叫张三,你的同学可能叫你老张,你的家里人可能叫你三儿,虽然叫法不同,但是它们都代表你这个人,而且引用是不额外开空间的,我们可以利用下面的代码简单的验证一下:

#include<iostream>
using namespace std;void Fun(int& b)
{cout <<  "b的地址为:" << &b << endl;
}int main()
{int a = 3;Fun(a);cout << "a的地址为:" << &a << endl;return 0;
}

运行截图为:
在这里插入图片描述
可以看到这里引用是不开空间的,因为ba的别名,所以编译器不会给它们开两份空间。

1.2 引用做返回值

#include<iostream>
using namespace std;int& Add(int& a, int& b)
{static int c = a + b;cout << "c的值为" << c << endl;cout << "c的地址为" << &c << endl;return c;
}int main()
{int a = 3;int b = 4;int& d = Add(a, b);cout << "d的值为" << d << endl;cout << "d的地址为" << &d << endl;return 0;
}

运行结果:

在这里插入图片描述
这里d就是c的别名,所以它们的地址是一样的,static修饰c,是因为临时变量开在栈区,出了函数的作用域,它就销毁了,但是如果加了staticc就变成了静态变量,静态变量的空间是开在静态区的,程序的结束,它的生命周期才算结束,至于为什么d作为c的别名,在外面还可以访问,可以类比,函数以引用传参理解,这里博主认为引用扩大了c的的作用域,只要c的生命周期没结束,它以引用返回,在main函数里面我们就是能访问到c
如果你不相信,我们可以通过如下代码简单的验证一下:

#include<iostream>
using namespace std;int& Add(int& a, int& b)
{static int c = a + b;cout << "c的值为" << c << endl;cout << "c的地址为" << &c << endl;return c;
}int main()
{int a = 3;int b = 4;int& d = Add(a, b);d++;Add(a, b);return 0;
}

运行结果截图:

在这里插入图片描述

  • 这里注意一点,由于c开在静态区,只要程序不结束,它的空间一直不会被系统回收,所以定义c那部分代码是不会重新执行一次的,这恰好可以帮助我们验证以引用返回,就可以在main函数里面访问原本作用域在Add函数里面的静态变量c

1.2.1 关于引用做返回值的几点补充

细心的朋友可能会在vs2019上发现这样的问题,上述代码,即使不加static似乎也能正常运行。
在这里插入图片描述
这里博主认为是编译器检查机制的一个漏洞,系统没有将空间及时的回收,如果我把代码改成这样,系统把空间回收使用后,d的值就变成随机值了,

在这里插入图片描述

但是如果你加了static就不会出现这种问题:

在这里插入图片描述

1.3 多引用(对一个变量取多个别名)

在C++中我们是支持对一个变量进行多次引用的:

#include<iostream>
using namespace std;int main()
{int a = 3;int& b = a;int& c = a;int& d = a;cout << "b的地址为:" << &b << endl << "b的值为" << b << endl;cout << "c的地址为:" << &c << endl << "c的值为" << c << endl;cout << "d的地址为:" << &d << endl << "d的值为" << d << endl;c = 2;cout << "b的地址为:" << &b << endl << "b的值为" << b << endl;cout << "c的地址为:" << &c << endl << "c的值为" << c << endl;cout << "d的地址为:" << &d << endl << "d的值为" << d << endl;return 0;
}

运行结果截图:

在这里插入图片描述
而且由于引用只是取别名,本质上它们是同一个变量,所以修改一个就修改了它们所有的值。

1.4 引用类型一致性原则以及权限的问题阐述

上述代码如果加上这样一行就会报错:

#include<iostream>
using namespace std;int main()
{int a = 3;int& b = a;double& c = a;int& d = a;cout << "b的地址为:" << &b << endl << "b的值为" << b << endl;cout << "c的地址为:" << &c << endl << "c的值为" << c << endl;cout << "d的地址为:" << &d << endl << "d的值为" << d << endl;c = 2;cout << "b的地址为:" << &b << endl << "b的值为" << b << endl;cout << "c的地址为:" << &c << endl << "c的值为" << c << endl;cout << "d的地址为:" << &d << endl << "d的值为" << d << endl;return 0;
}

报错截图:

在这里插入图片描述
所以我们在定义引用时,不能改变原变量的类型。
关于权限的问题,主要围绕const这个关键词展开:

  • const修饰的变量,当对其引用时,不能不加const,因为其是不可修改的常量,不加const是对其权限的放大,编译器是不允许的。
  • 但是如果一个变量没有被const修饰,在引用时,可以加上const,进行权限的缩小,这个编译器是允许的。
    在这里插入图片描述
    但是缩小权限又是允许的:
    在这里插入图片描述
    在这里插入图片描述
    这里有个比较奇怪的现象,为什么我const修饰b这个别名,b不能修改,我却可以通过修改a来修改a的值,与此同时b的值也被修改了,这里本博主也比较疑惑,大家可以在评论区或者私信来教教博主。
  • 关于引用还有一点需要说明,引用必须给初始值,引用的对象可以是全局变量、临时变量、但不能是常量(const修饰的变量例外)。

1.5引用的效率问题

引用的效率是很高的,因为它不会额外的去开空间,下面两段代码希望可以帮助你来理解:

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

运行截图:

在这里插入图片描述
这里clock函数是表示当前程序运行的时间,单位是毫秒,可以看到,传值和传引用的效率差的还是很大。

  1. 传值返回和传引用返回的效率比较
include<iostream>
using namespace std;
#include <time.h>struct A
{int a[100000];
};
A a;
//传值返回
A TestFunc1() 
{return  a;
}
//传引用返回
A& TestFunc2() 
{return a; 
}
void TestRefAndValue()
{// 以值作为函数的返回值类型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(A)-time:" << end1 - begin1 << endl;cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}int main()
{TestRefAndValue();return 0;
}

运行截图:

在这里插入图片描述
可以看到,由于传值传参和传值返回都需要额外拷贝开一份空间,而传引用不需要,所以效率是差别还是很大的,所以一般情况下,如果不是特殊需求,传引用的性价比还是更高的。

1.6引用和指针的比较

相同点:

  1. 效率都很高。
  2. 底层汇编代码很相似,引用的底层实现是指针,也就是引用在底层实现上是开了空间的。
  3. 传址引用和传引用返回都可以改变该变量的值,当然特殊情况例外(const修饰的变量)。

不同点:
1.引用在语法概念上没有开空间。
2.指针不初始化不会报错,但是引用不行。
3.创建一个引用之后,这个引用就不能再作为其它变量的别名了,但是指针变量可以指向其它相同类型的变量。

2.auto关键字

auto是C++上面的一个关键字,它可以自动识别右值的类型,我们主要介绍C++11标准的auto关键字。

#include<iostream>
using namespace std;int& Add(int& a, int& b)
{static int c = a + b;return c;
}
int main()
{int a = 2;int b = 3;auto c = Add(a, b);cout << "b的类型为:" << typeid(b).name() << endl;return 0;
}

运行结果:

在这里插入图片描述

  • 注意:引用的类型和被引用的对象是一致的,typeid(变量名).name()可以用来打印变量的类型。

可能会有人认为这样没有什么实质的作用,但是当那个函数的返回值类型(因为C++有很多自定义类型)非常复杂时,auto关键字就非常方便了。

2.1 auto关键字的使用细则

auto关键字可以和指针、引用结合起来使用,但是必须给它初始化,否则语法上是无法通过的。

#include<iostream>
using namespace std;
int main()
{int a = 2;int b = 3;auto* c = &a;auto d = &a;auto& e = a;cout << "c的类型为:" << typeid(c).name() << endl;cout << "d的类型为:" << typeid(d).name() << endl;cout << "e的类型为:" << typeid(e).name() << endl;return 0;
}

运行截图:
在这里插入图片描述

  • 这里在定义指针时autoauto*没有什么区别,但是在定义引用时必须加上&操作符。

如果不初始化,就会报这样的错误:

在这里插入图片描述

这也间接说明了auto不是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量的类型必须由编译器在编译时期推导而得,所以如果你不给初始值,那么编译器就无法进行推导,auto在编译期间会被替换。

auto关键词可以同时定义很多变量,但是它只会对第一个变量的类型进行推导,从而用这个变量的类型来定义其它变量,所以这些变量的类型要相同,详细请看下图:
在这里插入图片描述

2.2 auto关键字不能使用的场景

  1. auto关键字不能作为函数的参数,因为编译器无法对其类型进行推导。
  2. auto不能用来声明数组。
    在这里插入图片描述
  • 这里有一点需要说明的时,虽然VS2019上,以auto作为返回值的类型是可以编译通过的,但还是不建议这样去做,因为如果我们后期想要去找到其返回值的类型还是比较麻烦的,因为可能出现这样的情况:
    在这里插入图片描述
    这里还只有两个嵌套,如果工程量一大,嵌套的次数变多,想知道某个函数的返回值就是一件困难的事情,有人说可以用typeid(变量名).name()来知道其类型,我直接写出来不是更方便吗?

  • auto在实际中最常见的优势用法就是跟以后会提到的C++11提供的新式for循环,还有lambda表达式等进行配合使用。

3.特殊的for循环(基于范围)

3.1基于范围for的语法

在C语言/C++98中,如果我们想遍历一个数组,你可能会这样做:

#include<iostream>
using namespace std;int main()
{int array[] = { 1,2,3,4,5,6,7,8 };for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++){array[i] *= 2;}for (int* p = array; p < array + sizeof(array) / sizeof(array[0]); p++){cout << *p << " ";}return 0;
}

但是对于一个本身就有范围的集合来说,我们程序员自己去控制范围似乎有点多余了,而且还很容易出错,因此C++11中引出了基于范围的for循环。它的for循环括号里被:分为两部分,左边是用来迭代的变量(迭代可以理解为遍历),右边是范围,代码是这样实现的:

#include<iostream>
using namespace std;int main()
{int array[] = { 1,2,3,4,5,6,7,8 };for (auto& p : array){p *= 2;}for (auto p : array){cout << p << " ";}return 0;
}

这里auto可以换成数组相应的类型,但是使用auto编译器可以帮助我们在编译期间推导类型,十分方便,但是我们如果想要改变数组的值就得使用引用了,因为如果不是引用左边的变量只是我们数组值的一个拷贝,改变它不能改变我们数组中的值。

下面一段代码希望帮助你完全理解它们:

#include<iostream>
using namespace std;int main()
{int array[] = { 1,2,3,4,5,6,7,8 };for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++){cout << &array[i] << " ";}cout << endl;for (auto p : array){cout << &p << " ";}cout << endl;for (auto& p : array){cout << &p << " ";}return 0;
}

运行结果:

在这里插入图片描述

我们可以看到引用是控制台第三行,它的地址与数组每个元素的地址是相同的,说明编译器在迭代时如果是引用,每执行一次for循环,p的空间就会被回收,不然由于创建引用变量后,这个引用变量不能作为其它变量的别名可知,打印出的地址应该是相同的才对,此时不相同,所以博主猜测应该是回收了,一次for循环执行一次引用变量的创建,至于普通的迭代,可以看出第二行的地址是完全相同的,说明编译器只给这个变量开了一次空间,剩下的每次for循环都是简单的把数组中的值赋值给它。

3.2 基于范围for的使用规则

  1. for循环的范围必须是确定的。
    对于数组而言,它的范围就是从第一个元素到最后一个元素
  • 注意,以下代码就有问题,它的范围是不确定的。
#include<iostream>
using namespace std;void Fun(int array[])
{for (auto p : array){cout << p << " ";}
}
int main()
{int array[] = { 0,1,2,4,3,5 };Fun(array);return 0;
}

报错截图:

在这里插入图片描述

这是你这样写array似乎是一个数组,其实不然,它是一个保存了数组首元素的指针,你在里面计算数组的范围是无法计算出来的:

在这里插入图片描述
所以你也无法知道范围,自然就会报错。

在这里插入图片描述

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

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

相关文章

共谈信创谋发展 | 开源网安主办的信创生态构建沙龙圆满完成

​10月26日&#xff0c;由珠海市工业和信息化局、珠海市高新区科技创新和产业发展局指导&#xff0c;珠海华发产业园与开源网安珠海公司等联合主办的“赋能数字转型 提速国产替代”—Uni-Idea信创生态构建沙龙在华发信创产业园成功举办&#xff0c;近百位行业代表参加本次活动&…

使用requests库进行HTTP爬虫编程

目录 一、安装requests库 二、发送HTTP请求 三、解析HTML页面 四、处理HTTP响应和异常 五、使用代理和会话管理 六、使用多线程或多进程提高效率 七、数据存储和处理 八、注意事项和总结 在当今的数字化世界中&#xff0c;数据已经成为了一种宝贵的资源。而网络爬虫程序…

基本微信小程序的体检预约小程序

项目介绍 我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;体检预约系统小程序被用户普遍使用&#xff0c;为方便用户…

使用ControlNet生成视频(Pose2Pose)

目录 ControlNet 介绍 ControlNet 14种模型分别是用来做什么的 ControlNet 运行环境搭建 用到的相关模型地址 ControlNet 介绍 ControlNet 是一种用于控制扩散模型的神经网络结构&#xff0c;可以通过添加额外的条件来实现对图像生成的控制。它通过将神经网络块的权重复制到…

OPNET <<< Program Abort >>> Standard function stack imbalance

OPNET <<< Program Abort >>> Standard function stack imbalance OPNET 问题原因及解决办法 OPNET 问题 OPNET仿真时遇到此问题&#xff1a; <<< Program Abort >>> Standard function stack imbalance 原因及解决办法 出现此问题是因…

【设计模式】第14节:结构型模式之“代理模式”

一、简介 代理模式&#xff08;Proxy Design Pattern&#xff09;在不改变原始类&#xff08;或叫被代理类&#xff09;代码的情况下&#xff0c;通过引入代理类来给原始类附加功能。 二、优点 关注点分离访问控制延迟实例化远程访问缓存增加附加功能 三、应用场景 访问控…

【2021集创赛】海云捷迅杯一等奖:基于稀疏卷积与层融合的流水线优化方案

海云捷迅杯:基于FPGA C5Soc的MobileNetV1 SSD目标检测方案设计 本作品参与极术社区组织的有奖征集|秀出你的集创赛作品风采,免费电子产品等你拿~活动。 **杯赛题目&#xff1a;**海云捷迅杯——基于FPGA C5Soc的MobileNetV1 SSD目标检测方案设计 设计任务&#xff1a; 基于已训…

STM32G030F6P6 芯片实验 (一)

STM32G030F6P6 芯片实验 (一) 淘宝搞了几片, 没试过 G系列, 试试感觉. 先搞片小系统版: 套 STM32F103C8T6小系统板格式. 原理图: (1) Ref 有点跳, 从 STM32F103C8T6 系统板改的, 没重编号. (2) Type-C 纯给电, 砍了 16pin的, 直接换 6pin的。 (3) 测试LED放 B2。 (4) 测试底…

Android 10.0 framework关于systemUI状态栏透明背景的功能实现

1.概述 在10.0的系统产品定制化开发中,在对于系统原生SystemUI的状态栏背景在沉浸式状态栏的 情况下默认是会随着背景颜色的变化而改变的,在一些特定背景下状态栏的背景也是会改变的,所以由于产品开发需要 要求需要设置状态栏背景为透明的,所以就需要在Activity创建的时候…

仿真翻页企业内刊制作方法

现如今很多企业都会把自身的企业文化做成电子内刊形式&#xff0c;不再停留于传统纸质的形式&#xff0c;而这种电子版的书更容易被翻阅和传播。特别是员工可以随时随地来阅读企业的文化价值和发展趋向&#xff0c;进而创造出更多的经济效益。不得不说&#xff0c;一本企业文化…

Mysql数据库 4.SQL语言 DQL数据查询语言 查询

DQL数据查询语言 从数据表中提取满足特定条件的记录 1.单表查询 2.多表查询 查询基础语法 select 关键字后指定要查询到的记录的哪些列 语法&#xff1a;select 列名&#xff08;字段名&#xff09;/某几列/全部列 from 表名 [具体条件]&#xff1b; select colnumName…

大数据前置学习基础准备(非常详细!)

1.需要的环境 需要3台服务器&#xff0c;centos7 为集群&#xff0c;全部设置为nat模式 2.整个环境大体 1.设置三台Linux虚拟机的主机和固定ip 2.在Linux系统以及本机系统中配置了主机名映射 3.配置了三台服务器之间root用户的ssh免密互通 4.安装配置JDK环境 5.关闭防火墙和SEL…

【Linux】进程概念(下)

进程概念 一、环境变量1. 命令行参数2. 常见的环境变量&#xff08;1&#xff09;PATH&#xff08;2&#xff09;PWD&#xff08;3&#xff09;HOME&#xff08;4&#xff09;env 查看所有的环境变量 3. 获取环境变量&#xff08;1&#xff09;通过代码获取环境变量&#xff08…

应用开发平台集成工作流系列之17——流程建模功能前端设计与改造回顾

背景 对于流程设置不友好的问题&#xff0c;国内钉钉另行设计与实现了一套流程建模模式&#xff0c;跟bpmn规范无关&#xff0c;有人仿照实现了下&#xff0c;并做了开源&#xff08;https://github.com/StavinLi/Workflow-Vue3&#xff09;&#xff0c;效果图如下&#xff1a…

HiQPdf Library for .NET - HTML to PDF Crack

HiQPdf Library for .NET - HTML 到 PDF 转换器 .NET Core&#xff0c;用于 .NET 的 HiQPdf HTML 到 PDF 转换器 &#xff1a;HiQPdf HTML to PDF Library for .NET C# 和 HTML to PDF .NET Core 为您提供了一个现代、快速、灵活且强大的工具&#xff0c;只需几行代码即可创建复…

CRM客户管理系统源码 带移动端APP+H5+小程序

CRM客户管理系统源码 带移动端APPH5小程序 开发环境: thinkphp mysql 功能介绍&#xff1a; 1、 办公管理&#xff1a;审批管理、工作报告、日程管理、办公审批、公告管理 2、 客户管理&#xff1a;我的客户、客户列表、成交客户、行业类别、预查、地区列表、客户状态、客…

ES性能优化最佳实践- 检索性能提升30倍!

Elasticsearch是被广泛使用的搜索引擎技术&#xff0c;它的应用领域远不止搜索引擎&#xff0c;还包括日志分析、实时数据监控、内容推荐、电子商务平台、企业级搜索解决方案以及许多其他领域。其强大的全文搜索、实时索引、分布式性能和丰富的插件生态系统使其成为了许多不同行…

JavaScript组合模式

JavaScript组合模式 1 什么是组合模式2 宏命令3 示例&#xff1a;扫描文件夹4 引用父对象 1 什么是组合模式 组合模式是一种结构型设计模式&#xff0c;用于将对象组合成树形结构&#xff0c;并使客户端能够统一处理单个对象和组合对象。它通过使用继承和组合两个概念&#xf…

【MySQL】C语言连接数据库

文章目录 一、安装 MySQL 库二、MySQL C API 相关接口1、C API 官方文档2、初始化 MYSQL3、连接 MySQL4、下发 mysql 指令5、获取 mysql 查询结果6、释放 MYSQL_RES 对象7、关闭 MySQL 连接8、MySQL 其他操作9、总结 三、使用图形化工具连接 MySQL 一、安装 MySQL 库 我们之前…

java try throw exception finally 遇上 return break continue造成异常丢失

如下所示&#xff0c;是一个java笔试题&#xff0c;考察的是抛出异常之后&#xff0c;程序运行结果&#xff0c;但是这里抛出异常&#xff0c;并没有捕获异常&#xff0c;而是通过finally来进行了流程控制处理。 package com.xxx.test;public class ExceptionFlow {public sta…