inline
inline是C++关键字,在函数声明或定义中,函数返回类型前加上关键字inline,即可以把函数指定为内联函数。这样可以解决一些频繁调用的函数大量消耗栈空间(栈内存)的问题。关键字inline必须与函数定义放在一起才能使函数成为内联函数,仅仅将inline放在函数声明前面不起任何作用。inline是一种“用于实现”的关键字,而不是一种“用于声明”的关键字。
错误全局函数例子:function_inline
inline void function_inline();
void function_inline()
{
std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << std::endl;
}
正确全局函数例子:function_inline
inline void function_inline()
{
std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << std::endl;
}
错误成员函数例子:class_inline::function_inline
//class_inline.h
class class_inline
{
public:
inline void function_inline();
};
//class_inline.cpp
void class_inline::function_inline()
{
std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << std::endl;
}
正确成员函数例子:class_inline::function_inline
推荐!
//class_inline.h
class class_inline
{
public:
inline void function_inline()
{
std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << std::endl;
}
};
不推荐!
//class_inline.h
class class_inline
{
public:
inline void function_inline();
};
//class_inline.cpp
inline void class_inline::function_inline()
{
std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << std::endl;
}
不推荐!
//class_inline.h
class class_inline {
public:
void function_inline();
};
//class_inline.cpp
inline void class_inline::function_inline()
{
std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << std::endl;
}
inline函数仅仅是一个对编译器的建议,所以最后能否真正内联,看编译器的意思,它如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就会内联,声明内联只是一个建议而已。
The extent to which suggestions made by using the inline function specifier are effective (C99 6.7.4).
- GCC will not inline any functions if the -fno-inline option is used or if -O0 is used. Otherwise, GCC may still be unable to inline a function for many reasons; the -Winline option may be used to determine if a function has not been inlined and why not.
运的是大多数编译器提供了一个诊断级别:如果它们无法将你要求的函数 inline 化, 会给你一个警告信息。
inline限制
inline只适合函数体内简单代码的涵数使用。不能包含复杂的结构控制语句例如while、switch,并且不能内联函数本身不能是直接递归函数(即,自己内部还调用自己的函数)。而所有(除了最平凡,几乎什么也没做)的虚拟函数,都会阻止inline的进行。因为virtual意味着”等待,直到执行时期再确定应该调用哪一个函数“,而inline却意味着”在编译阶段,将调用动作以被调用函数的主体取代之“。如果编译器做决定时,尚不知道该调用哪一个函数,很难责成他们做出一个inline函数。
慎用inline
内联为提高函数的执行效率,以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。 如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。因此,需要酌情使用。
以下情况不宜使用内联:
- (1) 如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
- (2) 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。
- (3) 类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类声明中。
一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这进一步说明了 inline 不应该出现在函数的声明中)。即“inline函数仅仅是一个对编译器的建议,所以最后能否真正内联,看编译器的意思”,当有编译宏明确需要使用内联时,请使用__forceinline __attribute__((always_inline)
__forceinline
编译器尝试内联函数,而不考虑函数的特征,在某些情况下,编译器可能会选择忽略__forceinline,而不内联函数。例如:
- 递归函数永远不会内联到自身中。
- 使用alloca()的函数从不内联。
当有编译宏(COMPLIE_ZMODULE_NAME)替换时,最好还是强制内联以确保debug版本(不会编译内联函数,即使加了inline关键字)与release版本(可能优化为内联函数),从而导致COMPLIE_ZMODULE_NAME的内容存在差异引起一系列问题。
__forceinline void function_inline()
{
std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << "COMPLIE_ZMODULE_NAME:" << COMPLIE_ZMODULE_NAME << std::endl;
}
__attribute__((always_inline)
编译器尝试内联函数,而不考虑函数的特征,在某些情况下,编译器可能会选择忽略__attribute__((always_inline)函数属性,而不内联函数。例如:
- 递归函数永远不会内联到自身中。
- 使用alloca()的函数从不内联。
当有编译宏(COMPLIE_ZMODULE_NAME)替换时,最好还是强制内联以确保debug版本(不会编译内联函数,即使加了inline关键字)与release版本(可能优化为内联函数),从而导致COMPLIE_ZMODULE_NAME的内容存在差异引起一系列问题。
inline void __attribute__((always_inline)) function_inline()
{
std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << "COMPLIE_ZMODULE_NAME:" << COMPLIE_ZMODULE_NAME << std::endl;
}