内联函数的出现

inline_func

在c++中,预定义宏的概念是用内联函数来实现的,而内联函数本身也是一个真正的函数。内联函数具有普通函数的所有行为。唯一不同之处在于它在编译时会被直接插入到调用它的地方,而不是像普通函数那样在运行时被调用。这样,内联函数可以避免函数调用的开销,提高代码执行效率,所以不需要函数调用的开销。因此应该不使用宏,使用内联函数。

C++中的宏函数是一种使用预处理器定义的函数宏。它们是一种在编译阶段进行简单文本替换的机制,可以将代码中的宏函数调用替换为相应的文本。宏函数通常使用#define指令来定义。

预处理宏的缺陷

宏函数的定义格式如下:

#define 宏函数名(参数列表) 替换文本

这里的宏函数名是一个标识符,用于表示宏函数的名称,参数列表是宏函数的参数,替换文本是该宏函数在实际使用时要替换的代码。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#define ADD(x,y) x+y
void test() {
	int ref = ADD(10, 20) * 2;
	cout << "ref=" << ref << endl;
}

int main() {
	test();
	return 0;
}

image-20240204230330233

宏函数只是简单文本替换,它没有遵循运算符优先级.所以源代码被转译成了10+20*2=50

但是如果使用内联函数,则会效率提高且安全。

#define ADD(x,y) x+y
inline int Add(int x,int y){
	return x + y;
}
void test(){
	int ret1 = ADD(10, 20) * 10; //希望的结果是300
	int ret2 = Add(10, 20) * 10; //希望结果也是300
	cout << "ret1:" << ret1 << endl; //210
	cout << "ret2:" << ret2 << endl; //300
}

image-20240204230559304

再看一个示例:

#define COMPARE(x,y) ((x) < (y) ? (x) : (y))
#include<iostream>
using namespace std;
inline int compare(int x, int y) {
	return x < y? x:y;
}
void test() {
	int regina_1 = 1;
	int regina_2 = 1;
	int ivanlee = 3;
	cout << COMPARE(++regina_1, ivanlee) << endl;
	cout << compare(++regina_2, ivanlee) << endl;
}

int main() {
	test();
	return 0;
}

参数 ++a 和 b 都会在调用 COMPARE 函数前被求值,因此实际上是 COMPARE(2, 3)。由于宏函数只是简单的文本替换,所以++a被求了两次,最终的结果是

image-20240204231103284

内联函数

在c++中,预定义宏的概念是用内联函数来实现的,而内联函数本身也是一个真正的函数。内联函数具有普通函数的所有行为。唯一不同之处在于内联函数会在适当的地方像预定义宏一样展开,所以不需要函数调用的开销。因此应该不使用宏,使用内联函数。

在普通函数(非成员函数)函数前面加上inline关键字使之成为内联函数。但是必须注意必须函数体和声明结合在一起,否则编译器将它作为普通函数来对待。

inline void func**(**int a**);**

以上写法没有任何效果,仅仅是声明函数,应该如下方式来做:

inline int func**(**int a**){****return** **++;}**

注意: 编译器将会检查函数参数列表使用是否正确,并返回值(进行必要的转换)。这些事预处理器无法完成的。

内联函数的确占用空间,但是内联函数相对于普通函数的优势只是省去了函数调用时候的压栈,跳转,返回的开销。我们可以理解为内联函数是以空间换时间

任何在类内部定义的函数自动成为内联函数。

class Person{
public:
	Person(){ cout << "构造函数!" << endl; }
	void PrintPerson(){ cout << "输出Person!" << endl; }
}

内联函数和编译器

内联函数并不是何时何地都有效,为了理解内联函数何时有效,应该要知道编译器碰到内联函数会怎么处理?

对于任何类型的函数,编译器会将函数类型(包括函数名字,参数类型,返回值类型)放入到符号表中。同样,当编译器看到内联函数,并且对内联函数体进行分析没有发现错误时,也会将内联函数放入符号表。

当调用一个内联函数的时候,编译器首先确保传入参数类型是正确匹配的,或者如果类型不正完全匹配,但是可以将其转换为正确类型,并且返回值在目标表达式里匹配正确类型,或者可以转换为目标类型,内联函数就会直接替换函数调用,这就消除了函数调用的开销。假如内联函数是成员函数,对象this指针也会被放入合适位置。

类型检查和类型转换、包括在合适位置放入对象this指针这些都是预处理器不能完成的。

image-20240204231341347

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

posted @ 2024-02-04 23:24  ivanlee717  阅读(47)  评论(0编辑  收藏  举报