摘要:本文主要向大家介绍了C++语言之inline函数简介,通过具体的内容向大家展示,希望对大家学习C++语言有所帮助。
本文主要向大家介绍了C++语言之inline函数简介,通过具体的内容向大家展示,希望对大家学习C++语言有所帮助。
1.inline函数简介
inline函数是由inline关键字来定义,引入inline函数的主要原因是用它替代C中复杂易错不易维护的宏函数。
2.编译器对inline函数的处理办法
inline对于编译器而言,在编译阶段完成对inline函数的处理。将调用动作替换为函数的本体。但是它只是一种建议,编译器可以去做,也可以不去做。从逻辑上来说,编译器对inline函数的处理步骤一般如下:
(1)将inline函数体复制到inline函数调用点处;
(2)为所用inline函数中的局部变量分配内存空间;
(3)将inline函数的的输入参数和返回值映射到调用方法的局部变量空间中;
(4)如果inline函数有多个返回点,将其转变为inline函数代码块末尾的分支(使用GOTO)。
比如如下代码:
//求0-9的平方 inline int inlineFunc(int num){ if(num>9||num<0) return -1; return num*num; } int main(int argc,char* argv[]){ int a=8; int res=inlineFunc(a); cout<<"res:"<<res<<endl; pre=""><p>inline之后的main函数代码类似于如下形式:</p><pre class="brush:java;">int main(int argc,char* argv[]){ int a=8; { int _temp_b=8; int _temp; if (_temp_q >9||_temp_q<0) _temp = -1; else _temp =_temp*_temp; b = _temp; } } </pre> <p>经过以上处理,可消除所有与调用相关的痕迹以及性能的损失。inline通过消除调用开销来提升性能。</p> <h1 id="3inline函数使用的一般方法">3.inline函数使用的一般方法</h1> <p>函数定义时,在返回类型前加上关键字inline即把函数指定为内联,函数申明时可加也可不加。但是建议函数申明的时候,也加上inline,这样能够达到”代码即注释”的作用。</p> <p>使用格式如下:</p> <pre class="brush:java;">inline int functionName(int first, int secend,...) {/****/};</pre> <p>inline如果只修饰函数的申明的部分,如下风格的函数foo不能成为内联函数:</p> <pre class="brush:java;">inline void foo(int x, int y); // inline 仅与函数声明放在一起 void Foo(int x, int y){}</pre> <p>而如下风格的函数foo 则成为内联函数:</p> <pre class="brush:java;">void foo(int x, int y); inline void Foo(int x, int y) // inline 与函数定义体放在一起{}</pre> <h1 id="4inline函数的优点与缺点">4.inline函数的优点与缺点</h1> <p><strong>从上面可以知道,inline函数相对宏函数有如下优点:</strong><br> (1)内联函数同宏函数一样将在被调用处进行代码展开,省去了参数压栈、栈帧开辟与回收,结果返回等,从而提高程序运行速度。</p> <p>(2)内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换(同普通函数),而宏定义则不会。<br> 例如宏函数和内联函数:</p> <pre class="brush:java;">//宏函数 #define MAX(a,b) ((a)>(b)?(a):(b)) //内联函数 inline int MAX(int a,int b){ return a>b?a:b; }</pre> <p>使用宏函数时,其书写语法也较为苛刻,如果对宏函数出现如下错误的调用,<code>MAX(a,"Hello"); 宏函数会错误地比较int和字符串,没有参数类型检查。但是使用内联函数的时候,会出现类型不匹配的编译错误。</code></p> <p><code>(3)在类中声明同时定义的成员函数,自动转化为内联函数,因此内联函数可以访问类的成员变量,宏定义则不能。</code></p> <p><code>(4)内联函数在运行时可调试,而宏定义不可以。</code></p> <p><code><strong>万事万物都有阴阳两面,内联函数也不外乎如此,使用inline函数,也要三思慎重。inline函数的缺点总结如下:</strong><br> <strong>(1)代码膨胀。</strong><br> inline函数带来的运行效率是典型的以空间换时间的做法。内联是以代码膨胀(复制)为代价,消除函数调用带来的开销。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。</code></p> <p><code><strong>(2)inline函数无法随着函数库升级而升级。</strong><br> 如果f是函数库中的一个inline函数,使用它的用户会将f函数实体编译到他们的程序中。一旦函数库实现者改变f,所有用到f的程序都必须重新编译。如果f是non-inline的,用户程序只需重新连接即可。如果函数库采用的是动态连接,那这一升级的f函数可以不知不觉的被程序使用。</code></p> <p><code><strong>(3)是否内联,程序员不可控。</strong><br> inline函数只是对编译器的建议,是否对函数内联,决定权在于编译器。编译器认为调用某函数的开销相对该函数本身的开销而言微不足道或者不足以为之承担代码膨胀的后果则没必要内联该函数,若函数出现递归,有些编译器则不支持将其内联。</code></p> <h1 id="5inline函数的注意事项"><code>5.inline函数的注意事项</code></h1> <p><code>了解了内联函数的优缺点,在使用内联函数时,我们也要注意以下几个事项和建议。</code></p> <p><code><strong>(1)使用函数指针调用内联函数将会导致内联失败。</strong><br> 也就是说,如果使用函数指针来调用内联函数,那么就需要获取inline函数的地址。如果要取得一个inline函数的地址,编译器就必须为此函数产生一个函数实体,那么就内联失败。</code></p> <p><code><strong>(2)如果函数体代码过长或者有多重循环语句,if或witch分支语句或递归时,不宜用内联。</strong></code></p> <p><code><strong>(3)类的constructors和destructors往往不是inline函数的最佳选择。</strong><br> 类的构造函数(constructors)可能需要调用父类的构造函数,其背后隐藏这大量的代码,不适合作为inline函数。虚函数(destructors)往往是运行时确定的,而inline是在编译时进行的,所以inline虚函数往往无效。当然如果直接用类的对象来使用虚函数,那么对有的编译器而言,也可起到优化的作用。</code></p> <p><code><strong>(4)至于内联函数是定义在头文件还是源文件的建议。</strong><br> 内联展开是在编译时进行的,只有链接的时候源文件之间才有关系。所以内联要想跨源文件必须把实现写在头文件里。如果一个inline函数会在多个源文件中被用到,那么必须把它定义在头文件中。参考如下示例:</code></p> <pre class="brush:java;"><code>// base.h class Base{protected:void fun();}; // base.cpp #include base.h inline void Base::fun(){} //derived.h #include base.h class Derived: public Base{public:void g();}; // derived.cpp void Derived::g(){fun();} //VC2010: error LNK2019: unresolved external symbol</code></pre> <p><code>上面这种错误,就是因为内联函数fun()定义在编译单元base.cpp中,那么其他编译单元中调用fun()的地方将无法解析该符号,因为在编译单元base.cpp生成目标文件base.obj后,内联函数fun()已经被替换掉,编译器不会为fun()生成函数实体,链接器自然无法解析。所以如果一个inline函数会在多个源文件中被用到,那么必须把它定义在头文件中。</code></p> <p><code>这里有个问题,当在头文件中定义内联函数,那么被多个源文件包含时,如果编译器因为inline函数不适合被内联时,拒绝将inline函数进行内联处理,那么多个源文件在编译生成目标文件后都将各自保留一份inline函数的实体,这个时候程序在连接阶段就会出现重定义错误。解决办法是在需要inline的函数使用static。</code></p> <pre class="brush:java;"><code>//test.h static inline int max(int a,int b){ return a>b?a:b; }</code></pre> <p><code><strong>(5)能否强制编译器进行内联操作?</strong><br> 也有人可能会觉得能否强制编译器进行函数内联,而不是建议编译器进行内联呢?很不幸的是目前还不能强制编译器进行函数内联,如果使用的是MSVC++, 注意__forceinline如同inine一样,也是一个用词不当的表现,它只是对编译器的建议比inline更加强烈,并不能强制编译器进行inline操作。</code></p> <h1 id="6小结"><code>6.小结</code></h1> <p><code>可以将内联理解为C++中对于函数专有的宏,对于C的函数宏的一种改进。对于常量宏,C++提供const替代;而对于函数宏,C++提供的方案则是inline。C++ 通过内联机制,既具备宏代码的效率,又增加了安全性,还可以自由操作类的数据成员,算是一个比较完美的解决方案。</code></p> <p><code>关于上面的结论和观点,缺乏实践和权威的资料,难免不当甚至错误,仅供参考学习,如果大家发现错误和需要改进的地方,请大家留言给出宝贵的建议。</code></p> <hr> <h1 id="参考文献"><code>参考文献</code></h1> <p><code>[1]inline函数<br> [2]小问题大思考之C++里的inline函数<br> [3]把inline函数的定义放在头文件中<br> [4]aspx">Inline Functions (C++)<br> [5]Can I selectively (force) inline a function?<br> [6]C语言inline详细讲解</code></p> </res<<endl;>
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C/C+频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-IT技术咨询与就业发展一体化服务 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号