一、内联函数
什么是内联函数?
在C++中,函数调用是有开销的:参数压栈、跳转、返回等操作会消耗一定的时间和空间。如果一个函数很小且被频繁调用,这个开销就会变得明显。
内联函数就是用来解决这个问题的。当你把一个函数声明为inline时,编译器会尝试在调用该函数的地方直接插入函数体的代码,而不是生成一个真正的函数调用指令。
inlineintsquare(intx){returnx*x;}intmain(){inta=5;intb=square(a);// 编译器可能会将其展开为 int b = a * a;return0;}内联函数的语法
- 在函数定义前加上关键字inline
inlineintadd(inta,intb){returna+b;}- 内联函数通常定义在头文件中,因为编译器需要在调用点看到完整的函数体才能展开。
为什么要使用内联函数?
消除函数调用开销:对于频繁调用的小函数,可以提升性能。
比宏更安全:传统C语言使用#define宏实现类似功能,但宏只是文本替换,容易出错。内联函数有类型检查和作用域,更安全。
保留函数的形式:代码仍然清晰,不损失可读性。
内联函数的特点
建议性:inline只是对编译器的建议,编译器可以忽略它(比如函数体过大或递归函数通常不会被内联)。
代码膨胀:如果内联一个较大的函数,每个调用点都会复制代码,导致可执行文件变大,反而可能降低性能。
多次定义:内联函数可以定义在多个源文件中(只要定义相同),不会引发多重定义错误。
什么时候使用内联函数?
函数体较小(通常1~5行)。
函数被频繁调用。
追求极致性能的代码区域。
什么时候不要使用内联?
函数体较大(超过10行)。
函数包含循环、递归或复杂控制流。
函数地址会被取用(例如作为回调函数)。
编译器对内联函数的调整
构造函数和析构函数即使看起来是空函数,编译器也可能生成大量代码,不适合内联。
内联函数可能无法设置断点,调试时最好暂时禁用内联。
现代编译器即使没有inline关键字,也会自动内联小函数。所以inline更多是一种提示。
对于代码量较大的函数或者递归函数等,加上inline也会被编译器忽视。
二、nullptr
NULL的问题
- 在C++98中,我们通常用NULL表示空指针。NULL本质上是一个宏,通常被定义为
0或(void*)0。
int*p=NULL;// 实际上 p = 0- 这看起来没问题,但暗藏隐患。请看下面的例子:
voidf(intx){cout<<"f(int)"<<endl;}voidf(char*p){cout<<"f(char*)"<<endl;}intmain(){f(NULL);// 猜猜调用哪个?return0;}- 你可能会认为调用f(char*),但由于NULL是0,编译器会选择f(int),这很可能导致程序错误。
nullptr
C++11引入了nullptr关键字,专门表示空指针。它有明确的类型std::nullptr_t,可以隐式转换为任何指针类型,但不能转换为整数类型。
int*p=nullptr;// 正确char*q=nullptr;// 正确inti=nullptr;// 错误!不能转换为整数nullptr解决二义性问题
使用nullptr后,上面的例子就能正确调用指针版本:
voidf(intx){cout<<"f(int)"<<endl;}voidf(char*p){cout<<"f(char*)"<<endl;}intmain(){f(nullptr);// 正确调用 f(char*)return0;}使用场景
1. 初始化指针
int*ptr=nullptr;2. 检查指针是否为空
if(ptr==nullptr){// 空指针处理}3. 函数返回空指针
int*findValue(intkey){if(notFound)returnnullptr;return&value;}4. 与模板配合
template<typenameT>voidprocess(T*p){if(p==nullptr)return;// ...}三、结语
内联函数和nullptr都是C++中简单却实用的小特性,在实际开发中,养成为指针使用nullptr的习惯,并合理使用内联函数,你的代码会更干净、更可靠。