inline函数的总结

引入inline关键字的原因

在c/c++中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了inline修饰符,表示为内联函数。

栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间。

在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足而导致程序出错的问题,如,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。

#include <stdio.h>
//函数定义为inline即:内联函数
inline char* dbtest(int i) {
    return (i % 2 > 0) ? "奇" : "偶";
} 

int main()
{
   int i = 0;
   for (i=1; i < 100; i++) {
       printf("i:%d    奇偶性:%s /n", i, dbtest(i));    
   }
}

上面的例子就是标准的内联函数的用法,使用inline修饰带来的好处我们表面看不出来,其实,在内部的工作就是在每个for循环的内部任何调用dbtest(i)的地方都换成了(i%2>0)?”奇”:”偶”,这样就避免了频繁调用函数对栈内存重复开辟所带来的消耗

inline使用限制

inline的使用时有所限制的,inline只适合函数体内部代码简单的函数使用,不能包含复杂的结构控制语句例如while、switch,并且不能内联函数本身不能是直接递归函数(即,自己内部还调用自己的函数)。

inline仅是一个对编译器的建议

inline函数仅仅是一个对编译器的建议,所以最后能否真正内联,看编译器的意思

  • 它如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就会内联,声明内联只是一个建议而已。

建议:inline函数的定义放在头文件中

因为内联函数要在调用点展开,所以编译器必须随处可见内联函数的定义,要不然就成了非内联函数的调用了。所以,这要求:

  • 每个调用了内联函数的文件都出现了该内联函数的定义。

因此,将内联函数的定义放在头文件里实现是合适的,省却为每个文件实现一次的麻烦。

声明跟定义要一致

  • 如果在每个文件里都实现一次该内联函数的话,那么,最好保证每个定义都是一样的,否则,将会引起未定义的行为。如果不是每个文件里的定义都一样,那么,编译器展开的是哪一个,那要看具体的编译器而定。所以,最好将内联函数定义放在头文件中。

类中的成员函数与inline

定义在类中的成员函数缺省都是内联的

  • 如果在类定义时就在类内给出函数定义,那当然最好。
  • 如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上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 关键字,但我认为inline不应该出现在函数的声明中。这个细节虽然不会影响函数的功能,但是体现了高质量C++/C 程序设计风格的一个基本原则:

  • 声明与定义不可混为一谈,用户没有必要、也不应该知道函数是否需要内联

慎用inline

虽然说内联函数可以提高执行效率,但是不可以将所有的函数都定义为内联函数。
内联是以代码膨胀(复制)为代价仅仅省去了函数调用的开销,从而提高函数的执行效率
如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。
以下的情况不适合用内联函数:

  • 如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
  • 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类声明中。一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这进一步说明了 inline 不应该出现在函数的声明中)。

小结

内联函数并不是一个增强性能的灵丹妙药。只有当函数非常短小的时候它才能得到我们想要的效果;但是,如果函数并不是很短而且在很多地方都被调用的话,那么将会使得可执行体的体积增大。
最令人烦恼的还是当编译器拒绝内联的时候。在老的实现中,结果很不尽人意,虽然在新的实现中有很大的改善,但是仍然还是不那么完善的。一些编译器能够足够的聪明来指出哪些函数可以内联哪些不能,但是大多数编译器就不那么聪明了,因此这就需要我们的经验来判断。
如果内联函数不能增强性能,就避免使用它!

参考文章:
C++的inline用法

<think>嗯,用户现在问的是关于C++中的内联函数inline function)是什么。之前他们问了指针和引用的区别,现在继续深入C++的相关知识,可能是在学习C++的基础或者准备面试。 首先,我需要回顾一下内联函数的定义和用途。内联函数是为了减少函数调用的开销,通过在编译时将函数体插入到每个调用点来避免函数调用的机制。通常用于短小的函数,因为对于复杂的函数,内联可能会导致代码膨胀。 用户可能想知道内联函数的基本概念,为什么使用它,以及和普通函数的区别。同时,他们可能也关心使用内联的优缺点,或者实际应用场景。 考虑到之前的回答结构,用户可能希望得到结构清晰的解释,比如分点说明,可能还有示例代码。需要确保回答符合之前设定的格式,比如使用中文,行内公式用$符号,独立公式用$$,但在这个问题里可能不太涉及数学公式,但如果有代码示例,需要用代码块展示。 另外,用户可能容易混淆内联函数和宏定义(#define),所以需要指出两者的不同,比如类型安全和编译器处理方式的不同。同时,需要提到内联函数的限制,比如编译器可能忽略内联请求,特别是对于复杂函数或者递归函数。 还要注意内联函数的优缺点,比如减少调用开销但可能增加代码体积,适合小函数。可能还需要提到在类定义内直接实现的成员函数默认是内联的,这也是一个常见的考点。 需要确保回答正确,比如内联函数的声明方式,使用inline关键字,以及编译器处理的内联机制。同时,避免错误,比如内联函数必须在调用之前定义,而不仅仅是声明,这点可能需要强调。 可能用户的实际需求是优化代码性能,尤其是在频繁调用的小函数上。所以应用场景部分需要给出例子,比如简单的getter/setter,或者运算符重载。 总结起来,回答应该包括定义、作用、语法示例、与普通函数及宏的区别、优缺点、使用场景,以及注意事项。结构上分点说明,帮助用户逐步理解。</think>--- ### 一、内联函数Inline Function)的定义 **内联函数**是C++中一种**编译期优化机制**,通过在函数调用处直接插入函数体代码,避免传统函数调用产生的开销(如栈帧分配、参数传递等)。使用关键字`inline`声明。 --- ### 二、核心作用 1. **减少函数调用开销** 适合短小且频繁调用的函数(如简单计算、访问成员变量)。 2. **替代宏函数** 提供类型安全检查,避免宏(`#define`)的文本替换风险。 3. **优化性能** 消除跳转指令,提升代码执行效率。 --- ### 三、语法示例 ```cpp // 声明并定义内联函数 inline int add(int a, int b) { return a + b; } int main() { int result = add(3, 5); // 编译时可能被替换为 int result = 3 + 5; return 0; } ``` --- ### 四、与普通函数的区别 | **特性** | 普通函数 | 内联函数 | |------------------|----------------------------|----------------------------| | 调用方式 | 通过函数地址跳转执行 | 直接插入代码到调用处 | | 编译期行为 | 生成单独函数体 | 代码展开(由编译器决定是否生效) | | 适用场景 | 复杂逻辑、低频调用 | 简单逻辑、高频调用 | | 代码体积 | 不增加体积 | 可能增大体积(多次展开) | --- ### 五、与宏(#define)的区别 | **特性** | 宏函数 | 内联函数 | |------------------|----------------------------|----------------------------| | 处理阶段 | 预处理期文本替换 | 编译期代码插入 | | 类型安全 | 无类型检查(易出错) | 有类型安全检查 | | 调试支持 | 不可调试 | 可调试 | | 作用域 | 全局替换(可能污染命名空间) | 遵循函数作用域 | --- ### 六、使用注意事项 1. **适用场景** - 函数体短小(通常1-5行)。 - 频繁调用(如循环体内部)。 - 简单逻辑(如`getter`/`setter`)。 2. **不适用场景** - 递归函数(无法内联)。 - 复杂函数(代码膨胀反而降低性能)。 - 虚函数(动态绑定与内联冲突)。 3. **编译器决定权** `inline`仅是对编译器的建议,最终是否内联由编译器决定(例如,`Debug`模式可能禁用内联)。 --- ### 七、示例:内联函数优化 ```cpp // 普通函数调用:每次调用需跳转并分配栈帧 int multiply(int x, int y) { return x * y; } // 内联函数:调用处直接展开为 x*y inline int multiply_inline(int x, int y) { return x * y; } int main() { int a = multiply(2, 3); // 普通函数调用 int b = multiply_inline(2, 3); // 可能被替换为 int b = 2*3; return 0; } ``` --- ### 八、总结 - **优势**:提升高频小函数性能,替代宏的安全性。 - **劣势**:滥用会导致代码膨胀(需权衡空间与时间效率)。 - **最佳实践**:优先用于简单、高频调用的函数(如工具类函数)。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值