当前位置: 首页 > news >正文

C++入门(下)--《Hello C++ World!》(2)(C/C++)

文章目录

  • 前言
  • 引用
    • 临时变量的一个小知识点
  • 引用和指针的区别
  • 内联函数
    • 内联函数
    • 遗忘的补充:宏函数
  • 指针空值---nullptr
  • 作业部分

前言

这期会把上一期C++入门没有讲完的引用,内联函数跟nullptr给讲完,下期将会讲解类和对象
关于 C++的文章通通收录在了这个专栏里面:
在这里插入图片描述

引用

概念:引用是给已存在变量取了一个别名,它和它引用的变量共用同一块内存空间。

规则:

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

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

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

4.引用过程中,权限不可以扩大,只能平移和缩小(权限指的是eg:以后可以既读又改,现在只能读了)

使用场景:

1.作为函数的形参

1.做输出型参数–想里面改变,对应外面也改变的那种

2.减少拷贝,提高效率–对大对象(参数很多的那种,比如:一万个形参都用引用)或者深拷贝对象有用

2.作为函数的返回值

1.减少拷贝,提高效率–对大对象(参数很多的那种,比如:一万个形参都用引用)或者深拷贝对象有用(传值返回用拷贝,传引用返回不要拷贝)

2.修改返回值/获取返回值

 使用举例:
int a = 10;
int& ra = a;int* x = &a;
int*& p = x;引用一旦引用一个实体,再不能引用其他实体:举例:
int a = 10;
int& ra = a;
int b = 11;
ra = b;//这句话是改变ra的值,不是让ra成为b的引用一个变量可以有多个引用,举例:
int a =0;
int& b = a;
int& c= b;引用过程中,权限不可以扩大,只能平移和缩小,举例:(这个点属于常引用的)const int a = 0;int& b = a;//这个是不行的,扩大了int x = 0;
int& y = x;
const int& z = x;//这里是权限缩小
x++;//这里会间接导致z++,但是z不能直接自己++const int& m = 10;这样也是允许的
但是int& m =10;就不行
引用作函数返回值:
先举个例:
非引用:
int Count()
{int n = 0;n++;
return n;//这个的话是靠创建临时变量(变量占字节小的时候才是用寄存器)去过渡
}引用:
int& Count()
{int n = 0;n++;
return n;
}
int main()
{
int ret = Count();
}这样的话,分两种情况:
1.如果Count函数结束,栈帧销毁,没有清理栈帧的话,那么ret的结果侥幸是正确的2.如果Count函数结束,栈帧销毁,清理栈帧,那么ret的结果是随机值
(特别是Count用了之后还进行了其他操作去用栈帧)总结:引用做返回值时,出了函数作用域,对象不在了,就不能用引用返回,还在就可以用引用返回
eg:
int& Count()
{static int n = 0;n++;
return n;//这里返回的相当于是n的别名
}
int& SLAt(SeqList*ps,int pos)
{
return ps->a[pos];
}
易错点:
int& Count()
{int n = 0;n++;// ...return n;
}int main()
{int ret = Count();//这里的ret是n的拷贝,不是引用,要int& ret = Count();才是return 0;
}

临时变量的一个小知识点

编译器为了执行代码搞的大部分临时变量都具有常性

举例: double dd = 12.21;int& i = dd;//这时就会报错但是
double dd = 12.21;
int i1 = dd;
int& i = i1;//这就不会报错int func()
{static int x = 0;return x;
}
main函数里面int& ret = func();//这样就不行要const int& ret = func();//这样才行

常见情况:

1.发生类型转换时会产生临时变量来辅助,这时的就具有常性

2.传值引用返回值返回时也会产生临时变量,也有常性

3.隐式转换

(临时变量没有常性的情况以后才会提及)

引用和指针的区别

语法层面:

引用是不开空间,是对a取别名 指针是开空间,存储a的地址

从底层汇编指令实现的角度来看:引用是类似指针的方式实现的–也就是引用也会开空间

平时理解:引用的语法理解当不开空间,要是谈到底层的话,那就是会开空间

引用和指针的不同点:
1.引用概念上定义一个变量的别名,指针存储一个变量地址。
2.引用在定义时必须初始化,指针没有要求
3.引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
4.没有NULL引用,但有NULL指针
5.在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
6.引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
7.有多级指针,但是没有多级引用
8.访问实体方式不同,指针需要显式解引用,引用编译器自己处理(底层)
9.引用比指针使用起来相对更安全

内联函数

内联函数

概念:以inline修饰的函数叫做内联函数

作用:在编译器编译时,会在调用内联函数的地方把内联函数在那展开(不会像自定义函数一样是通过call搞得),这样可以避免函数调用建立栈帧的开销

适用范围:适用于短小并且频繁调用的函数。

原因:如果不频繁的话,用的效果不明显;函数比较长的话,在编译过程中的指令会非常长,导致可执行程序的占用内存会很大(现在编译器可以主动避免这个了)

inline对于编译器只是一个建议,最终是否成为inline,是由编译器自己决定的

编译器会ban掉的一些函数(不让他成为内联,让他就是普通的函数):

1.比较长的函数

2.大多数递归函数

注意:深度深的递归跟频繁调用的函数是有差别的,不能等同:深度深的递归eg:1000*999一直乘到1这样一下算下来的话会导致文件变得很大,频繁调用函数则不会

默认debug模式下,inline在编译过程中不会起作用,否则就不方便调试了

在release版本下才会起作用,但是release版本下又看不了编译,这个需要自己去设置(不同软件的设置方法不一样)

注意:inline不建议声明和定义分离(要在同一文件下),分离会导致链接错误–原因:inline被展开之后,定义就没有函数地址了,链接就找不到那个函数的地址,就会报错

内联函数展示:
inline f(int a)
{return a*10;
}//也就在前面加个inline就行了

遗忘的补充:宏函数

优点:不需要建立栈帧,提高调用效率

缺点:复杂,容易出错;可读性差;不能调试(因为预编译阶段进行了替换)

C++中替代宏函数的方法:内联函数

举例:
正确写法:#define f(x,y) ((x)+(y))
易错点:1.后面加了;号和return2.f(x,y)写成了f(int x, int y)  3.x和y没有加()以及大整体没有加括号--出错原因:万一是f(a|b,a&b)隐藏知识点:+比&和|的优先级高

指针空值—nullptr

在后续表示指针空值时建议最好使用nullptr。

跟用0或者NULL表示指针空值的区别:

void f(int)
{
}void f(int*)
{
}f(0);//会调用第一个--默认情况下0被看作是一个整形常量,不行的话才是(void*)0
f(NULL);
//会调用第一个--NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量(具体看编译器是哪种)
f(nullptr);//会调用第二个

补充知识:像这种形参只给了类型的,没有给名字的也行,如果函数里面没有使用这个形参的话

作业部分

引用传值,指针传地址(X)
原因:引用表面好像是传值,其本质也是传地址删除空指针是无害的,但是不能删除引用(对)
原因:空指针没有任何指向,删除无害,引用是别名,删除引用就删除真实对象
关于c++的inline关键字,以下说法正确的是(BC) 
A.使用inline关键字的函数会被编译器在调用处展开
B.头文件中可以包含inline函数的声明
C.可以在同一个项目的不同源文件内定义函数名相同但实现不同的inline函数
D.递归函数也都可以成为inline函数A:要看编译器有没有把他处理成内联函数
B:如果头文件里有内联函数的声明和定义的话就行
http://www.xdnf.cn/news/333721.html

相关文章:

  • 【C++】手搓一个STL风格的string容器
  • 【开源解析】基于Python的智能文件备份工具开发实战:从定时备份到托盘监控
  • 键盘固件刷写详解:Bootloader
  • AppInventor2如何实现写文件不覆盖,而是在文件尾部追加?
  • 使用 React 实现语音识别并转换功能
  • Java游戏服务器开发流水账(2)开发中Maven的管理
  • CROSS 技术全解析:边缘计算如何成为行业价值新引擎
  • Linux下使用openssh搭建sftp服务
  • SQL:MySQL函数:字符串函数
  • 金仓数据库征文-金仓KES数据同步优化实践:逻辑解码与增量同步
  • 深入理解负载均衡:传输层与应用层的原理与实战
  • KRaft (Kafka 4.0) 集群配置指南(超简单,脱离 ZooKeeper 集群)还包含了简化测试指令的脚本!!!
  • WSL部署CosyVoice
  • node.js 实战——express图片保存到本地或服务器(七牛云、腾讯云、阿里云)
  • 能耗优化新引擎:EIOT平台助力企业降本增效
  • 需求分析阶段测试工程师主要做哪些事情
  • 华为云Astro后端开发中对象、事件、脚本、服务编排、触发器、工作流等模块的逻辑关系如何?以iotDA数据传输过程举例演示元素工作过程
  • 精品,架构师总结,MySQL 5.7 查询入门详解
  • trae ai编程工具
  • C++ STL入门:set 集合容器
  • 从父类到子类:C++ 继承的奇妙旅程(1)
  • Windows环境下MySQL Installer安装后执行`mysql`和`mysql -v`报错的问题解决方法
  • git 多个提交记录合并为一个
  • MySQL从入门到精通(四):SQL语言—DML
  • ts-node 深入讲解
  • Open CASCADE学习|ApplicationFramework 框架使用指南
  • linux查java进程CPU高的原因
  • 人工智能数据标注服务规范
  • SQL 更新U9C的请购单终止后不能转PO的问题
  • C++实现摩斯电码