博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++内联函数
阅读量:4072 次
发布时间:2019-05-25

本文共 3751 字,大约阅读时间需要 12 分钟。

目录


1.内联函数的作用

内联函数是C++中的一个重要特性。所以,首先探讨下为何要使用内联函数,并且它的作用是什么。

当程序执行函数调用指令时,CPU会将执行函数调用的指令的内存地址保存起来,将函数的参数拷贝到栈内存中,最后将控制权转移给这个函数。

然后CPU执行函数代码,在一个预先定义的内存/寄存器中存储函数返回值,然后将控制权转移给主调函数。 如果函数的执行时间,少于主调函数切换到被调函数的切换时间,则证明切换消耗很大。
如果是一个执行复杂任务的函数,则函数调用的消耗通常远小于执行任务所需时间,可以不予考虑切换开销。
然而,对于小函数,函数的调用时间通常比函数代码本身执行时间大很多,一般是由于小函数执行时间比切换时间小。

C++使用内联函数来减少函数调用的开销。内联函数被调用时,会按行进行展开,整个函数代码会插入/替换到被调用的地方。这个替换的工作,是在编译期间进行的,由C++编译器实现。如果内联函数比较小,可以提高效率。

内联函数的定义:

inline return-type function-name(parameters list){    // function code}

注意:内联仅仅是对编译器的一个请求,而不是一个命令。也就是说,编译器可以忽略内联请求。在下面的情况下,编译器可以不执行内联。

1) 函数包含循环操作. (for, while, do-while)
2) 函数包含静态变量.
3) 函数是递归的.
4) 函数返回值类型非void, 且返回主体不存在于函数体中。
5) 函数包含switch或goto主体。

2.内联函数的优点

1) 不会产生函数调用开销。

2) 节约函数调用时将参数压栈的开销。
3) 节约函数return call的开销。
4) 当实现一个内联函数时,会让编译器在函数体中执行上下文的优化。这样的优化不会出现在普通的函数调用中。
5) 嵌入式系统中,内联函数可能有效,也可能无效。因为内联函数依靠牺牲代码大小,换取函数调用以及返回的优先权。

3.内联函数的缺点

1) 内联函数中增加的变量需要消耗更多的寄存器。在将函数内联后,如果它所需的寄存器数量增加了,则会给寄存器利用增加额外负担。也就是说,内联函数在展开的过程中,它所需要的变量数量也随之增长,这样变量所需的寄存器数量也会增长。因此,在函数内联后,如果变量数量增长很大,则所需要的寄存器开销随之快速增长。

2) 如果使用了太多的内联函数,则最终的二进制文件会变得很大,因为里边包含了很多重复代码。
3) 太多的内联函数,也会减少cache的命中率,于是会减少从cache memory取指令到primary memory的速度。
4) 内联函数可能会增加编译时间,如果内联函数被修改了,则所有调用它的地方都需要重新编译,因为编译器需要重新在这些地方展开新的代码,来反映所做的修改,否则还是会用旧的代码。
5) 对于很多的嵌入式系统来说,内联函数可能没有什么作用。因为对于嵌入式系统,代码大小远比速度重要。
6) 内联函数可能会引起系统失效/系统波动,因为可能增大了二进制可执行文件的大小。内存的波动会降低计算机性能。

下面举例说明内联函数的作用:

#include 
using namespace std;inline int cube(int s){ return s*s*s;}int main(){ cout << "The cube of 3 is: " << cube(3) << "\n"; return 0;}

输出:

The cube of 3 is: 27

4.内联函数和类

在类中也可以定义内联函数。实际上,在类中实现的所有函数,都是隐式的内联函数。因此,所有内联函数的限制在此同样适用。如果想要显示的声明内联函数,则可以在类内部声明函数,在类外部使用inline定义函数。

例如:

class S{public:    inline int square(int ss) //inline是多余的    {        //这个函数默认地被实现为内联函数    }};

上面展示的是一个不好的编程风格.。最佳方式是在类里边只声明函数原型,在类外边的函数定义处使用inline。

例如:

class S{public:    int square(int ss); //声明函数}; inline int S::square(int ss)//使用inline前缀{}

下面程序演示了这种方式:

#include 
using namespace std;class operation{private: int a,b,add,sub,mul; float div;public: void get(); void sum(); void difference(); void product(); void division();};inline void operation::get(){ cout << "Enter first value:"; cin >> a; cout << "Enter second value:"; cin >> b;}inline void operation::sum(){ add = a+b; cout << "Addition of two numbers: " << a+b << "\n";}inline void operation::difference(){ sub = a-b; cout << "Difference of two numbers: " << a-b << "\n";}inline void operation::product(){ mul = a*b; cout << "Product of two numbers: " << a*b << "\n";}inline void operation::division(){ div=a/b; cout<<"Division of two numbers: "<

输出:

Enter first value:11
Enter second value:22
Addition of two numbers: 33
Difference of two numbers: -11
Product of two numbers: 242
Division of two numbers: 0

5.为何不使用宏呢

熟悉C语言的人知道C会使用宏。预处理程序会使用宏代码替换所有调用宏的地方。

C++中通常更推荐使用内联函数而不是宏。根据C++之父的观点,C++几乎从来不必使用宏,并将宏视作错误实践。在C++中使用宏会存在一些问题。宏无法访问类的私有成员。宏看起来像是函数调用,但实际上并不是。
例如:

#include 
using namespace std;class S{private: int m;public:#define MAC(S::m) //编译错误};

C++ 编译器会检测内联函数的参数类型以及类型转换是否合法,而预处理程序无法做这样的检测。另外,宏是由预处理程序来管理,而内联函数由C++编译器管理。

6.总结

牢记:在类中实现的所有函数确实都是隐式的内联函数,并且C++编译器在所有这些函数被调用的地方会按照内联方式处理。

但是,如果是虚函数(virtual),则C++编译器无法进行内联处理。原因在于, 虚函数是在运行时(runtime)期间调用的,而不是编译期间。virtual代表着运行时,而inline代表着编译期间。如果编译器不知道哪个函数会被调用,它怎能知道执行内联处理。

注意:仅当函数调用开销时间大于函数本身执行时间时,函数定义为内联才有意义。

下面是一个不需要定义为inline的例子:

inline void show(){    cout << "value of S = " << S << endl;}

上述函数会花费相对比较多的时间来执行。通常执行I/O操作的函数不应该定义为内联函数,因为它们会消耗相对比较多的时间。技术上可以预期,show函数执行所需时间,远大于调用开销。

通常如果函数没有扩展为内联函数,则编译器一般会给出warning。像Java & C#等语言,不支持内联。

但是,在Java中,当小的final method被调用时,编译器会进行内联处理,因为final method 不能被子类覆盖/覆写,并且final method的调用可以在编译期间就确定。C# JIT编译器也能将小的函数进行内联处理。

总结:内联是C++中的一个特性,恰当使用它能增强程序性能,但是反之会降低性能。不需要把每个函数都定义为内联。应该尽可能的把内联函数限制到最小。

转载地址:http://wqeji.baihongyu.com/

你可能感兴趣的文章
大数据入门:ZooKeeper工作原理
查看>>
大数据入门:Zookeeper结构体系
查看>>
大数据入门:Spark RDD基础概念
查看>>
大数据入门:SparkCore开发调优原则
查看>>
大数据入门:Java和Scala编程对比
查看>>
大数据入门:Scala函数式编程
查看>>
【数据结构周周练】002顺序表与链表
查看>>
C++报错:C4700:使用了非初始化的局部变量
查看>>
【数据结构周周练】003顺序栈与链栈
查看>>
C++类、结构体、函数、变量等命名规则详解
查看>>
C++ goto语句详解
查看>>
【数据结构周周练】008 二叉树的链式创建及测试
查看>>
《软件体系结构》 第九章 软件体系结构评估
查看>>
《软件体系结构》 第十章 软件产品线体系结构
查看>>
《软件过程管理》 第六章 软件过程的项目管理
查看>>
《软件过程管理》 第九章 软件过程的评估和改进
查看>>
《软件过程管理》 第八章 软件过程集成管理
查看>>
分治法 动态规划法 贪心法 回溯法 小结
查看>>
《软件体系结构》 练习题
查看>>
《数据库系统概论》 第一章 绪论
查看>>