C++入门(中篇)

news2024/12/22 22:04:12

🔥🔥本章重内容
在这里插入图片描述

C++入门

  • 1. 函数重载
    • C++是怎么支持函数名重载的呢?
  • 2.引用
    • 2.1引用特性
    • 2.2常引用
    • 2.3使用场景
      • 1. 做参数
      • 2. 做返回值
    • 2.4引用和指针的区别
  • 3.内联函数

1. 函数重载

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表==(参数个数 或 类型 或 类型顺序)==不同,常用来处理实现功能类似数据类型不同的问题。
那函数重载是上面样呢?
列如:

#include <iostream>
using namespace std;

void Add(int a, int b)
{
	cout << a + b << endl;
}

void Add(double a, double b)
{
	cout << a + b << endl;
}

int main()
{
	Add(1, 2);
	Add(1.1, 2.2);
	return 0;
}

上面这段代码在C语言中肯定是编不过去的,C语言会报错说,函数重命名
但是C++支持这样写,只要函数的**(参数个数 或 类型 或 类型顺序)**不同就可以。

//参数类型不同
void Add(int a, int b)
{
	cout << a + b << endl;
}

void Add(double a, double b)
{
	cout << a + b << endl;
}

//参数个数不同
void fun()
{
	cout << "fun()" << endl;
}

void fun(int a)
{
	cout << "fun(int a)" << endl;
}

//参数顺序不同
void f(int a, char b)
{
	cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
	cout << "f(char b, int a)" << endl;
}

C++是怎么支持函数名重载的呢?

C++中有函数名修饰规则,但是这个规则不是C++创始人给的,是由写编译器的人来给的,所以Linux与Windows的函数名修饰规则是不同的。
函数名修饰规则会给函数另起一个名字,简单的了解一下汇编代码。
在这里插入图片描述
图中箭头是main函数中对应的指令,call的意思是跳转到被调用的函数那里,可以看到两次call他们的地址是不同的,所以C++允许函数名相同,但(参数个数 或 类型 或 类型顺序)不能相同。
看一下输出结果:
在这里插入图片描述
注意:如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分。

2.引用

引用就相当于是起别名,相当于你的大名和小名,它都指的是你。

//类型& 引用变量名(对象名) = 引用实体;
void fun()
{
	int a = 10;
	int& ra = a;//<====定义引用类型
	printf("%p\n", &a);
	printf("%p\n", &ra);
}

代码中的int& ra = a;就是对a的引用
既然是对a起别名那他们的地址相同吗?
如下图所示:
在这里插入图片描述
所以如果我们改变别名,原本的值也会发生改变。

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

2.1引用特性

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体
int main()
{
	//这样的代码是不行的
	int& b;

	//一个变量可以有多个引用
	int a = 0;
	int& c = a;
	int& d = a;

	//引用一旦引用一个实体,再不能引用其他实体
	int x = 5;
	int y = 6;
	int& z = x;
	int& z = y;//错误写法

	return 0;
}

前面两种好理解,讲一下后面这种为什么不行。

解释为什么引用一旦引用一个实体,再不能引用其他实体
当我们写了两个
int& z = ;
int& z = ;
编译器会认为 z 是重定义,进行了多次初始化。
就像我们写了两个 int a = ; int a = ;这样肯定是不行的。
如果我们写成
int& z = x;
z = y;
这相当于我们把y的值赋给了z
所以说:引用一旦引用一个实体,再不能引用其他实体

2.2常引用

什么是常引用呢,就是我们引用的是常量。
先来说结论,大家来判断下面的代码是否正确。
结论:引用过程中权限可以平移或缩小,但不能扩大。

int main()
{
	//问题一
	const int a = 10;
	int& ra = a;

	//问题二
	int b = 10;
	const int& rb = b;

	//问题三
	double d = 1.1;
	int& rd = d;
	return 0;
}

前两个问题大家都可以根据结论判断它是否正确。
问题一是错误的,因为它将a的权限扩大了,原本a是const的常量,我们在起别名的时候只能是常量别名。
问题二是正确的,我们说权限可以缩小或平移,所以别名可以加上const。
问题三呢?它是给一个double类型的变量d起别名,但别名的类型是int类型。
有的同学可能会说,给double类型的数据起别名只能用double类型。
那我们先来看结果。
在这里插入图片描述
为什么会有警告说是从“double”转换到“const int”呢?
在这里插入图片描述
所以会有上面那样的警告。
我们说权限可以缩小和平移,如果我们给int前加上const那么程序是不是就可以正常运行了?
在这里插入图片描述
答案是正确的。

2.3使用场景

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;
}

我们发现引用使用起来更方便,使用指针有时候我们可能还会传值错误,忘记传地址过去。
但我们使用引用的话,根本不需要去考虑传地址的问题。
引用直接给变量起别名,使用起来更方便。

2. 做返回值

那我们先来回忆一下之前C语言用类型做返回值。
在这里插入图片描述
如果返回值的内存占用大,我们直接返回引用值。
如果是返回引用的话,可以直接跳过拷贝,赋值给a。减少时间和空间的使用。
那我们以后是所有带返回值的函数都要用引用返回吗?
当然不是。
就比如上面那段代码如果用引用值返回的话,会带来后果呢?
在这里插入图片描述
图片中的输出结果看似没有问题,但实际是有问题的。
我们返回c的别名,但函数调用结束后,栈帧会被销毁,空间会被释放掉。

这里打印ret的值是不确定的
如果Count函数结束,栈帧销毁,没有清理栈帧,那么ret的结果侥幸是正确的
如果Count函数结束,栈帧销毁,清理栈帧,那么ret的结果是随机值

当我们再调用依次其他函数,或再多写几行代码,之前的函数的数据会被覆盖。
那怎么证明呢?
我们用引用来接收返回值。相当于是返回值的别名。
在这里插入图片描述
因为a是返回值的别名,所以后面再调函数,会改变a的值。
在这里插入图片描述
当我们调用其它函数后,它会覆盖掉当前的函数。所以a会变为随机值。
在这里插入图片描述
注意:如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用
引用返回,如果已经还给系统了,则必须使用传值返回。

2.4引用和指针的区别

在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。(他们的地址相同我们有证明过)。
在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

int main()
{
	int a = 10;

	int& ra = a;
	ra = 20;

	int* pa = &a;
	*pa = 20;
	return 0;
}

在这里插入图片描述
可以看到引用的汇编代码与指针的汇编代码是相同的,所以引用也会开辟一个指针的空间
32位平台下开4个字节,64位平台下开8个字节

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

3.内联函数

内联函数是怎么来的呢?
我们先看一段代码。

void Print(int i)
{
	cout << i << endl;
}

int main()
{
	for (int i = 0; i < 100; i++)
	{
		Print(i);
	}
	return 0;
}

这个函数会被调用100次,函数调用建立栈帧的开销会比较大。
而内联函数是在调用函数时直接在被调用的位置上展开的,不会开辟栈帧。
只需要在函数前加一个inline。这个函数就可能会成为内联函数。

//内联函数写法
inline void Print(int i)
{
	cout << i << endl;
}

在这里插入图片描述
在我们看汇编代码时,它为什么还是会call(调用函数)呢?
因为我们在debug版本下,debug模式下,编译器默认不会对代码进行优化。
但我们也可以对其进行修改,使inline可以在debug版本下进行优化。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
修改完成之后便可以观察汇编代码,看其是否以优化。
在这里插入图片描述
所以这就是内联函数的优点,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。
既然内联函数没有函数调用建立栈帧的开销,又可以提升运行效率那我们是不是应该全去使用内联函数呢?
在这里插入图片描述
如果我们都去使用内敛函数的话,上图会产生1000 * 50行代码,
不用内联函数的话就只会有1000行调用代码和20行Add的代码。
所以如果函数内容代码量大的话,我们就不能使用内联函数。

如果函数内代码行数大于10,我们就不能使用内联函数。

使用内联函数还需要注意一个点,就是内联函数的声明和定义必须放在一起。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果像我们这样写程序是过不去的。
因为内联函数是没有地址的,当我们链接时发现找不到fun函数,内联函数不会函数名修饰。
在这里插入图片描述

所以我们要把内联函数的定义与声明放在一起写。
当我们把声明和定义都写在头文件里时,程序就可以链接上了。
在这里插入图片描述

特性

  1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
  2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
  3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到

在这里插入图片描述

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

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

相关文章

安卓手机(微信小程序)抓蓝牙通信数据包

前言 因为公司需要......所以我就弄了一下,参考了很多别人的文章。 成果:它可以抓取微信小程序、安卓APP的蓝牙数据通信包。 开始 我是小米手机,所以我以我自己手机为例 通信过程操作 第一步 打开开发者选项,打开蓝牙调试日志和蓝牙数据包日志开关(如果两者只有其中…

电动汽车路径规划问题(Electric Vehicle-Routing Problem, EVRP)

今天给大家带来的是电动汽车路径规划问题(Electric Vehicle-Routing Problem, EVRP)的介绍&#xff0c;按照惯例先上目录&#xff0c;其中第三部分的主要内容出自文献“The Electric Vehicle-Routing Problem with Time Windows and Recharging Stations”。 目录 问题简介 …

Ceph入门都精通-设备类型都是hdd ssd设置错误

手动设置class [rootceph02 ~]# ceph osd crush rm-device-class osd.0 osd.1 osd.2 done removing class of osd(s): 0,1,2 [rootceph02 ~]# ceph osd crush set-device-class ssd osd.0 osd.1 osd.2 osd.3 Error EBUSY: osd.3 has already bound to class hdd, can not res…

【LeetCode】416. 分割等和子集

416. 分割等和子集&#xff08;中等&#xff09; 方法一&#xff1a; 0-1背包问题的普通解法 思路 首先&#xff0c;对题目做一个等价转换&#xff1a; 「是否可以从数组中选择一些正整数&#xff0c;使这些数的和等于整个数组元素和的一半」。 这样就可以看作一个 0-1背包问题…

【CAN总线】CAN驱动程序分析

文章目录 一.CAN介绍二.CAN的特点二.CAN的错误检测三.OSI七层模型 一.CAN介绍 CAN具有很高的可靠性&#xff0c;广泛应用于&#xff1a;汽车电子&#xff0c;工业自动化&#xff0c;船舶&#xff0c;医疗设备&#xff0c;工业设备等方面。 当只有2个设备,简单通信,可以看成US…

【GoodERP专题】第一章 GoodERP应用专题之good_expense 费用报销 的使用

文章目录 一、模块设计二、模块1.主数据2.费用申请单3.借款单【付款】4.费用报销单【挂账/付款】5.出差申请单6.出差借款单【付款】7.差旅费报销单【挂账/付款】 总结 一、模块设计 费用报销模块是基于GoodERP框架设计的&#xff0c;该模块是为了解决企业内外费用相关报销任务处…

设计模式:结构型模式 - 装饰者模式

文章目录 1.概述2.结构3.案例4.使用场景5.JDK源码解析6.代理和装饰者的区别 1.概述 我们先来看一个快餐店的例子。 快餐店有炒面、炒饭这些快餐&#xff0c;可以额外附加鸡蛋、火腿、培根这些配菜&#xff0c;当然加配菜需要额外加钱&#xff0c;每个配菜的价钱通常不太一样&…

Tars-Cpp 协程实现分析

作者&#xff1a;vivo 互联网服务器团队- Ye Feng 本文介绍了协程的概念&#xff0c;并讨论了 Tars Cpp 协程的实现原理和源码分析。 一、前言 Tars 是 Linux 基金会的开源项目&#xff08;THE TARS FOUNDATION PROJECTS GitHub&#xff09;&#xff0c;它是基于名字服务使用…

VMware安装

1.首先去官网下载vmware for windows 2.按以下步骤进行点击 1&#xff09; 2&#xff09; 3&#xff09; 4&#xff09; 5&#xff09;两个取消勾选 6&#xff09; 7&#xff09; 9&#xff09;会出现重新启动的窗口&#xff0c;重新启动就可以&#xff01;

z时代,汽车品牌如何玩转年轻化营销?

随着2.6亿Z世代成为消费主力军&#xff0c;越来越多的品牌意识到&#xff1a;抓住年轻消费者&#xff0c;就等于抓住了一个消费时代。但信息大爆炸的背景下&#xff0c;年轻人的关注阈值越来越高。如何在消费新浪潮下&#xff0c;通过营销打通圈层壁垒&#xff0c;刷新Z世代的品…

浏览器页面操作——实时监控网页变化,读取网页内容

浏览器页面操作功能介绍 浏览器页面操作是集简云的一款免费内置应用&#xff0c;它可以定时监控网页变化&#xff0c;精准捕捉所需信息。一键设置指定网页与元素&#xff0c;全自动监测并即时推送通知&#xff0c;助您在第一时间了解网页最新情况&#xff0c;让您更高效便捷地…

搞懂API,创建供外部系统更新数据 API 的最佳方法

在创建一个供外部系统更新本系统数据的 API 时&#xff0c;需要考虑以下几个方面&#xff1a; 身份认证和安全性&#xff1a;首先需要确保 API 能够安全地接收外部系统发送的请求&#xff0c;可以使用身份认证和加密等方式保护 API 的安全性&#xff0c;避免非法和恶意请求。 …

4年的测试工程师,你遇到过自身瓶颈期吗?又是怎样度过的?

从毕业到现在已经快4年啦&#xff0c;一直软件测试行业混迹。我不是牛人&#xff0c;但是自我感觉还算是个合格的测试工程师&#xff0c;有必要写下自己将近4年来的经历&#xff0c;给自我以提示&#xff0c;给刚入行的朋友提供点参考。 貌似这一点适应的行业最广&#xff0c;…

如何雇佣一名全民开发者?

注&#xff1a;全民开发的英文是Citizen Development&#xff0c;由咨询公司Gartner在2010年提出的概念&#xff0c;指非专业开发人员使用低代码或无代码平台创建应用程序&#xff0c;无需IT部门的支持&#xff0c;旨在提高生产力并降低开发成本。 国内普遍将Citizen Developme…

Node服务端开发 【什么是Node】

文章目录 &#x1f31f;前言&#x1f31f;Node.js&#x1f31f;特性&#xff1a;&#x1f31f;1. 单线程&#x1f31f;2.异步IO&#x1f31f;前端中的异步&#x1f31f;Node中的异步 &#x1f31f;3.跨平台&#x1f31f;4.运行速度快 &#x1f31f; 劣势&#xff1a;&#x1f3…

7.java程序员必知必会类库之数据库连接池

前言 在java中&#xff0c;“池”化的设计思想随处可见&#xff0c;池化的最终目的是为了对象复用&#xff0c;降低系统创建、销毁对象的成本&#xff0c;提升资源的可管理性。 尤其是一些大对象&#xff0c;创建销毁比较消耗资源的对象&#xff0c;池化可以极大提高效率&…

EMQX vs Mosquitto | 2023 MQTT Broker 对比

引言 物联网开发者需要为自己的物联网项目选择合适的 MQTT 消息产品或服务&#xff0c;从而构建可靠高效的基础数据层&#xff0c;保障上层物联网业务。目前市面上有很多开源的 MQTT 产品&#xff0c;在性能功能等方面各有优点。本文将选取目前最为流行的两个开源 MQTT Broker…

最新!芯片行业有哪些知名企业?

01、芯片设计 芯片设计是产业链中重要的一环&#xff0c;影响后续芯片产品的功能、性能和成本&#xff0c;对研发实力要求较高。根据不同的下游应用&#xff0c;可分为四类&#xff1a; &#xff08;一&#xff09;集成电路&#xff1a;存储器、逻辑芯片&#xff08;CPU、GPU&…

进击的 Java !

编者按&#xff1a;近几年&#xff0c;随着云原生时代的到来&#xff0c;Java 遭受了诸多质疑。国际形势和行业格局的变化&#xff0c;大家一定充分感受到了云原生这个话题的热度&#xff0c;难道 Java 真的已过巅峰时期&#xff0c;要走向末路了吗&#xff1f;龙蜥社区 Java 语…

【Auto-GPT】会自主完成任务的 AI!安整的安装&使用教学

ChatGPT 需要我们不停的输入指令,引导 AI 的回答方向才能得到期待的结果;而 Auto-GPT 之所以爆红,就是因为他能够“自我反思”,只要给他任务,他就会不停地自问自答,不需要人为插手。 听起来是不是棒呆了?就让笔者透过这篇文章带大家了解如何安装 Auto-GPT,以及如何使用…