c++ inline function

1. Inline function

In C++ we usually define the following function to find the maximum of two integers:

Copy the code The  code is as follows:

int max(int a, int b)
{
 return a > b ? a : b;
}

The benefits of defining a function for such a small operation are:

① It is much easier to read and understand the call to the function max than to read an equivalent conditional expression and explain its meaning

② If you need to make any changes, it is much easier to modify the function than to find and modify every equivalent expression

③ Using functions ensures uniform behavior, each test is guaranteed to be implemented in the same way

④ Functions can be reused without having to rewrite code for other applications

While there are so many benefits, writing as a function has a potential disadvantage: calling a function is much slower than solving the equivalent expression. On most machines, calling a function does a lot of work: registers are saved before the call, restored on return, arguments are copied, and the program must move to a new location.

Inline functions are supported in C++. The purpose is to improve the execution efficiency of functions. Use the keyword inline before the function definition (note that it is a definition rather than a declaration, which will be described below) to specify a function as an inline function. Inlining a function is usually just about expanding it "inline" at every call site in the program, let's say we define max as an inline function:

Copy the code The  code is as follows:

inline int max(int a, int b)
{
 return a > b ? a : b;
}

Then call: cout<<max(a, b)<<endl;


Expands at compile time to: cout<<(a > b ? a : b)<<endl;

Thus eliminating the extra execution overhead of writing max as a function

2. Inline functions and macros

Whether it is the "Prefer consts, enums, and inlines to #defines" clause in "Effective C++", or "Replace macros with function inlining" in "Guide to High Quality Programming - C++/C Language", macros are used in C++ In the book "High Quality Programming Guide - C++/C Language", it is explained as follows:

3. Put the inline function in the header file

The keyword inline must be placed with the body of the function definition to make the function inline, just putting inline before the function declaration has no effect.

A function Foo of the following style cannot be an inline function:

Copy the code The  code is as follows:

inline void Foo(int x, int y); // inline only with function declaration   
void Foo(int x, int y)
{
 ...

A function Foo of the following style becomes an inline function:

Copy the code The  code is as follows:

void Foo(int x, int y);   
inline void Foo(int x, int y) // inline with function body
{
 ...

So, the C++ inline function is a "keyword for implementation", not a "keyword for declaration". Typically, the user can read the declaration of a function, but cannot see the definition of the function. Although inline function declarations and definitions are preceded by the inline keyword in most textbooks, I don't think inline should appear in function declarations. Although this detail does not affect the function of the function, it reflects a basic principle of high-quality C++/C programming style: declaration and definition should not be confused, and the user does not need and should not know whether the function needs to be inlined.

Member functions defined in class declarations are automatically inlined, for example:

Copy the code The  code is as follows:

class A
{  
public:
 void Foo(int x, int y) { ... } // automatically becomes an inline function  

But whether the compiler actually inlines it depends on how the Foo function is defined

Inline functions should be defined in header files, unlike other functions. When the compiler inlines the code for the expanded function at the call site, it must be able to find the definition of the inline function in order to replace the calling function with the function code, and it is not enough for the function declaration in the header file.

Of course, inline function definitions can also be placed in source files, but at this time only the defined source file can use it, and a copy of the definition must be copied for each source file (that is, the definitions in each source file must be exactly the same ), of course, even if it is placed in the header file, it will make a copy of each definition, but the compiler will complete this copy for you. But compared to the source file, placing it in the header file can not only ensure that the definition of the calling function is the same, but also ensure that the function definition can be found at the calling site to complete inlining (replacement).

But you'd be wondering, repeating the definition so many times doesn't generate a linking error?

Let's look at an example:

A.h :

Copy the code The  code is as follows:

class A
{
public:
 A(int a, int b) : a(a),b(b){}
 int max();

private:
 int a;
 int b;
};

A.cpp : 

Copy the code The  code is as follows:

#include "A.h"

inline int A::max()
{
 return a > b ? a : b;
}

Main.cpp : 

Copy the code The  code is as follows:

#include <iostream>
#include "A.h"
using namespace std;

inline int A::max()
{
 return a > b ? a : b;
}

int main()
{
 A a(3, 5);
 cout<<a.max()<<endl;
 return 0;
}

一切正常编译,输出结果:5

 


倘若你在Main.cpp中没有定义max内联函数,那么会出现链接错误:

error LNK2001: unresolved external symbol "public: int __thiscall A::max(void)" (?max@A@@QAEHXZ)main.obj
找不到函数的定义,所以内联函数可以在程序中定义不止一次,只要 inline 函数的定义在某个源文件中只出现一次,而且在所有源文件中,其定义必须是完全相同的就可以。

在头文件中加入或修改 inline 函数时,使用了该头文件的所有源文件都必须重新编译。

4.  慎用内联

内联虽有它的好处,但是也要慎用,以下摘自《高质量程序设计指南——C++/C语言》:

而在Google C++编码规范中则规定得更加明确和详细:

内联函数:

Tip: 只有当函数只有 10 行甚至更少时才将其定义为内联函数.

定义: 当函数被声明为内联函数之后, 编译器会将其内联展开, 而不是按通常的函数调用机制进行调用.
优点: 当函数体比较小的时候, 内联该函数可以令目标代码更加高效. 对于存取函数以及其它函数体比较短, 性能关键的函数, 鼓励使用内联.
缺点: 滥用内联将导致程序变慢. 内联可能使目标代码量或增或减, 这取决于内联函数的大小. 内联非常短小的存取函数通常会减少代码大小, 但内联一个相当大的函数将戏剧性的增加代码大小. 现代处理器由于更好的利用了指令缓存, 小巧的代码往往执行更快。
结论: 一个较为合理的经验准则是, 不要内联超过 10 行的函数. 谨慎对待析构函数, 析构函数往往比其表面看起来要更长, 因为有隐含的成员和基类析构函数被调用!
另一个实用的经验准则: 内联那些包含循环或 switch 语句的函数常常是得不偿失 (除非在大多数情况下, 这些循环或 switch 语句从不被执行).
有些函数即使声明为内联的也不一定会被编译器内联, 这点很重要; 比如虚函数和递归函数就不会被正常内联. 通常, 递归函数不应该声明成内联函数.(递归调用堆栈的展开并不像循环那么简单, 比如递归层数在编译时可能是未知的, 大多数编译器都不支持内联递归函数). 虚函数内联的主要原因则是想把它的函数体放在类定义内, 为了图个方便, 抑或是当作文档描述其行为, 比如精短的存取函数.

-inl.h文件:


Tip: 复杂的内联函数的定义, 应放在后缀名为 -inl.h 的头文件中.


内联函数的定义必须放在头文件中, 编译器才能在调用点内联展开定义. 然而, 实现代码理论上应该放在 .cc 文件中, 我们不希望 .h 文件中有太多实现代码, 除非在可读性和性能上有明显优势.

如果内联函数的定义比较短小, 逻辑比较简单, 实现代码放在 .h 文件里没有任何问题. 比如, 存取函数的实现理所当然都应该放在类定义内. 出于编写者和调用者的方便, 较复杂的内联函数也可以放到 .h 文件中, 如果你觉得这样会使头文件显得笨重, 也可以把它萃取到单独的 -inl.h 中. 这样把实现和类定义分离开来, 当需要时包含对应的 -inl.h 即可。

转自:https://blog.csdn.net/u011327981/article/details/50601800

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325687368&siteId=291194637