内联函数 Inline Function 小结

来源:互联网 发布:网络语吃瓜是什么意思 编辑:程序博客网 时间:2024/04/29 13:09
参考源自C++ Primer
及以下两篇文章(蓝色字体部分为引用):

在C++语言中,关于内联函数(inline)的入门教程!


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

可能说到这里,很多人还不明白什么是栈空间,其实
栈空间就是指放置程序的局部数据也就是函数内数据的内存空间,在系统下,栈空间是有限的,如果频繁大量的使用就会造成因栈空间不足所造成的程序出错的问题,涵数的死循环递归调用的最终结果就是导致栈内存空间枯竭。

下面的例子来自C++ Primer Section 7.6:
以一个比较两个字符串长度的函数为例:
  1. //find shorter of two strings
  2. const string &shorterString(const string &s1, const string &s2)
  3. {
  4.       return s1.size()<s2.size()?  s1:s2;               
  5. }
 机器进行函数调用时,需要做以下步骤:
1。 在调用函数前保存寄存器中的数据,然后执行完return 语句后再恢复寄存器中的数据;
2。传递实参给调用的函数;
3。程序被分配到一个新的地址/空间运行调用的函数;运行完再返回主函数;

为了减少上述调用函数时的内存开销,使用内联“inline”, 如:上面的程序改为:
  1. //find shorter of two strings
  2. inline const string &shorterString(const string &s1, const string &s2)
  3. {
  4.       return s1.size()<s2.size()?  s1:s2;               
  5. }
那么,如果主程序中有这样一句函数调用:
  1. cout<<shorterString(s1,s2)<<endl;
的话,在编译时就会被处理成如:
  1. cout<<(s1.size()<s2.size()? s1:s2)<<endl;
这种形式,也就是说,在函数调用的内部步骤中,避免了开辟额外的内存空间,因为inline带来的效果相当于直接处理一句表达式:
  1. A<B? A:B;
特别是在主程序中,在反复调用某一个简单函数的情况下,避免了频繁调用函数对栈内存重复开辟所带来的消耗。

说到这里很多人可能会问,既然inline这么好,还不如把所谓的函数都声明成inline,嗯,这个问题是要注意的,inline的使用是有所限制的,inline只适合涵数体内代码简单的涵数使用,不能包含复杂的结构控制语句例如whileswitch,并且不能内联函数本身不能是直接递归函数(自己内部还调用自己的函数)。

说到这里我们不得不说一下在c语言中广泛被使用的宏定义语句(#define): 是的. define的确也可以做到inline的这些工作,但是define是会产生副作用的,尤其是不同类型参数所导致的错误,由此可见inline有更强的约束性和能够让编译器检查出更多错误的特性,在c++中是不推荐使用define的。



宏和内联函数的区别(精辟!)


先说宏和函数的区别:


1. 宏做的是简单的字符串替换(注意是字符串的替换,不是其他类型参数的替换),而函数是参数的传递,参数是有数据类型的,可以是各种各样的类型.

2. 宏的参数替换是不经计算而直接处理的,而函数调用是将实参的值传递给形参,既然说是值,自然是计算得来的.

3. 宏在编译之前进行,即先用宏体替换宏名,然后再编译的,而函数显然是编译之后,在执行时,才调用的.因此,宏占用的是编译的时间,而函数占用的是执行时的时间.

4. 宏的参数是不占内存空间的,因为只是做字符串的替换,而函数调用时的参数传递则是具体变量之间的信息传递,形参作为函数的局部变量,显然是占用内存的.

5. 函数的调用是需要付出一定的时空开销的,因为系统在调用函数时,要保留现场,然后转入被调用函数去执行,调用完,再返回主调函数,此时再恢复现场,这些操作,显然在宏中是没有的.


现在来看内联函数:


所谓"内联函数"就是将很简单的函数"内嵌"到调用他的程序代码中,只样做的目的是为了避免上面说到的第5点,目的旨在节约下原本函数调用时的时空开销.但必须注意的是:作为内联函数,函数体必须十分简单,不能含有循环、条件、选择等复杂的结构,否则就不能做为内联函数了。事实上,即便你没有指定函数为内联函数,有的编译系统也会自动将很简单的函数作为内联函数处理;而对于复杂的函数,即便你指定他为内联函数,系统也不会理会的。


注意两点:

1。 "inline" 放在函数前面相当于对这个函数的一个约束条件。对编译器而言, 仅仅是一个请求(request)而非强制命令。如上面所言,编译器有可能不理会这个请求。


2.  内联函数应该定义在头文件里。

     一般来函数只要在头文件里被声明,在源文件中被定义。 
     C++ Primer (4th) Page. 257 原文解释为:

    "To expand the code of an inline function at point of call, the compiler must have access to the function defination. The function prototype is insufficient."

     即, “要在调用的那一时刻展开内联函数的代码(内容),编译器必须得到函数的定义。仅有函数原型是不够的。”

     也就是说,对内联函数的调用,编译器在头文件中如果只发现了它的声明 [函数原型:return_type function_name (parameter_list )]是不够的,还需要更进一步知道这个内联函数的定义是什么。所以要把内联函数的定义写在头文件里以使其对编译器可见。


(关于这一点的更多讨论,请看讨论帖 “为什么内联函数必须放在头文件中呢?”)


 3. 类的成员函数,如果其被定义在类的内部,则这个成员函数被隐式处理为内联函数。如:

 

  1. class Sales_item{
  2. public:
  3.                 double avg_price() const;
  4.                 bool same_isbn(const Sales_item &rhs) const
  5.                                      { return isbn==rhs.isbn; }
  6. };
则same_isbn在编译时就被当做内联函数处理了。