【C++】C++入门 内联函数详解

简介: 【C++】C++入门 内联函数详解

一、为什么要学习内联函数

我们在回答这个问题之前先自己独立写一个简单的宏函数吧,写一个让两个数相加的宏Add()

写完之后我们来对一下答案

正确的写法是:

#define Add(x,y) ((x)+(y))

别小看这么简单的一个问题,有许多人都写不出一个真正可用的宏,就算写出来了也不能详细的回答出:“为什么要加第一个第二个括号?”

  1. 第一个括号是为了防止因为操作符优先级造成bug
    例如:
Add(1,2)*3;

在下面这种代码下就会产生bug

#define Add(x,y) x + y

在下面这种两种代码下就不会产生bug

#define Add(x,y) (x + y)
#define Add(x,y) ((x)+(y))
  1. 第二个括号是为了防止因为参数是表达式造成bug
    例如:
Add(a|b,a&b);// a|b+a&b  //加号的优先级比位运算符高

在下面这种代码下就会产生bug

#define Add(x,y) (x + y)

在下面这种代码下就不会产生bug

#define Add(x,y) ((x)+(y))

所以宏其实是很难写好的,而且也不利于调试,但是宏的优点又是运行时很快,不用建立栈帧节省空间与时间,我们想要继承宏的优点,同时改进宏的缺点那么我们应该怎么办呢?没错,C++里面的内联函数就是用来解决这样的问题而出现的。

二、内联函数的使用

内联函数的使用非常简单,在函数的声明或定义、函数的返回类型前加上关键字inline,就可以把函数指定为内联函数从而提升程序运行的效率。

例如:

#include<iostream>
using namespace std;
inline int Add(int x, int y)
{
  int z = x + y;
  return z;
}
int main()
{
  int ret = Add(1,2);
  cout << ret << endl;
  return 0;
}

我们来看一下汇编代码,我们发现在我们调用Add函数时并没有出现call指令(call指令是函数调用指令),这说明我们使用Add函数时并没有建立栈帧,说明inline确实可以向宏一样,不建立栈帧从而提升函数运行的效率。

注意:因为debug模式下,编译器默认不会对代码进行优化,内联函数不会起作用,你可能想看到debug模式下内联函数起作用后的的汇编代码,以下给出vs2022的设置方式)

点击项目右键

点击属性

设置为程序数据库(/Zi)

点击优化选择只适应于_inline(/Ob1)

三、内联函数的特性

  1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用。
    缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
  2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。下图为《C++prime》第五版关于inline的建议:

一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数。很多编译器都不支持内联递归函数,而且一个太多行的函数也不大可能在调用点内联地展开.

  1. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。
// test.h
#include <iostream>
using namespace std;
inline void f(int i);
// test.cpp
#include "test.h"
void f(int i)
{
  cout << i << endl;
}
// main.cpp
#include "test.h"
int main()
{
  f(10);
  return 0;
}
//报错误 C3861 “f”: 找不到标识符

内联函数是在编译阶段展开的,不像函数那样会形成符号表,在链接阶段进行链接。

正确写法

// test.h
#include <iostream>
using namespace std;
inline void f(int i)
{
  cout << i << endl;
}
// main.cpp
#include "test.h"
int main()
{
  f(10);
  return 0;
}
相关文章
|
3月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
52 2
C++入门12——详解多态1
|
3月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
93 1
|
3月前
|
存储 安全 编译器
【C++打怪之路Lv1】-- 入门二级
【C++打怪之路Lv1】-- 入门二级
34 0
|
3月前
|
自然语言处理 编译器 C语言
【C++打怪之路Lv1】-- C++开篇(入门)
【C++打怪之路Lv1】-- C++开篇(入门)
39 0
|
3月前
|
分布式计算 Java 编译器
【C++入门(下)】—— 我与C++的不解之缘(二)
【C++入门(下)】—— 我与C++的不解之缘(二)
|
3月前
|
编译器 Linux C语言
【C++入门(上)】—— 我与C++的不解之缘(一)
【C++入门(上)】—— 我与C++的不解之缘(一)
|
3月前
|
编译器 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
43 0
|
3月前
|
程序员 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
48 0
|
3月前
|
存储 算法 C++
C++入门10——stack与queue的使用
C++入门10——stack与queue的使用
51 0
|
3月前
|
存储 C++ 容器
C++入门9——list的使用
C++入门9——list的使用
23 0