九、内联函数(Inline Functions)
九、内联函数(Inline Functions)
引言
- 如何提高效率
- 在C语言中,为了保持效率,常用的方法之一使用预处理宏(preprocessor macros) 。预处理器会将所有的宏调用直接替换成对应的宏代码 。
- 在C++中,预处理宏存在两个问题:
- 宏可能会隐藏难以发现的bug
- 预处理宏不能作为类的成员函数使用
- 为了既保持效率 ,又能拥有真是函数的安全性 和类作用域 ,C++引入了内敛函数(Inline function)。
9.1 预处理器陷阱(Preprocessor pifalls)
-
宏只是简单文本替换,可能导致不可预料的错误。
-
下面是一个简单例子,思考一下
#define F(x) (x + 1)
-
它应该像
F(1)
这样被调用 -
预处理器会将它展开为:
(x)(x+1)(1)
-
9.2 内联函数(Inline Functions)
概述
-
当一个函数代码只有几行,但会被频繁调用时,我们可以使用
inline
来节省时间并提高效率。 -
内联函数是一个真正的函数,它在调用处被直接展开 ,就像预处理宏一样,因此可以消除函数调用的开销。
内联函数 = 真正的函数 + 具有宏替换的效率。
-
因此,我们在写代码是,应该永远不要使用宏,而是使用内联函数。
示例
inline int add(int x,int y,int z){return x+y+z;
}
void main(){int a(1),b(2),c(3),sum(0);sum = add(a,b,c);
}
注意事项
- 内联函数必须定义在调用之前。
- 函数体中不能包含循环(for/while)、switch语句、异常处理。
- 不能递归 调用自己。
类的内联函数
- 在类定义内部写出的函数,默认是
inline
的,即使不写inline
关键字。
示例:类内的内联函数(默认)
//C09:Inline.cpp
#include <iostream>
#include <string>
using namespace std;class Point
{int i, j, k;
public:Point() :i(0), j(0), k(0) {}Point(int ii, int jj, int kk) :i(ii), j(jj), k(kk) {}void print(const string& msg = "") const {if (msg.size() != 0) cout << msg;cout << i << "," << j << "," << k << endl;}
};void main() {Point p, q(1, 2, 3);p.print("value of p: ");q.print("value of q: ");
}
输出
value of p: 0,0,0
value of q: 1,2,3
示例:类外的内联函数
#include <iostream>
#include <string>
using namespace std;
class Point{int i,j,k;
public:Point();Point(int ii,int jj,int kk);void print(const string& msg = "")const;
}
inline Point::Point():i(0),j(0),k(0){}
inline Point::Point(int ii,int jj,int kk):i(ii),j(jj),k(kk){}
inline void Point::print(const string& msg) const{if (msg.size() != 0) cout << msg;cout << i << "," << j << "," << k << endl;
}
如果内联函数定义写在类外,需要加上
inline
关键字
inline
与 #define
对比
define
可能导致运算歧义。inline
保证正确的表达式求值。
#define示例:
#include <iostream>
using namespace std;
#define f(x) x*xvoid main(){int x(2);cout << f(2) << endl;cout << f(x+1) << endl;
}
输出:
4
5
这里我们想要的不是5而是9
inline示例
#include <iostream>
using namespace std;
inline int f(int x)
{return x*x;
}
void main(){int x(2);cout << f(x) << endl;cout << f(x+1) << endl;
}
输出:
4
9
9.3 Stash和Stack中的内联应用
- 小函数适合内联,大型复杂函数不适合内联。
- 一个内联函数应该定义在与其类声明相同的文件中 。
Stash
//C09:Stasha4.h
…………
class Stash{…………void inflate(int increase);
public:Stash(int sz):size(sz),quantity(0),next(0),storage(0){}Stash(int sz,int initQuantity):size(sz),quantity(0),next(0),storage(0){inflate(initQuantity);}~Stash{if(storage != 0) delete []storage;}int add(void* element);void* fetch(int index) const{…………}int count const{return next;}
};
Stack
//C09:Stack4.h
…………
class Stack{struct Link{…………Link(void* dat,Link* nxt):data(dat),next(nxt){}}* head;
public:Stack():head(0){}~Stack(){assert(head == 0 && "Stack not empty");}void push(void* dat){head = new Link(dat,head);}void* peek() const{return (head!=0)?head->data : 0;}void* pop(){…………}
};
9.4 内联函数与编译器(Inlines & the compiler)
- 和任何函数一样,编译器会将函数类型 (即函数原型,包括函数名和参数类型 ,以及返回类型的组合)记录在它的符号表(symbol table)中。
- 对于一个内联函数 ,当编译器看到这个函数的类型并且函数体语法正确时,函数体的代码 也会被一并带入符号表中。
限制
- 内联函数应该是小型的或简单的。
- 如果函数太复杂(例如有循环等等),编译器就无法进行内联展开。
- 如果函数的地址被获取(无论显示还是隐式地),编译器也无法进行内联展开。也就是说,不能有内联函数的函数指针。
向前引用(Forward refrences)
//C09:EvaluationOrder.cpp
class Forward
{int i;
public:Forward():i(){}int f() const{return g() + 1;}int g() const{return i;}
};
int main(){Foraward frwd ;frwd.f();return 0;
}
构遭函数与析构函数中的隐藏操作
-
成员对象的构造、析构也是自动进行的。
-
示例:
// C09:Hidden.cpp #include <iostream> using namespace std;class Member {int i, j, k; public:Member(int x = 0) : i(x), j(x), k(x) {cout << "Member(" << x << ")" << endl;}~Member() {cout << "~Member" << endl;} };class WithMembers {Member q, r, s; // 成员对象,定义顺序决定构造顺序!int i; public:WithMembers(int ii) : i(ii) {cout << "WithMembers(" << ii << ")" << endl;}~WithMembers() {cout << "~WithMembers" << endl;} };int main() {WithMembers wm(1);return 0; }
输出:
Member(0) Member(0) Member(0) WithMembers(1) ~WithMembers ~Member ~Member ~Member
构造顺序:
Member q
调用构造函数,输出Member(0)
Member r
调用构造函数,输出Member(0)
Member s
调用构造函数,输出Member(0)
- 然后
WithMembers
自己构造,输出WithMembers(1)
这说明类中包含其他类对象(即成员对象)时,它们的构造和析构顺序是由定义顺序决定的,而不是在构造函数初始化列表中的顺序。 同时先构造成员对象,最后构造主类体。
9.5 减少接口杂乱(Reducing clutter)
- 在实际项目中,如果在类内部定义内联函数,可能会使类的接口变得杂乱,从而增加类的使用难度。
- 为了保持接口简洁,应当将函数的定义放在类外部。
//C09:Noinsitu.cpp
class Rectangle{
public:Rectangle(int w = 0,int h = 0);int getWidth() const;void setWidth(int w);int getHeight() const;void setHeight(int h);
private:int witdth,height;
};inline Rectangle::Rectangle(int w,int h):width(w),height()h {}
inline int Rectangle::getWidth() const (return width;)
inline void Rectangle::setWidth(int w) {width = w;}
inline int Rectangle::getHeight() const {return height;}
inline void Rectangle::setHeight(int h) {height = h;}
9.6 总结
- 内联函数 = 结合了宏的效率和函数的安全性。
- 类内定义的函数默认使
inline
- 大型工程中,最好将
inline
函数定义写在类外,保持接口整洁。