在C/C++中, 如果有一些函数被频繁调用, 那么就会不断的进行函数入栈出栈的操作, 这会造成栈空间以及程序运行时间的消耗. 栈空间就是指放置程序的局部数据以及函数内数据的内存空间, 在正常情况下, 栈空间都是有限的, 如果频繁大量的使用就会造成栈空间不足的问题, 函数的死循环递归调用的最终结果就是导致占内存空间的枯竭
特征:
- 相当于把内联函数里面的内容直接写在了调用内联函数处 (类似于
#define
或者typedef
的那种替换
操作) - 相当于不用执行进入函数的步骤, 直接执行函数体
- 相当于 宏, 却比宏多了类型检查, 真正具有函数特性(这也是与
#define
的区别所在) - 在类声明中定义的函数, 除了虚函数的其他函数都会自动隐式的成为内联函数
- 关键字
inline
必须与函数定义体放在一起才能使函数成为内联, 仅将inline放在函数声明前不起任何作用 - 与非内联函数不同的是,
inline
函数必须在调用该函数的每个文本文件中定义. 当然, 对于同一程序的不同文件, 如果inline
函数出现的话,其定义必须相同.
优点:
- 内敛函数会像宏函数一样在被调用处展开, 省去了参数压栈, 栈帧开辟, 结构返回等步骤, 从而提高了程序的运行速度
- 内联函数相比宏函数来说, 在代码展开处会做安全检查和类型转换(同普通函数), 而宏定义函数则不会
- 在类声明中同时定义的成员函数, 会自动转化为内联函数, 因此 内联函数可以访问类的成员变量, 宏定义则不能.
- 内联函数在运行时可以调试, 而宏定义则不行(因为宏定义是被预定义处理的, 所以不会有人黑的编译符号和调试信息, 调试的时候基本只能用肉眼去看)
缺点:
- 代码膨胀. 内联函数是以代码膨胀(复制)为代价, 消除函数调用带来的开销. 如果执行函数体内代码的时间, 相比于函数调用的开销较大, 那么效率的收获就会很少. 另一方面, 每一处内联函数的调用都要复制代码, 将使程序的总代码量增大, 消耗更多的内存空间.
inline
函数无法随着函数库升级而升级.inline
函数的改变需要重新编译, 不像非内联函数那样可以直接链接- 是否内联, 程序员不可控, 内联函数只是对编译器的建议, 对于最终实现的决定权在于编译器.
虚函数可以是内联函数吗
虚函数在语法上可以是内联函数, 内联是可以修饰虚函数的, 但是当虚函数表现多态性的时候是不能内联的. (在具体实现时, 到底是否内联, 是由编译器决定的), 如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Base {
virtual ~Base() {}
virtual void Foo() { printf("Base::Foo()\n"); }
};
struct Derived: Base {
virtual void Foo() { printf("Derived::Foo()\n"); }
};
Base* b = new Derived; // 非 static 令编译器不能在编译期知道 b 指向那个类型的对像
int main() {
b->Foo(); // 不可能内联
b->Base::Foo(); // 非多态调用,可以内联(但具体是否内联由编译器决定)
delete b;
}内联实际上是在建议编译器内联, 而虚函数的多态性需要在运行期起作用, 编译器无法知道运行期具体调用哪个代码, 因此虚函数表现为多态性时, 不可以内联
inline virtual
唯一可以内联的时候是: 编译器知道所调用的对象是哪个类(如 Base::who()), 这只有在编译器具有 实际对象而不是对象的指针或引用时才会发生.