CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛
CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛
CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛

在C++中使用宏的一些总结-源码交易平台丞旭猿

一个众所周知的事实是-宏是很糟糕的,宏是一个历史的遗留的产物,已经无法很好的适应现代C++的发展。当然,有也一些宏是也是很不错的。

每条规则的背后都是有例外的,所以不要轻易的说禁止使用宏,虽然有一些宏可能会另代码看起来很不舒服(让人困惑),但还是有一些宏可以让显著地提升代码的可读性和正确性。

一个很糟糕的宏: max

宏有很多缺点,其中一个是宏是没有作用域的,这也就是如果一个文件中定义了一个宏,如header.hpp中场景了一个define指令,那么该文件后续所有行的代码都会受到该宏的影响,直接或间接include该头文件的文件也一样。

voidinnerFunc(){define MACRO_IN_FUNCTION 1}// 我们可以在超出函数作用域的地方继续使用该宏ifdef MACRO_IN_FUNCTION// TODO something.endif

如在上面的代码中我们在函数内部分定义了一下宏MACRO_IN_FUNCTION,那么这函数定义之后的所有代码中都可以使用该宏,即是超出了函数的作有域,也就是说你不能将宏限制在一个函数,namesapce,或者类中。

考虑下面一个例子

define max(a,b) (a < b) ? b : aintx=42;inty=43;intz=max(x,y);std::cout<<x<<\n<<y<<\n<<z<<\n;

这个代码的输出是多少呢?毫无疑问,输出应该是:

424343

好的,下面我们来稍微改变一下我们的代码

intx=42;inty=43;intz=max(++x,++y);std::cout<<x<<\n<<y<<\n<<z<<\n;

从语法上来讲,就是一段合理的代码,在语义上我们期望的结果应当是x是43,y和z是44,然而我们得到的结果是:

434545

为什么会是这个结果呢?当考虑到宏所做的事情后,这个结果却是正确的:宏只是简单的进行文本替换,如何让查看编译器进行宏展开后的结果呢,本人使用g++,只需要在g++命令中添加‘-E’即可,即g++ -E *.cpp,下面就是宏展开后的代码:

intx=42;inty=43;intz=(++x<++y)?++y:++x;std::cout<<x<<\n<<y<<\n<<z<<\n;

从展开后的结果中可以看出,最大的值y,有两次的自增(++)操作。基于文本的替换的宏在与C++进行结合时,可能会产生非常危险的混合,例如,如果你在其它文件中定义了函数max,然而你是无法调用到的,预处理器(preprocessor)会首先将其按宏的方式进行展开。

除此之外,宏还有很多其它问题,例如无法进行断点调试等等。虽然宏有很多问题,但在很多情况下宏却可以用来提升代码的质量。

1. 用于连接两个C++特性

C++具有非常丰富的特性,但是在一些高级的设计,多个部分之间无法做到无缝连接。

例如,我们需要在库中实现Accept并将此函数注入到应用程序的DocElement层次结构中。可惜,c++没有这样的直接机制。也有使用虚拟继承的变通方法,但虚函数的调用具有一定的性能开销。我们可以定义一个宏,并要求visitable层次结构中的每个类在类定义中使用该宏。但是,当我们在代码中使用宏时,需要保持代码的可控性,Andrei Alexandrescu提供了一些指导意见-定义宏的一个最重要的规则是让它尽可能少地自己执行,并尽可能快地将其转发给真正的实体(函数、类),也即是宏的逻辑足够简单。

define DEFINE_VISITABLE() \virtualReturnTypeAccept(BaseVisitor&amp;guest)\{returnAcceptImpl(*this,guest);}

2. 利用宏来减少冗余

在写代码时,如果一段相同的代码需要键入多次,那么使用宏可以减少这种冗余,并让代码看起来足够赏心悦目。

在现代C++中我们可能需要经常用到std::forward来传递左值或右值引用:

template<typenameMyType,typenameMyOtherType>voidf(MyType&&myValue,MyOtherType&&myOtherValue){g(std::forward<MyType>(myValue),std::forward<MyOtherType>(myOtherValue));}

此模板代码中的&&表示值可以是l-value或r-value引用,具体取决于它们绑定的值是l-value还是r-value。std:forward允许将此信息传递给g。

但这需要很多代码来表达,每次输入都很麻烦,而且在阅读时还会占用一些空间。所以,为了简洁起见,我们可以定义一个宏来完成这个事情:

define FWD(...) ::std::forward(__VA_ARGS__)

这样我们的代码看起来就是这要的:

template<typenameMyType,typenameMyOtherType>voidf(MyType&&myValue,MyOtherType&&myOtherValue){g(FWD(myValue),FWD(myOtherValue));}

显然,这段代码比原始代码更加直观,更具有可读性。

宏可以带来更底层的多态机制

宏可以被用于多态,但这只是多态的一个特殊情况,需要在编译阶段前被resolved,也就是发生在预处理阶段。

这是怎么做到的呢?您可以定义以-D开头的编译参数,并且可以使用代码中的ifdef指令测试这些参数的存在性。根据它们的存在,您可以使用不同的define来为代码中的表达式赋予不同的含义。

至少有两种信息你可以通过这种方式传递给你的程序:

  • 允许系统调用代码可移植的操作系统类型(UNIX vs Windows)
  • 可用的c++版本(c++ 98、c++ 03、c++ 11、c++ 14、c++ 17等)。

在设计用于不同项目的库代码中,让代码知道c++的版本是很有用的。它为库代码提供了灵活性,使其能够在可用的情况下编写高效的代码实现。

在使用c++高级特性的库中,如果库必须处理某些编译器bug,那么传递有关编译器本身及其版本的信息也是有意义的。这是Boost中的一个常见实践方式。

无论哪种方式,对于与环境或语言相关的指令,您都希望将这种检查保持在尽可能低的级别,并将其深深封装在实现代码中。

总结

在C++代码中烂用宏是一个很evil的事情,理解宏的机制,合理适合宏往往能带来非常大的收益,例如本人最近的工作中,需要将C++对象导致到js层,使用宏就大大提升了工作效率。

声明:本文部分素材转载自互联网,如有侵权立即删除 。

© 版权声明
THE END
喜欢就支持一下吧
点赞0赞赏 分享
相关推荐
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容