Inline Functions in C++

来源:互联网 发布:红后 知乎 编辑:程序博客网 时间:2024/05/18 18:01

关于Inline Functions in C++的介绍:

内联函数(Inline Functions),大家多少有所耳闻,但是你能详细地讲解一下内联函数吗?估计,一部分人说得出来,但是说得并不完整。下面,让我们一起详细地回顾一下什么是内联函数?


一、内联函数定义

内联函数的定义,请参考维基百科的文章(英文的链接地址),下面是部分摘录:

在计算机科学中,内联函数(有时称作在线函数或编译时期展开函数)是一种编程语言结构,用来建议编译器对一些特殊函数进行内联扩展(有时称作在线扩展);也就是说建议编译器将指定的函数体插入并取代每一处调用该函数的地方(上下文),从而节省了每次调用函数带来的额外时间开支。


二、介绍内联函数的文章(1)

Inline Functions in C++

Although you've already learned about basic functions in c++, there is more: the inline function. Inline functions are not always important, but it is good to understand them. The basic idea is to save time at a cost in space. Inline functions are a lot like a placeholder. Once you define an inline function, using the 'inline' keyword, whenever you call that function the compiler will replace the function call with the actual code from the function.


How does this make the program go faster? Simple, function calls are simply more time consuming than writing all of the code without functions. To go through your program and replace a function you have used 100 times with the code from the function would be time consuming not too bright. Of course, by using the inline function to replace the function calls with code you will also greatly increase the size of your program.

Using the inline keyword is simple, just put it before the name of a function. Then, when you use that function, pretend it is a non-inline function.

Example Inline Function

#include <iostream>using namespace std;inline void hello(){    cout<<"hello";}int main(){    hello(); //Call it like a normal function...    cin.get();}
However, once the program is compiled, the call to hello(); will be replaced by the code making up the function.

A WORD OF WARNING: Inline functions are very good for saving time, but if you use them too often or with large functions you will have a tremendously large program. Sometimes large programs are actually less efficient, and therefore they will run more slowly than before. Inline functions are best for small functions that are called often.

Finally, note that the compiler may choose, in its infinite wisdom, to ignore your attempt to inline a function. So if you do make a mistake and inline a monster fifty-line function that gets called thousands of times, the compiler may ignore you.

In the future, we will discuss inline functions in terms of C++ classes. Now that you understand the concept I will feel more comfortable using inline functions in later tutorials. 

(原文地址:http://www.cprogramming.com/tutorial/lesson13.html)


三、介绍内联函数的文章(2)

 Inline functions

(Part of C++ FAQ, Copyright © 1991-2011, Marshall Cline, cline@parashift.com)
FAQs in section [9]:

[9.1] What's the deal with inline functions?

When the compiler inline-expands a function call, the function's code gets inserted into the caller's code stream (conceptually similar to what happens with a #define macro). This can, depending on a zillion other things, improve performance, because the optimizer can procedurally integrate the called code — optimize the called code into the caller.

There are several ways to designate that a function is inline, some of which involve the inline keyword, others do not. No matter how you designate a function as inline, it is a request that the compiler is allowed to ignore: it might inline-expand some, all, or none of the calls to an inline function. (Don't get discouraged if that seems hopelessly vague. The flexibility of the above is actually a huge advantage: it lets the compiler treat large functions differently from small ones, plus it lets the compiler generate code that is easy to debug if you select the right compiler options.)

[9.2] What's a simple example of procedural integration?

Consider the following call to function g():

void f(){    int x = /*...*/;    int y = /*...*/;    int z = /*...*/;    ...code that uses x, y and z...    g(x, y, z);    ...more code that uses x, y and z...}
Assuming a typical C++ implementation that has registers and a stack, the registers and parameters get written to the stack just before the call to g(), then the parameters get read from the stack inside g() and read again to restore the registers while g() returns to f(). But that's a lot of unnecessary reading and writing, especially in cases when the compiler is able to use registers for variables x, y and z: each variable could get written twice (as a register and also as a parameter) and read twice (when used within g() and to restore the registers during the return to f()).
void g(int x, int y, int z){    ...code that uses x, y and z...}
If the compiler inline-expands the call to g(), all those memory operations could vanish. The registers wouldn't need to get written or read since there wouldn't be a function call, and the parameters wouldn't need to get written or read since the optimizer would know they're already in registers.

Naturally your mileage may vary, and there are a zillion variables that are outside the scope of this particular FAQ, but the above serves as an example of the sorts of things that can happen with procedural integration.

[9.3] Do inline functions improve performance?

Yes and no. Sometimes. Maybe.

There are no simple answers. inline functions might make the code faster, they might make it slower. They might make the executable larger, they might make it smaller. They might cause thrashing, they might prevent thrashing. And they might be, and often are, totally irrelevant to speed.

inline functions might make it faster: As shown above, procedural integration might remove a bunch of unnecessary instructions, which might make things run faster.

inline functions might make it slower: Too much inlining might cause code bloat, which might cause "thrashing" on demand-paged virtual-memory systems. In other words, if the executable size is too big, the system might spend most of its time going out to disk to fetch the next chunk of code.

inline functions might make it larger: This is the notion of code bloat, as described above. For example, if a system has 100 inline functions each of which expands to 100 bytes of executable code and is called in 100 places, that's an increase of 1MB. Is that 1MB going to cause problems? Who knows, but it is possible that that last 1MB could cause the system to "thrash," and that could slow things down.

inline functions might make it smaller: The compiler often generates more code to push/pop registers/parameters than it would by inline-expanding the function's body. This happens with very small functions, and it also happens with large functions when the optimizer is able to remove a lot of redundant code through procedural integration — that is, when the optimizer is able to make the large function small.

inline functions might cause thrashing: Inlining might increase the size of the binary executable, and that might cause thrashing.

inline functions might prevent thrashing: The working set size (number of pages that need to be in memory at once) might go down even if the executable size goes up. When f() calls g(), the code is often on two distinct pages; when the compiler procedurally integrates the code of g() into f(), the code is often on the same page.

inline functions might increase the number of cache misses: Inlining might cause an inner loop to span across multiple lines of the memory cache, and that might cause thrashing of the memory-cache.

inline functions might decrease the number of cache misses: Inlining usually improves locality of reference within the binary code, which might decrease the number of cache lines needed to store the code of an inner loop. This ultimately could cause a CPU-bound application to run faster.

inline functions might be irrelevant to speed: Most systems are not CPU-bound. Most systems are I/O-bound, database-bound or network-bound, meaning the bottleneck in the system's overall performance is the file system, the database or the network. Unless your "CPU meter" is pegged at 100%, inline functions probably won't make your system faster. (Even in CPU-bound systems, inline will help only when used within the bottleneck itself, and the bottleneck is typically in only a small percentage of the code.)

There are no simple answers: You have to play with it to see what is best. Do not settle for simplistic answers like, "Never use inline functions" or "Always use inline functions" or "Use inline functions if and only if the function is less than N lines of code." These one-size-fits-all rules may be easy to write down, but they will produce sub-optimal results.

[9.4] How can inline functions help with the tradeoff of safety vs. speed?

In straight C, you can achieve "encapsulated structs" by putting a void* in a struct, in which case the void* points to the real data that is unknown to users of the struct. Therefore users of the struct don't know how to interpret the stuff pointed to by the void*, but the access functions cast the void* to the appropriate hidden type. This gives a form of encapsulation.

Unfortunately it forfeits type safety, and also imposes a function call to access even trivial fields of the struct (if you allowed direct access to the struct's fields, anyone and everyone would be able to get direct access since they would of necessity know how to interpret the stuff pointed to by the void*; this would make it difficult to change the underlying data structure).

Function call overhead is small, but can add up. C++ classes allow function calls to be expanded inline. This lets you have the safety of encapsulation along with the speed of direct access. Furthermore the parameter types of these inline functions are checked by the compiler, an improvement over C's #define macros.

[9.5] Why should I use inline functions instead of plain old #define macros?

Because #define macros are evil in 4 different ways: evil#1, evil#2, evil#3, and evil#4. Sometimes you should use them anyway, but they're still evil.

Unlike #define macros, inline functions avoid infamous macro errors since inline functions always evaluate every argument exactly once. In other words, invoking an inline function is semantically just like invoking a regular function, only faster:
// A macro that returns the absolute value of i#define unsafe(i)  \        ( (i) >= 0 ? (i) : -(i) ) // An inline function that returns the absolute value of iinlineint safe(int i){    return i >= 0 ? i : -i;} int f(); void userCode(int x){    int ans;     ans = unsafe(x++);   // Error! x is incremented twice    ans = unsafe(f());   // Danger! f() is called twice     ans = safe(x++);     // Correct! x is incremented once    ans = safe(f());     // Correct! f() is called once}
Also unlike macros, argument types are checked, and necessary conversions are performed correctly.

Macros are bad for your health; don't use them unless you have to.

[9.6] How do you tell the compiler to make a non-member function inline?

When you declare an inline function, it looks just like a normal function:
 void f(int i, char c);
But when you define an inline function, you prepend the function's definition with the keyword inline, and you put the definition into a header file:
inlinevoid f(int i, char c){    ...}
Note: It's imperative that the function's definition (the part between the {...}) be placed in a header file, unless the function is used only in a single .cpp file. In particular, if you put the inline function's definition into a .cpp file and you call it from some other .cpp file, you'll get an "unresolved external" error from the linker.

[9.7] How do you tell the compiler to make a member function inline?

When you declare an inline member function, it looks just like a normal member function:
class Fred {public:    void f(int i, char c);};
But when you define an inline member function, you prepend the member function's definition with the keyword inline, and you put the definition into a header file:
inlinevoid Fred::f(int i, char c){    ...}
It's usually imperative that the function's definition (the part between the {...}) be placed in a header file. If you put the inline function's definition into a .cpp file, and if it is called from some other .cpp file, you'll get an "unresolved external" error from the linker.

[9.8] Is there another way to tell the compiler to make a member function inline?

Yep: define the member function in the class body itself:
class Fred {public:    void f(int i, char c)    {        ...    }};
Although this is easier on the person who writes the class, it's harder on all the readers since it mixes "what" a class does with "how" it does them. Because of this mixture, we normally prefer to define member functions outside the class body with the inline keyword. The insight that makes sense of this: in a reuse-oriented world, there will usually be many people who use your class, but there is only one person who builds it (yourself); therefore you should do things that favor the many rather than the few. This approach is further exploited in the next FAQ.

[9.9] With inline member functions that are defined outside the class, is it best to put the inline keyword next to the declaration within the class body, next to the definition outside the class body, or both?

Best practice: only in the definition outside the class body.
class Foo {public:    void method();  ← best practice: don't put the inline keyword here    ...}; inline void Foo::method()  ← best practice: put the inline keyword here{ ... }
Here's the basic idea:

    The public: part of the class body is where you describe the observable semantics of a class, its public member functions, its friend functions, and anything else exported by the class. Try not to provide any inklings of anything that can't be observed from the caller's code.
    The other parts of the class, including non-public: part of the class body, the definitions of your member and friend functions, etc. are pure implementation. Try not to describe any observable semantics that were not already described in the class's public: part.

From a practical standpoint, this separation makes life easier and safer for your users. Say Chuck wants to simply "use" your class. Because you read this FAQ and used the above separation, Chuck can read your class's public: part and see everything he needs to see and nothing he doesn't need to see. His life is easier because he needs to look in only one spot, and his life is safer because his pure mind isn't polluted by implementation minutiae.

Back to inline-ness: the decision of whether a function is or is not inline is an implementation detail that does not change the observable semantics (the "meaning") of a call. Therefore the inline keyword should go next to the function's definition, not within the class's public: part.

NOTE: most people use the terms "declaration" and "definition" to differentiate the above two places. For example, they might say, "Should I put the inline keyword next to the declaration or the definition?" Unfortunately that usage is sloppy and somebody out there will eventually gig you for it. The people who gig you are probably insecure, pathetic wannabes who know they're not good enough to actually acomplish something with their lives, nonetheless you might as well learn the correct terminology to avoid getting gigged. Here it is: every definition is also a declaration. This means using the two as if they are mutually exclusive would be like asking which is heavier, steel or metal? Almost everybody will know what you mean if you use "definition" as if it is the opposite of "declaration," and only the worst of the techie weenies will gig you for it, but at least you now know how to use the terms correctly.
(原文地址:http://www.parashift.com/c++-faq-lite/inline-functions.html)