2008年(909)
分类:
2008-05-06 21:15:34
文/赵湘宁
这儿是个关于宏的问题,我曾用过ATL的串转换宏,包括W2A,开始有些东西我还不太明白。为了使用这些宏,必须在函数的开始处用USES_CONVERSION来初始化某些局部变量。用就用吧,但是看看这个宏的定义,它有类似下面的代码:// 在atlconv.h文件中 #define USES_CONVERSION \ int _convert; _convert; \ UINT _acp = GetACP(); _acp; \ LPCWSTR _lpw; _lpw; \? LPCSTR _lpa; _lpa为什么它们用“int x;x;”——这种后面跟着变量的声明?
int x; // 声明 x; // 使用这个变量在C 之前的时代,程序员有时在C中用函数形参做同样的事情来避免“unreferenced formal parameter”或其它的深奥费解的编译错误。
void MyFunc(int x, char y) { x; y; ...... }当然,现在用下面的代码可以更有效地完成同样的事情:
// 参数 x 不是用 void MyFunc(int /* x */) { ...... }也就是说声明参数,但不给它起名,不能这样使用局部变量;必须显式地引用它。这样做不会增加任何指令到代码中。最多可能多增加几个字节到堆栈(为x预留空间)。灵巧的编译器甚至不会操心x从来没有被使用过——虽然好奇心可能想知道:如果编译器够厉害,知道了从没有使用x,为什么要抱怨(编译出错)呢?答案是因为苛刻的程序员(且编程能力与个人的记性有关)使用编译警告提醒自己删除某部分代码时发生变量荒废。这种警告在C时代很有用,你必须在每个函数的顶部声明变量,这就远离了实用它们的代码。
#define W2A(x) \ int _convert; _convert; \ ……etc很明显这样做不行,因为如果你使用W2A两次,得到一个复制的变量。那为什么不把整个宏放进花括弧创建新的范围?
#define W2A(x) Q{ \ int _convert; _convert; \ …… \ }这样解决了命名冲突,但不能进行如下编码:
inline LPCSTR W2A(LPWSTR w) { int_convert; …… }这解决了范围问题——任何W2A需要的变量多可以在这个函数中,在自己的范围内声明,不需要另外的宏。它也提供了一种返回值的方式,使你可以在函数调用和赋值中使用W2A(x)。但是这种方法不灵,也是因为W2A的缘故和其它的宏更复杂。
int len = MultiByteToWideChar(...,? mystr, NULL, 0); // 或的长度 LPWSTR p = new WCHAR[nLen]; // 分配内存 MultiByteToWideChar(...,p,len); // 转换 SomeCOMFunction(p); // 使用之 delete [] p; // 销毁这段代码不仅令人讨厌,而且还没有效率;必须调用MultiByteToWideChar两次(一次是计算长度,一次是实际的转换),你得从堆中分配p,这样很慢。通过分配2*len个字节解决第一个问题,这里长度len是ASCII串的长度——但第二个问题怎么办?如果看看A2W是如何展开的,请看:
// 简化版 #define A2W(s) \ _len = 2*strlen(s); AfxA2WHelper((LPWSTR)alloca(_len);AfxA2Whelper是一个调用MultiByteToWideChar的辅助函数。A2W使用2*len巧妙地避免了两次调用MultiByteToWideChar。但A2W及其它转换宏真正聪明的地方是不调用new操作分配临时串,而是调用alloca——在栈中分配字节,而不是在堆中。这样做非常快,因为编译器要做的只是增加栈指针。不调用函数,不处理内存块。它也避免了内存碎片,并且也没有必要调用delete操作,因为当控制离开alloca被调用的地址后,内存被自动释放。这正好说明了为什么A2W不能时内联函数;如果是的话,alloca创建的临时串会在返回前被摧毁,并且你会以删除串的方式终止SomeCOMFunction(使用这个例子)调用。
char* p = new char[len]; DoSomething(p); delete [] p;使用下面的代码替代之会效率更高:
char *p = (char*)alloca(len); DoSomething(p); // 不用调用delete p!当然,如果栈中的没那么多,你还这么做的话,就会出现一个讨厌的消息框。这个方法还有一些局限,详细内容请参考文档。经验告诉我,不管什么时候在MFC、ATL或其它什么地方发现奇怪的事情,最好是钻进去研究一下,你可能会发现有用的东西。 下载本文示例代码