inline内联的用法与作用

来源:互联网 发布:js confirm 样式 编辑:程序博客网 时间:2024/05/30 23:51

inline内联的用法与作用

     内联函数是一种编译机制,优点从代码上是看不出来的,但是程序的执行效率上有差别,通常,编译器对函数调用的处理是一种类似中断的方式,即当执行到函数调用语句时,程序把当前所有的状态信息比如CPU所有寄存器(其中一个很重要的就是指令指针寄存器)的值保存起来,然后放心大胆地转去执行那个函数的代码,执行完后再返回原来的地方,恢复原先保存过的状态信息,于是也就可以接着原来被中断的指令继续往下执行。这样,就很容易实现代码的结构化,因为可以把一些独立的功能模块写成函数,函数内部的变量和外部的变量互不影响,而且函数执行完后就可以释放这个函数内部变量的所使用的内存空间(这就是为什么函数退出后,其内部变量不再有效),对内存的使用也是很经济的(否则,如果一个大的程序全部由一个函数组成,那么所有的变量都得自始至终地占用内存空间),当然,还有其他优点,比如可以实现递归,总之是好处多多。  
     可是,任何事情往往都有两方面,这样做虽然好处多多,但也是有代价的,那就是前面所说的,任何一次函数调用,程序都得进行保存和恢复状态信息的动作,用数据结构的术语说就是进栈和退栈,当然,还有内存分配的过程,如果函数的代码非常少,这种代价并不是可忽略的,比如说,你编写一个类,里面有个记录状态的成员变量:  
Class MyClass  {  private:  int m_iState;  }  
    按照面向对象的思想,函数的属性应尽量的私有化,但外部怎么获得这个属性值呢?一般的方法就是加一个共有函数,这就实现的面向对象思想中所谓“通过公用接口操作对象的私有属性”。于是就变成了:  
Class MyClass  {   public:   int GetState();   private:   int m_iState;  }  int MyClass::GetState()  {   return m_iState;  }  
    这样一来,面向对象思想倒是体现出来了,但你的CPU会恨你:“你丫的,一个鸟样小的函数就返回一个整数却让老子进一次栈、弹一次栈”,内存也会憋屈的说:“兄弟,老子也得跟着分配内存啊!”  
    但对你来说,也很委屈,怎么办,把所有的属性都改成public?让外部内码直接访问?况且,那样也不解决所有问题,因为有时候即使不是为了面向对象,我们也需要把独立的功能模块做成函数,比如说产生随机数的函数。我想  
int iRand=rand();  
总比:  
int iRand=((int)(MULTIPLIER * Seed + INCREMENT)>>16)&0x7fff;  
    看起来舒服吧?(我这里只是打个比方,VC的rand函数并不是内联函数)  
    而内联函数就是解决这个问题了,对于程序员,他还是把独立功能写成函数的形式,但只要声明为内联,编译器就不把它编译成一次函数调用,而只是类似于把函数的代码拷贝到被调用的地方,而且这完全是编译器私下里完成的,原来的访问权限等问题丝毫不受影响。这不是两全齐美了吗:在保证代码的面向对象性和结构化不受损失的条件下,程序的效率也没有损失,比如上面那个类,就变成了:  
Class MyClass  {   public:   inline int GetState();   private:   int m_iState;  }  int inline MyClass::GetState()  {   return m_iState;  }  
    有一点要注意,内联函数要跟类的声明写在同一个文件中,否则编译会出错。按照VC管理源文件的风格来说,就是内联函数最好写在声明类的.h文件中,而不是像一般函数那样写在实现类的.cpp文件中。  
    当然,内联函数还有另外一种写法,就是直接写在类中,此时,不必使用“inline”关键字。  
Class MyClass  {  public:  int GetState(){ return m_iState; }  private:  int m_iState;  }  
    最后,还要注意,内联函数只是一种编译机制,用上面两种形式声明的函数仅仅是建议编译器进行内联,而编译器是否内联不一定。正如前面所说,函数调用的开销只是对小的函数不可忽略,对于重量级的函数还是可以忽略的,而且在绝大多数的场合,函数调用才是人间正道,才是解决问题的最佳。所以大多数编译器并不把带有循环、递归等或者代码比较多的函数进行内联编译,有的甚至不允许声明成内联的。    
    在解决C + +中宏存取私有的类成员的问题过程中,所有和预处理器宏有关的问题也随着消失了。这是通过使宏被编译器控制来实现的。在 C + +中,宏的概念是作为内联函数来实现的,而内联函数无论在任何意义上都是真正的函数。唯一不同之处是内联函数在适当时像宏一样展开,所以函数调用的开销被取消。因此,应该永远不使用宏,只使用内联函数。  
    任何在类中定义的函数自动地成为内联函数,但也可以使用i n l i n e关键字放在类外定义的函数前面使之成为内联函数。但为了使之有效,必须使函数体和声明结合在一起,否则,编译器将它作为普通函数对待。因此  
inline int PlusOne(int x);  
    没有任何效果,仅仅只是声明函数(这不一定能够在稍后某个时候得到一个内联定义)。成功的方法如下:  
inline int PlusOne(int x) { return ++x ;}  
    注意,编译器将检查函数参数列表使用是否正确,并返回值(进行必要的转换)。这些事情是预处理器无法完成的。假如对于上面的内联函数,我们写成一个预处理器宏的话,将有不想要的副作用。  
    一般应该把内联定义放在头文件里。当编译器看到这个定义时,它把函数类型(函数名+  返回值)和函数体放到符号表里。当使用函数时,编译器检查以确保调用是正确的且返回值被  正确使用,然后将函数调用替换为函数体,因而消除了开销。内联代码的确占用空间,但假如函数较小,这实际上比为了一个普通函数调用而产生的代码(参数压栈和执行C A L L)占用的空间还少。在头文件里,内联函数默认为内部连接——即它是static, 并且只能在它被包含的编译单元看到。因而,只要它们不在相同的编译单元中声明,在内联函数和全局函数之间用同样的名字也不会在连接时产生冲突。  
    为了定义内联函数,通常必须在函数定义前面放一个i n l i n e关键字。但这在类内部定义内联函数时并不是必须的。任何在类内部定义的函数自动地为内联函数。如下例:  
#include <iostream.h>  class point{  private:  int i,j,k;  public:  point() {i=j=k=0; }  point(int I,int J,int K) {  i=I;  j=J  k=K;  }  void print(const char* msg="") const{  if(*msg) cout<<"msg"<<endl;  cout<<"i="<<i<<endl;  cout<<"j="<<j<<endl;  cout<<"k="<<k<<endl;  }  };  main(){  point p,q(1,2,3);  p.print("value of p");  q.print("value of q");  }  
    当然,因为类内部的内联函数节省了在外部定义成员函数的额外步骤,所以我们一定想在类声明内每一处都使用内联函数。但应记住,内联的目的是减少函数调用的开销。假如函数较大,那么花费在函数体内的时间相对于进出函数的时间的比例就会较大,所以收获会较小。而且内联一个大函数将会使该函数所有被调用的地方都做代码复制,结果代码膨胀而在速度方面获得的好处却很少或者没有。
0 0
原创粉丝点击