1. 程式人生 > >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

這段程式碼可以放到函式內部,用時定義,用完即銷。 

本次關於巨集的介紹就到此為止,如有不對之處,請多多指教!