C++宏的简单理解
C++中的宏是一种代码片段,可以被宏的值所替换。宏是用#define指令来定义的,用#undef指令来取消定义。宏有两种类型:类对象宏和类函数宏。
类对象宏就像一个常量,只不过不需要分配内存空间。
类函数宏就像一个函数,只不过不需要调用和返回。
大白话来说,宏就是一种让你写代码时更方便、更简洁、更灵活的工具
举例说明
假设你想定义一个宏,用来计算两个数的最大值。你可以这样写:
#define MAX(a,b) ((a)>(b)?(a):(b))
这就是一个类函数宏,它接受两个参数a和b,然后用三目运算符返回较大的那个。
当你在程序中使用这个宏时,比如写:
int x = MAX(3,5);
编译器会在编译之前把宏名替换成宏的定义,也就是:
int x = ((3)>(5)?(3):(5));
这样就可以得到x的值为5
类对象宏和类函数宏
- 类对象宏是一个标识符,被一段代码片段所替换。它看起来像一个数据对象,比如一个常量。
例如,你可以这样定义一个类对象宏:
#define PI 3.14159
这样在程序中使用PI时,就相当于使用3.14159。
- 类函数宏是一个标识符,后面跟着一对括号和一些参数,被一段代码片段所替换。它看起来像一个函数调用,但实际上不是。
例如,你可以这样定义一个类函数宏:
#define SQUARE(x) ((x)*(x))
这样在程序中使用SQUARE(5)时,就相当于使用((5)*(5))。
什么时候用宏呢
宏的使用场景一般是:
当你需要在编译时替换一些常量或表达式,而不是在运行时计算它们。
当你需要定义一些简单的操作,而不想为它们创建函数或模板。
当你需要根据不同的编译选项生成不同的代码,比如调试信息或条件编译。
当你需要使用一些特殊的语法或符号,比如变长参数或字符串化。
但是宏也有很多局限和风险,所以应该谨慎使用,并尽量用C++提供的其他特性来代替它们
那么C++提供了哪些特性可以代替宏呢?
C++提供了一些特性可以代替宏,比如:
用inline函数或模板来代替函数式宏,可以保证类型安全和编译优化。
用constexpr或const来代替常量宏,可以避免重复定义和命名冲突。
用using或typedef来代替类型别名宏,可以增加可读性和灵活性。
用enum class或namespace来代替枚举宏,可以提供作用域和强类型检查。
用if constexpr或模板特化来代替条件编译宏,可以实现编译时分支选择。
用字符串字面量或运算符重载来代替字符串化或连接宏,可以实现更自然的语法。
还有哪些情况是必须使用宏的呢?
宏在C++中的必要性已经大大降低了,但是还有一些情况是无法用其他特性替代的,比如:
当你需要定义一些编译器或平台相关的常量或标志,比如__FILE__或_WIN32。
当你需要使用一些预处理指令,比如#include或#pragma。
当你需要实现一些复杂的元编程技巧,比如BOOST_PP库。
宏和内联函数的区别
宏和内联函数有以下几个区别:
宏是由预处理器展开的,而内联函数是由编译器复制到调用处的。
宏必须在程序开始处定义,而内联函数可以在程序任何地方声明。
宏不能访问类的数据成员,而内联函数可以。
宏没有类型检查和返回类型,而内联函数有。
宏可能会导致副作用和错误,而内联函数不会。
代码示例
以下是宏和内联函数的一些代码示例:
// 定义一个宏,用于计算两个数的最大值 #define MAX(a,b) ((a) > (b) ? (a) : (b)) // 定义一个内联函数,用于计算两个数的最大值 inline int max(int a, int b) { return a > b ? a : b; } // 调用宏和内联函数 int x = 10; int y = 20; int z = MAX(x++, y++); // z = 21, x = 11, y = 22 int w = max(x++, y++); // w = 22, x = 12, y = 23
可以看到,宏会导致x和y被增加两次,而内联函数不会。这是因为宏只是简单地替换文本,而不考虑副作用
为什么宏会导致x和y被增加两次?
这是因为在调用宏的时候,宏的参数会被直接替换到宏的定义中,而不会像函数一样先计算参数的值。所以,当你调用MAX(x++, y++)时,实际上相当于执行了以下代码:
((x++) > (y++) ? (x++) : (y++))
这样就导致了x和y被增加了两次。如果你想避免这种情况,你可以先把x和y的值存储在临时变量中,然后再调用宏。或者你可以直接使用内联函数,因为内联函数会先计算参数的值,然后再复制到函数体中。
详细解释:
首先,你定义了一个宏MAX,用于计算两个数的最大值。然后,你定义了两个变量x和y,分别赋值为10和20。接着,你调用了宏MAX(x++, y++),并把结果赋值给变量z。这时候,预处理器会把宏的参数x++和y++直接替换到宏的定义中,得到以下代码:
((x++) > (y++) ? (x++) : (y++))
这段代码是一个三元运算符,它的意思是如果x++大于y++,就返回x++的值,否则返回y++的值。注意这里的x++和y++都是后置自增运算符,它们会先返回原来的值,然后再增加1。所以我们可以把这段代码拆分成以下几步:
1.比较x++和y++的值。因为x和y分别是10和20,所以x++返回10,y++返回20。比较结果是false。
2.因为比较结果是false,所以执行三元运算符的第三部分:(y++)。这时候y已经被增加了1,变成了21。所以(y++)返回21,并且再次增加1。
3.把(y++)的结果赋值给z。所以z等于21。
4.因为执行完了三元运算符的第三部分:(y++),所以跳过第二部分:(x++)。但是这时候x已经被增加了1,变成了11。
5.代码执行完毕。
最终结果是z等于21,x等于11,y等于22(被增加两次)。