在ACE的源代码目录里,有源文件.cpp、头文件.h,我们还发现有以.i和.inl为扩展名的文件。其实,以.i和.inl为扩展名的文件是ACE源码中inline函数的存放形式。
在说明ACE中为什么采用这种方式来存放inline函数之前,我们来说一下inline关键字是什么意识。我们知道当调用一个函数的时候,涉及到返回地址和参数压栈等一些操作,这些操作是函数调用本身的开销。在原来的C代码中,通常采用宏定义的方式模拟函数,来消除函数调用的开销,因此我们知道宏是在预编译时候进行处理的。但是,宏定义本身也有很多缺陷,很容易造成错误的使用。这就是inline关键字诞生的原因。用inline关键字定义的函数,在编译的时候,并不会产生真正的函数,而是在该函数调用处直接展开代码,这样就消除了函数调用的开销。注意,inline关键字,只是给编译器的一个暗示,是不是真的进行了inline处理,是由编译器决定的。
那为什么ACE会采用这样一个特殊的方式来存放inline函呢?我们结合实例给出答案。
我们看一下,Reactor.h文件的结尾处,有如下的处理:
#if defined (__ACE_INLINE__)
#include "ace/Reactor.i"
#endif /* __ACE_INLINE__ */
在看一下Reactor.cpp文件开头处的宏处理:
#if !defined (__ACE_INLINE__)
#include "ace/Reactor.i"
#endif /* __ACE_INLINE__ */
上面的Reactor.h,Reactor.cpp和Reactor.i文件是ACE的Reactor框架的相关代码。上面的宏定义我们很好理解,在头文件中的处理为:如果定义了宏__ACE_INLINE__,那么我们就把Reactor.i文件include到头文件中。在源文件中处理为:如果没有定义宏__ACE_INLINE__,那么就把Reactor.i文件include到源文件中。其实有了上面inline含义的介绍,我们不难理解为什么采用这种方式来进行处理。这里我们假设定义了宏__ACE_INLINE__,并且Reactor.i文件是被include到源文件里,而不是被include头文件里,那么会产生什么后果那?我们知道inline函数,在编译器编译后,是不会产生真正的函数的,因此,如果有其它源文件,例如zhx.cpp,调用了Reactor.i文件中的inline函数,那么在连接的时候,就会抛出符号无法解析的错误,而如果Reactor.i文件是被include到了头文件中,并且我们在zhx.cpp中有调用Reactor.i文件中的函数,那么在zhx.cpp中,只需要包含Reactor.h头文件即可,则Reactor.i的相关inline函数在zhx.cpp也进行了代码展开处理。如果没有定义宏__ACE_INLINE__,则Reactor.i被include到源文件中,没有任何问题,因为Reactor.i中的函数在编译后,会产生真正的函数,而不是被inline处理。这就是ACE为什么采用这样的方式进行处理的原因。
在上面的介绍中,我们同时也发现了inline的缺点,就是它会造成代码的膨胀。因此,不是什么样的函数都适合用inline来定义,只有那些短小的函数才适合采用inline处理。
注:ACE为什么会采用.i和.inl两种扩展名形式的文件来存放inline函数,我还不是很清楚,但感觉以.inl形式存放的文件是早期ACE代码中的方式,后期的ACE代码采用.i方式来存放inline函数,也就是说这应该是一个历史遗留问题^_^,开源项目的缺点。