面试题答案
一键面试宏定义
- 优点:
- 简单替换:预编译阶段进行简单文本替换,不进行类型检查,能实现一些简单的代码片段替换,如定义一些常用的数值、文本等,代码简洁直观。例如
#define PI 3.1415926
,在代码中使用PI
就会被替换为3.1415926
。 - 灵活性高:可以定义复杂的文本替换,甚至可以带参数,实现一些类似于函数的功能,但又不需要函数调用开销。如
#define MAX(a, b) ((a) > (b)? (a) : (b))
。
- 简单替换:预编译阶段进行简单文本替换,不进行类型检查,能实现一些简单的代码片段替换,如定义一些常用的数值、文本等,代码简洁直观。例如
- 缺点:
- 无类型检查:因为是简单文本替换,不进行类型检查,可能会导致难以发现的错误。例如
#define ADD(a, b) a + b
,如果调用ADD(2 * 3, 4 + 5)
,实际替换后为2 * 3 + 4 + 5
,结果可能并非预期。 - 调试困难:宏展开后会使代码变得混乱,在调试时,错误信息指向的是展开后的代码,不利于定位问题。
- 代码膨胀:多次使用宏定义会导致代码膨胀,因为每次使用都会进行文本替换。
- 无类型检查:因为是简单文本替换,不进行类型检查,可能会导致难以发现的错误。例如
适用场景:
- 简单常量定义:对于一些简单的,不需要类型检查的常量定义,如固定的数值、文本等。例如定义程序中的版本号
#define VERSION "1.0"
。 - 复杂文本替换:实现一些简单的,类似于函数功能但又不需要函数调用开销的场景,如上述的
MAX
宏。不过这种情况尽量用内联函数替代。
常量(const)
- 优点:
- 类型安全:定义常量时指定类型,编译器会进行类型检查,避免类型不匹配错误。例如
const int num = 10;
,编译器会确保num
只能是int
类型。 - 调试友好:在调试时,常量的错误信息更易于理解和定位,因为它的行为和普通变量类似,只是值不能改变。
- 内存优化:对于全局常量和静态常量,编译器可以将其优化到只读数据段,节省内存空间。
- 类型安全:定义常量时指定类型,编译器会进行类型检查,避免类型不匹配错误。例如
- 缺点:
- 灵活性受限:不能像宏定义那样进行复杂的文本替换,尤其是带参数的替换。
适用场景:
- 常规常量定义:当需要定义一个有类型的常量时,优先使用
const
。例如定义数学常量const double pi = 3.1415926;
,在程序中使用该常量进行数学计算,编译器能确保类型安全。 - 函数参数和返回值:在函数声明中使用
const
修饰参数和返回值,提高代码的安全性和可读性。如void func(const int value);
表示函数不会修改传入的value
。
内联函数
- 优点:
- 类型安全:和普通函数一样,进行严格的类型检查,保证代码的正确性。
- 性能优化:在编译时,内联函数的代码会直接嵌入到调用处,避免了函数调用的开销(如压栈、跳转等),提高程序运行效率。特别是对于短小的频繁调用的函数效果明显。
- 调试友好:和普通函数类似,调试时错误信息清晰,易于定位问题。
- 缺点:
- 代码膨胀:如果内联函数代码较长,多次调用会导致代码体积增大,因为每次调用都会嵌入代码。
适用场景:
- 短小频繁调用函数:对于一些功能简单,且会被频繁调用的函数,将其定义为内联函数可以提高性能。例如获取对象的某个属性的简单函数
inline int getValue() const { return value; }
。 - 替代宏函数:尽量用内联函数替代带参数的宏定义,以获得类型检查和调试友好等优点。如上述
MAX
宏定义可以用内联函数inline int max(int a, int b) { return a > b? a : b; }
替代。