【C++】你是怎么用内联函数(inline)的?

前言:

        本篇文章将从什么是内联函数、内联函数和普通函数的区别、预处理宏的缺陷、内联函数的优缺点以及使用内联函数的一些技巧等方面进行介绍。希望读者读完该篇博客,能对内联函数(inline)有更深刻的理解。

一、什么是内联函数

        内联函数是一种采用展开函数体的方式减少函数调用开销的技术。可以理解为将其它函数调用的函数体直接嵌入到调用处,省去了调用时的参数传递、挂起和返回的开销,从而提高了程序的执行效率。在C++中,可以使用inline关键字来声明内联函数。

下面介绍一下定义内联函数的方式:

inline 函数返回类型 函数名(参数列表)
{
    函数体
}

例如,我们定义一个计算两个整数之和的内联函数:

inline int Sum(int a, int b)
{
    return a + b;
}

类内部的内联函数:

         为了定义内联函数,通常必须在函数定义前面放一个 inline 关键字。但是在类内部定义内联函数时并不是必须的。任何在类内部定义的函数自动成为内联函数

class Person {
public:
  void PrintInfo() {  // 在类内部定义的函数默认为内联函数
    cout << "This is a person." << endl;
  }
};

二、内联函数、普通函数和预处理宏

1.内联函数与普通函数的区别:

        虽然内联函数和普通函数的功能是一样的,但是它们之间还是存在着一些区别。

1)内联函数的函数体要比普通函数的函数体短

        这是因为内联函数会在调用处展开,如果函数体过长则展开后的代码会很长,反而增加了代码量,降低了可维护性。

2)内联函数会在编译时进行替换,而普通函数则需要在运行时进行调用

        因此,内联函数比普通函数的执行速度要快。但是,如果内联函数被频繁调用,可能会对代码的体积造成一定影响,因为每次调用都会生成相应的代码。

3)内联函数不允许递归调用

        这是因为内联函数的展开式在编译的时候就确定了,而递归调用需要在程序运行时进行,两者无法兼容。

2.预处理宏的缺陷:

        预处理宏在某些情况下可以替代函数调用,但是预处理宏存在一些问题,可能会给程序带来一些潜在的风险和不利的影响。具体而言,预处理宏存在以下几点缺陷:

1)缺乏类型检查和作用域

        预处理宏是在预处理阶段进行文本替换,它不具备类型检查和作用域的功能。这就意味着,预处理宏可能会引入一些类型错误和作用域问题,增加程序的调试难度。

#include <iostream>
#define MULTIPLY(a,b) a*b

using namespace std;

inline int multiply(int a, int b) {
    return a * b;
}

int main()
{
    int i = 3, j = 4, x,y;
    x = MULTIPLY(i + 1, j + 1); // 预处理宏展开式为i+1*j+1 结果:8
    y = multiply(i + 1, j + 1); // 内联函数  结果:20
    cout << "The value of x is: " << x << endl;
    cout << "The value of y is: " << y << endl;
    return 0;
}

2)容易引发副作用

        预处理宏的替换是在预处理阶段进行的,不会考虑其在程序执行过程中可能引发的副作用。如果在宏定义中进行复杂的操作,则可能会引发一些意料之外的结果,增加程序的难度和不确定性。

#include <iostream>
#define MAX(a,b) a > b ? a++ : b++

using namespace std;

inline int max(int a, int b) {
    return a > b ? a++: b++;
}

int main()
{
    int x = 2, y = 2;
    //int m = MAX(++x, y); // 宏展开式为++x > y ? ++x++ : y++
    int xx = 2, yy = 2; 
    int n = max(++xx, yy); //内联函数结果:3
    //cout << "The value of m is: " << m << endl;
    cout << "The value of n is: " << n << endl;
  
    return 0;
}

3)可读性差

        预处理宏替换后的代码展开式通常比较长,难以阅读和理解。这会增加程序的维护难度和扩展性。

#include <iostream>
#define SQUARE(x) x*x

using namespace std;
inline int square(int x) {
    return x * x;
}

int main()
{
    int i = 2, j;
    j = SQUARE(SQUARE(i + 1)); // 预处理宏展结果:9
    cout << "The value of j is: " << j << endl;
    j = square(square(i + 1)); //内联函数结果:81
    cout << "The value of j is: " << j << endl;
    return 0;
}

三、内联函数的优缺点

1.优点:

1)减少函数调用的开销

        内联函数在调用时直接展开,避免了参数传递、挂起和返回的开销,从而提高了程序的执行效率。

2)增强代码可读性

        内联函数会将函数体替换到调用处,可以避免中间插入无用的函数调用,从而减少代码行数,降低代码的复杂度,增强了代码的可读性。

2.缺点:

1)增大了代码的体积

        虽然内联函数的优点是节省了函数调用的时间,但是每次调用都会生成相应的代码,会增加代码的体积。

2)过度使用会导致代码性能下降

        内联函数存在展开式大小的限制,如果函数体过长,则每次调用展开式的代码量会很大,从而降低程序的性能。

四、编译器决定内联函数

        内联函数和编译器是密不可分的。只有在编译器的支持下,内联函数才能够真正发挥出其优势,提高程序的执行效率。

        当编译器处理源代码时,会将内联函数的函数定义展开到调用处,从而将函数调用转换成对函数体的直接调用,避免了函数调用时的参数传递、挂起和返回的开销,从而提高了程序的执行效率。同时,由于内联函数的展开式已经替换了函数调用语句,因此程序在执行的时候不会产生函数调用的开销,从而降低了程序的运行时间。

        需要注意的是,内联函数并非在所有情况下都能够产生最佳效果。编译器之所以能够将内联函数进行展开替换,是因为编译器可以对程序进行优化,分析程序结构,将函数调用替换为直接调用的方式。但是在某些情况下,编译器可能难以对程序进行优化,因此内联函数可能并不是最佳选择。例如,如果内联函数的函数体过大,在展开后可能会导致生成的代码过长,从而增加了程序体积,反而降低了程序的性能。

此外c++内联编译会有一些限制,以下情况编译器可能考虑不会将函数进行内联编译:

  • 不能存在任何形式的循环语句
  • 不能存在过多的条件判断语句
  • 函数体不能过于庞大
  • 不能对函数进行取址操作

        内联仅仅只是给编译器一个建议,编译器不一定会接受这种建议,如果你没有将函数声明为内联函数,那么编译器也可能将此函数做内联编译。一个好的编译器将会内联小的、简单的函数。

        因此,在使用内联函数时,需要根据具体的情况和需求进行选择,以达到最佳的效果。需要考虑内联函数的变化和效率之间的平衡,同时也要考虑编译器的支持和约束。只有在编译器的支持下,内联函数才能够真正发挥其优势,提高程序的运行效率。        

五、使用内联函数的一些技巧

下面介绍一些使用内联函数的一些技巧:

1.函数体很小

        通常情况下,内联函数的函数体应该很小,因为展开式是直接替换调用处的语句,如果函数体过大,则展开式可能会很长,反而增加了代码量。

例如,下面定义一个简单的内联函数,用于比较两个值的大小:

inline int Max(int a, int b)
{
    return a > b ? a : b;
}

2.函数被频繁调用

        对于被频繁调用的函数,可以将其定义为内联函数。这样做可以避免函数调用的开销,提高程序的执行效率。

例如,下面的内联函数用于将字符串转换为大写字母:

inline void ToUpperCase(string& str)
{
    transform(str.begin(), str.end(), str.begin(), ::toupper);
}

3.函数的返回值类型是常量表达式

        对于函数的返回值类型是常量表达式的函数,可以将其定义为内联函数。这样做可以减少函数调用和返回的开销。

例如,下面的内联函数用于计算两个数的乘积:

inline constexpr int Multiply(int a, int b)
{
    return a * b;
}

        需要注意的是,在使用内联函数时,要根据实际情况进行选择。如果函数体比较大,或者函数不是被频繁调用的,那么将其定义为内联函数可能会降低程序的性能。此外,内联函数也不适合递归调用,因为重复展开可能会导致程序占用大量的栈空间。

总结:

        虽然使用内联函数可以提高程序的执行效率,但是要注意内联函数的展开式大小,以及是否具有被频繁调用的特征。需要根据具体的情况和需求进行选择,以达到最佳的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SecureCode

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值