VC中一个关于宏的使用问题

12 篇文章 0 订阅

VC中一个关于宏的使用问题

作者:赵湘宁

http://www.vckbase.com/document/viewdoc/?id=1698

    这儿是个关于宏的问题,我曾用过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;”——这种后面跟着变量的声明? 
   
很多人都碰到过这个令人困惑的问题,后来发现简单的答案是:禁止编译器的警告信息(warning(就是定义了一个变量,但没有使用的那个警告“unreferenced local variablex)

 

现在来看看另一个问题:为什么在开始位置要用USES_CONVERSION?即为什么W2A之类的宏还需要另外的宏声明自己的变量;为什么不直接在W2A中声明这个变量?

#define W2A(x) /
int _convert; _convert; /
……etc

很明显这样做不行,因为如果你使用W2A两次,得到一个复制的变量那为什么不把整个宏放进花括弧创建新的范围?

#define W2A(x) Q{ /
    int _convert; _convert; /
    …… /
}

这样解决了命名冲突,但不能进行如下编码:

DoSomething(W2A(pwstr));

没有办法从代码块返回值,所以不能在函数调用中传递W2A

真笨啊,那么内联函数怎么样?

inline LPCSTR W2A(LPWSTR w) {
    int_convert;
    ……
}

  这解决了范围问题——任何W2A需要的变量多可以在这个函数中,在自己的范围内声明,不需要另外的宏。它也提供了一种返回值的方式,使你可以在函数调用和赋值中使用W2Ax)。但是这种方法不灵,也是因为W2A的缘故和其它的宏更复杂。
不管什么时候进行Unicode转换,都不能就地转换串,必须分配一个临时串容纳被转换的字节。典型地,通过调用new分配一个串:

int len = MultiByteToWideChar(..., 
mystr, NULL, 0); // 或的长度
LPWSTR p = new WCHAR[nLen]; // 分配内存
MultiByteToWideChar(...,p,len); // 转换
SomeCOMFunction(p); // 使用之
delete [] p; // 销毁

  这段代码不仅令人讨厌,而且还没有效率;必须调用MultiByteToWideChar两次(一次是计算长度,一次是实际的转换),你得从堆中分配p,这样很慢。通过分配2*len个字节解决第一个问题,这里长度lenASCII串的长度——但第二个问题怎么办?如果看看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(使用这个例子)调用。
  A2W必须从alloca被调用的相同的地址处调用alloca——所以A2W必须是一个宏,不是一个函数;因此它需要另一个宏USERS_CONVERSION来声明_len以及其它一些用到的变量(为了简化,我省略了)。当你仔细想想,整个处理告诉我们要想写一组类似A2W的宏从栈中的分配内存会减少很多不必要的麻烦。另外,任何时候,只要你想要快速地获取临时内存,都可以调用alloca。下面的代码是我们常常见到的:

char* p = new char[len];
DoSomething(p);
delete [] p;

使用下面的代码替代之会效率更高:

char *p = (char*)alloca(len);
DoSomething(p);
// 不用调用delete p!

  当然,如果栈中的没那么多,你还这么做的话,就会出现一个讨厌的消息框。这个方法还有一些局限,详细内容请参考文档。经验告诉我,不管什么时候在MFCATL或其它什么地方发现奇怪的事情,最好是钻进去研究一下,你可能会发现有用的东西。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值