C++必修:从C到C++的过渡(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C++学习
贝蒂的主页:Betty’s blog

1. 缺省参数

1.1. 缺省参数的使用

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。

void func(int a = 0)
{
	cout << a << endl;
}
int main()
{
	func();  // 没有传参时,使用参数的默认值,输出0
	func(1); // 传参时,使用指定的实参,输出1
	return 0;
}

img

1.2. 缺省参数的分类

根据其缺省参数的个数,我们我可以将缺省参数分为全缺省半缺省。

1.2.1. 全缺省

每一个参数都有缺省值。

#include<iostream>
using namespace std;
void func(int a = 0,int b = 1,int c = 2)
{
	cout <<"a=" << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}
int main()
{
	func();//不穿参数
	func(10,20);//半传参数
	func(10, 20, 30);//全传
	return 0;
}

img

1.2.2. 半缺省

只有一部分参数有缺省值,并且半缺省参数必须从右往左依次来给出,不能间隔着给。

#include<iostream>
using namespace std;
void func(int a ,int b=1,int c=2)
{
	cout <<"a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}
int main()
{

	func(10,20);//半传参数
	cout << endl;
	func(10, 20, 30);//全传
	return 0;
}

img

1.2.3. 注意

在使用缺省参数时,我们也要知道一些注意事项:

  1. 传参时不能间隔传参。
void func(int a ,int b=1,int c=2)
{
	cout <<"a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}
func(,10,20)//error
  1. 缺省参数不能在函数声明和定义中同时出现
//test.h
void Func(int a = 10);//声明
// test.cpp
void Func(int a = 20)//定义
{}
  1. 缺省值必须是常量或者全局变量。
  2. C语言不支持(编译器不支持)

2. 函数重载

2.1. 函数重载的定义

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

2.2. 函数重载的分类

2.2.1. 参数类型不同
int Add(int a, int b)
{
	return a + b;
}
double Add(double a, double b)
{
	return a + b;
}
2.2.2. 参数个数不同
int Add(int a, int b)
{
	return a + b;
}
int Add(int a, int b, int c)
{
	return a + b;
}
2.2.3. 参数类型顺序不同
int Add(char a, int c)
{
	return a + c;
}
int Add(int a, char c)
{
	return a + c;
}

2.3. 注意

  1. 返回值类型不同无法构成函数重载
int Add(int a, int b)
{
	return a + b;
}
double Add(int a,int b)//error
{
	return a + b;
}
  1. 缺省值不同也不能构成函数重载
int Add(int a=1, int b=20)
{
	return a + b;
}
int Add(int a=1, int b=2)//error
{
	return a + b;
}

2.4. 函数名修饰规则

为什么返回值不同,缺省值不同就不能构成函数重载呢?这就要涉及C++的函数名修饰规则。

我们在C语言当中学习编译与链接时就知道C/C++程序运行起来要经历的四个阶段:

  • 预处理:头文件展开、宏替换、条件编译、去掉注释,生成 .i 的文件。.h的文件直接被展开。
  • 编译: 语法检查(语法分析、语义分析、词法分析)、符号汇总、生成汇编代码,生成.s文件。
  • 汇编: 把汇编代码转换为二进制机器码,形成符号表,生成.o文件。符号表里存放定义函数的地址信息。
  • 链接: 合并目标文件、段表,符号表的合并和符号表的重定位,.o格式的目标文件合并到一起,生成.out/.exe文件。

img

我们在调用函数时,就需要去.o文件调用对应的函数地址,然后去调用函数。因为要区分函数重载的相同函数名的函数,所以函数名肯定要被修饰过,并且每个编译器的修饰规则都不一样。而在C语言中就没有这样的修饰规则,所以C语言不支持函数重载。

下面我们可以看看在g++编译下的函数修饰规则:

int Add(int a, int b)
{
	return a + b;
}
void func(int a, double b, int* p)
{
}
int main()
{
	Add(1, 2);
	func(1, 2, NULL);
	return 0;
}

img

g++的函数修饰规则相对较简单:

_Z+函数名长度+每个参数类型

3. 引用

3.1. 引用的概念

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

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

引用类似于指针,因为指向同一块空间,所以改变引用变量引用实体也会改变。

#include<iostream>
using namespace std;
int main()
{
	int a = 1;
	int& b = a;//引用
	cout << &a << endl;
	cout << &b << endl;
	b++;
	cout << a << endl;
	cout << b << endl;
	return 0;
}

img

3.2. 注意

  1. 引用在定义时必须初始化
int&b;//必须初始化
  1. 一个变量可以有多个引用
int a=1;
int&b=a;
int&c=a;//多个引用
  1. 引用一旦引用一个实体,再不能引用其他实体
int a=1;
int&b=a;
b=2;//这时是赋值,相当于a=b=2;

3.3. 常引用

我们可以通过const修饰引用来让其变为常引用。这时引用变量是不能被修改的,并且只能将常变量复杂给常引用,不能将常变量赋值给引用。也可以将变量赋值给常引用。

#include<iostream>
using namespace std;
int main()
{
	const int a = 1;//常变量
	const int& b = a;//right
	int& c = a;//error
	int c = 2;
	const int& d = c;//right
	double pi = 3.14;
	int& e = pi;//error
	//pi是浮点型,赋值给整型类型会发生隐式类型
	//这个隐式类型转换的值是个常变量
	const int& f = pi;
	return 0;
}

3.4. 引用的使用场景

3.4.1. 作为函数的参数
int swap(int& a, int& b)
{
	int tmp = 0;
	tmp = a;
	a = b;
	b = tmp;
}

做参数就可以解决C语言中形参的改变无法影响实参的问题。

3.4.2. 做函数返回值

做函数返回值要注意,返回的值应在出了作用域不被销毁。不能可能出现于野指针类似的问题。

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

3.5. 错误示例

int& Add(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int& ret = Add(1, 2);
	Add(3, 4);
	cout << ret <<endl;
	return 0;
}//输出什么

img

为什么会输出7呢?那是因为在第二次调用函数Add(3,4)时,会在原来第一次调用Add(1,2)建立栈帧的空间上建立栈帧所以返回值c的值会被重新覆盖,ret值也会发生改变。但因为这块空间出了作用域也会还给操作系统,所以具体结果也是未定义的。

那我们应该如何修改呢?

int Add(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int ret = Add(1, 2);
	Add(3, 4);
	cout << ret << endl;
	return 0;
}

传值返回在函数栈帧销毁前,会先将返回值拷贝放在寄存器中

3.6. 传值与传引用

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

我们可以通过下列代码观察一下:

#include<iostream>
using namespace std;
#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 < 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;
}

img

3.7. 引用与指针的区别

引用的底层实现与指针其实并没有什么区别。

int main()
{
	int a = 10;
	int& ra = a;
	ra = 20;
	int* pa = &a;
	*pa = 20;
	return 0;
}

我们可以通过代码的汇编观察一下:

img

但是引用与指针还是有些区别。

不同点引用指针
概念变量的别名变量的地址
初始化必须建议
对象引用一个后不能修改可以修改指向对象
大小引用类型的大小在32位平台为4,64位平台为8
多级没有多级引用有多级指针
安全性更安全并不安全

4. 内联函数

在C语言中,无论宏常量还是宏函数都有易出错,无法调试等缺陷。而C++为了弥补这一缺陷,一般用constenum代替宏常量,引入了内联函数的概念代替宏函数。

4.1. 内联函数的定义

以关键字inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调
用建立栈帧的开销,内联函数提升程序运行的效率。

#include<iostream>
using namespace std;
inline int Add(int x, int y)
{
	return x + y;
}
int main()
{
	Add(1, 2);
	return 0;
}

4.2. 注意

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

因为内联函数会在调用时直接展开,如果声明与定义分离内联函数的地址根本不会进入符号表,链接时就无法找到定义的函数,就会发生链接错误。

5. auto关键字

5.1. auto的简介

在C++中,随着程序越来越复杂,程序所用的类型也越来越复杂。为了简化代码,增加代码的可读性,C++11引入了自动类型推断auto。在C语言中,**auto修饰的变量,是具有自动存储器的局部变量。**但是实用性很小,所以C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得

int a = 1;
auto b = a;//自动推断b的类型

5.2. 注意

  1. 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
int x = 1;
auto a = &x;
auto* b = &x;
auto& c = x;
  1. 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
auto a = 1, b = 2;
auto c = 2, d = 3.14;//error
  1. auto不能作为函数的参数或者声明数组
void TestAuto(auto a)
{
    //auto不能推断形参的类型
}

6. 范围for

在C++98之前,我们遍历一个数组,需要按照以下的形式:

#include<iostream>
using namespace std;
int main()
{
	int arr[] = { 1,2,3,4,5 };
	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		cout << arr[i] << endl;
	}
	return 0;
}

但是在C++11,又引入了一种新的遍历方法——范围for。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

#include<iostream>
using namespace std;
int main()
{
	int arr[] = { 1,2,3,4,5 };
	for (auto e : arr)
	{
		cout << e << endl;
	}
	//取数组arr的值依次赋值给e
	//自动递增,自动判断结束
	return 0;
}

由于e是临时变量,所以要想改变数组的值,需要引用。

#include<iostream>
using namespace std;
int main()
{
	int arr[] = { 1,2,3,4,5 };
	for (auto&e : arr)
	{
		e *= 2;
	}
	return 0;
}
  • 注意:与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环。

7. nullptr空指针

在C语言中,定义了一个宏NULL,在传统的C头文件(stddef.h)中,可以看到如下代码 :

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

由此我们知道NULL既可以代表数字0,也可以代表空指针。这种模棱两可的定义就可能引出一些问题,比如下面这段代码:

#include<iostream>
using namespace std;
void func(int a)
{
	cout << "func(int)" << endl;
}
void func(int*p)
{
	cout << "func(int*)" << endl;
}
//函数重载
int main()
{
	func(0);
	func(NULL);
	func((int*)NULL);
	return 0;//输出??
}

img
我们的本意可能是将NULL当成一个指针,但是在默认情况下NULL被编译器当做数字0。这种问题是我们并不想看见的,所以C++11引入了nullptr来代替NULL。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

直接插入排序与希尔排序的详解及对比

目录 1.直接插入排序&#xff08;至少有两个元素才可以使用&#xff09; 排序逻辑 B站动画演示&#xff1a;直接插入排序 逻辑转为代码&#xff1a; 稳定性&#xff1a;稳定 时间复杂度&#xff1a;O(N^2) 空间复杂度&#xff1a;O(1) 应用场景 2.希尔排序&#xff08;对…

VUE父组件向子组件传递值

创作灵感 最近在写一个项目时&#xff0c;遇到了这样的一个需求。我封装了一个组件&#xff0c;这个组件需要被以下两个地方使用&#xff0c;一个是搜索用户时用到&#xff0c;一个是修改用户信息时需要用到。其中&#xff0c;在搜索用户时&#xff0c;可以根据姓名或者账号进…

C++之STL-String

目录 一、STL简介 1.1 什么是STL 1.2 STL的版本 1.3 STL的六大组件 ​编辑 1.4 STL的重要性 二、String类 2.1 Sting类的简介 2.2 string之构造函数 2.3 string类对象的容量操作 2.3.1 size() 2.3.2 length() 2.3.3 capacity() 2.3.4 empty() 2.3.5 clear() 2.3.6…

【Unity】苹果(IOS)开发证书保姆级申请教程

前言 我们在使用xcode出包的时候&#xff0c;需要用到iOS证书(.p12)和描述文件(.mobileprovision) 开发证书及对应的描述文件用于开发阶段使用&#xff0c;可以直接将 App 安装到手机上&#xff0c;一个描述文件最多绑定100台测试设备 1.证书管理 进入网站Apple Developer &…

从虚拟化走向云原生,红帽OpenShift“一手托两家”

汽车行业已经迈入“软件定义汽车”的新时代。吉利汽车很清醒地意识到&#xff0c;只有通过云原生技术和数字化转型&#xff0c;才能巩固其作为中国领先汽车制造商的地位。 和很多传统企业一样&#xff0c;吉利汽车在走向云原生的过程中也经历了稳态业务与敏态业务并存带来的前所…

视频美颜SDK原理与实践:从算法到应用

当下&#xff0c;从社交媒体到视频通话&#xff0c;人们越来越依赖于视频美颜功能来提升自己的形象。而视频美颜SDK作为支撑这一技术的重要工具&#xff0c;其原理和实践至关重要。 一、什么是视频美颜SDK&#xff1f; 视频美颜SDK是一种软件开发工具包&#xff0c;用于集成到…

FloodFill算法---DFS

目录 floodfill算法概念&#xff1a; 算法模板套路&#xff1a; 例题1&#xff1a;图像渲染 例题2&#xff1a;岛屿数量 例题3&#xff1a;岛屿的最大面积 例题4&#xff1a;被围绕的区域 floodfill算法概念&#xff1a; floodfill算法是一种常用的图像处理算法&#xf…

【IDEA】在IntelliJ IDEA中导入Eclipse项目:详细指南

IntelliJ IDEA和Eclipse是两款常用的集成开发环境&#xff08;IDE&#xff09;&#xff0c;在软件开发中经常会遇到需要在它们之间迁移项目的情况。本文将重点介绍如何在IntelliJ IDEA中导入Eclipse项目&#xff0c;以帮助开发者顺利地迁移他们的项目&#xff0c;并在IntelliJ …

云主机修复监控插件异常的方法

首先&#xff0c;进入云监控服务--选择主机监控&#xff0c;勾选上网络配置异常的云主机&#xff0c;最上面的修复插件配置&#xff0c;然后等待大约半个小时多&#xff0c;再观察下主机的状态。 一般情况下问题都可以被解决&#xff0c;如果解决不了&#xff0c;可以尝试卸载…

剑指 Offer 03.:数组中重复的数字

剑指 Offer 03. 数组中重复的数字 找出数组中重复的数字。 在一个长度为 n 的数组 nums 里的所有数字都在 0&#xff5e;n-1 的范围内。数组中某些数字是重复的&#xff0c;但不知道有几个数字重复了&#xff0c;也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。…

Linux下的进程管理:创建、终止、切换与等待

文章目录 一、引言二、进程创建1、进程创建的概念与场景2、进程创建的方式a、fork() 系统调用b、fork() 后的执行流程 3、进程创建的过程a、进程创建过程b、子进程创建过程 4、父子进程关系与属性继承 三、进程终止1、进程终止的原因2、进程的错误码和退出码a、错误码b、退出码…

Golang基础5-指针、结构体、方法、接口

指针 和c/c类似&#xff0c;但是go语言中指针不能进行偏移和运算&#xff0c;安全指针 &&#xff08;取地址) *(根据地址取值) nil(空指针&#xff09; make和new之前对比&#xff1a;make用于初始化slice&#xff0c;map&#xff0c;channel这样的引用类型 而new用于类…

热知识:更多团队采用3个及以上内部开发者平台

01 介绍 根据 Perforce Puppet 的一份新报告中&#xff0c;平台工程的采用已经在一些企业内看到了成效&#xff0c;78% 的受访者表示他们的组织拥有专门的平台团队至少三年了。 然而&#xff0c;这并不意味着这些组织只使用同一套工具。四分之三的调查参与者表示&#xff0c;他…

如何使用SOLIDWORKS添加装饰螺纹线规格

在我们的设计过程中&#xff0c;有很多的时候螺纹规格在机械设计手册上没有&#xff0c;而我们的SOLIDWORKS软件里面录制的都是符合标准的的螺纹&#xff0c;至于其他的特种或者超出的规格需要我们设计人员去手工添加&#xff0c;以下介绍我们装饰螺纹线新规格的添加方法&#…

关于PMO卓越中心职能建设的实践与思考︱PMO大会

全国PMO专业人士年度盛会 浪潮电子信息产业股份有限公司PMO时军先生受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾&#xff0c;演讲议题为“让组织持续卓越——关于PMO卓越中心职能建设的实践与思考”。大会将于5月25-26日在北京举办&#xff0c;敬请关注&#xff01; …

菜单访问url/接口url为什么要带时间戳

一&#xff0c; 问题 1&#xff0c;菜单url中如果不加时间戳&#xff0c;会导致什么问题。我们现在做一个东西&#xff0c;需要获取菜单的访问地址&#xff0c;我们要拼这个地址 2&#xff0c;查询接口中&#xff0c;时间戳&#xff0c;如果不加&#xff0c;具体导致什么问题 二…

Vue集成three.js,加载glb、gltf类型的3d模型

安装基本依赖 // 注意OrbitControls要加{}&#xff0c;注意路径是jsm import { OrbitControls } from ‘three/examples/jsm/controls/OrbitControls.js’; // import { dat } from ‘three/examples/jsm/controls/dat.gui.js’; // dat gui这个插件&#xff0c;是另外自己下载…

杰理使用USB声卡模式时关闭MIC

杰理在使用PC模式的时候&#xff0c;想只保留扬声器&#xff0c;但不要打开MIC功能&#xff0c;可以配置USB_DEVICE_CLASS_CONFIG中把MIC_CLASS去掉&#xff0c;然后重新编译就可以了。

Kimi 高效使用技巧,80%的人都不知道

关注我, AI 学习之旅上&#xff0c;我与您一同成长&#xff01; 一、引言 Kimi 作为国产之光&#xff0c;在过去的一个多月里成为国内大模型的香饽饽。据数据分析&#xff0c;Kimi 网页、APP、小程序等各端的日活已经突破 300 万&#xff0c;超过文心一言、通义千问、智谱清言…