Inline
宏不是函数,只是在编译前(编译预处理阶段)将程序中有关字符串替换成宏体。
内联函数从 源代码层看,有函数的结构,而在编译后,却不具备函数的性质。编译时,类似宏替换,使用函数体替换调用处的函数名。一般在代码中用inline修饰,但是能否形成内联函数,需要看 编译器对该函数定义的具体处理。
优点:inline函数是一个真正的函数,它可以进行参数检测,相比较于普通函数,它的执行效率上更加快速。 缺点:浪费内存。inline函数在函数调用的地方会在预编译的时候生成一份函数的拷贝,也就是说,只要有调用inline函数的地方,就会生成一处拷贝。而普通的函数在函数调用的地方只是存储了此函数的地址。
diff #define
内联函数直接嵌入到目标代码中,宏是简单的做文本替换.
在内联函数内不容许用循环语句和开关语句。如有则编译器将该函数视为普通函数那样产生函数调用代码。 递归函数(本身调用本身)是不能做为内联函数的。 内联函数只适用于1——5行的小函数。对于含有不少语句的大函数,函数调用和返回的开销相对于来讲微不足道,因此没不要用内联函数。
inline 与 #define的区别: (1)内联函数在运行时可调试,而宏定义不能够;递归
(2)编译器会对内联函数的参数类型作安全检查或自动类型转换(同普通函数),而宏定义则不会; 编译器
(3)内联函数能够访问类的成员变量,宏定义则不能; 编译
(4)在类中声明同时定义的成员函数,自动转化为内联函数。
实例
定义在类中的成员函数默认都是内联的,如果在类定义时就在类内给出函数定义,那当然最好。如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上 inline,否则就认为不是内联的。
class A
{
public:void Foo(int x, int y) { } // 自动地成为内联函数
}
将成员函数的定义体放在类声明之中虽然能带来书写上的方便,但不是一种良好的编程风格,上例应该改成:
// 头文件
class A
{
public:
void Foo(int x, int y);
}
// 定义文件
inline void A::Foo(int x, int y){}
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
内联能提高函数的执行效率,为什么不把所有的函数都定义成内联函数?如果所有的函数都是内联函数,还用得着"内联"这个关键字吗? 内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。 如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。
以下情况不宜使用内联: (1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。 (2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如"偷偷地"执行了基类或成员对象的构造函数和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类声明中。一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这进一步说明了 inline 不应该出现在函数的声明中)。
|