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

【C++】内联函数、auto、范围for循环,nullptr

前言

        今天就是 C++ 新手村的最后一站啦,之后小黄就要开始学习 C++ 的类啦,也就是出了新手村之后 的第一个挑战,大家要一起坚持下去,一起努力变好呀!


 

目录

 一、内联函数

1.1 消除函数栈帧的两种思路

1.1.1 宏函数(C语言也可使用)

1.1.2 内联函数

1.1.3 宏函数相对于内联函数的缺点

1.2 如何观察到内联函数

1.3 内联函数不一定展开

 1.4 内联函数的声明与定义

二、auto关键字

2.1 typeid 关键字

2.2 auto 使用细则

三、基于范围的 for 循环

四、指针空值 nullptr (关键字)


 一、内联函数

以inline修饰的函数叫做内联函数,在编译时编译器会在调用内联函数的地方直接将函数展开,因此没有函数调用建立栈帧的开销,所以内联函数可以提升程序运行的效率,适用于一些重复使用且多此调用的小函数。

1.1 消除函数栈帧的两种思路

1.1.1 宏函数(C语言也可使用)

在此以两数相加为例:

#include<iostream>
using namespace std;

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

int main()
{
    int a = 1, b = 2;
    cout << add(a, b) << endl;
    cout << 2 * add(a, b) << endl;
    cout << add(a | b, a & b);
    return 0;
}

需要注意的是宏函数还是一种替换,因此要非常注意括号的添加,对于 add 函数而言,其三个括号的具体作用可以参考三个 cout 中的实例。

1.1.2 内联函数

内联函数的写法相当简单,只需在函数前简单加上 inline 关键字即可,但是其作用却是与宏替换非常类似,但是可以展开进行调试。

例如:

#include<iostream>
using namespace std;

inline add(int x, int y)
{
    return a + b;
}

int main()
{
    cout << add(1, 2) << endl;
    return 0;
}

1.1.3 宏函数相对于内联函数的缺点

① 宏函数直接替换一句,无法展开进行调试;

② 没有对参数的类型检查,缺乏一定安全性;

③ 宏函数相对更容易写错。

1.2 如何观察到内联函数

在 vs2022 debug 的环境下,是无法直接观察到内联函数的展开的,因为 debug 版本下不会进行优化,而内联函数本质上是一种优化。

下面教大家如何观察内联函数是否展开了:

1. 首先右击结局方案种的源文件点击属性

2.  在 C/C++ 常规中将“调试信息格式”改为程序数据库一项,然后优化中“内联函数拓展”改为只适用于 _inline 一项。

3. 在调试程序时右击项目,转到反汇编 

 4. 在调用函数之后,观察反汇编是否有 call + 函数名 的一行,若存在说明内联函数没有在此展开。

 如上图,第二张中出现的一行则表示没有展开。

1.3 内联函数不一定展开

乍一听,既然内联函数可以减少栈帧的开销,那么所有函数都用栈帧不就好了吗?

但实际上,并不是所有的内联函数都会展开。

1. inline 是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会 用函数体替换函数调用:

缺陷:可能会使目标文件变大;

优势:少了调用开销,提高程序运 行效率。

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

 1.4 内联函数的声明与定义

inline 内联函数不建议声明和定义分离,分离会导致链接错误。因为 inline 被展开,就没有函数地址了,链接过程无法找到函数,尤其是多文件编译的时候,函数的声明放在同文件中,函数的定义放在一个源文件,函数的调用放在另一个源文件,此时就会报错。(直接定义在调用的源文件时,可以正常展开)

 因此建议,在头文件中声明内联函数的时候就直接定义该函数

二、auto关键字

2.1 typeid 关键字

作用:返回变量类型名称的字符串

使用方法:用 string 类接收,或直接打印------typeid( 变量名 ).name()

常用范围:新式 for 循环,lambda 表达式等。

例如:

#include < iostream>
using namespace std;

int main()
{
	int* x = NULL;
	int y = 10;
	int& z = y;
	string s = typeid(x).name();
	cout << s << endl << typeid(y).name() << endl << typeid(z).name();
	return 0;
}

对于引用 z ,因为是对于 int 变量 y 的别名,所以 z 可以看作 int 类型的变量。

2.2 auto 使用细则

我们可以将 auto 理解为一种自动的变脸类型,会根据初始化内容,自动判断变量的类型

例如:

#include < iostream>
using namespace std;

// void func(auto num);

int main()
{
	auto x = 10;
	// auto y;
	// auto arr[]{ 0 };
	auto a = &x;
	auto* a2 = &x;
	auto& a3 = x;
	// auto m = 10, n = 'b';
	cout << *a << endl << *a2 << endl << a3 << endl;
	return 0;
}

通过上面的例子,我们可以得出 auto 的几个使用规则:

1. auto 作为变量类型的时候必须要初始化

2. auto 不可以用于数组类型的声明

3. auto 声明指针类型时,效果与 auto* 声明指针类型等价

4. auto 声明引用时,需要加上 & 符号

5. auto 在同时声明多个变量的时候,变量必须是相同类型

6. auto 不能作为函数传参类型

三、基于范围的 for 循环

对于一个有范围的集合,为了方便遍历整个集合,引入了基于范围的 for 循环,其使用方式为

for ( 用于迭代的变量 :被迭代的范围) 

例如:

#include < iostream>
using namespace std;

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

通过上面的例子,我们可以知道循环中对于迭代变量实际上是一种赋值操作,因此对变量 x 进行修改的时候是不会改变数组中的值的,但是我们可以将迭代变量设置为引用类型,这样就可以修改数组中的值了

四、指针空值 nullptr (关键字)

在 C++98 中,NULL 实际上是一种宏,其值为 0,但是未经过强转,也就是说,NULL 就可以单独理解为数字 0 .

例如:

#include < iostream>
using namespace std;

void func(int)
{
	cout << "int" << endl;
}

void func(int*)
{
	cout << "int *" << endl;
}

int main()
{
	func(0);
	func(NULL);
	func(nullptr);
	return 0;
}

通过函数 func 的重载我们发现,编译器确实没有将 NULL 识别为指针类型,而是作为 int 类型进行了传参,这就使得很多情况下,会出现一些莫名的 bug。而在新的 C++ 标准中,新定义了一个关键字 nullptr,他是一种 void* 类型的指针,其值为 0,但是明确了是空指针类型

欢迎来和小黄一起学习呀~

相关文章:

  • Mybatis(第一篇)
  • nodejs+vue+elementui旅游资源网站python-java景点门票预订网站php
  • ArrayList 源码浅析
  • 毕业设计 基于单片机的智能音响设计与实现 -物联网 嵌入式 stm32
  • 【区块链】从社区平台MOJOR看,为何Web3需要原生?
  • 2022年 研究生数学建模题目
  • C# 算数运算符
  • TC8:TCP_HEADER_01-11
  • Mysql出现问题:慢查询日志失效解决方案
  • Unity接入TopOn聚合广告平台SDK【聚合了穿山甲,优量汇(腾讯广告),快手,Mintegral,sigmob等各大广告平台SDK】
  • 【leetcode】和最小的 k 个数对
  • Java 程序控制结构(4)
  • C++动态空间申请
  • WEB安全之javascript基础(一):js的引入方法注释变量数据类型
  • 【node进阶】深度解析Express框架--路由、中间件
  • 实现windows 窗体的自己画,网上摘抄的,学习了
  • 《Java编程思想》读书笔记-对象导论
  • 【翻译】babel对TC39装饰器草案的实现
  • extract-text-webpack-plugin用法
  • laravel 用artisan创建自己的模板
  • Linux编程学习笔记 | Linux多线程学习[2] - 线程的同步
  • React+TypeScript入门
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 测试开发系类之接口自动化测试
  • 基于MaxCompute打造轻盈的人人车移动端数据平台
  • 讲清楚之javascript作用域
  • 漂亮刷新控件-iOS
  • 前端面试之CSS3新特性
  • 前嗅ForeSpider中数据浏览界面介绍
  • 如何在 Tornado 中实现 Middleware
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 微信小程序--------语音识别(前端自己也能玩)
  • 为视图添加丝滑的水波纹
  • 异步
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • (1)Android开发优化---------UI优化
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (接口封装)
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (一) springboot详细介绍
  • ./indexer: error while loading shared libraries: libmysqlclient.so.18: cannot open shared object fil
  • .net core使用ef 6
  • .NET/C# 异常处理:写一个空的 try 块代码,而把重要代码写到 finally 中(Constrained Execution Regions)
  • .skip() 和 .only() 的使用
  • @DateTimeFormat 和 @JsonFormat 注解详解
  • @LoadBalanced 和 @RefreshScope 同时使用,负载均衡失效分析
  • [ vulhub漏洞复现篇 ] Apache Flink目录遍历(CVE-2020-17519)
  • []指针
  • [20171113]修改表结构删除列相关问题4.txt
  • [202209]mysql8.0 双主集群搭建 亲测可用