C++语法与面向对象特性(2)
一.inline函数
1.inline的基本特性
被inline修饰的函数被称为内联函数。inline函数设计的初衷是为了优化宏的功能,编译器会在编译阶段对inline函数进行展开。然而需要注意的是,inline对于编译器而言是一种建议,它通常会展开一些简短的,非递归的函数,遇到较为复杂的函数即使有inline修饰编译器也会忽略展开
2.与宏的不同之处
宏也会在编译时展开,对比宏,inline有什么优势?inline修饰的函数可以调试
有了inline修饰的函数,就不需要再考虑宏中复杂的传参问题了。例如:
#include<iostream>
using namespace std;
// 实现⼀个ADD宏函数的常⻅问题
//#define ADD(int a, int b) return a + b;
//#define ADD(a, b) a + b;
//#define ADD(a, b) (a + b)
// 正确的宏实现
#define ADD(a, b) ((a) + (b))
// 为什么不能加分号?
// 为什么要加外⾯的括号?
// 为什么要加⾥⾯的括号?
int main()
{
int ret = ADD(1, 2);
cout << ADD(1, 2) << endl;
cout << ADD(1, 2)*5 << endl;
int x = 1, y = 2;
ADD(x & y, x | y); // -> (x&y+x|y)
return 0;
}
3.使用的注意事项
通常将inline函数定义与声明都放在头文件中
不建议将inline的声明与定义分文件写,会出现链接错误。因为inline函数展开后会没有函数地址
// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{
cout << i << endl;
} /
/ main.cpp
#include "F.h"
int main()
{
// 链接错误:⽆法解析的外部符号 "void __cdecl f(int)" (?f@@YAXH@Z)
f(10);
return 0;
}
地址丢失的底层机制
函数地址的产生条件:
普通函数:编译器生成函数实体,分配唯一地址,符号表记录强符号。
inline
函数:若被展开:无独立函数实体 → 无地址。
若未展开:生成弱符号实体,链接器可丢弃未使用的定义。
分文件编写的致命缺陷:
调用方(
main.cpp
)无法看到函数体 → 编译器必须生成函数调用指令。定义方(F
.cpp
)的inline
函数可能无实体 → 链接器找不到地址。
二.nullptr
nullptr的出现是为了解决C语言中NULL的二义性。C语言中的NULL实际为一个宏,如下:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
C++中的NULL会被定义为常量0,或C中会被定义为无类型指针(void*),这在函数调用时会产生歧义
这里通过重载f()作为例子
#include<iostream>
using namespace std;
void f(int x)
{
cout << "f(int x)" << endl;
}void f(int* ptr)
{
cout << "f(int* ptr)" << endl;
}
在调用f()时传入f(NULL)会出现什么情况?
显然这里的调用结果与预期不符。
那么尝试将NULL强转为int*会出现什么情况?
因此我们使用nullptr来规避这样的情况
nullptr是一种特殊类型的字面量,它可以隐式转换为各种类型的指针类型,而无法被转换为整型