C++之内联函数
C++继承C的一个重要特性是效率,在C中保护效率的一个方法是使用宏(macro),宏的实现是使用预处理器而不是编译器,预处理器直接用宏代码替换宏调用,所以就没有了参数压栈、生成汇编语言的CALL、返回参数、执行汇编语言的RETURN的时间花费,所有的工作由预处理器完成,因此不用花费什么就具有了程序调用的便利和可读性。
C++中使用预处理器宏存在两个问题,一是不安全性,二是C++特有的,预处理器不容许存取私有数据,这意味着预处理器在用作成员函数时变得非常无用。
为了既保持预处理器宏的效率又增加安全性,而且还能像一般的成员函数一样可以在类里访问自如,C++使用了内联函数。
内联函数与编译器
内联函数使用inline关键字定义,为了使之有效,必须使函数体和声明结合在一起,否则,编译器将它作为普通函数对待
一般应该把内联定义在头文件中,当编译器看到这个定义时,它把函数类型(函数名+返回值)和函数体放到符号表里,当使用函数时,编译器检查以确保调用和返回是否正确,然后将函数调用替换为函数体,因而消除了开销,内联代码的确占用空间,但假如函数较小,这实际比为了一个普通函数调用而产生的代码(参数压栈和执行CALL)占用的空间少。
1. 局限性
编译器在以下两种情况下不能处理内联:
(1) 函数体很大或很复杂,任何种类的循环都被认为太复杂,编译器遇到这种情况都会放弃内联方式,因为这时内联将可能不为我们提供任何效率
(2) 假如我们要显示或隐含地取函数地址,编译器也不能执行内联,因为这时编译器必须为函数代码分配内存从而为我们产生一个函数的地址。
我们必须理解内联仅仅是编译器的一个建议,编译器不强迫内联任何代码,一个好的编译器将会内联小的,简单的函数,同时明智的忽略那些太负责的内联
2. 赋值顺序
class forward { public: forward():i(0){} int f() const {return g() + 1;} // 注意 int g() const {return i;} private: int i; }; void main() { forward F; F.f(); return; }
观察上面的代码,虽然函数g()还没有定义,但在函数f()里对函数g()进行了调用,编译器会不会报错呢?
事实上这是可行的,因为语言定义规定非内联函数直到类声明结束才赋值。
假如一个内联函数对于一个还没有在内里面声明的函数进行向前引用,编译器就不会把它当做内联函数处理!
错误检查示例
inline void allege_error(int nVal, char *cMsg) { if (!nVal) { fprintf(stderr, cMsg); } #ifdef NDEBUG exit(1); #endif } #define allege(expr, msg)\ {\ allege_error((expr ? 1:0), msg);\ assert(expr);\ } #define allegemem(expr)\ {\ allege(expr, "out of memory");\ } #define allegefile(expr)\ {\ allege(expr, "could not open file\r\n");\ } void main() { ifstream nofile; nofile.open("nofile.xxx", ios::in); allegefile(nofile); return; }
#define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )
assert()宏中包含__FILE__和__LINE__