inline的使用

本文详细介绍了C++中的inline函数,包括其作用、好处、与宏的区别、类中inline函数的使用方式以及慎用原因。inline函数旨在减少函数调用开销,通过在编译时代码展开实现,但可能导致代码膨胀。了解何时适当使用inline至关重要,因为它并非性能提升的万能解决方案。

目录

 

1.inline函数的介绍:

1.1.引入inline相关知识:

         1.2.使用inline的好处:

2.inline函数和宏的区别:

3.如何使用类的inline函数:

  3.1.在类中定义这种函数:

  3.2.在类外定义前加inline关键字:

  3.3.此外含有一些规则需注意:

  3.4.inline 是一种“用于实现的关键字”

4.慎用inline

5.小结


1.inline函数的介绍:

 

1.1.引入inline相关知识:

1.Inline:

在c++中,用户可以创建实际上不调用的短函数,它们的代码在每次调用的程序行得到扩展,这个过程类似于使用类函数的宏,为了使一个函数在程序行内进行代码扩展而不被调用,只要在函数前面加上inline关键字即可

Inline 对于编译器来说是一种请求,而不是命令

在类声明内定义短函数是有可能的,如果一个函数是在类声明内定义的,它将被自动转换为内联函数,没有必要在函数声明之前加上inline 关键字,构造函数和析构函数也可以是内联函数。                                                           from:《c/c++参考大全》

2.先讲下函数调用的具体流程:

常规函数调用使程序跳到另外一个地址:函数地址,并在函数结束时返回

具体实现:执行到函数调用指令时,程序将在函数调用时立即存储该指令的内存地址,并将函数参数复制到堆栈,跳到标记函数起点的内存单元,执行函数代码,然后调回到地址被保存的指令处,来回跳跃并记录位置:普通函数调用时需要一定的开销。

C++中的内联提供了另一种选择,编译器将使用相应的函数代码替换函数调用,对于内联代码,程序无需跳到另一个位置执行代码,然后再跳回来,所以内联函数的运行速度比常规函数快,但是需要占用更多的内存

****如果程序在10个不同的地方调用同一个内联函数,则该程序将包含该函数的10个代码拷贝。     from:《c++primer plus》

1.2.使用inline的好处:

引入inline关键字的原因

在c/c++中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了inline修饰符,表示为内联函数。

栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间。

在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足而导致程序出错的问题,如,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。

例子:

#include <stdio.h>
//函数定义为inline即:内联函数
inline char* dbtest(int a) {
    return (i % 2 > 0) ? "奇" : "偶";
} 

int main()
{
   int i = 0;
   for (i=1; i < 100; i++) {
       printf("i:%d    奇偶性:%s /n", i, dbtest(i));    
   }
}

上面的例子就是标准的内联函数的用法,使用inline修饰带来的好处我们表面看不出来,其实,在内部的工作就是在每个for循环的内部任何调用dbtest(i)的地方都换成了(i%2>0)?”奇”:”偶”。使用inline就:避免了频繁调用函数对栈内存重复开辟所带来的消耗
局限:

当然,inline函数不是在任何条件下都可以使用的,只有函数本身没有调用本身并且函数不是复杂函数(当函数中存在while循环或swich语句时为复杂函数,但是不是复杂函数还要看编译器对它的判断)时,才可以使用;

 

2.inline函数和宏的区别:

1、内联函数在编译时展开,而宏在预编译时展开

2、在编译的时候,内联函数直接被嵌入到目标代码中去,而宏只是一个简单的文本替换

3、内联函数可以进行诸如类型安全检查、语句是否正确等编译功能,宏不具有这样的功能。

4、宏不是函数,而inline是函数

5、宏在定义时要小心处理宏参数,一般用括号括起来,否则容易出现二义性。而内联函数不会出现二义性。

6、inline可以不展开,宏一定要展开。因为inline指示对编译器来说,只是一个建议,编译器可以选择忽略该建议,不对该函数进行展开。

7、宏定义在形式上类似于一个函数,但在使用它时,仅仅只是做预处理器符号表中的简单替换,因此它不能进行参数有效性的检测,也就不能享受C++编译器严格类型检查的好处,另外它的返回值也不能被强制转换为可转换的合适的类型,这样,它的使用就存在着一系列的隐患和局限性。

3.如何使用类的inline函数:

  简单提一下inline 的使用吧:

  3.1.在类中定义这种函数:

  class ClassName{

  .....

  ....

  INT GetWidth(){return m_lPicWidth;}; // 如果在类中直接定义,不需要用inline修饰,编译器自动化为内联函数

  .... //此说法在《C++ Primer》中提及

  ....

  }

  3.2.在类外定义前加inline关键字:

  class Account {

  public:

  Account(double initial_balance) { balance = initial_balance; } //与1相同

  double GetBalance(); //在类中声明

  double Deposit( double Amount );

  double Withdraw( double Amount );

  private:

  double balance;

  };

  inline double Account::GetBalance() { return balance; } //在类外定义时添加inline关键字

  inline double Account::Deposit( double Amount ) { return ( balance += Amount ); }

  inline double Account::Withdraw( double Amount ) { return ( balance -= Amount ); }

  3.3.此外含有一些规则需注意:

  1、inline说明对对编译器来说只是一种建议,编译器可以选择忽略这个建议。比如,你将一个长达1000多行的函数指定为inline,编译器就会忽略这个inline,将这个函数还原成普通函数。

  2、在调用内联函数时,要保证内联函数的定义让编译器""到,也就是说内联函数的定义要在头文件中,这与通常的函数定义不一样。但如果你习惯将函数定义放在CPP文件中,或者想让头文件更简洁一点,可这样做:

  //SomeInline.h中

  #ifndef SOMEINLINE_H

  #define SOMEINLINE_H

  inline Type Example(void);

  //........其他函数的声明

  #include“SomeInlie.cpp” //源文件后缀名随编译器而定

  #endif

  //SomeInline.cpp中

  #include"SomeInline.h"

  Type Example(void)

  {

  //..........

  }

  //...............其他函数的定义

  3.4.inline 是一种“用于实现的关键字”

关键字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 与函数定义体放在一起

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

    声明与定义不可混为一谈,用户没有必要、也不应该知道函数是否需要内联。

4.慎用inline

虽然说内联函数可以提高执行效率,但是不可以将所有的函数都定义为内联函数。
内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。
如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。
以下的情况不适合用内联函数:

    1。如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
   2. 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类声明中。一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这进一步说明了 inline 不应该出现在函数的声明中)。

5.小结

内联函数并不是一个增强性能的灵丹妙药。只有当函数非常短小的时候它才能得到我们想要的效果;但是,如果函数并不是很短而且在很多地方都被调用的话,那么将会使得可执行体的体积增大。
最令人烦恼的还是当编译器拒绝内联的时候。在老的实现中,结果很不尽人意,虽然在新的实现中有很大的改善,但是仍然还是不那么完善的。一些编译器能够足够的聪明来指出哪些函数可以内联哪些不能,但是大多数编译器就不那么聪明了,因此这就需要我们的经验来判断。
如果内联函数不能增强性能,就避免使用它!
 


 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值