一、内联函数 inline

​ 在函数名前添加关键字inline,该函数就被声明为内联函数。每当程序中出现对该函数的调用时,C++编译器会将函数体中的代码插入到调用该函数的代码处,同时使用实参代替形参,从而使程序运行时不再进行函数调用。

​ 引入内联函数主要是为了消除调用函数时的系统开销,以提高代码运行速度。

说明:

  • 内联函数在第一次被调用之前必须进行完整的定义
  • 在内联函数体内一般不能含有复杂的控制语句,如for语句和switch语句等
  • 类中的成员函数默认是内联函数(要求成员函数没有循环和递归,当其中存在循环的递归的时候,编译器会将其默认为一个普通函数处理)
  • 使用内联函数是一种空间换时间的措施,若内联函数较长,较复杂且调用较为频繁时不建议使用
  • 使用内联函数替代宏定义,能消除宏定义的不安全性(宏定义不检查函数参数,返回值)

示例:

#include <iostream>
using namespace std;

/*inline*/
inline double CircleArea(double r)  //内联函数
{double PI = 3.14;return PI * r * r;
}

int main() 
{
	for (int i = 1; i <= 5; i++)
​		cout << "r = " << i << " area = " << CircleArea(i) << endl;
	return 0;
}

二、宏定义 #define

#define命令是C++中的宏定义,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。

1. 无参宏定义

//#define <宏名> <字符串>
#define PI 3.1415926

示例:

#include <iostream>
using namespace std;

/*#define <宏名> <字符串>*/
#define NUM 1+1	 //宏定义
int main()
{int Sum = NUM * NUM;
	cout << "Sum = " << Sum << endl;
    return Sum;
}

​ 上述代码中,宏NUM代表的字符串是1+1,我们在理解main函数时,容易产生的误解是先求出NUM为1+1=2,然后在程序中计算Sum时再使用乘法,即NUM * NUM= 2 * 2 = 4,然而Sum的实际结果为3。

​ 这是因为宏展开是在预处理阶段完成的,这个阶段把替换文本只是看作一个字符串,并不会有任何的计算发生,在展开时是在宏NUM出现的地方,并且只是简单地使用字符串2+2来代替NUM,并不会添加任何符号,所以对该程序展开后的计算结果是Sum = 1+1*1+1,计算后Sum = 3,这就是宏替换的实质。

​ 如何使计算结果Sum = 4:如下列代码所示,只需要将宏定义改为#define NUM (1+1)

#include <iostream>
using namespace std;

#define NUM (1+1)	 //宏定义
int main()
{int Sum = NUM * NUM;
	cout << "Sum = " << Sum << endl;
    return Sum;
}

2. 有参宏定义

//#define <宏名> (<参数表>) <宏体>
#define AREA(i) i*i

说明: 需要在宏体中的每个参数加上括号,并在整个宏体上再加一个括号

#include <iostream>
using namespace std;

#define AREA(i) i*i	 //宏定义
int main()
{int Sum = AREA(2+2);
	cout << "Sum = " << Sum << endl;
    return Sum;
}

​ 上述程序中,宏AREA参数i为2+2,Sum所得的结果“理应”为4 * 4=16,但该程序的实际结果为8。在这个程序中,2+2为AREA(i)宏中的参数,应该将2+2替换宏定义中的i,即为2 + 2 * 2 + 2 = 8。那如果按照前文中的解决办法,将2+2括起来,即把宏体中的i括起来,是否可行?#define AREA(i) (i)*(i),对于AREA(2+2),替换为(2+2) * (2+2) = 16,确实可以解决。

​ 但是对于AREA(2+2)/AREA(2+2)又会如何?是否 = 1呢?这个宏替换后会变为 (2+2) * (2+2) / (2+2) * (2+2)即4 * 4 / 4 * 4,结果为16。解决方法为在整个宏体上再加一个括号,即#define AREA(i) ((i)*(i))

#include <iostream>
using namespace std;

#define AREA(i) ((i)*(i))	 //宏定义
int main()
{int Sum = AREA(2+2)/AREA(2+2);
	cout << "Sum = " << Sum << endl;
    return Sum;
}

3. 多行宏定义函数

示例1: 无return

#include <iostream>
using namespace std;

#define PI 3.14

#define AREA(r,Sum)\   //Sum在宏内部赋值
if(r >= 0)\
{\
	 Sum = PI * r * r;\
}

int main()
{
	double Sum;
    int r = 2;AREA(r, Sum);
	cout << "Sum = " << Sum << endl;
	return Sum;
}
//结果
Sum = 12.56

示例2: 有return

#include <iostream>
using namespace std;

#define PI 3.14
#define ZERO 0

#define AREA(r)\
if (r >= ZERO)\
{\
	return PI*(r)*(r);\
}\
else\
{\
	return -1;\
}

double calcArea(double r)
{
	AREA(r);
}

int main() {double Sum = calcArea(2);
	cout << "Sum = " << Sum << endl;
	return 0;
}
//结果
Sum = 12.56

4. 宏中的#和##

在C++中,# 和 ## 是两个预处理操作符,通常用于宏定义中。

  1. #:字符串化操作符。在宏定义中,# 可以将参数转换为字符串形式。当 # 跟在参数前面时,它会将参数转换为一个字符串常量,其形式为双引号括起来的参数文本。例如:
#define STR(x) #x
std::cout << STR(hello); // 输出 "hello"
  1. ##:连接操作符。在宏定义中,## 可以将两个标记连接成一个标记。例如:
#define CONCAT(x, y) x##y
int xy = CONCAT(10, 20); // 实际上被展开为 int xy = 1020;

注意,### 这两个操作符只能用于宏定义中,在编译器进行预处理时会将它们的作用应用到宏的参数或者标记上,生成相应的文本。使用这两个操作符时需要注意它们的使用场景和语法规则,以避免意外的错误。

5. 常用预定义宏

说明: 这些C++编译器内置的预定义宏不能被取消定义(#undef)也不能被编程人员重新定义。

  • __DATE__代表日期,形式为Mmm dd yyyy 的字符串常量

  • __FILE__表示当前源文件名,类型为字符串常量

  • __TIME__代表时间,hh:mm:ss 形式的字符串型常量

  • __func____FUNCTION__都代表当前函数的函数名,类型为字符串常量

  • __LINE__代表当前程序行的行号,类型为十进制整数常量

#include <iostream>
using namespace std;

int main() 
{
	const char* date = __DATE__;
	const char* file = __FILE__;
	const char* time = __TIME__;
	const char* func = __func__;
	const char* function = __FUNCTION__;
	int line = __LINE__;

	cout << "date: " << date << endl;
	cout << "file: " << file << endl;
	cout << "time: " << time << endl;
	cout << "func: " << func << endl;
	cout << "function: " << function << endl;
	cout << "line: " << line << endl;

	return 0;
}
//结果
date: Apr 28 2023
file: c:\users\ADiao\source\repos\c++\main.cpp
time: 17:10:00
func: main
function: main
line: 11

三、内联函数与宏定义的区别

内联函数和宏定义都可以用来实现代码的快速执行,但它们有以下几点区别:

  1. 参数类型安全性: 内联函数比宏定义更加类型安全。内联函数会对参数进行类型检查,而宏定义不会。这意味着,使用内联函数可以避免一些潜在的类型错误。
  2. 编译器优化: 内联函数是在编译期间展开的,因此它可以进行更多的编译器优化。而宏定义则是在预处理器展开,不能进行编译器优化。因此,使用内联函数通常可以获得更好的性能。
  3. 调试: 内联函数比宏定义更容易进行调试。因为内联函数是实际函数的一份副本,可以通过调试器跟踪到内联函数的执行过程。而宏定义则无法通过调试器进行调试。
  4. 名称空间: 内联函数位于名称空间中,而宏定义不属于任何名称空间。这意味着,内联函数可以避免名称冲突问题,而宏定义可能会导致名称冲突。
  5. 大小和可读性: 内联函数比宏定义更易于阅读和维护。宏定义的代码通常比较冗长,而内联函数则可以使用常规的C++语法编写,更加简洁易懂。另外,内联函数可以利用C++的函数重载和模板等特性,提高代码的可读性和可维护性。

总结: 虽然内联函数和宏定义都可以用来实现代码的快速执行,但内联函数通常比宏定义更优秀,因为它更加类型安全、可读性更强、容易调试和进行编译器优化。


如果这篇文章对你有所帮助,渴望获得你的一个点赞!

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐