C++ inline 内联函数
因为SP's jump, 函数调用要引入额外的时间开销。 C++针对此问题,引入 inline 函数避免此开销,提高程序运行效率。
Inline functions are a C++ enhancement designed to speed up programs.The primary dis- tinction between normal functions and inline functions is not in how you code them but in how the C++ compiler incorporates them into a program.To understand the distinc- tion between inline functions and normal functions, you need to peer more deeply into a program’s innards than we have so far. Let’s do that now.
The final product of the compilation process is an executable program, which consists of a set of machine language instructions.When you start a program, the operating system loads these instructions into the computer’s memory so that each instruction has a partic- ular memory address.The computer then goes through these instructions step-by-step. Sometimes, as when you have a loop or a branching statement, program execution skips over instructions, jumping backward or forward to a particular address. Normal function calls also involve having a program jump to another address (the function’s address) and then jump back when the function terminates. However, Jumping back and forth and keeping track of where to jump means that there is an overhead in elapsed time to using functions.
【函数调用是如何实现的】Let’s look at a typical implementation of that process in a little more detail.When a program reaches the function call instruction, the program stores the memory address of the instruction immediately following the function call, copies function arguments to the stack (a block of memory reserved for that purpose), jumps to the memory location that marks the beginning of the function, exe- cutes the function code (perhaps placing a return value in a register), and then jumps back to the instruction whose address it saved.
【inline函数的工作方式】C++ inline functions provide an alternative. In an inline function, the compiled code is 'in line' with the other code in the program. That is, the compiler relaces the function call with the corresponding function code. Within inline code, the program doesn't have to jump to another location to execute the code and then jump back.【inline函数的优缺点】 Inline functions thus run a little faster than regular functions, but they come with a memory penalty. If a program calls an inline function at ten separate locations, then the program winds up with ten copies of the function inserted into the code.
Note that n = 2; n = 4; .. n=10; are also inserted as the beginning of hubba's function body once inline function is invoked as the right sub-picture indicates.
You should be selective about using inilne functions. If the time needed to execute the function code is long compared to the time needed to handle the function call mechanism, then the time saved is a relatively small portion of the entire process. If the code execution time is short, then an inline call can save a large portion of the time used by the non-inline call. On the other hand, you are now saving a large portion of a relatively quick process, so the absolute tiem savings may not be that great unless the function is called frequently.
To use this feature, you must take at least one of two actions:
- Preface the function declaration with the keyword inline
- Preface teh function definition with the keyword inline
【inline函数的可用性】The compiler does not have to honor your request to make a function inline. It might decide the function is too large or notice that it calls itself (recursion is not allowed or indeed possible for inline functions), or the feature might not be turned on or implemented for your particular compielr. 总结 inline 函数不可用的几个场景
- 递归函数
- 编译器拒绝不合理的 inline 请求
- 编译器无此 inline 的功能
【题目2-22】为什么要引入内联函数?
引入内联函数的主要目的是,用它替代C语言中表达式形式的宏定义来解决程序中函数调用的效率用题。C语言的宏定义在形式及使用上像一个函数,但它使用预处理器实现,没有了参数压栈,代码生成等乙烯利从操作,因此效率很高。但是在用时,仅仅只是做预处理符号表中的简单替换,因此宏定义不能进行参数有效性的检,也就不能销售C++编译器严格类型检查的好处。另外,宏定义的返回值不能被强制转化为合适类,这样,宏定义的使用就存在着一系列的隐患和局限性。
另外,在C++中引入了类及类型的访问限制,这样,如果一个操作或者说一个表达式涉及类的保护成员或私有成员,你就不可能使用这种宏定义来实现(因为无法将this指针放在合适的位置)。inline推出的目的就是为了取代这种表达式的宏定义,消除它的缺点同时又很好地继承了它的优点。
【题目2-23】为什么 inline 能很好地取代表达式形式的预定义
- inline 定义的类的内联函数,函数的代码被放入符号表中,在使用时直接进行替换(像宏一样展开),没有了调用的开销,效率也很高
- 类的内联函数也是一个真正的函数。编译器在调用一个内联函数时,首先会检查它的参数类型,保证调用正确;然后进行一系列的相关检查,就像对待任何一个真正的函数一样。这样就消除了它的隐患和局限性
- inline 可以作为某个类的成员函数,当然就可以在其中使用所有类的保护成员以及私有成员
【题目2-24】说明内联函数使用的场合
使用 inline 函数可以完全取代表达式形式的宏定义
内联函数在C++类中应用最广的,应该是用定义存取函数。我们定义了类中一般会把数据成员定义成私有的或者保护的,这样,外界就不能直接读写我们类成员的数据。对于私有或者保护成员的读写就必须使用成员接口函数来进行。如果我们把这这些读写成员函数定义成内联函数的话,将会获得比较好的效率。如
1 class A{ 2 private: 3 int nTest; 4 public: 5 int readTest(){return nTest;} 6 void setTest(int i); 7 }; 8 inline void A::setTest(int i){ 9 nTest = i; 10 }
类A的成员函数 readTest() 和 setTest() 都是 inline 函数
- readTest() 函数的定义体被放在类声明中,因而 readTest() 自动转换成 inline 函数
- setTest()函数的定义体在类声明外,因而要加上inline关键字。
【题目2-25】为什么不把所有的函数都定义成内联函数
内联是以代码膨胀(复制)为代价的,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间相比于函数调用的开销较大,那么效率的收获会很少。另一个方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。以下情况不宜使用内联
- 如果函数体内的代码比较长,使用内联函数将导致内存消耗代价较高
- 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大
- 类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如‘偷偷地’执行了基类或成员对象的构造函数和析构函数。所以,不要随便地将构造函数和析构函数的定义体放在类声明中。
注意:一个好的编译器将会更具函数的定义体,自动地取消不值得的内联 (这说明了 inline 不应该出现在函数的声明中)。
【题目2-26】内联函数与宏又什么区别?
- 内联函数在编译时展开,宏在预编译时展开
- 在编译的时候,内联函数可以直接被镶嵌到目标代码中,而宏只是一个简单的文本替换
- 内联函数可以完成诸如类型检测,语句是否正确等编译功能,而宏就不具有这样的功能
- 宏不是函数,inline 函数是函数
- 宏在定义时要小心处理宏参数(一般情况是把参数用括号括起来),否则会出现二义性。而内联函数定义时不会出现二义性