VC中一個(gè)關(guān)于宏的使用問題
這兒是個(gè)關(guān)于宏的問題,我曾用過ATL的串轉(zhuǎn)換宏,包括W
// 在atlconv.h文件中
#define USES_CONVERSION \
int _convert; _convert; \
UINT _acp = GetACP(); _acp; \
LPCWSTR _lpw; _lpw; \?
LPCSTR _lpa; _lpa
為什么它們用“int x;x;”——這種后面跟著變量的聲明?
很多人都碰到過這個(gè)令人困惑的問題,后來發(fā)現(xiàn)答案很簡單:禁止編譯器的警告信息(warning)。如果單獨(dú)有一行代碼:
int x;
且從來沒有使用過x,那么編譯器匯報(bào)錯(cuò)“unreferenced local variable:x”,意思是未引用過的局部變量x,如果將警告信息的輸出調(diào)到最大。為了避免討厭的警告,USES_CONVERSION引用聲明的變量。
int x; // 聲明
x; // 使用這個(gè)變量
在C++之前的時(shí)代,程序員有時(shí)在C中用函數(shù)形參做同樣的事情來避免“unreferenced formal parameter”或其它的深?yuàn)W費(fèi)解的編譯錯(cuò)誤。
void MyFunc(int x, char y)
{
x;
y;
......
}
當(dāng)然,現(xiàn)在用下面的代碼可以更有效地完成同樣的事情:
// 參數(shù) x 不是用
void MyFunc(int /* x */)
{
......
}
也就是說聲明參數(shù),但不給它起名,不能這樣使用局部變量;必須顯式地引用它。這樣做不會(huì)增加任何指令到代碼中。最多可能多增加幾個(gè)字節(jié)到堆棧(為x預(yù)留空 間)。靈巧的編譯器甚至不會(huì)操心x從來沒有被使用過——雖然好奇心可能想知道:如果編譯器夠厲害,知道了從沒有使用x,為什么要抱怨(編譯出錯(cuò))呢?答案是因?yàn)榭量痰某绦騿T(且編程能力與個(gè)人的記性有關(guān))使用編譯警告提醒自己刪除某部分代碼時(shí)發(fā)生變量荒廢。這種警告在C時(shí)代很有用,你必須在每個(gè)函數(shù)的頂部聲明變量,這就遠(yuǎn)離了實(shí)用它們的代碼。
現(xiàn)在來看看另一個(gè)問題:為什么在開始位置要用USES_CONVERSION?即為什么W
#define W
int _convert; _convert; \
……etc
很明顯這樣做不行,因?yàn)槿绻闶褂?span lang="EN-US">W
#define W
int _convert; _convert; \
…… \
}
這樣解決了命名沖突,但不能進(jìn)行如下編碼:
DoSomething(W
沒有辦法從代碼塊返回值,所以不能在函數(shù)調(diào)用中傳遞W
inline LPCSTR W
int_convert;
……
}
這解決了范圍問題——任何W
不管什么時(shí)候進(jìn)行Unicode轉(zhuǎn)換,都不能就地轉(zhuǎn)換串,必須分配一個(gè)臨時(shí)串容納被轉(zhuǎn)換的字節(jié)。典型地,通過調(diào)用new分配一個(gè)串:
int len = MultiByteToWideChar(...,?
mystr, NULL, 0); // 或的長度
LPWSTR p = new WCHAR[nLen]; // 分配內(nèi)存
MultiByteToWideChar(...,p,len); // 轉(zhuǎn)換
SomeCOMFunction(p); // 使用之
delete [] p; // 銷毀
這段代碼不僅令人討厭,而且還沒有效率;必須調(diào)用MultiByteToWideChar兩次(一次是計(jì)算長度,一次是實(shí)際的轉(zhuǎn)換),你得從堆中分配p, 這樣很慢。通過分配2*len個(gè)字節(jié)解決第一個(gè)問題,這里長度len是ASCII串的長度——但第二個(gè)問題怎么辦?如果看看A2W是如何展開的,請(qǐng)看:
// 簡化版
#define A2W(s) \
_len = 2*strlen(s);
AfxA2WHelper((LPWSTR)alloca(_len);
AfxA2Whelper是一個(gè)調(diào)用MultiByteToWideChar的輔助函數(shù)。A2W使用2*len巧妙地避免了兩次調(diào)用 MultiByteToWideChar。但A2W及其它轉(zhuǎn)換宏真正聰明的地方是不調(diào)用new操作分配臨時(shí)串,而是調(diào)用alloca——在棧中分配字節(jié), 而不是在堆中。這樣做非常快,因?yàn)榫幾g器要做的只是增加棧指針。不調(diào)用函數(shù),不處理內(nèi)存塊。它也避免了內(nèi)存碎片,并且也沒有必要調(diào)用delete操作,因 為當(dāng)控制離開alloca被調(diào)用的地址后,內(nèi)存被自動(dòng)釋放。這正好說明了為什么A2W不能時(shí)內(nèi)聯(lián)函數(shù);如果是的話,alloca創(chuàng)建的臨時(shí)串會(huì)在返回前被 摧毀,并且你會(huì)以刪除串的方式終止SomeCOMFunction(使用這個(gè)例子)調(diào)用。
A2W必須從alloca被調(diào)用的相同的地址處調(diào)用alloca——所以A2W必須是一個(gè)宏,不是一個(gè)函數(shù);因此它需要另一個(gè)宏 USERS_CONVERSION來聲明_len以及其它一些用到的變量(為了簡化,我省略了)。當(dāng)你仔細(xì)想想,整個(gè)處理告訴我們要想寫一組類似A2W的 宏從棧中的分配內(nèi)存會(huì)減少很多不必要的麻煩。
另外,任何時(shí)候,只要你想要快速地獲取臨時(shí)內(nèi)存,都可以調(diào)用alloca。下面的代碼是我們常常見到的:
char* p = new char[len];
DoSomething(p);
delete [] p;
使用下面的代碼替代之會(huì)效率更高:
char *p = (char*)alloca(len);
DoSomething(p);
// 不用調(diào)用delete p!
當(dāng)然,如果棧中的沒那么多,你還這么做的話,就會(huì)出現(xiàn)一個(gè)討厭的消息框。這個(gè)方法還有一些局限,詳細(xì)內(nèi)容請(qǐng)參考文檔。經(jīng)驗(yàn)告訴我,不管什么時(shí)候在MFC、ATL或其它什么地方發(fā)現(xiàn)奇怪的事情,最好是鉆進(jìn)去研究一下,你可能會(huì)發(fā)現(xiàn)有用的東西。
posted on 2010-07-10 02:05 肥仔 閱讀(388) 評(píng)論(0) 編輯 收藏 引用 所屬分類: MFC存檔