C++入门知识(下)

news2024/9/25 1:19:54

目录

一、内联函数

1.1内联函数的概念

1.2内联函数的使用

 1.3内联函数的特性

1.4宏的优缺点

1.5C++中可替代宏的技术

二、auto关键字

2.1什么是auto关键字

2.2auto简介

2.3auto的使用细则

2.4auto不能推导的场景

三、基于范围的for循环(C++11)

3.1范围for的基本语法

3.2范围for的使用条件

四、指针空值nullptr(C++11)

4.1.C++98中的指针空值

4.2注意事项


一、内联函数

1.1内联函数的概念

内联函数是在C++中增加的一个功能,可以提高程序执行效率。如果函数是内联的,编译器在编译时,会把内联函数在函数调用处展开,这样就不会有函数栈帧建立的额外开销,提高了代码运行的效率。

内联函数可以和宏函数作类比,但宏函数不会进行类型的检查。

#define  Add(x,y) ((x)+(y)) 

宏定义的Add函数,需要我们注意的是

①在x,y前面加类型了吗?

②x,y都加括号了吗?

#define Add(x,y) (x+y)

int main()
{
	int x = 1;
	int ret = Add(x | 10, x & 11);
	cout << ret << endl;
	return 0;
}

3e4c9c334279404db4e56a31db9991c7.png

可以发现计算的结果和预期的结果不符,为什么会这样呢?因为宏函数是直接在调用处将Add(x,y)

换成了(x+y)——>(1|10+1&11),+的优先级高于|和&,所以最后就变成了(1|11&11)

因此结果与预期结果不符

下面的是加了括号的

#define Add(x,y) ((x)+(y))

int main()
{
	int x = 1;
	int ret = Add(x | 10, x & 11);
	cout << ret << endl;
	return 0;
}

f7b7462437924e87ba50b134a20ecb72.png

③整体都加括号了吗?

#define Add(x,y) (x)+(y)

int main()
{
	int x = 1,y = 2;
	int ret = Add(x+y, x +y)*10;
	cout << ret << endl;
	return 0;
}

8957f0c90d0440dfae44c344c9168514.png

 可以发现整体没加括号的结果与预期结果不符

下面为加了括号的

#define Add(x,y) ((x)+(y))

int main()
{
	int x = 1,y = 2;
	int ret = Add(x+y, x +y)*10;
	cout << ret << endl;
	return 0;
}

69c00d1647ee484ab66743f4b3837f6a.png

结果与 预期结果相符

根据上面的宏函数可以发现一个简单的函数需要我们注意非常多的地方,一稍有不慎就会有bug,显然这不是我们想要的结果,同时宏还不支持调试,所以C++的大佬发现了该问题,于是就引出了内联函数。

1.2内联函数的使用

只需要在函数的最前面加一个关键字inline就欧克了,如:

inline int Add(int x,int y)
{
	return x + y;
}

上述函数使用inline,编译器会在编译时将函数体替换掉函数的调用,这样可以避免不必要的函数栈帧的创建所花费的时间

cb9a45c78c9b43379f14ba18b8895d1f.png

 通过上图可以发现函数并没有展开,这是因为此时我们在debug模式下,debug模式下为了方便我们的调试所以默认不展开,而如果在Release模式下,该函数会展开

在Debug模式下观察内联函数展开的方法

6774a7f90e0f4ab683acb063eda7b73d.png

 现在就可以观察内联函数了在函数调用处展开了

7ebe6e752ce2406183abb46be78e307a.png

 1.3内联函数的特性

1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。

2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。

3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到
注:inline对于编译器而言只是一个建议,最终是不是内联函数由编译器决定

1.4宏的优缺点

(1)优点

①提高代码复用性

②提高性能

(2)缺点

①不方便调试(因为预处理阶段进行了替换)

②代码可读性差,可维护性差,容易误用

③没有类型安全的检查

1.5C++中可替代宏的技术

1.常量定义,换用const enum

2.短小函数定义,换用内联函数

二、auto关键字

2.1什么是auto关键字

auto关键字含义:类型别名的思考

随着程序越来越复杂,程序中用到的类型也越来越复杂,体现在:

①类型难以拼写

②类型含义不明确,容易导致出错

如下:

#include<iostream>
#include<string>
#include<vector>
int main()
{
	std::vector < std::string > str;
	std::vector < std::string > ::iterator it = str.begin();
	return 0;
}

std::vector < std::string > ::iterator是一个类型,看到这可能就会想到使用typedef不就好了

#include<iostream>
#include<string>
#include<vector>
typedef std::vector < std::string > ::iterator Vi;
int main()
{
	std::vector < std::string > str;
	Vi it = str.begin();
	return 0;
}

虽然这样也能实现,代码的简化,但是当频繁地使用了typedef时,可能就会忘了我们自己typedef出来的类型

所以此时我们就可以用auto直接推导it的类型,这样方便观看和书写,不易犯错

#include<iostream>
#include<string>
#include<vector>
int main()
{
	std::vector < std::string > str;
	auto it = str.begin();
	return 0;
}

2.2auto简介

①在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但遗憾的
是一直没有人去使用它,大家可思考下为什么?

因为编译器默认定义在函数内的为局部变量,定义在函数外的为全局变量,一般不需要写
所以C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
 

#include<iostream>
using namespace std;
int Add(int x, int y)
{
	return 1 + 2;
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto d = 1.000;
	auto a1 = Add(1,2);
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;
	cout << typeid(a1).name() << endl;
	return 0;
}

typeid().name()用来推导变量类型,结果如下:

fdbf4f860b4740cdb31e5fb865298768.png

使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。

int main()
{
	auto a;//会报错,因为没有推导的类型
	return 0;
}

2.3auto的使用细则

1.auto和指针,引用结合起来使用

①与指针结合使用

#include<iostream>
using namespace std;

int main()
{  
	int a = 20;
	auto pa=&a;
	auto* pa1 = &a;
	cout << pa << ' ' << pa1 << endl;
	return 0;
}

4bd1b57d57b04599aafb34710745b3ee.png

此时可以发现auto和auto*没有任何区别

②auto和引用结合使用

#include<iostream>
using namespace std;

int main()
{  
	int a = 20;
	auto a1 = a;//表示推导类型为int,不是引用
	auto& a2 = a;//表示推导类型为int&,是引用
	a1 += 1;
	cout << a << endl;
	a2 += 1;
	cout << a << endl;
	return 0;
}

 577d6ea7b0c54ca8847c10be8993a185.png

 可以发现,如果不加引用的话那么就是单纯的推导赋值,而加&符号,就是引用,所以要引用的话不能将&去掉

2.在同一行定义多个变量

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译
器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

#include<iostream>
using namespace std;

int main()
{  
	auto a = 1, b = 2;
	auto c = 'a', d = &a;//该行会报错,因为类型不同
	return 0;
}

2.4auto不能推导的场景

1.auto不能用来作为函数的参数

int Add(auto x,auto,y)
{
	return x + y;
}

76e2522d1bcb463fa671c3c81ca6f18a.png

2.auto不能用来推到数组

int main()
{  
	int a[] = { 1,3,4,5 };
	auto arr[] = { 1,2,3,4,5 };
	return 0;
}

b0c15ccda3884eeb9af47fad174f95cb.png

注: 为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法

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


三、基于范围的for循环(C++11)

3.1范围for的基本语法

在C++98中如果要遍历一个数组可以使用传统写法

#include<iostream>
using namespace std;

int main()
{  
	int a[] = { 1,2,3,4,5 };
	for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
		cout << a[i] << ' ';
	cout << endl;
	return 0;
}

对于有范围的集合而言,由程序员来说明循环的范围是多余的,有的时候还容易犯错。因此C++11引入了基于范围的for循环,for循环后的括号由冒号“:”分为两部分,第一部分是范围内用于迭代的变量,第二部分表示被迭代的范围

#include<iostream>
using namespace std;

int main()
{  
	int a[] = { 1,2,3,4,5 };
	for (auto i : a)
		cout << i << ' ';
	cout << endl;
	return 0;
}

注:与普通循环类似,可以用continue来跳过本次循环,和break来结束整个循环

3.2范围for的使用条件

1.for循环迭代的范围必须是确定的

对于数组而言,就是第一个数组的元素和最后一个数组的元素的范围,对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围

#include<iostream>
using namespace std;

void print(int *a)
{
	for (auto i : a)
		cout << i << ' ';
	cout << endl;
}
int main()
{  
	int a[] = {1,2,3,4,5};
	print(a);
	return 0;
}

以上代码就是错误的,因为for的范围是不明确的

2.迭代器对象要实现++--的操作(后续会更新迭代器的相关内容)

四、指针空值nullptr(C++11)

4.1.C++98中的指针空值

在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现
不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下
方式对其进行初始化:
 

#include<iostream>
using namespace std;

int main()
{  
	int* p = NULL;
	int* pa = 0;
	return 0;
}

NULL其实是一个宏,在传统的C头文件(stddef.h)中可以看到如下代码:

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

可以看到NULL可能被定义为字面常量0,也有可能被定义为无类型指针(void*)的常量,无论采用何种定义,在采用空值的指针时,都不可避免的遇到一些麻烦,如:

#include<iostream>
using namespace std;


void f(int)
{
	cout << "f(int)" << endl;
}
void f(int*)
{
	cout << "f(int*)" << endl;
}
int main()
{
	f(0);
	f(NULL);
	f((int*)NULL);
	return 0;
}

c7371be3acef443ca7fefb2296fca779.png

 可以发现第二次调用本意是为了调用f(int*)函数,但是NULL被定义为0,此时调用了第一个函数f(int),因此与程序的初衷相悖。

4.2注意事项

在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void*)0
注意:

1.在使用nullptr表示指针空值时,不用包含头文件,因为nullptr是C++11作为关键字引入的。

2.在C++中sizeof(nullptr)与sizeof((void*)0)所占字节数相同

3.为了提高代码的健壮性,后续写代码表示指针空值时建议使用nullptr

分享就到这里了,感谢大家的支持,有错的地方还请指出,886!

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

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

相关文章

大屏只用来做汇报?知道这6个应用场景,直接升职加薪!

五一假几个朋友小聚了一下&#xff0c;好久没联系了&#xff0c;现在才知道大家从事行业五花八门的。知道我从事IT行业好几年&#xff0c;他们非要让我讲讲现在异常火爆的大屏&#xff0c;说是所在企业单位都在研究这玩意儿&#xff0c;有的业务人员焦虑不已不知道如何下手&…

Lenovo m93 mini 电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。&#xff08;下载请直接百度黑果魏叔&#xff09; 硬件型号驱动情况 主板Lenovo m93 mini 处理器Intel i5-4590T 2.20GHz (35w) 4-core/4-thread已驱动 内存8GB (2x4) DDR3 1600MHz已驱动 硬盘2.5" SSD Samsung 8…

《Linux 内核设计与实现》11. 定时器和时间管理

文章目录 内核中时间的概念节拍率&#xff1a;HZ理想的 HZ 值高 HZ 的优势高 HZ 的劣势 jiffiesjiffies 的内部表示jiffies 的回绕用户空间和 HZ 硬时钟和定时器实时时钟系统定时器 时钟中断处理程序实际时间定时器使用定时器定时器竞争条件实现定时器 延迟执行忙等待短延迟sch…

跨境商城APP开发需要注意的问题

随着全球化的趋势&#xff0c;跨境电商发展迅猛&#xff0c;越来越多的企业开始进军跨境市场。而跨境商城APP已经成为跨境电商非常重要的一部分。在开发跨境商城APP时&#xff0c;需要注意以下问题&#xff1a; 1.多语言支持 跨境商城APP需要支持不同国家和地区的语言&#x…

在基于Android以及Jetson TK平台上如何写32位的Thumb-2指令

由于Android以及Jetson TK的编译工具链中的汇编器仍然不支持大部分的32位Thumb-2指令&#xff0c;比如 add.w&#xff0c;因此我们只能通过手工写机器指令码来实现想要的指令。下面我将简单地介绍如何在ARM GCC汇编器中手工去写机器指令码。 对于GCC或Clang的汇编器&#xff0…

es6 学习笔记-1

学习视频&#xff1a;尚硅谷Web前端ES6教程&#xff0c;涵盖ES6-ES11_哔哩哔哩_bilibili 一、介绍 ES&#xff1a;全称为EcmaScript,是脚本语言的规范 ECMAScript&#xff1a; 由Ecma国际通过ECMA-262标准化的脚本程序设计语言。 es6兼容性&#xff1a;ECMAScript 6 compa…

adb logcat 保存日志文件到本地

指令 adb logcat > logcat.log例如&#xff1a;例如&#xff1a;adb logcat > D:\logcat.log 注意window中直接输入可能会出现log文件打开显示乱码问题&#xff1b; 请打开cmd检查 输入 chcp 如图 查看结果 如果不是65001 则 执行 chcp 65001 之后执行 例如&#x…

antd 中日期组件添加左侧日期范围选择

一、产品需求 产品有这样一个需求&#xff0c; 在实时的日期组件左侧添加一个快捷时间范围选择&#xff0c;并且选择后&#xff0c;窗口不会自动关闭。 大致样式长这样&#xff1a; 二、需求拆解 拆解一下这个需求&#xff0c;需要满足三个要点&#xff1a; ① 快捷时间范围…

Linux学习之Shell(一)

Shell概述 1&#xff09;Linux提供的Shell解析器有 [xiaominghadoop101 ~]$ cat /etc/shells /bin/sh /bin/bash /sbin/nologin /usr/bin/sh /usr/bin/bash /usr/sbin/nologin /bin/tcsh /bin/csh2&#xff09;bash和sh的关系 [xiaominghadoop101 bin]$ ll | grep bash -rwxr…

LinkFlow CDP洞察能力升级,结合订单开启营销新趋势

4月26日&#xff0c;悠易科技LinkFlow在春季产品发布会上对其洞察产品能力进行了升级。 在技术赋能以人为本的营销5.0阶段&#xff0c;伴随技术的发展&#xff0c;消费者很容易接触到不同的产品和服务&#xff0c;也可以很方便的通过社交网络以及各种社群找到跟自己有相同兴趣…

第四十一章 Unity 输入框 (Input Field) UI

本章节我们学习输入框 (Input Field)&#xff0c;它可以帮助我们获取用户的输入。我们点击菜单栏“GameObject”->“UI”->“Input Field”&#xff0c;我们调整一下它的位置&#xff0c;效果如下 我们在层次面板中发现&#xff0c;这个InputField UI元素包含两个子元素&…

PMP项目管理-[第十章]沟通管理

沟通管理知识体系&#xff1a; 规划沟通管理&#xff1a; 10.1 沟通维度划分 10.2 核心概念 定义&#xff1a;通过沟通活动(如会议和演讲)&#xff0c;或以工件的方式(如电子邮件、社交媒体、项目报告或项目文档)等各种可能的方式来发送或接受消息 在项目沟通中&#xff0c;需要…

聊聊并发编程的12种业务场景

前言 并发编程是一项非常重要的技术&#xff0c;无论在面试&#xff0c;还是工作中出现的频率非常高。 并发编程说白了就是多线程编程&#xff0c;但多线程一定比单线程效率更高&#xff1f; 答&#xff1a;不一定&#xff0c;要看具体业务场景。 毕竟如果使用了多线程&…

用DevExpress WinForms富文本编辑器,集成高级文本编辑功能(一)

DevExpress WinForm富文本编辑器&#xff08;RTF编辑器&#xff09;控件允许用户将高级文本编辑功能集成到下一个WinForms项目中&#xff0c;它包括全面的文本格式选项、支持邮件合并&#xff0c;并附带了丰富的终端用户选项集&#xff0c;因此可以轻松交付受Microsoft word启发…

Linux本地套接字通信

1. 本地套接字 socket API原本为网络通信而设计&#xff0c;后来在其基础上扩展出本地套接字机制用于本地进程间通信。 本地套接字为全双工通信方式。 2. 本地套接字的使用 本地套接字通信步骤 &#xff08;1&#xff09;创建本地socket 本地套接字使用文件来标识&#xff0c;…

【JUC基础】04. Lock锁

1、前言 java.util.concurrent.locks为锁定和等待条件提供一个框架的接口和类&#xff0c;说白了就是锁所在的包。 2、什么是Lock Lock是一种锁机制&#xff0c;比同步块&#xff08;synchronized block&#xff09;更加灵活&#xff0c;同时也更加复杂的线程同步机制。在JDK…

Node.js,多环境配置

目录 1、多环境简介 2、多环境配置 3、命令运行 1、多环境简介 在前端项目的开发过程中&#xff0c;我们需要把项目发布到不同服务器环境中&#xff0c;例如&#xff0c;测试&#xff0c;生产&#xff0c;开发&#xff0c;预生产等环境。在这个我们需要对不同的环境设置不同…

函数的运用

函数的运用 一、函数的定义二、简单函数实验两个数字的运算&#xff1a;调用位置变量函数变量的作用范围 三、函数的递归阶乘递归目录 四、函数库 一、函数的定义 shell函数是经常使用的&#xff0c;因为有些命令序列是需要反复调用执行的&#xff0c;将命令序列按格式写在一起…

三分钟教你Mac下安装VmWare虚拟机

大数据课程课前环境准备&#xff1a;mac中安装三台linux服务器 一、课前准备 准备一台内存最少8G&#xff08;建议16G&#xff09;、cpu i7 4核的电脑 二、课堂主题 安装虚拟化软件VMware准备3台linux虚拟机 三、课堂目标 完成mac下3个虚拟机的安装 四、知识要点 文档说…

洗稿用什么软件-洗稿软件免费

洗稿文章的主要优势 洗稿文章的主要优势在于提高文章的质量和效率。以下是洗稿文章的几个主要优势&#xff1a; 优化结构和语言 洗稿可以删除冗余、无用和重复的内容&#xff0c;同时对文章的结构和语言进行优化&#xff0c;提高文章的可读性和吸引力。这可以使文章更加专业…