勿在浮沙築高臺:關於宏定義的幾個小技巧

大道不過三倆行,說破不值一文錢~~

假如有過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行以內的宏定義,而在後面的幾乎每個類之中都或多或少的用到了,如果不是用宏定義,全部用普通代碼來寫,則會直接增加幾百行的代碼~~~,

想象也會很可怕,這些也算是冗餘代碼,因爲規格比較統一,但是用一些常用的抽象,泛型也無法很好的解決這類奇怪的問題,這個時候宏定義的優秀就充分反應了出來。

希望這些小技巧可以幫助大家快樂編碼,早日擺脫重複工作




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章