C++宏编程技巧_c++使用宏定义注册服务-程序员宅基地

技术标签: C++  c++  开发语言  

下面的代码并非按照规范格式来写,仅作示范用途。

常用符号

  • ##

连接符,可将多个标识符拼接起来,组成一个完整的标识符。

//定义宏,用来打印整型变量
#define PRINT(x)	printf("%d\n", a##x)

int a1 = 1;
int a2 = 2;

PRINT(1);	//等同于printf("%d\n", a1),输出1
PRINT(2);	//等同于printf("%d\n", a2),输出2
  • #

添加双引号,转成字符串。

//定义宏,转成字符串
#define STR(x)	#x

//等同于printf("%s\n", "Hello, world!");
printf("%s\n", STR(Hello, world!));
  • #@ 

添加单引号,转成字符。

//定义宏,将x转成字符
#define CH(x)	#@x

char a = CH(M);	//等同于char a = 'M'
printf("%c\n", a);

除了这些基本符号以外,还有一些巧妙使用技巧。

可以利用宏来完成注册类功能的实现。

高阶技巧

  • 注册类

在微软的MFC框架中,时常可以见到窗口注册类的身影。

通过API将用户自定义的类注册一下,便可以使用它们执行某些特定的功能。

这里我要介绍的是,自定义注册方法和调用类的方法,将用户自定义类注册成功后就可以通过类名来获取该类的对象,这在没有反射机制的C++中是十分有用的功能。

  • 用户可以用map容器存储['A', func]键值对,实现类与特定函数一一对应关系。
  • 规定注册类的权限,只有注册后才能执行某些操作。
  • 将类名字符串保存在数组中,使用for循环语句或if...else条件语句动态创建所需的类。

下面对注册类的一种简单使用进行举例:

//定义用来生成类、注册类的宏
//组合类名
#define CLS(x)	My##x
//创建注册类
#define CREATE_CLS(x) \
class CLS(x) : public RegCls	\
{	\
public:	\
	CLS(x) *Instance() 	\
	{	\
		static CLS(x) *pInstance = new CLS(x);	\
		if (pInstance == nullptr)	\
		{	\
			pInstance = new CLS(x);	\
		}	\
		return pInstance;	\
	}	\
}
//使用类名进行类注册,#x用来将类名转成字符串
#define REG(x)	\
CREATE_CLS(x)	\
g_ClsMap[#x] = CLS(x)::Instance

 其中g_ClsMap是全局变量,需要自定义。

“\”符号用来连接多行代码,表明它们属于同一个宏定义。

CLS(x)用来组合连接类名,生成标识符My##x,如CLS(Dog)会产生标识符MyDog。

下面定义方法来调用注册类:

//定义类型func,返回RegCls *类型,无参函数
typedef std::function<RegCls *()> func;
//定义全局map变量,保存类名字符串和获取单例的函数指针
std::map<string, func> g_ClsMap;

//注册两个类,类名分别为MyDog和MyCat
REG(Dog);
REG(Cat);

//使用"Dog"和"Cat"字符串获取类的单例对象
RegCls *pDog = g_ClsMap["Dog"]();
RegCls *pCat = g_ClsMap["Cat"]();

本人在使用宏的过程中,还总结了一个可简化代码的宏使用技巧。

相信在编程过程中,经常会遇到相似代码重复多次出现的情况,显然宏就是为这种重复工作而生的东东。

举例如下:

//fd为文件描述符,以只读模式打开a.txt文件
int fd = open("a.txt", O_RDONLY);
if (fd < 0)
{
	cout << "open failed\n";
	return -1;
}

//读取fd所指的文件内容,并保存在buf中
int res = read(fd, buf, sizeof(buf));
if (res < 0)
{
	cout << "read failed\n";
	return -1;
}

上面是Linux系统中常见的打开文件并读取文件内容的操作,可发现判断返回结果的代码都是类似的,很容易联想到使用宏进行简化。其实宏定义的位置也有讲究,不仅可在文件头部定义,也可以在函数内部定义。无论在函数内部还是外部定义宏,都可以在之后使用该宏,作用域均为全局范围。

通过#define和#undef组合,可以将宏作用域限制在标签之间。

使用宏简化代码如下:

//定义宏,检查变量值
#define CHECK(x, str)	\
if (x < 0)	\
{	\
	cout << str << " failed\n";	\
	return -1;	\
}

int fd = open("a.txt", O_RDONLY);
CHECK(fd, "open");

int res = read(fd, buf, sizeof(buf));
CHECK(res, "read");

//取消宏定义
#undef CHECK

这段代码可以放到函数内部,用时定义,用完即销。

咦!为啥代码行数反而增加了!其实不然,这里我们定义的宏只用到了两次,所以效果不佳,但当使用次数增加到3次、10次甚至更多时,宏的作用将会体现得淋漓尽致! 

本次关于宏的介绍就到此为止,如有不对之处,请多多指教!

关于我


> 我是一位喜欢创新、乐观向上的少年
> 爱好是看书、踢足球、玩LOL等
> 喜欢我的文章的朋友,可以添加个人微信:`CS-huo`
> 有问题可以相互探讨,共同学习!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/gkzscs/article/details/82934054

智能推荐

linux下,redis 3.2.1双节点集群安装部署-程序员宅基地

文章浏览阅读468次。为什么80%的码农都做不了架构师?>>> ..._linux redis双节点安装部署

最全面的AI学习路线和资源整理-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏18次。目录大纲基础知识1.数学2 统计学3 编程数据分析/挖掘1数据分析的基础书籍2特征工程3 数据挖掘项目机器学习公开课 吴恩达《Machine Learning》公开课 吴恩达 CS229公开课 林轩田《机器学习基石》公开课 林轩田《机器学习技法》书籍《机器学习》书籍《统计学习方法》书籍《Scikit-Learn 与 TensorFlow 机器学习实用指南》实战 Kaggle 比赛工具 Sciki...

SpringBoot-2.3.4集成MybatisPlus-3.4.0(gradle)_spring 2.3.4.release用哪个版本的mybatis-程序员宅基地

文章浏览阅读446次。SpringBoot-2.3.4集成MybatisPlus-3.4.0MybatisPlus官方文档版本环境IntelliJ IDEA:2020.1.2java:jdk-14.0.1springboot:v2.3.4.RELEASEgradle:gradle-6.7-rc-4springfox(swagger):version: '3.0.0'swagger-bootstrap-ui:version: '1.9.6'mysql-connector-java:version: '8.0.16_spring 2.3.4.release用哪个版本的mybatis

人工机器:基于视觉的机械手控制_机器视觉控制机械手的具体过程-程序员宅基地

文章浏览阅读5k次。 《机器人学、机器视觉与控制》一书中,第五部分开始,第15章之前——基于视觉的控制,第442页这样写到。 第二个问题:确保机器人能达到一个期望的位姿,也不是一个简单的事情。正如我们在第七章讨论的那样,机器人末端执行器要通过计算要求的关节角度才能向一个位姿运动。这就要求机器人的运动模型是准确的,它反过来就要要求机器人的加工精度必须非常高:连杆长度必须准确,关节轴之间..._机器视觉控制机械手的具体过程

Android 上自定义的复式折线图(一)_andriod 自定义折线图多条-程序员宅基地

文章浏览阅读1.6k次,点赞3次,收藏5次。最近公司想做一个表格统计图,而且要符合可以多条折线的那种,自己也是翻阅了好多资料也没有找到很合适的一种,后来果断决定还是自己定义一个咯,下面先附上效果图.这里有三条折线,其实里面是有两个列表:一个是管颜色的,另一个是管折线上的数据.同时这里的控件外面也包了一个HorizontalScrollView,当超过屏幕时就可以进行水平滚动.而且这里的控件LineView也提供了很过设置属性的方法._andriod 自定义折线图多条

在vue项目中使用codemirror插件实现代码编辑器功能(代码高亮显示及自动提示)...-程序员宅基地

文章浏览阅读3k次。在vue项目中使用codemirror插件实现代码编辑器功能(代码高亮显示及自动提示)1、使用npm安装依赖npm install --save codemirror;2、在页面中放入如下代码<template> <div> <textarea ref="mycode" class="codesql" v-model="c..._codemirror自定义代码提示

随便推点

约瑟夫环——POJ3379-程序员宅基地

文章浏览阅读1.4k次。题目描述:给出一个长度是n的字符串环,每次搁k个加入字符串中对应位置的字母序的下一个字母,执行m次,问最后一次插入的是什么字母。大致思路:正着想的话只能用模拟的方法解决,但是m有10^9这么大,而把问题倒过来想一下的话,那就变成了给出一个n+m的字符串每次搁k个字符删掉一个,最后剩下一个长度为n的字符串,问起始位置是什么字母。这样的话就变成了约瑟夫问题,约瑟夫环问题可以在不用考虑内容的_poj3379

Gym - 101652P Fear Factoring-程序员宅基地

文章浏览阅读220次。Fear Factoring 题 意:输入a,b让你求[a,b]所有的数的因数和。 数据范围: 1&amp;lt;=a&amp;lt;=b&amp;lt;=1e12 0&amp;lt;=b-a&amp;lt;=1e6输入样例:101 101输出样例:102思 路:我们先求这个[1,n]所有的数因数和,然后用cal(b)-cal(a-1)就是[a,b]内的所有因数和了。问题是怎么求这个cal(b)呢?用的是除法..._gym - 101652p

jQuery中的closest()和parents()的区别_jquery closest 和parents 区别-程序员宅基地

文章浏览阅读1.9k次。jQuery中的closest()和parents()的区别jQuery中closest()和parents()的作用非常相似,都是向上寻找符合选择器条件的元素,但是他们之间有一些细微的差别,官网也给出了说明: .closest() .parents() Begins with the current element Begins with the parent element T_jquery closest 和parents 区别

踩坑记 | Flink 天级别窗口中存在的时区问题-程序员宅基地

文章浏览阅读1.7k次。本系列每篇文章都是从一些实际的 case 出发,分析一些生产环境中经常会遇到的问题,抛砖引玉,以帮助小伙伴们解决一些实际问题。本文介绍 Flink 时间以及时区问题,分析了在天级别的窗口..._flink时区报错

安装yii2.0 advanced_yii advance 汉化版-程序员宅基地

文章浏览阅读295次。1.在官网上下载(http://www.yiiframework.com/download/)选择 advanced 版2.解压到D:\phpStudy\WWW\ 并且改名为blogdemo2 phpstudy配置好hosts和站点域名3.初始化模板 打开cmd切换到 项目所在路径,运行php init ,选择开发模式 0 ,然后yes 4.打开_yii advance 汉化版

小程序 ----踩坑 ---安卓iOS兼容等-程序员宅基地

文章浏览阅读567次。关于小程序一些小功能的代码都在这个GitHub上,感兴趣的可以去看看,https://github.com/huihuijiang/miniProgram目前有:列表左滑删除,拖拽浮标一、小程序坑1.scroll-view横向滚动的时候,包含文字图片等,元素错位,第二个元素掉下去;hack:给子元素添加vertical-align:top;当使用scroll-view横..._小程序 ios parsefloat