C++内联函数

目录

1.内联函数的作用

2.内联函数的优点

3.内联函数的缺点

4.内联函数和类

5.为何不使用宏呢

6.总结


1.内联函数的作用

内联函数是C++中的一个重要特性。所以,首先探讨下为何要使用内联函数,并且它的作用是什么。

当程序执行函数调用指令时,CPU会将执行函数调用的指令的内存地址保存起来,将函数的参数拷贝到栈内存中,最后将控制权转移给这个函数。
然后CPU执行函数代码,在一个预先定义的内存/寄存器中存储函数返回值,然后将控制权转移给主调函数。 如果函数的执行时间,少于主调函数切换到被调函数的切换时间,则证明切换消耗很大。
如果是一个执行复杂任务的函数,则函数调用的消耗通常远小于执行任务所需时间,可以不予考虑切换开销。
然而,对于小函数,函数的调用时间通常比函数代码本身执行时间大很多,一般是由于小函数执行时间比切换时间小。

C++使用内联函数来减少函数调用的开销。内联函数被调用时,会按行进行展开,整个函数代码会插入/替换到被调用的地方。这个替换的工作,是在编译期间进行的,由C++编译器实现。如果内联函数比较小,可以提高效率。

内联函数的定义:

inline return-type function-name(parameters list)
{
    // function code
}

注意:内联仅仅是对编译器的一个请求,而不是一个命令。也就是说,编译器可以忽略内联请求。在下面的情况下,编译器可以不执行内联。
1) 函数包含循环操作. (for, while, do-while)
2) 函数包含静态变量.
3) 函数是递归的.
4) 函数返回值类型非void, 且返回主体不存在于函数体中。
5) 函数包含switch或goto主体。

2.内联函数的优点

1) 不会产生函数调用开销。
2) 节约函数调用时将参数压栈的开销。
3) 节约函数return call的开销。
4) 当实现一个内联函数时,会让编译器在函数体中执行上下文的优化。这样的优化不会出现在普通的函数调用中。
5) 嵌入式系统中,内联函数可能有效,也可能无效。因为内联函数依靠牺牲代码大小,换取函数调用以及返回的优先权。

3.内联函数的缺点

1) 内联函数中增加的变量需要消耗更多的寄存器。在将函数内联后,如果它所需的寄存器数量增加了,则会给寄存器利用增加额外负担。也就是说,内联函数在展开的过程中,它所需要的变量数量也随之增长,这样变量所需的寄存器数量也会增长。因此,在函数内联后,如果变量数量增长很大,则所需要的寄存器开销随之快速增长。
2) 如果使用了太多的内联函数,则最终的二进制文件会变得很大,因为里边包含了很多重复代码。
3) 太多的内联函数,也会减少cache的命中率,于是会减少从cache memory取指令到primary memory的速度。
4) 内联函数可能会增加编译时间,如果内联函数被修改了,则所有调用它的地方都需要重新编译,因为编译器需要重新在这些地方展开新的代码,来反映所做的修改,否则还是会用旧的代码。
5) 对于很多的嵌入式系统来说,内联函数可能没有什么作用。因为对于嵌入式系统,代码大小远比速度重要。
6) 内联函数可能会引起系统失效/系统波动,因为可能增大了二进制可执行文件的大小。内存的波动会降低计算机性能。

下面举例说明内联函数的作用:

#include <iostream>
using namespace std;
inline int cube(int s)
{
    return s*s*s;
}
int main()
{
    cout << "The cube of 3 is: " << cube(3) << "\n";
    return 0;
}

输出:
The cube of 3 is: 27

4.内联函数和类

在类中也可以定义内联函数。实际上,在类中实现的所有函数,都是隐式的内联函数。因此,所有内联函数的限制在此同样适用。如果想要显示的声明内联函数,则可以在类内部声明函数,在类外部使用inline定义函数。
例如:

class S
{
public:
    inline int square(int ss) //inline是多余的
    {
        //这个函数默认地被实现为内联函数
    }
};

上面展示的是一个不好的编程风格.。最佳方式是在类里边只声明函数原型,在类外边的函数定义处使用inline。
例如:

class S
{
public:
    int square(int ss); //声明函数
};
 
inline int S::square(int ss)//使用inline前缀
{
}

下面程序演示了这种方式:

#include <iostream>
using namespace std;

class operation
{
private:
    int a,b,add,sub,mul;
    float div;
public:
    void get();
    void sum();
    void difference();
    void product();
    void division();
};

inline void operation::get()
{
    cout << "Enter first value:";
    cin >> a;
    cout << "Enter second value:";
    cin >> b;
}

inline void operation::sum()
{
    add = a+b;
    cout << "Addition of two numbers: " << a+b << "\n";
}

inline void operation::difference()
{
    sub = a-b;
    cout << "Difference of two numbers: " << a-b << "\n";
}

inline void operation::product()
{
    mul = a*b;
    cout << "Product of two numbers: " << a*b << "\n";
}

inline void operation::division()
{
    div=a/b;
    cout<<"Division of two numbers: "<<a/b<<"\n" ;
}

int main()
{
    cout << "Program using inline function\n";
    operation s;
    s.get();
    s.sum();
    s.difference();
    s.product();
    s.division();
    return 0;
}

输出:
Enter first value:11
Enter second value:22
Addition of two numbers: 33
Difference of two numbers: -11
Product of two numbers: 242
Division of two numbers: 0

5.为何不使用宏呢

熟悉C语言的人知道C会使用宏。预处理程序会使用宏代码替换所有调用宏的地方。
C++中通常更推荐使用内联函数而不是宏。根据C++之父的观点,C++几乎从来不必使用宏,并将宏视作错误实践。在C++中使用宏会存在一些问题。宏无法访问类的私有成员。宏看起来像是函数调用,但实际上并不是。
例如:

#include <iostream>
using namespace std;
class S
{
private:
    int m;
public:
#define MAC(S::m)    //编译错误
};

C++ 编译器会检测内联函数的参数类型以及类型转换是否合法,而预处理程序无法做这样的检测。另外,宏是由预处理程序来管理,而内联函数由C++编译器管理。

6.总结

牢记:在类中实现的所有函数确实都是隐式的内联函数,并且C++编译器在所有这些函数被调用的地方会按照内联方式处理。
但是,如果是虚函数(virtual),则C++编译器无法进行内联处理。原因在于, 虚函数是在运行时(runtime)期间调用的,而不是编译期间。virtual代表着运行时,而inline代表着编译期间。如果编译器不知道哪个函数会被调用,它怎能知道执行内联处理。

注意:仅当函数调用开销时间大于函数本身执行时间时,函数定义为内联才有意义。

下面是一个不需要定义为inline的例子:

inline void show()
{
    cout << "value of S = " << S << endl;
}

上述函数会花费相对比较多的时间来执行。通常执行I/O操作的函数不应该定义为内联函数,因为它们会消耗相对比较多的时间。技术上可以预期,show函数执行所需时间,远大于调用开销。

通常如果函数没有扩展为内联函数,则编译器一般会给出warning。像Java & C#等语言,不支持内联。
但是,在Java中,当小的final method被调用时,编译器会进行内联处理,因为final method 不能被子类覆盖/覆写,并且final method的调用可以在编译期间就确定。C# JIT编译器也能将小的函数进行内联处理。

总结:内联是C++中的一个特性,恰当使用它能增强程序性能,但是反之会降低性能。不需要把每个函数都定义为内联。应该尽可能的把内联函数限制到最小。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值