C++ 内联函数inline

http://blog.csdn.net/u011327981/article/details/50601800

1.  内联函数

在C++中我们通常定义以下函数来求两个整数的最大值:

复制代码 代码如下:

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

为这么一个小的操作定义一个函数的好处有:

① 阅读和理解函数 max 的调用,要比读一条等价的条件表达式并解释它的含义要容易得多

② 如果需要做任何修改,修改函数要比找出并修改每一处等价表达式容易得多

③ 使用函数可以确保统一的行为,每个测试都保证以相同的方式实现

④ 函数可以重用,不必为其他应用程序重写代码

虽然有这么多好处,但是写成函数有一个潜在的缺点:调用函数比求解等价表达式要慢得多。在大多数的机器上,调用函数都要做很多工作:调用前要先保存寄存器,并在返回时恢复,复制实参,程序还必须转向一个新位置执行

C++中支持内联函数,其目的是为了提高函数的执行效率,用关键字 inline 放在函数定义(注意是定义而非声明,下文继续讲到)的前面即可将函数指定为内联函数,内联函数通常就是将它在程序中的每个调用点上“内联地”展开,假设我们将 max 定义为内联函数:

复制代码 代码如下:

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

则调用: cout<<max(a, b)<<endl;

在编译时展开为: cout<<(a > b ? a : b)<<endl;

从而消除了把 max写成函数的额外执行开销

2.  内联函数和宏

无论是《Effective C++》中的 “Prefer consts,enums,and inlines to #defines” 条款,还是《高质量程序设计指南——C++/C语言》中的“用函数内联取代宏”,宏在C++中基本是被废了,在书《高质量程序设计指南——C++/C语言》中这样解释到:

3.  将内联函数放入头文件

关键字 inline 必须与函数定义体放在一起才能使函数成为内联,仅将 inline 放在函数声明前面不起任何作用。

如下风格的函数 Foo 不能成为内联函数:

复制代码 代码如下:

inline void Foo(int x, int y);   // inline 仅与函数声明放在一起   
void Foo(int x, int y)
{
 ...
}

而如下风格的函数 Foo 则成为内联函数:

复制代码 代码如下:

void Foo(int x, int y);   
inline void Foo(int x, int y)   // inline 与函数定义体放在一起
{
 ...
}

所以说,C++ inline函数是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。一般地,用户可以阅读函数的声明,但是看不到函数的定义。尽管在大多数教科书中内联函数的声明、定义体前面都加了 inline 关键字,但我认为 inline 不应该出现在函数的声明中。这个细节虽然不会影响函数的功能,但是体现了高质量C++/C 程序设计风格的一个基本原则:声明与定义不可混为一谈,用户没有必要、也不应该知道函数是否需要内联。

定义在类声明之中的成员函数将自动地成为内联函数,例如:

复制代码 代码如下:

class A
{  
public:
 void Foo(int x, int y) { ... }   // 自动地成为内联函数  
}

但是编译器是否将它真正内联则要看 Foo函数如何定义

内联函数应该在头文件中定义,这一点不同于其他函数。编译器在调用点内联展开函数的代码时,必须能够找到 inline 函数的定义才能将调用函数替换为函数代码,而对于在头文件中仅有函数声明是不够的。

当然内联函数定义也可以放在源文件中,但此时只有定义的那个源文件可以用它,而且必须为每个源文件拷贝一份定义(即每个源文件里的定义必须是完全相同的),当然即使是放在头文件中,也是对每个定义做一份拷贝,只不过是编译器替你完成这种拷贝罢了。但相比于放在源文件中,放在头文件中既能够确保调用函数是定义是相同的,又能够保证在调用点能够找到函数定义从而完成内联(替换)。

但是你会很奇怪,重复定义那么多次,不会产生链接错误?

我们来看一个例子:

A.h :

复制代码 代码如下:

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

private:
 int a;
 int b;
};

A.cpp :

复制代码 代码如下:

#include "A.h"

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

Main.cpp :

复制代码 代码如下:

#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)" ([email protected]@@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 即可。

时间: 2024-08-08 09:38:21

C++ 内联函数inline的相关文章

【转】ios内联函数 inline

ios内联函数 inline 缘由 由于在学习使用UIScrollVew开发的过程中,碰到下面这个属性(设置内边距): @property(nonatomic) UIEdgeInsets scrollIndicatorInsets; // default is UIEdgeInsetsZero. adjust indicators inside 1 光看UIEdgeInsets这个类型,一时还不知道它的具体内部结构是怎么样的,于是继续点进去发现它的定义如下: typedef struct UIE

内联函数inline

1:使用inline函数的时候,必须使函数体和inline说明结合一起,否则编译器将视他为普通函数处理: false: inline void Coord::setcoord(int a,int b); true: inline void Coord::setcoord(int a ,int b) { x = a; y =b; } 2:通常只有较短的成员函数才会定义为内联函数. 3:内联函数是编译时展开到调用处.

内联函数 inline 漫谈

内联函数存在的结论是: 引入内联函数是为了解决函数调用效率的问题 由于函数之间的调用,会从一个内存地址调到另外一个内存地址,当函数调用完毕之后还会返回原来函数执行的地址.函数调用会有一定的时间开销,引入内联函数就是为了解决这一问题. 不用inline修饰的函数, 汇编时会出现 call 指令.调用call指令就是就需要: (1)将下一条指令的所在地址入栈 (2)并将子程序的起始地址送入PC(于是CPU的下一条指令就会转去执行子程序). 原因 因为调用函数实际上将程序执行顺序转移到函数所存放在内存

内联函数 inline

(一)inline函数(摘自C++ Primer的第三版) 在函数声明或定义中函数返回类型前加上关键字inline即把min()指定为内联. inline int min(int first, int secend) {/****/}; inline 函数对编译器而言必须是可见的,以便它能够在调用点内展开该函数.与非inline函数不同的是,inline函数必须在调用该函数的每个文本文件中定义.当然,对于同一程序的不同文件,如果inline函数出现的话,其定义必须相同.对于由两个文件comput

iOS OC内联函数 inline的详解

inline 在iOS中的一些框架中,static inline是经常出现的关键字组合. static自不用多说,表示在当前文件中应用,如 static A, 在其它文件中也可以出现static A.不会导致重名的错误. inline.内联函数. 作用:替代宏. (如果你在看框架时,看到inline不解,搜索到这篇文章,看到这里可以不用看下面的详述了) 在框架中出现inline时,如YYKit框架.我们稍加观察就会发现它出现在.h文件中. such as: static inline CGFlo

【转】新手入门:关于C++中的内联函数(inline)

原文地址:http://www.pconline.com.cn/pcedu/empolder/gj/c/0503/563879.html 在c++中,为了解决一些频繁调用的小函数大量消耗栈空间或者是叫栈内存的问题,特别的引入了inline修饰符,表示为内联函数. 可能说到这里,很多人还不明白什么是栈空间,其实栈空间就是指放置程序的局部数据也就是函数内数据的内存空间,在系统下,栈空间是有限的,如果频繁大量的使用就会造成因栈空间不足所造成的程序出错的问题,函数的死循环递归调用的最终结果就是导致栈内存

c++中的内联函数inline

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

(转)内联(inline)函数与虚函数(virtual)的讨论

本文转自: http://topic.csdn.net/t/20051220/09/4469273.html 函数的inline属性是在编译时确定的, 然而,virtual的性质是在运行时确定的,这两个不能同时存在,只能有一个选择,文件中的inline关键字只是对编译器的建议,编译器是否采纳是编译器的事情. 1.内联函数是个静态行为,而虚函数是个动态行为,他们之间是有矛盾的. 2.我们之所以能看到一些象内联函数的虚函数,是因为某个函数是否是内联函数不是由我们说的算,而是由编译器决定的.我们只能向

内联函数详解

什么是内联性和外联函数 类的成员函数可以分为内联函数和外联函数.内联函数是指那些定义在类体内的成员函数,即该函数的函数体放在类体内.而说明在类体内,定义在类体外的成员函数叫外联函数.外联函数的函数体在类的实现部分. 内联函数在调用时不是像一般的函数那样要转去执行被调用函数的函数体,执行完成后再转回调用函数中,执行其后语句,而是在调用函数处用内联函数体的代码来替换,这样将会节省调用开销,提高运行速度. 内联函数与前面讲过的带参数的宏定义进行一下比较,它们的代码效率是一样的,但是内联函数要优于宏定义