勿在浮沙筑高台:关于宏定义的几个小技巧

时间:2021-12-03 02:43:51

大道不过三俩行,说破不值一文钱~~

假如有过C++应用程序开发的经历,相信很多时候经常会遇到一些莫名其妙的宏定义,宏展开之类的~~

其实用法很简单,一旦说破就什么都没了,只是经常让人疑惑,为什么要用宏定义,直接写不好么?

答案是但凡是用宏定义的地方都可以用正常写法完成,但是又是宏定义会带来很多不一样的神奇的化学反应~


技巧一:

使用宏定义代替现有类型,有助于跨平台

#define TRUE 1
#define FALSE 0
#define FLOAT float
在很多时候不同平台下很多类型的值,默认路径等等的都是不一样的,举个栗子,浮点数的精度就是,

这时如果我们直接调用该类型则可能会有问题,我们通过使用宏定义来封装一层变量类型,这样当需要考虑跨平台时,直接去修改对应的宏定义即可,而不会由于平台问题干扰到我们的项目的高层逻辑


技巧2:

使用宏定义快速定义和访问变量:

经常有时候,我们需要定义一些变量,他们的成员变量为了匹配某一标准而要按某一模式命名,这些命名的格式总是有很类似的地方,这个时候宏定义又可以有用处了

举个栗子:(摘自《升入浅出MFC》)

#define _IMPLEMENT_RUNNTIMECLASS(class_name , base_class_name , wSchema , pfnNew) \
		static char _lpsz##class_name[] = #class_name; \
		CRuntimeClass class_name::class##class_name = { \
			_lpsz##class_name , sizeof(class_name) , wSchema ,pfnNew , \
				RUNTIME_CLASS(base_class_name),NULL}; \
	static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name); \
		CRuntimeClass* class_name::GetRuntimeClass() const \
			{ return &class_name::class##class_name; } \
在这里我们用到了另外一个宏的高级用法:宏替换

这里我们的宏开始增加了参数,之后我们的后面的宏定义中所有用到了上面的宏定义中宏参数的部分都会在实际使用中被替换掉

这里附带2个小技巧,已知CWnd是一个类名,如果要实现字段名与其他关键字的组合如“”classCWndid“这种变量名的声明,我们可以这样写:

#define TTT(class_name) \
public: \
		int class##class_name##id ;
在这里我们我们只需要将CWnd作为参数带入到这个宏定义中便可以得到一个对应的要求的变量

还有的时候我们需要得到代码中的某一字段的名字,并将此作为字符串保存,我们可以这样写:

#define TT(class_name) \
public: \
	char name[] = #class_name;
我们同样只需要将对应的字段名作为参数传入宏即可,我们便可以很快的得到一个名为name的值为对应字段名的字符串。

以上的这些方法现在看起来很像一些简单的奇技淫巧,但是当他们组合到一起是便会有想象不到的魔力,可以极大地降低代码的冗余,提高代码的可读性(当然前提是不是新手,习惯了之后觉得读起来贼方便,很优美),举个栗子MFC中就采用了大量的宏定义保证了框架的稳定构架而没有大量的冗余代码。

上一段代码大家就懂了,

//MFC.h
#pragma once
#define BOOL int
#define TRUE 1
#define FALSE 0
#define LPCSTR LPSTR
typedef char* LPSTR;
#define UINT int
#define PASCAL _stdcall
#define TRACEL printf

#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
using namespace std;




class CObject;

struct CRuntimeClass
{
	//属性
	LPCSTR m_lpszClassName;
	int  m_nObjectSize;
	UINT m_wSchema;
	CObject* (PASCAL* m_pfnCreateObject)();  //NULL => abstract class
	CRuntimeClass* m_pBaseClass;

	CObject* CreateObject();
	static CRuntimeClass* PASCAL Load();


	//运行时类型的对象会被链接到一条链表上
	static CRuntimeClass* pFirstClass;   //型录的起点
	CRuntimeClass* m_pNextClass;    //被链接到一条型录上
};

struct AFX_CLASSINIT {
	AFX_CLASSINIT(CRuntimeClass* pNewClass);
};

#define RUNTIME_CLASS(class_name) \
		(&class_name::class##class_name)

#define DECLARE_DYNAMIC(class_name) \
public: \
		static CRuntimeClass class##class_name; \
		virtual CRuntimeClass* GetRuntimeClass() const;

#define DECLARE_DYNCREATE(class_name) \
		DECLARE_DYNAMIC(class_name) \
		static CObject* PASCAL CreateObject();

#define _IMPLEMENT_RUNNTIMECLASS(class_name , base_class_name , wSchema , pfnNew) \
		static char _lpsz##class_name[] = #class_name; \
		CRuntimeClass class_name::class##class_name = { \
			_lpsz##class_name , sizeof(class_name) , wSchema ,pfnNew , \
				RUNTIME_CLASS(base_class_name),NULL}; \
	static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name); \
		CRuntimeClass* class_name::GetRuntimeClass() const \
			{ return &class_name::class##class_name; } \

#define IMPLEMENT_DYNAMIC(class_name ,base_class_name) \
		_IMPLEMENT_RUNNTIMECLASS(class_name ,base_class_name , 0xFFFF,NULL)

#define IMPLEMENT_DYNCREATE(class_name , base_class_name) \
		CObject* PASCAL class_name::CreateObject() \
			{ return new class_name ;} \
		_IMPLEMENT_RUNNTIMECLASS(class_name , base_class_name , 0xFFFF , \
			class_name::CreateObject)

class CObject
{
public:
	CObject::CObject() {}
	CObject::~CObject() {}

	virtual CRuntimeClass* GetRuntimeClass() const;
	BOOL IsKindOf(const CRuntimeClass* pClass) const;

public:
	static CRuntimeClass classCObject;
	virtual void SayHello() { cout << "Hello CObject \n"; }
};

class CCmdTarget : public CObject
{
	DECLARE_DYNAMIC(CCmdTarget)
public:
	CCmdTarget::CCmdTarget() {}
	CCmdTarget::~CCmdTarget() {}
};


class CWinThread :public CCmdTarget
{
	DECLARE_DYNAMIC(CWinThread)
public:
	CWinThread::CWinThread() {}
	CWinThread::~CWinThread() {}
	virtual BOOL InitInstance() {
		return TRUE;
	}

	virtual int Run() {
		return 1;
	}
};


class CWnd;

class CWinApp :public CWinThread
{
	DECLARE_DYNAMIC(CWinApp)
public:
	CWinApp* m_pCurrentWinApp;
	CWnd* m_pMainWnd;

public:
	CWinApp::CWinApp() {
		m_pCurrentWinApp = this;
	}

	CWinApp::~CWinApp() {}

	virtual BOOL InitApplication() {
		return TRUE;
	}

	virtual BOOL InitInstance() {
		return TRUE;
	}

	virtual int Run() {
		return CWinThread::Run();
	}
};

class CDoocument : public CCmdTarget
{
	DECLARE_DYNAMIC(CDoocument)
public:
	CDoocument::CDoocument() {
	}
	CDoocument::~CDoocument() {
	}
};


class CWnd :public CCmdTarget
{
	DECLARE_DYNCREATE(CWnd)
public:
	CWnd::CWnd() {
		cout << "CWnd Constructor" << endl;
	}

	CWnd::~CWnd() {}
	virtual BOOL Create();

	BOOL CreateEx();
	virtual BOOL PreCreateWindow();
	void SayHello() {
		cout << "Hello CWnd \n" << endl;
	}
};


class CFrameWnd :public CWnd
{
	DECLARE_DYNCREATE(CFrameWnd)
public:
	CFrameWnd::CFrameWnd() {
		cout << "CFrameWnd Constructor" << endl;
	}
	CFrameWnd::~CFrameWnd() {
	}

	BOOL Create();
	virtual BOOL PreCreateWindow();
	void SayHello() {
		cout << "Hello CFrameWnd"<<endl;
	}
};

class CView :public CWnd
{
	DECLARE_DYNAMIC(CView)
public:
	CView::CView() {}
	CView::~CView() {}
};

这里我们大概写了40行以内的宏定义,而在后面的几乎每个类之中都或多或少的用到了,如果不是用宏定义,全部用普通代码来写,则会直接增加几百行的代码~~~,

想象也会很可怕,这些也算是冗余代码,因为规格比较统一,但是用一些常用的抽象,泛型也无法很好的解决这类奇怪的问题,这个时候宏定义的优秀就充分反应了出来。

希望这些小技巧可以帮助大家快乐编码,早日摆脱重复工作