C++入门--auto关键字、内联函数学习

news2024/11/23 11:45:57

目录

  • 1.auto关键字(C++11)
    • 1.1auto简介
    • 1.2auto的使用细则
    • 1.3auto函数不能推导的场景
      • 1.auto不能作为函数的参数
      • 2.auto不能直接用来声明数组
  • 2.基于范围的for循环(C++)
    • 2.1范围for循环的语法
    • 2.2使用auto的for循环
    • 2.3基于for循环的使用条件
  • 3.指针空值nullptr(C++11)
  • 4.内联函数
    • 4.1内联函数概念
    • 4.2内联函数的特性
  • 5.总结

1.auto关键字(C++11)

1.1auto简介

C++11中auto的全新的含义:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时推导而得。

#include<iostream>
using namespace std;
int main()
{
	int a = 10;
	double b = 1.1;
	auto c = 20;//根据右边的表达式,推导出c的类型
	auto d = 30.1;//根据右边的表达式,推导出d的类型

	//typeid也是C++的关键字,作用是打印类型
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;
	return 0;
}

代码运行的结果为:
在这里插入图片描述

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

1.2auto的使用细则

1.auto与指针和引用结合起来使用,用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时必须加&

#include<iostream>
using namespace std;
int main()
{
	int x = 10;
	auto a = &x;
	auto* b = &x;
	auto& c = x;

	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;

	*a = 20;
	cout << a << endl;
	*b = 25;
	cout << a << endl;
	c = 30;
	cout << a << endl;
	return 0;
}

代码运行的结果为:
在这里插入图片描述

2.当在同一行定义多个变量时,这些变量的类型必须相同,编译器是根据第一个变量的表达式进行推导的,然后根据推导出来的变量类型定义其他变量,如果不相同编译器就会报错

#include<iostream>
using namespace std;
void TestAuto()
{
	auto a = 1, b = 2;
	auto c = 1, d = 1.1;
}
int main()
{
	TestAuto();
	return 0;
}

代码运行的结果为:
在这里插入图片描述

1.3auto函数不能推导的场景

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

//1.auto不能作为函数的参数
//此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的类型进行推导
void TestAuto(auto a)
{
}

编译器报错的结果:
在这里插入图片描述

2.auto不能直接用来声明数组

void TestAuto()
{
	int a[] = { 1,2,3 };
	auto b[] = { 5,6,7 };
}

编译器报错的结果:
在这里插入图片描述

2.基于范围的for循环(C++)

2.1范围for循环的语法

#include<iostream>
using std::cout;
using std::endl;
void TestFor1()
{
	int array[] = { 1,2,3,4,5,6 };
	for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++)
		array[i] *= 2;

	for (int* p = array; p < array + sizeof(array) / sizeof(array[0]); p++)
		cout << *p << endl;
}
int main()
{
	TestFor1();
	return 0;
}

代码运行的结果为:
在这里插入图片描述

2.2使用auto的for循环

#include<iostream>
using std::cout;
using std::endl;
void TestForAuto()
{
	int Array[] = { 1,2,3,4,5,6 };
	for (auto& e : Array)
		e *= 2;

	for (auto& e : Array)
		cout << e << " ";
}
//使用auto后相当于把数组的每个元素取别名为e,然后对e进行操作,遍历数组工作编译器自动完成
int main()
{
	TestForAuto();
	return 0;
}

代码运行的结果为:
在这里插入图片描述
总结: for (auto& e : Array)for循环后的括号由冒号":"分为两部分:第一部分是范围内的迭代变量第二部分则表示被迭代的范围;注意:与普通循环一样,可以用continue来结束本次循环,也可以用break跳出整个循环。

2.3基于for循环的使用条件

#include<iostream>
using std::cout;
using std::endl;
void TestFor(int arry[])
{
	for (auto& e : array)
		cout << e << endl;
}

编译器报错的结果:
在这里插入图片描述

上面的代码有问题,for (auto& e : array),array是指针,不是数组,所以不可以使用auto
1.for循环迭代的范围必须是确定的。对于数组而言就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for的迭代的范围;2.迭代的对象要实现++和==的操作。

3.指针空值nullptr(C++11)

C++98中的指针空值,在之前C语言的学习中,声明指针变量的时候,我们需要进行初始化。

//指针的初始化如下
void TestPtr()
{
	int* p1 = NULL;
	int* p2 = 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 std::cout;
using std::endl;
void Func(int)
{
	cout << "void Func()" << endl;
}
void Func(int*)
{
	cout << "void Func(int*)" << endl;
}
int main()
{
	Func(0);
	Func(NULL);
	Func((int*)NULL);//通过NULL调用指针版本的Func(int*)函数
	return 0;
}

代码运行的结果为:
在这里插入图片描述

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

正确使用的栗子:

//正确调用Func(int*)函数应该使用nullptr
#include<iostream>
using std::cout;
using std::endl;
void Func(int)
{
	cout << "void Func()" << endl;
}
void Func(int*)
{
	cout << "void Func(int*)" << endl;
}
int main()
{
	Func(0);
	Func(nullptr);
	return 0;
}

代码运行的结果为:
在这里插入图片描述

注意: 1.在使用nullptr的时候不需要包含头文件,因为nullptr是C++作为新关键字引入的;2.C++中,sizeof(nullptr)sizeof((void*)0)所占的字节数相同。;3.为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

4.内联函数

我们正常调用函数的时候,编译器会进行压栈、出栈的操作(创建函数栈帧),当执行程序的时间小于创建栈帧的时间的时候(即代码量较小的时候),就会**“得不偿失”**。在C语言中,我们会使用宏代替函数。而在C++中引入了内联函数进行代替。

宏的优点:1.增强代码的复用性;2.提高性能
宏的缺点:1.不方便调试宏。(因为编译阶段进行了替换);2.导致代码可读性差,可维护性差,容易误用。

4.1内联函数概念

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

//没有使用inline修饰
#include<iostream>
using std::cout;
using std::endl;
int Add(int left, int right)
{
	return left + right;
}
int main()
{
	int ret = 0;
	ret = Add(1, 2);
	return 0;
}

转化为汇编代码为:
在这里插入图片描述

查看是否使用内联函数的方式
1.在release模式下,查看编译器生成的汇编代码是否存在Call Add
2.在debug模式下,需要对编译器进行设置,否则不会展开(因为debug模式下,编译器默认不会对代码进行优化)。

debug版本操作如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//用inline修饰
#include<iostream>
using std::count;
using std::endl;
inline int Add(int left, int right)
{
	return left + right;
}
int main()
{
	int ret = 0;
	ret = Add(1, 2);
	return 0;
}

转化为汇编代码为:
在这里插入图片描述

在正常调用函数的汇编代码中,需要进行压栈、出栈的操作;而在使用内联函数的代码中,则不需要创建函数栈帧,直接执行Add函数操作的汇编指令。

//func.h
#include<iostream>
using namespace std;
inline void f(int i);
//func.cpp
#include"fun.h"
void f(int i)
{
	cout << i << endl;
}
//test.cpp
#include"fun.h"
int main()
{
	f(10);
	return 0;
}

编译器报错的结果:
在这里插入图片描述
正确的示例:

#include<iostream>
using namespace std;
inline void f(int i)
{
	cout << i << endl;
}
#include"fun.h"
int main()
{
	f(10);
	return 0;
}

代码运行的结果为:
在这里插入图片描述

在编译的时候,#include"fun.h"包含的内联函数f声明,编译器就回事f(10)展开,但是只有声明不能展开,所以只能call修饰的f函数的地址,然而在链接的时候,因为f是内联函数,它的地址不会进入符号表,call指令失效,发生链接错误。

4.2内联函数的特性

内联函数的特性
1.内联函数是一种以空间换时间的做法,如果编译器将函数当成内联函数处理会替换函数调用,缺陷:可能会使目标文件变大,优势:减少调用开销,提高程序运行效率。
2.inline对于编译仅仅只是一个建议,最终是否成为inline,取决于编译器。①比较长的函数②递归函数像类似这样的函数就像加了inline也会被编译器否决掉。
3.inline不建议声明和定义分离,分离会导致链接错误。建议内联函数声明定义在一起,即直接在func.h写内联函数的定义,#include"fun.h"包含的内联函数f定义,这样在编译的时候,使用内联函数的地方都会被展开,有定义,直接用,直接展开,不会有链接的问题。

5.总结

本章我们一起学习了C++auto关键字、内联函数的知识,希望能帮助大家进步了解C++!感谢大家阅读!如有不对,欢迎纠正!🍭🍭🍭

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

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

相关文章

【linux网络】firewalld 防火墙

firewalld防火墙 一、firewalld防火墙1.1firewalld防火墙概述1.2firewalld与iptables的区别1.3Firewalld网络区域1.4firewalld数据处理的流程1.5firewalld防火墙的配置方法 二、firewall-cmd的命令选项2.1默认区域2.2网卡接口增删改查区域2.3源地址增删改查区域2.4区域规则的增…

接口测试之测试原则、测试用例、测试流程......

之前都是在网上杂乱得看了很多接口测试的资料&#xff0c;但总感觉不够系统&#xff0c;特地找了本书重新系统的学习一下&#xff0c;结合之前收集的一些资料查漏补缺。 《接口自动化测试持续集成postman》&#xff0c;这本大部分内容还是讲的是 postman 工具的使用方法&#…

spark应用程序的执行

1 SparkContext -》{ sparkconf --配置对象&#xff0c;基础配置 sparkEnv --环境对象&#xff0c;通讯环境 SchedulerBackend --通讯后台 住哟啊用于和Executor之间进行通讯 TaskScheduler – 任务调度器 任务调度 DAGScheduler – 阶段调度器 阶段划分 } spark.sparkContext…

贪心算法专练

⭐️前言⭐️ 本篇文章主要分享几道贪心算法的题目&#xff0c;贪心算法是一种基于自然智慧的算法&#xff0c;这类题目并没有统一的解法&#xff0c;但通常都是每一步做出一个局部最优的选择&#xff0c;最终的结果就是全局最优。 &#x1f349;欢迎点赞 &#x1f44d; 收藏 …

图神经网络:(化学领域)再次认识图神经网络

文章说明&#xff1a; 1)参考资料&#xff1a;PYG官方文档。超链。 2)博主水平不高&#xff0c;如有错误还望批评指正。 3)我在百度网盘上传了这篇文章的jupyter notebook和有关文献。超链。提取码8848。 文章目录 Mutagenicity数据集搭建模型训练模型文献阅读重新回来 Mutagen…

day36_JQuery

今日内容 零、 复习昨日 一、正则表达式 二、JQuery 零、 复习昨日 零、正则表达式 Regular expression RegExp 0.1 正则表达式 正则表达式是描述字符模式的对象。正则表达式用于对字符串模式匹配及检索替换&#xff0c;是对字符串执行模式匹配的强大工具。语法&#xff1a; va…

京东云技术团队 —— 浅谈测试用例设计

一、测试用例为什么存在 1.1 定义 测试用例(Test Case)是指对特定的软件产品进行测试任务的描述&#xff0c;体现测试方案、方法、技术和策略。测试用例内容包括测试目标、测试环境、输入数据、测试步骤、预期结果、测试脚本等&#xff0c;最终形成文档类的输出。简而言之&am…

04. 数据结构之栈

前言 栈&#xff08;stack&#xff09;是一种线性数据的逻辑存储结构。栈中的元素只能先入后出&#xff08;First In Last Out&#xff0c;简称FILO&#xff09;。最早进入的元素存放的位置叫作栈底&#xff08;bottom&#xff09;&#xff0c;最后进入的元素存放的位置叫作栈…

网络故障老搞不定,就看这篇笔记

大家好&#xff0c;我是老杨。 做咱们这行&#xff0c;每天遇到的故障千奇百怪什么都有。很多网工每天只是在工作而已&#xff0c;遇到一个问题&#xff0c;就解决一个问题&#xff0c;每天的日子都是一样的&#xff0c;枯燥无趣。 但是&#xff0c;就很少有人去汇总问题&…

分布式事务的21种武器 - 1

在分布式系统中&#xff0c;事务的处理分布在不同组件、服务中&#xff0c;因此分布式事务的ACID保障面临着一些特殊难点。本系列文章介绍了21种分布式事务设计模式&#xff0c;并分析其实现原理和优缺点&#xff0c;在面对具体分布式事务问题时&#xff0c;可以选择合适的模式…

Scala学习(五)---面向对象

文章目录 1.Scala面向对象的构造器1.1 主构造器和从构造器(辅助构造器)1.2 主构造器参数 2.继承2.1 抽象属性和方法2.2 匿名子类 1.Scala面向对象的构造器 1.1 主构造器和从构造器(辅助构造器) //主构造器 class ConstructorTest(name:String) {//主构造器调用val name1:Stri…

【MyBatis框架】

文章目录 Mybatis1.简介1.1MyBatis历史1.2MyBatis特性1.3MyBatis下载1.4和其它持久化层技术对比 2.搭建MyBatis2.1创建maven工程2.2创建MyBatis的核心配置文件2.3创建mapper接口2.4创建实体类2.5创建MyBatis的映射文件2.6通过junit测试功能2.7加入log4j日志功能2.8MyBatis的增删…

pytorch的学习与总结(第二次组会)

pytorch的学习与总结 一、pytorch的基础学习1.1 dataset与dataloader1.2 可视化工具(tensorboard)、数据转换工具(transforms)1.3 卷积、池化、线性层、激活函数1.4 损失函数、反向传播、优化器1.5 模型的保存、加载、修改 二、 pytorch分类项目实现2.1 网络模型2.2 具体代码 一…

新星计划2023【《计算之魂》读书会】学习方向报名入口!

前排提醒&#xff1a;这里是新星计划2023【《计算之魂》读书会】学习方向的报名入口&#xff0c;一经报名&#xff0c;不可更换。 ↓↓↓报名方式&#xff1a;&#xff08;下滑到本页面底部&#xff09; 一、关于本学习方向导师 博客昵称&#xff1a;异步社区博客主页&#x…

AI大模型时代,云从科技携“从容大模型”入场如何“从容”?

5月18日&#xff0c;在“AI赋能数字中国产业论坛暨2023云从科技人机协同发布会”上&#xff0c;云从科技自研“从容大模型”正式亮相。 根据发布会信息&#xff0c;“从容大模型”具备问答、阅读理解、文学创作以及解题方面的能力。受发布会消息影响&#xff0c;5月18日午间休盘…

【libdatachannel】cmake+vs2022 构建

libdatachannel libdatachannel 是基于c++17实现的构建 OpenSSL 找不到 Selecting Windows SDK version 10.0.22000.0 to target Windows 10.0.22621. The CXX compiler identification is MSVC 19.35.32217.1 Detecting CXX compiler ABI info Detecting CXX compiler ABI inf…

利用GPIO线进行板间通信-23-5-22

本项目基于VU9P(xcvu9pflga2105)板卡以及ZYNQ(xc7z015clg485) 简单结构流程介绍&#xff1a; 1.上位机通过千兆网将指令下发到ZYNQ&#xff0c;ZYNQ进行解帧&#xff0c;将数据解析出来后存储到RAM中,RAM将数据不断输送给GPIO模块&#xff0c;GPIO模块根据对应地址输出数据是…

新来的00后实习生太牛了,已经被取代了.....

前几天有个朋友向我哭诉&#xff0c;说她在公司工作&#xff08;软件测试&#xff09;了7年了&#xff0c;却被一个00后实习生代替了&#xff0c;该何去何从&#xff1f; 这是一个值得深思的问题&#xff0c;作为职场人员&#xff0c;我们确实该思考&#xff0c;我们的工作会被…

1718_Linux命令模式下查看日历

全部学习汇总&#xff1a; GreyZhang/bash_basic: my learning note about bash shell. (github.com) 前面发布了一份学习笔记&#xff0c;涉嫌过渡宣传&#xff0c;虽然我也没搞懂为什么。有一系列修改建议&#xff0c;我觉得直接放弃了。还是发一份新的吧&#xff01; Linux命…

【数据结构】哈希底层结构

目录 一、哈希概念 二、哈希实现 1、闭散列 1.1、线性探测 1.2、二次探测 2、开散列 2.1、开散列的概念 2.2、开散列的结构 2.3、开散列的查找 2.4、开散列的插入 2.5、开散列的删除 3、性能分析 一、哈希概念 顺序结构以及平衡树中&#xff0c;元素关键码与其存储位…