inline函数的优势:
比宏优秀,像函数一样使用但是没有函数调用那么大的开销
编译器的最优化机制有能力对不含函数调用的代码进行语境最优化
inline的缺点:
目标码膨胀,内存空间消耗增加,还容易造成虚内存机制的额外换页行为,增加开销,降低缓冲命中率,造成效率损失,但是反之而言,如果inline函数很小,产生的目标码比函数调用产生的目标码还小,那么无疑会增加效率
注意:inline是申请,不是强制命令。
隐式申请:
class person
{
public:
int age() const {return 43;} //隐式inline申请
}
显示申请:
template<tempname T>
inline const T& std::max(const T& a, const T&b)
{return a<b?b:a;}
inline函数通常一定要放置于头文件(template也要放在头文件,因为模板具现也是编译期行为),因为inline通常是编译期动作,虽然有些编译器是连接期和运行期(.net cli)处理inline,但是很少。同样的virtual也不会被inline因为虚函数意味着运行期确定函数,同样的涉及递归,循环等复杂函数也不会inline,同样的,以函数指针形式调用的函数也不会被inline,因为函数指针必须指向存在的函数实体,而不是inline。
综上:inline的实现和编码与编译器都有关系。
注意:不要将构造函数和析构函数inline
构造函数如果:
class a
{
public:
a(){};//隐式inline申请
}
通常构造函数由编译器自动添加一些目标码,构造所有成员,即使你什么都没写,所以真实的构造和析构函数是很复杂的,inline不是很恰当。
如果inline函数改变,那么必须重新编译,如果是函数调用的话,客户只需要重新连接就好了,如果程序库采用动态链接,那么甚至不用额外处理
inline会造成调试困难,因为不能对inline函数设置断点,大多数编译器支持在调试版中禁止inline
对inline的态度:一开始不要将任何函数声明为inline,或者只将一定成为inline的函数设置为inline。对日后调试有好处,之后再用inline或其它方法将代码瘦身。
总结:大多数inline被限制在小型的频繁使用的函数身上,可以使代码膨胀最小,程序提升效果最好。
备注:inline成功意味着目标码中可能看不到call指令,但是现在编译器已经聪明到可以自动判断一个函数是否适合inline,因此源文件中的inline往往不是必须的,头文件因为vague linkage的原因还是要 inline的(每个编译单元生成弱定义,由连接器选择一个)。
通过观察class template 中的短成员函数有没有被inline可以判断当前是调试版本还是发布版本
g++ -wall fun.cc
nm ./ a.out |grep size|c++filt
会出现短函数,因为是-o0编译,调试版本
g++ -wall -O2 fun.cc
nm ./ a.out |grep size|c++filt
不会出现短函数,因为是O2编译,发布版本,被优化了class中的析构函数一般都是inline的,为了防止代码碰撞,编译错误,要有意显示noinline声明
现代编译器编译和连接更模糊了,现在g++的 -flto选项可以使inline在连接期做
而且现在g++中有大量内置函数,在应用一些常用函数的时候不一定会真正调用库函数,优化更方便