
一、宏和模板
对于经常使用C/C++编程的小伙伴来说,宏和模板算是“大宝天天见”了。特别在早期的代码中,但凡稍微复杂或有名气一些的代码都会有大段大段的宏处理的模块。虽然大家都明白宏的缺点,但在有些时候儿它确实也“香”。不得不使用它来完成一些复杂的,特别是技巧性很强的工作。
宏是一种预编译器指令,它意味着简单的文本替换,这是它的优势,更是它的劣势。复杂的代码需要宏,但宏的复杂性又难于调试和复现错误。这就需要开发者自己仔细权衡了。宏的最强的地方在于灵活,灵活到很多人都觉得无法理解但程序却运行的很好。另外其也具有一定的扩展能力,这使得它在跨平台中更有优势。但宏的缺点亦是相当明显,它几乎和C++提倡的一系列技术概念都不兼容,如名空间、错误处理。它还无法看到实际生成的代码导致了代码维护的困难。
模板作为C++编程的底层库的必备技术,是C++的精髓所在。它同样是在编译期处理相关的代码生成,但模板提供了相对于宏更加完善的错误和异常处理机制,它也更符合C++语言本身的特点和发展方向。所以,模板技术在C++新标准的不断迭代中,也在不断的前进。但模板也有其另外一面,比如增加了编译时间,代码膨胀。但更难的在于代码的复杂性特别是元编程中,更让开发者闹心。
二、区别和联系
在现代的C++编程中,一般是不建议使用较为复杂的宏来进行开发。倾向于转向模板技术,这样更容易进行代码的安全控制和调试。宏和模板的共同点在于:
都是在编译期进行处理
宏是在预处理阶段,也算编译期。而模板则是在编译解析时期。可以进行代码复用
其实宏的主要功能就是实现通用代码;而模板也几乎类似。但模板会更强大二者在某些情况下可以互相替换
在一些通用函数生成如求极值等的情况下,二者可以互相替换元编程中经常是模板和宏一起应用
这个在前面的学习中应该是反复分析过,宏和模板一起在元编程中产生了很强的能力
三、二者的不同在于:
类型的安全检查
宏没有安全检查,只是单纯的替换;而模板提供了一系列的相关类型控制和推导机制等作用域管理
宏一般是全局作用域;而模板则严格遵守C++中的作用域管理编译问题
宏的编译友好性相当差,一般很难通过编译器定位;模板则是编译器虽可检查,但过于复杂错误处理
宏本身不支持安全控制和异常处理;模板支持,如概念等调试的友好程度
宏基本无法断点调试(太简单的除外);但模板一般可以像普通代码一样进行调试代码的可维护性
宏的代码是一种替换原则,稍微复杂的替换往往让维护者难于看到代码的实际情况;模板虽然同样复杂,但一般代码最终的形态还是可以看到的
宏和模板都是在编译期处理的,所以一般来说,如果不考虑代码本身的问题,其编译产生的结果,都不会影响到运行时的效率。
四、应用如何选择
在明白了宏和模板的应用后,就可以根据开发者的具体环境进行选择。正所谓没有哪种技术最好,只有哪种技术最合适。对于宏和模板可以通过下面的几个方面进行考量:
- 根据实际应用场景确定使用宏和模板。在绝大多数情况下,优先考虑使用模板。尽量避免引入宏
- 对于必须使用宏,如预编译条件等情况下,一定要慎重处理
- 要深刻理解环境提供的相关宏的系统定义如__FILE__、__LINE__等的使用
- 对于编译时间和代码膨胀敏感的情况下考虑使用宏
- 如果有可能,尽量减少二者的使用
五、扩展
在C++11后,constexpr的出现,让它与这二者也有一定的联系。比如它可以把宏变量或函数定义转成constexpr,它与模板一样也可以进行类型检查等安全机制管理。而且它的代码明显可见,更容易维护。constexpr在元编程时可以更好的和模板一起实现编译期计算或展开。
通过简单的分析,可以发现constexpr与宏和模板在很多领域都有重合和可替换的场景,这就需要开发者在实际开发中,多使用新标准中的新技术点。尽量减少宏的应用。
然而,技术往往会因为组合产生强大的功能。所以,根据情况将这些技术进行有机的融合,才是开发者最优先掌握的基础技能。
六、例程
看一下相关的代码:
//1. 阶乘
//宏
#define FACTORIAL(n) ((n) <= 1 ? 1 : (n) * FACTORIAL((n)-1))
//模板
template<int N>
structFactorial {
staticconstint value = N * Factorial<N-1>::value;
};
template<>
structFactorial<0> {
staticconstint value = 1;
};
int arr[Factorial<6>::value];
//constexpr
constexprintfactorial(int n){
return (n <= 1) ? 1 : n * factorial(n - 1);
}
constexprint val = factorial(6);
//2. 普通函数
//宏
#define MAX(a, b) ((a) > (b) ? (a) : (b)) //此处注意看是否有问题
//模板
template <typename T>
T max(T a, T b){
return (a > b) ? a : b;
}
//constexpr
constexprintmax_int(int a, int b){
return (a > b) ? a : b;
}
//3. 变量定义
//宏
#define SIZE 10
//模板
template<typename T>
constexpr T pi = T(3.1415926535897932385L);
//constexpr
constexprint size = 10;
上面的代码很简单,一看就明白了。
七、总结
再好的技术也需要开发者具体的权衡使用而不能乱用。只要能以最小的代价完成实际的项目功能,达到设计的目的,完成既定的任务。这就是技术选择的原则和方法。
夜雨聆风