C++深入探索内联函数inline与auto关键字的使用

目录
  • 1.内敛函数
    • 1.1问题引入
    • 1.2内联函数的概念
    • 1.3内敛函数的特性
  • 2.auto关键字
    • 2.1 auto简介
    • 2.2 auto的使用细则
    • 2.3 auto不能推导的场景
    • 2.4 auto与新式for循环使用

1.内敛函数

1.1问题引入

我们在使用C语言中我们都学过函数,我们知道函数在调用的过程中需要开辟栈帧。如果我们需要频繁的调用一个函数,假设我们调用10次Add()函数,那我们就需要建立10次栈帧。我们都知道在栈帧中要做很多事情,例如保存寄存器,压参数,压返回值等等,这个过程是很麻烦的。那在C语言中,我们可以通过宏来解决这个问题。在C++中,我们便引入了内敛函数(inline)。

1.2内联函数的概念

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

我们这里依然使用Add()函数举例。这段代码是我们常写的Add()函数。假设我们多次调用Add函数,在C语言中,我们可以使用宏替换。在C++我们可以在函数前加上inline

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

C语言中用宏来代替Add函数:

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

C++中在函数前加上inline使之成为内联函数

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

那C语言已经有了宏替换,为什么C++还要出现内联函数呢?

主要是有两个原因:

1.宏晦涩难懂不好控制,特别容易写错.语法机制设计不好。

2.宏不支持调试,但是内敛在debug下支持调试(在debug下不会展开,在release下才会展开),这样我们对代码的理解和掌握将大大提高。

如果在Add函数前增加 inline 关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用。 查看方式:

1. 在 release 模式下,查看编译器生成的汇编代码中是否存在 call Add

2. 在 debug 模式下,需要对编译器进行设置,否则不会展开 ( 因为 debug 模式下,编译器默认不会对代码进 行优化,以下给出 vs2019 的设置方式 )

1.3内敛函数的特性

1. inline 是一种 以空间换时间 的做法,省去调用函数额开销。所以 代码很长 或者有 循环 / 递归 的函数不适宜使用作为内联函数。

2. inline 对于编译器而言只是一个建议 ,编译器会自动优化,如果定义为 inline 的函数体内有循环 / 递归等等,编译器优化时会忽略掉内联。

3. inline 不建议声明和定义分离,分离会导致链接错误。因为 inline 被展开,就没有函数地址了,链接就会找不到

  • inline是一种以空间换时间的做法,省去调用函数额开销(建立栈帧).所以代码很长或者递归的函数不适宜用内联函数.

这里代码多长算长呢? 一般是10行左右,具体取决于编译器。

  • inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。

这里可以举个例子,假设我们有一个代码需要10行,但是我们需要调用1000次。如果inline替换的话,我们要有1000*10条指令,如果不替换则有1000+10条指令。因此最终是否替换,取决于编译器。

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

我们发现,虽然Add函数前加了inline,但是最终却没有展开。

  • inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到

我们在f.cpp中调用一下:发现报错了

在这里他就会发生链接错误:

这是因为test.h中替换到test.cpp中发现是内联函数,内联函数不需要生成地址,因为内联函数调用的地方都展开了,因此不会存在在符号表中。外部调用时就找不到。因此不要将声明和定义分开。

2.auto关键字

2.1 auto简介

在早期 C/C++ 中 auto 的含义是:使用 auto 修饰的变量,是具有自动存储器的局部变量。C++11中,标准委员会赋予了auto 全新的含义即: auto 不再是一个存储类型指示符,而是作为一个新的类型 指示符来指示编译器, auto 声明的变量必须由编译器在编译时期推导而得

auto在C语言中我们是接触过的:最宽宏大量的关键字,由于局部变量默认都是auto修饰的,因此auto可以省略,这就导致auto常常被人忽略。那么在C++11中,auto进行了升级,有了新的功能--自动推导。(注意:auto新功能只是在C++11之后才有此功能)

那么auto是怎么自动推导呢?

我们可以使用typeid,来打印一个变量的类型。

int main()
{
    const int  a = 10;
	auto b = &a;
	auto c = 'a';
	cout << typeid(b).name() << endl;//typeid可以打印一个变量的类型
	cout << typeid(a).name() << endl;//typeid可以打印一个变量的类型
	cout << typeid(c).name() << endl;//typeid可以打印一个变量的类型
	return 0;
}

我们发现auto的自动推导还是很智能的。

auto意义之一:类型很长时,懒得写,可以让他自动推导

注意:

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

2.2 auto的使用细则

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

int main()
{
	int x = 10;
	auto a = &x;
	auto& c = x;
	cout << typeid(x).name() << endl;//typeid可以打印一个变量的类型
	cout << typeid(a).name() << endl;//typeid可以打印一个变量的类型
	cout << typeid(c).name() << endl;//typeid可以打印一个变量的类型
	return 0;
}

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

	auto a = 1, b = 2;
	auto c = 3, d = 4.0;//c和d类型不同,auto推导会冲突的

2.3 auto不能推导的场景

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

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

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

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

2.4 auto与新式for循环使用

在平常我们打印一个array数组,我们需要依次打印遍历。如下代码所示:

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

我们也可以使用auto结合范围for循环打印这个数组:

	for (auto e : array)
		cout << e << " ";
	cout << endl;

在这里我们使用到了一个新的for循环,范围for。它会依次自动取array中的数据,赋值给e,并且会自动判断结束,因此我们使用auto可以自动识别数组元素的类型。

注意:e只是array的拷贝,改变e不会改变array数组的内容。如果我们想改变array的内容,只需要加个引用即可,意思是让e成为array的别名,代码如下:

	//加个引用就可以访问到array
	for (auto& e : array)
	{
		e /= 2;
	}
	for (auto e : array)
		cout << e << " ";

到此这篇关于C++深入探索内联函数inline与auto关键字的使用的文章就介绍到这了,更多相关C++ inline与auto内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ inline内联函数详解

    函数是一个可以重复使用的代码块,CPU 会一条一条地挨着执行其中的代码.CPU 在执行主调函数代码时如果遇到了被调函数,主调函数就会暂停,CPU 转而执行被调函数的代码:被调函数执行完毕后再返回到主调函数,主调函数根据刚才的状态继续往下执行. 一个 C/C++ 程序的执行过程可以认为是多个函数之间的相互调用过程,它们形成了一个或简单或复杂的调用链条,这个链条的起点是 main(),终点也是 main().当 main() 调用完了所有的函数,它会返回一个值(例如return 0;)来结束自己的生

  • C++11关于auto关键字的使用示例

    一.概述 auto关键字在c++98中已经出现,在98中定义为具有自动存储器的局部变量, c++11中标准委员会重新定义了auto关键字,表示一个类型占位符,告诉编译器,auto声明变量的类型必须由编译器在编译时期推导 而得. 注意事项: 1.auto关键字类型推断发生在编译期,程序运行时不会造成效率降低 2.auto关键字定义时就需要初始化 3.auto仅仅是一个占位符,它并不是一个真正的类型, 因此sizeof(auto)是错误的 4.auto不能作为函数的参数 5.auto不能定义数组,如

  • C++11新特性之auto的使用

    前言 C++是一种强类型语言,声明变量时必须明确指出其类型.但是,在实践中,优势我们很难推断出某个表达式的值的类型,尤其是随着模板类型的出现,要想弄明白某些复杂表达式的返回类型就变得更加困难.为了解决这个问题,C++11中引入的auto主要有两种用途:自动类型推断和返回值占位.auto在C++98中的标识临时变量的语义,由于使用极少且多余,在C++11中已被删除.前后两个标准的auto,完全是两个概念. 一.自动类型推断 auto自动类型推断,用于从初始化表达式中推断出变量的数据类型.通过aut

  • C++ auto类型说明符

    编程时常常需要把表达式的值赋给变量,这就要求在声明变量的时候清楚知道表达式的类型.然而要做到这一点并非那么容易,有时候甚至根本做不到.为了解决这个问题,C++11标准引入了auto类型说明符,用它就能让编译器替我们去分析表达式所属的类型. 与原来那些只对应一种特定类型的说明符不同,auto让编译器通过初值来推算变量类型.显然,auto定义的变量必须要有初始值. 使用auto具有以下几点好处: 可靠性:如果表达式的类型发生更改(包括函数返回值发生更改的情况),它也能工作. 性能:确保将不会进行转换

  • 详析C++中的auto

    目录 1.auto介绍 2.auto使用细则 3.auto不能推导的场景 1.auto介绍 在早期,C/C++中 auto 的含义是:使用auto修饰的变量,auto是具有自动存储器的局部变量.C++11中标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得. 可以使用typeid().name 去打印对象的类型. 案例分析: int TestAuto() {     return 2; }

  • C++ 内联函数详解

    目录 一.C++ 内联函数 1.内联 2.语法 3.评价 总结 一.C++ 内联函数 1.内联 C 语言中有宏函数的概念.宏函数的特点是内嵌到调用代码中去,避免了函数调用的开销.但是由于宏函数的处理发生在预处理阶段,缺失了语法检测和有可能带来的语意差错. 2.语法 C++提供了 inline 关键字,实现了真正的内嵌. 宏函数 VS inline函数 #include <iostream> #include <string.h> using namespace std; //优点:

  • C++内联函数详情

    内联函数是C++当中为了提高程序运行效率的设计,老实讲我没有在其他语言当中看到类似的设计.它和常规函数之间的主要区别不在于编写的方式,而是在于C++编译器会将内联函数组合到程序当中执行. 要解释这个过程会稍稍有些复杂,我们需要从编译的过程说起.对于编译型语言而言,编译器做的事情是把人类写出来人能读懂的代码翻译成机器能够识别.执行的机器语言,一般是一串十六进制的指令.随后计算机逐步执行这些指令,完成我们想要的功能. 当我们调用函数时,其实本质上是指令跳转,先记录下当前运行的指令位置,跳转到函数所在

  • c++内联函数(inline)使用详解

    介绍内联函数之前,有必要介绍一下预处理宏.内联函数的功能和预处理宏的功能相似.相信大家都用过预处理宏,我们会经常定义一些宏,如 复制代码 代码如下: #define TABLE_COMP(x) ((x)>0?(x):0) 就定义了一个宏. 为什么要使用宏呢?因为函数的调用必须要将程序执行的顺序转移到函数所存放在内存中的某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方.这种转移操作要求在转去执行前要保存现场并记忆执行的地址,转回后要恢复现场,并按原来保存地址继续执行.因此,函数调

  • 深入理解C++内联函数

    目录 内联函数的概念 内联函数和宏 内联函数的特性 总结 内联函数的概念 以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数的使用可以提升程序的运行效率. 举个例子: 在C++中我们通常定义以下函数来求两个整数的最大值: 代码如下: int max(int a, int b) { return a > b ? a : b; } 为这么一个小的操作定义一个函数的好处有: 阅读和理解函数 max 的调用,要比读一条等价的条件表达式并解释它

  • C++深入探索内联函数inline与auto关键字的使用

    目录 1.内敛函数 1.1问题引入 1.2内联函数的概念 1.3内敛函数的特性 2.auto关键字 2.1 auto简介 2.2 auto的使用细则 2.3 auto不能推导的场景 2.4 auto与新式for循环使用 1.内敛函数 1.1问题引入 我们在使用C语言中我们都学过函数,我们知道函数在调用的过程中需要开辟栈帧.如果我们需要频繁的调用一个函数,假设我们调用10次Add()函数,那我们就需要建立10次栈帧.我们都知道在栈帧中要做很多事情,例如保存寄存器,压参数,压返回值等等,这个过程是很

  • 内联函数inline与宏定义深入解析

    内联函数的优越性:一:inline定义的类的内联函数,函数的代码被放入符号表中,在使用时直接进行替换(像宏定义一样展开),没有了调用的开销,效率很高.二:类的内敛函数是一个真正的函数.三:使用内联函数inline可以完全取代表达式形式的宏定义. 例子: 复制代码 代码如下: Class A{public:int readTest(){return nTest:}void setTest(int i);};inline void A::setTest(int i){nTest=i;}; 说明:类A

  • C语言中的内联函数(inline)与宏定义(#define)详细解析

    先简明扼要,说下关键:1.内联函数在可读性方面与函数是相同的,而在编译时是将函数直接嵌入调用程序的主体,省去了调用/返回指令,这样在运行时速度更快. 2.内联函数可以调试,而宏定义是不可以调试的.内联函数与宏本质上是两个不同的概念如果程序编写者对于既要求快速,又要求可读的情况下,则应该将函数冠以inline.下面详细介绍一下探讨一下内联函数与宏定义. 一.内联函数是什么?内联函数是代码被插入到调用者代码处的函数.如同 #define 宏(但并不等同,原因见下文),内联函数通过避免被调用的开销来提

  • c++中的内联函数inline用法实例

    问题描述:类中成员函数缺省默认是内联的,如果在类定义时就在类内给出函数定义,那当然最好.如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上 inline,否则就认为不是内联的.内联函数的inline要加在函数前面,不可以加在声明前面. class A { public:void Foo(int x, int y) { } // 自动地成为内联函数 } //正确写法: // 头文件 class A { public: void Foo(int x, int y); } // 定义文

  • C++ 内联函数inline案例详解

    使用函数能够避免将相同代码重写多次的麻烦,还能减少可执行程序的体积,但也会带来程序运行时间上的开销. 函数调用在执行时,首先要在栈中为形参和局部变量分配存储空间,然后还要将实参的值复制给形参,接下来还要将函数的返回地址(该地址指明了函数执行结束后,程序应该回到哪里继续执行)放入栈中,最后才跳转到函数内部执行.这个过程是要耗费时间的. 另外,函数执行 return 语句返回时,需要从栈中回收形参和局部变量占用的存储空间,然后从栈中取出返回地址,再跳转到该地址继续执行,这个过程也要耗费时间. 总之,

  • 浅谈内联函数与宏定义的区别详解

    用内联取代宏:1.内联函数在运行时可调试,而宏定义不可以;2.编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会: 3.内联函数可以访问类的成员变量,宏定义则不能: 4.在类中声明同时定义的成员函数,自动转化为内联函数.文章(一)内联函数与宏定义 在C中,常用预处理语句#define来代替一个函数定义.例如: #define MAX(a,b) ((a)>(b)?(a):(b)) 该语句使得程序中每个出现MAX(a,b)函数调用的地方都被宏定义中后面的表达式((a)

  • C++超详细分析讲解内联函数

    目录 宏函数(带参数的宏)的缺点 inline修饰的函数就是内联函数 内联函数的特点 宏函数和内联函数的区别 宏函数(带参数的宏)的缺点 第一个问题:宏函数看起来像一个函数调用,但是会有隐藏一些难以发现的问题. 例如: #define FUN(x, y) (x * y) printf("%d", add(3, 3 + 2)) //3 * 3 + 2 = 11 以上情况可以通过加 “()” 解决: #define FUN(x, y) (x * y) printf("%d&quo

  • Go语言编程通过dwarf获取内联函数

    目录 dwarf组成 如何将 addr 转换为行号 内联函数 如何展开内联函数 使用 parca 展开内联函数 parca 输出有以下问题 dwarf组成 dwarf 由 The Debugging Information Entry . type Entry struct { Offset Offset Tag Tag // 描述其类型 Children bool Field []Field // 包含的字段 } 不同的 entry 有不同的类型: tag compile unit, 在 go

随机推荐