C++的string实现MFC的CString::GetBuffer

来源:互联网 发布:cos圈滥交知乎 编辑:程序博客网 时间:2024/05/23 14:42

C++的string实现MFC的CString::GetBuffer

今天一个老同学QQ留言给我。

老同学:“STL的string有没有类似MFC的CString::GetBuffer的函数?"

我当时正在搜夏娃种子没空鸟他。

过了一会,他问得更直接了:“如果调用SDK的::GetWindowText的时候,使用STL的string做为输出缓冲区,该怎么办?”

为了打发他,我毫不犹豫的回到“(LPSTR)string::c_str();”

5秒钟后,老同学:“。。。。。。”。

一看见他的一大串“点点点”,我猛然意识到我可能错了。

接着放下手头的事情,夏娃可以慢慢找,老同学可不能瞎忽悠。随后仔细想这件事,似乎还真没这么简单。

string::c_str()返回的是const char* 类型。强制转成char* 类型,是有不足的。一共有两点:

第一点显而易见的是缓冲区溢出问题,解决这个问题只要分配一个足够大的缓冲区就好了。比如在定义string类型的时候:

string str(MAX_PATH,'\0');

又或者:

string str;str.resize(MAX_PATH);

两种方法都使得string的成员变量“size”,变得足够大。这样只要保证对string::c_str()返回的地址写操作的时候不超过MAX_PATH个字节就行了。到这里,似乎问题就解决了。不过别急,刚才不是说有两点么,现在才第一点呢。如果第一点算是隐患,那么接下来的完全就是缺陷了。

假设,刚才我们调用::GetWindowText的代码片段如下:

using namespace std;typedef basic_string<TCHAR> tsring;tstring strWndTitle(MAX_PATH,'\0');::GetWindowText(hWnd,(LPTSTR)strWndTitle.c_str(),MAX_PATH);if(strWndTitle == _T("hello world")){   ::MessageBox(NULL,NULL,NULL,MB_OK);    //do something...      }

这段代码有问题吗?咋一眼,没有问题啊。其实不然,这样的话,if里的代码块永远都执行不到!

假设hWnd所标示的窗口标题确实是为“hello world”,如果在if语句下个断点,程序跑起来断下来后,可以查看此时strWndTitle的内容确实是“hello world”,那么为什么执行不到if里面的语句块呢?为了好说明,我们再看下面的代码:

// 第一种char* pBuff = (char*)string::c_str(); // 第二种string::allocator_type alctor = string::get_allocator();string::pointer pBuff = alctor.address(*(string.begin()));

这两种方式获得的pBuff指针指向的地址其实是一样的。第二种方式不常用,之所以让大家看这两种方式,是为了让大家看看string::c_str()返回的地址究竟指向哪。本质上,这两种方式是一模一样的,也就是指向string的开始迭代器。

“==”关系运算符,实际上是重载了string::compare,我们跟踪进STL的源码发现compare最后的实现是用memcmp实现的代码如下:

static int __CLRCALL_OR_CDECL compare(const _Elem *_First1, const _Elem *_First2,        size_t _Count)        {    // compare [_First1, _First1 + _Count) with [_First2, ...)        return (_CSTD memcmp(_First1, _First2, _Count));        }

看着有点头大,我帮大家稍微转换下,上面调用memcmp的时候实际相当于:

memcmp(strTitle.c_str(),"hello world",strlen("hello world"));

看到这里,似乎没有什么问题,事实也的确如此。OK,我们退栈看看上层主调函数。

// 这个compare就是上面那个调用了memcmp的那个size_type _Ans = _Traits::compare(_Myptr() + _Off, _Ptr,            _N0 < _Count ? _N0 : _Count);return (_Ans != 0 ? (int)_Ans : _N0 < _Count ? -1            : _N0 == _Count ? 0 : +1);

呵呵,是不是更加头大的感觉?哈哈,下面是我给大家转化的等价代码,方便大家容易看明白:

int result = memcmp(strTitle.c_str(),"hello world",strlen("hello world"));if(result == 0){    if (strTitle.size == strlen("hello world"))    {        result = 0;    }    else    {        result = 1;    }    if (strTitle.size < strlen("hello world"))    {        result = -1;    }}            

这下清晰多了吧,从上面很容易看出,string::compare先用memcmp比较内存,再检查sting对象的size成员。尽管我们在memcmp的时候返回的是0,但是由于我们的strTitle的size大于strlen("hello world"),所以最终compare将返回1,即判定strTitle大于"hello world"。

找到了原因,我们就不难理解刚才所说的为什么执行不到if语句块中的代码了。

那么解决方案也很好办,直接看码:

using namespace std;typedef basic_string<TCHAR> tsring;tstring strWndTitle;strWndTitle.resize(MAX_PATH);    // 类似于MFC的CString::GetBuffer(MAX_PATH);LPTSTR pBuff = (LPTSTR)strWndTitle.c_str();::GetWindowText(hWnd,(LPTSTR)strWndTitle.c_str(),MAX_PATH);strWndTitle.resize(_tcslen(_T("hello world")));// 类似于MFC的CString::ReleaseBuffer()if(strWndTitle == _T("hello world")){   ::MessageBox(NULL,NULL,NULL,MB_OK);    //do something...      }

关键在于对pBuff写操作后,再次调用string::resize。

哈哈,这样我就可以比较完美的给老同学一个交代了。

总结:

首先调用string::resize,相当于CSting::GetBuffer,进行内存分配。

最后再次调用string::resize,相当于CString::ReleaseBuffer,进行释放闲置内存。

其间,需要注意没有释放闲置内存之前,使用string类的其他方法,会引起不可预料的意外情况。这跟MFC的CString进行GetBuffer后,没有ReleaseBuffer之前,是一样的。

0 0
原创粉丝点击