跟我学C++中级篇——宏应用的限制

一、宏及应用

宏的使用,是c/c++程序员绕不过去的话题。不管它是用多用少,用好用坏,争议却是非常大的。宏在早期的C/C++编程中,应用非常广泛。就如前面提到的MFC的内部动态创建类就是使用宏生成的,但也最是饱受诟病。复杂的代码和难于调试的过程,让多少人都感觉到崩溃并劝退了多少想学习的开发者。
宏可以做为变量定义、类型定义,也可以动态生成一段语句、函数甚至类和文件(类似模板有代码复用的效果)。宏可以用来反射生成对象也可以处理变参,在一些模板中应用宏可能有意想不到的惊喜。同时,在处理一些编译选项编译条件时,也可以使用宏来处理。
宏不但可以在代码中使用,也可以在编译的命令行中动态赋值,使得其应用更简单方便。甚至为了解决宏单纯替换导致的一些运算顺序等问题时还有一些专门的小技巧如do…while(0)等。这些,在代码中开发者或许经常遇到。
但需要泼点凉水了,在现代的C++编程中,要尽量限制使用宏。除非使用宏能做到其它方法做不到的事或者事半功倍!

二、C++中限制使用宏

为什么要限制使用宏呢?其实最重要的就两点:
1、不好调试
2、维护困难
宏有不少的缺点,如展开等的副作用(就是前面提到的do-while处理)、没有安全检查机制以及作用域和生命周期的问题等。但这些,对C/C++来说都不是让开发者觉得是多大的问题。但不好调试就会让很多开发者难于深入到宏应用的内部去。记得在初学宏代码时,想看源码,还得一点点的用命令选项生成,然后再根据想的和实际生成的对比,这开发的效率一下子就没有了。少量的宏代码或不太复杂的宏应用,相信大多数开发者还是肯去分析和处理的。但如果整页的宏代码,再加上关键的地方应用了宏的处理,往往让维护者有一种放弃的冲动。
如果单纯只是用还好说一些,大不了能跑就行。可如果需要对宏代码进行完善或修改,这就给后来的维护者提出一个大大的难题,不是到万不得已,估计没人愿意在大段的宏代码中掉头发。
那么既然宏有很多不方便的地方,C++中哪些地方还是需要使用宏呢?
1、编译交互处理
这种情况下算是宏的一个擅长的场景,而且这种应用形式也相对简单,不容易引起误会。
2、宏的简单应用
比如生成一段代码(函数或类等)进行复用,且这些生成的代码很简单,宏本身的代码也容易理解。但即使是如此,如果能用其它方式替代,尽量还是减少使用宏。
3、在一些基础软件中容易复用或提高效率的场景
比如在库中通过将模板和宏进行综合应用,能更好的实现类似反射、工厂等应用,大幅的减少重复代码和提高效率的情况下,建议使用。因为毕竟这种基础的底层的库、框架和底层等相关的开发人员,水平相对较高,整体代码对外封闭,专业性很强。
4、硬件或一些特殊场景
在硬件中,很多的厂商提供的代码都有大量的宏,这些代码可能是多少年复用累积的,用就好了。另外比如代码的应用的C++标准较低(C++98甚至更低)且C和C++混合编程,宏的一些应用仍然无法抛弃。

三、例程

宏的应用在各种书籍和资料上很多,这里只对推荐的编译交互和模板反射提供两个例子:

1、编译交互

int main() {

  std::cout << "start create shared acrmem!" << std::endl;

#ifdef SEND
  auto r = writeData();
#else
  std::cout << "start read sm" << std::endl;
  auto r = readData();
#endif

  return 0;
}

这段代码不全,但那个不重要,这里只是说通过宏SEND来处理一个程序内如何在编译时生成读还写的代码。使用的方法是:

g++ -o test -DSEND main.cpp

如果在Cmake中则可以使用“add_compile_definitions(SEND)”来达到同样的处理效果。

2、模板和宏综合应用

#define TEM_GET_PARAM_SIZE_BY_CONSTEXPR (...) TempGetParmCons(__VA_ARGS__)
#define TEM_GET_PARAM_SIZE_BY_SIZEOF(...) (sizeof(TemGetParmBySize)(__VA_ARGS__))/sizeof(char) -1
#define TEM_GET_TYPE_SIZE(...) TemGetTypeSize<__VA_ARGS__>::value

template<typename ...T>
constexpr size_t TempGetParmCons(T ...params)
{
    return sizeof ... (T);
}
template<typename ...T>
auto TemGetParmBySize(T&& ...param)->char(&)[sizeof ...(T) + 1];

template<typename ...T>
struct TemGetTypeSize
{
    static constexpr size_t value = sizeof ... (T);
};

有兴趣可以翻看一下以前的反射相关的文章,有不少的类似的代码。

四、总结

要想用好宏,就得把宏的了解的相当深入。宏的本质就是一种替换技术,用得好,可以产生意想不到的效果;反之,也会有意想不到效果。不过一个总的原则是,能不用宏的情况下,尽量避免使用宏编写代码。建议所有开发者能深以为之!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值