CString的GetBuffer用法GetBuffer本质GetBuffer常见问题解决方法.docx
- 文档编号:28747442
- 上传时间:2023-07-19
- 格式:DOCX
- 页数:19
- 大小:24.36KB
CString的GetBuffer用法GetBuffer本质GetBuffer常见问题解决方法.docx
《CString的GetBuffer用法GetBuffer本质GetBuffer常见问题解决方法.docx》由会员分享,可在线阅读,更多相关《CString的GetBuffer用法GetBuffer本质GetBuffer常见问题解决方法.docx(19页珍藏版)》请在冰豆网上搜索。
CString的GetBuffer用法GetBuffer本质GetBuffer常见问题解决方法
CString的GetBuffer用法,GetBuffer本质,GetBuffer常见问题解决方法
char*GetBuffer(n)
当n大于0时,是为CString变量分配一个长度为n的字节数组,返回值是这个数组的地址
当n等于0时,返回CString变量本身拥有的字符串数组的头
ReleaseBuffer一般用在GetBuffer,因为在调用了GetBuffer后变量本身会给自己上锁,于是所有能改变自身值的函数都不能用(如果Left,Mid),要用ReleaseBuffer解锁
一.函数原型
CString:
:
GetBuffer
LPTSTRGetBuffer(intnMinBufLength);
throw(CMemoryException);
ReturnValue
AnLPTSTRpointertotheobject’s(null-terminated)characterbuffer.
Parameters
nMinBufLength
Theminimumsizeofthecharacterbufferincharacters.Thisvaluedoesnotincludespaceforanullterminator.
Remarks
ReturnsapointertotheinternalcharacterbufferfortheCStringobject.ThereturnedLPTSTRisnotconstandthusallowsdirectmodificationofCStringcontents.
IfyouusethepointerreturnedbyGetBuffertochangethestringcontents,youmustcallReleaseBufferbeforeusinganyotherCStringmemberfunctions.
二.函数作用及使用范围
对一个CString变量,你可以使用的唯一合法转换符是LPCTSTR,直接转换成非常量指针(LPTSTR-[const]char*)是错误的。
正确的得到一个指向缓冲区的非常量指针的方法是调用GetBuffer()方法。
GetBuffer()主要作用是将字符串的缓冲区长度锁定,releaseBuffer则是解除锁定,使得CString对象在以后的代码中继续可以实现长度自适应增长的功能。
CString:
:
GetBuffer有两个重载版本:
LPTSTRGetBuffer();LPTSTRGetBuffer(intnMinBufferLength);
在第二个版本中,当设定的长度小于原字符串长度时,nMinBufLength=nOldLen,该参数会被忽
略,不分配内存,指向原CString;当设定的长度大于原字符串本身的长度时就要重新分配(reallocate)一块比较大的空间出来。
而调用第一个版本时,应如通过传入0来调用第二个版本一样。
是否需要在GetBufer后面调用ReleaseBuffer(),是根据你的后面的程序是否需要继续使用该字符串变量,并且是否动态改变其长度而定的。
如果你GetBuffer以后程序自函数就退出,局部变量都不存在了,调用不调用ReleaseBuffer没什么意义了。
最典型的应用就是读取文件:
CFilefile;
//FILE_NAME为实现定义好的文件名称
if(file.Open(FILE_NAME,CFile:
:
modeRead))
{
CStringszContent;
intnFileLength=file.GetLength();
file.Read(szContent.GetBuffer(nFileLength),nFileLength);
szContent.ReleaseBuffer();
//取得文件內容放在szContent中,我们之后可以对其操作
}
三.测试
以下就CString:
:
GetBuffer,做简单测试:
测试1:
//exampleforCString:
:
GetBuffer
#include
#include
voidmain(void)
{
CStrings("abcd");
printf("
(1)beforeGetBuffer:
\n");
printf("CStrings.length=%d\n",s.GetLength());
printf("CStrings=%s\n",s);
LPTSTRp=s.GetBuffer
(2);
printf("
(2)afterGetBufferandbeforeReleaseBuffer:
\n");
printf("LPTSTRp=%s\n",p);
printf("p.length=%d\n",strlen(p));
printf("CStrings=%s\n",s);
printf("CStrings.length=%d\n",s.GetLength());
s.ReleaseBuffer();
printf("(3)afterReleaseBuffer:
\n");
printf("LPTSTRp=%s\n",p);
printf("p.length=%d\n",strlen(p));
printf("CStrings=%s\n",s);
printf("CStrings.length=%d\n",s.GetLength());
}
测试结果1:
(1)beforeGetBuffer:
CStrings.length=4
CStrings=abcd
(2)afterGetBufferandbeforeReleaseBuffer:
LPTSTRp=abcd
p.length=4
CStrings=abcd
CStrings.length=4
(3)afterReleaseBuffer:
LPTSTRp=abcd
p.length=4
CStrings=abcd
CStrings.length=4
Pressanykeytocontinue
测试2:
将LPTSTRp=s.GetBuffer
(2);修改为:
LPTSTRp=s.GetBuffer(10);
测试结果同1。
测试3:
在测试二的LPTSTRp=s.GetBuffer(10);后添加p[5]='f';
测试结果同1。
测试4:
将测试三的p[5]='f';修改为p[4]='e';
测试结果4:
(1)beforeGetBuffer:
CStrings.length=4
CStrings=abcd
(2)afterGetBufferandbeforeReleaseBuffer:
LPTSTRp=abcde屯屯?
p.length=10
CStrings=abcde屯屯?
CStrings.length=4
(3)afterReleaseBuffer:
LPTSTRp=abcde屯屯?
p.length=10
CStrings=abcde屯屯?
CStrings.length=10
Pressanykeytocontinue
很显然
(2)afterGetBufferandbeforeReleaseBuffer:
中CStrings.length=4结果有问题。
注意:
以上测试是在_MBCS环境下,如果换成_UNICODE则结果有可能不同。
LPTSTRCString:
:
GetBuffer(intnMinBufLength)
{
ASSERT(nMinBufLength>=0);
if(GetData()->nRefs>1||nMinBufLength>GetData()->nAllocLength)
{
#ifdef_DEBUG
//giveawarningincaselockedstringbecomesunlocked
if(GetData()!
=_afxDataNil&&GetData()->nRefs<0)
TRACE0("Warning:
GetBufferonlockedCStringcreatesunlockedCString!
\n");
#endif
//wehavetogrowthebuffer
CStringData*pOldData=GetData();
intnOldLen=GetData()->nDataLength;//AllocBufferwilltrompit
if(nMinBufLength nMinBufLength=nOldLen; AllocBuffer(nMinBufLength); memcpy(m_pchData,pOldData->data(),(nOldLen+1)*sizeof(TCHAR)); GetData()->nDataLength=nOldLen; CString: : Release(pOldData); } ASSERT(GetData()->nRefs<=1); //returnapointertothecharacterstorageforthisstring ASSERT(m_pchData! =NULL); returnm_pchData; } voidCString: : ReleaseBuffer(intnNewLength) { CopyBeforeWrite();//justincaseGetBufferwasnotcalled if(nNewLength==-1) nNewLength=lstrlen(m_pchData);//zeroterminated ASSERT(nNewLength<=GetData()->nAllocLength); GetData()->nDataLength=nNewLength; m_pchData[nNewLength]='\0'; } ============= 看了很多人写的程序,包括我自己写的一些代码,发现很大的一部分bug是关于MFC类中的CString的错误用法的.出现这种错误的原因主要是对CString的实现机制不是太了解。 CString是对于原来标准c中字符串类型的一种的包装。 因为,通过很长时间的编程,我们发现,很多程序的bug多和字符串有关,典型的有: 缓冲溢出、内存泄漏等。 而且这些bug都是致命的,会造成系统的瘫痪。 因此c++里就专门的做了一个类用来维护字符串指针。 标准c++里的字符串类是string,在microsoftMFC类库中使用的是CString类。 通过字符串类,可以大大的避免c中的关于字符串指针的那些问题。 这里我们简单的看看MicrosoftMFC中的CString是如何实现的。 当然,要看原理,直接把它的代码拿过来分析是最好的。 MFC里的关于CString的类的实现大部分在strcore.cpp中。 CString就是对一个用来存放字符串的缓冲区和对施加于这个字符串的操作封装。 也就是说,CString里需要有一个用来存放字符串的缓冲区,并且有一个指针指向该缓冲区,该指针就是LPTSTRm_pchData。 但是有些字符串操作会增建或减少字符串的长度,因此为了减少频繁的申请内存或者释放内存,CString会先申请一个大的内存块用来存放字符串。 这样,以后当字符串长度增长时,如果增加的总长度不超过预先申请的内存块的长度,就不用再申请内存。 当增加后的字符串长度超过预先申请的内存时,CString先释放原先的内存,然后再重新申请一个更大的内存块。 同样的,当字符串长度减少时,也不释放多出来的内存空间。 而是等到积累到一定程度时,才一次性将多余的内存释放。 还有,当使用一个CString对象a来初始化另一个CString对象b时,为了节省空间,新对象b并不分配空间,它所要做的只是将自己的指针指向对象a的那块内存空间,只有当需要修改对象a或者b中的字符串时,才会为新对象b申请内存空间,这叫做写入复制技术(CopyBeforeWrite)。 这样,b31.org仅仅通过一个指针就不能完整的描述这块内存的具体情况,需要更多的信息来描述。 首先,需要有一个变量来描述当前内存块的总的大小。 其次,需要一个变量来描述当前内存块已经使用的情况。 也就是当前字符串的长度 另外,还需要一个变量来描述该内存块被其他CString引用的情况。 有一个对象引用该内存块,就将该数值加一。 CString中专门定义了一个结构体来描述这些信息: structCStringData { longnRefs;//referencecount intnDataLength;//lengthofdata(includingterminator) intnAllocLength;//lengthofallocation //TCHARdata[nAllocLength] TCHAR*data()//TCHAR*tomanageddata {return(TCHAR*)(this+1);} }; 实际使用时,该结构体的所占用的内存块大小是不固定的,在CString内部的内存块头部,放置的是该结构体。 从该内存块头部开始的sizeof(CstringData)个BYTE后才是真正的用于存放字符串的内存空间。 这种结构的数据结构的申请方法是这样实现的: pData=(CStringData*)newBYTE[sizeof(CStringData)+(nLen+1)*sizeof(TCHAR)]; pData->nAllocLength=nLen; 其中nLen是用于说明需要一次性申请的内存空间的大小的。 从代码中可以很容易的看出,如果想申请一个256个TCHAR的内存块用于存放字符串,实际申请的大小是: sizeof(CStringData)个BYTE+(nLen+1)个TCHAR 其中前面sizeof(CstringData)个BYTE是用来存放CstringData信息的。 后面的nLen+1个TCHAR才是真正用来存放字符串的,多出来的一个用来存放’/0’。 CString中所有的operations的都是针对这个缓冲区的。 比如LPTSTRCString: : GetBuffer(intnMinBufLength),它的实现方法是: 首先通过CString: : GetData()取得CStringData对象的指针。 该指针是通过存放字符串的指针m_pchData先后偏移sizeof(CstringData),从而得到了CStringData的地址。 然后根据参数nMinBufLength给定的值重新实例化一个CStringData对象,使得新的对象里的字符串缓冲长度能够满足nMinBufLength。 然后在重新设置一下新的CstringData中的一些描述值。 C 最后将新CStringData对象里的字符串缓冲直接返回给调用者。 这些过程用C++代码描述就是: if(GetData()->nRefs>1||nMinBufLength>GetData()->nAllocLength) { //wehavetogrowthebuffer CStringData*pOldData=GetData(); intnOldLen=GetData()->nDataLength;//AllocBufferwilltrompit if(nMinBufLength nMinBufLength=nOldLen; AllocBuffer(nMinBufLength); memcpy(m_pchData,pOldData->data(),(nOldLen+1)*sizeof(TCHAR)); GetData()->nDataLength=nOldLen; CString: : Release(pOldData); } ASSERT(GetData()->nRefs<=1); //returnapointertothecharacterstorageforthisstring ASSERT(m_pchData! =NULL); returnm_pchData; 很多时候,我们经常的对大批量的字符串进行互相拷贝修改等,CString使用了CopyBeforeWrite技术。 使用这种方法,当利用一个CString对象a实例化另一个对象b的时候,其实两个对象的数值是完全相同的,但是如果简单的给两个对象都申请内存的话,对于只有几个、几十个字节的字符串还没有什么,如果是一个几K甚至几M的数据量来说,是一个很大的浪费。 因此CString在这个时候只是简单的将新对象b的字符串地址m_pchData直接指向另一个对象a的字符串地址m_pchData。 所做的额外工作是将对象a的内存应用CStringData: : nRefs加一。 CString: : CString(constCString&stringSrc) { m_pchData=stringSrc.m_pchData; InterlockedIncrement(&GetData()->nRefs); } 这样当修改对象a或对象b的字符串内容时,首先检查CStringData: : nRefs的值,如果大于一(等于一,说明只有自己一个应用该内存空间),说明该对象引用了别的对象内存或者自己的内存被别人应用,该对象首先将该应用值减一,然后将该内存交给其他的对象管理,自己重新申请一块内存,并将原来内存的内容拷贝过来。 其实现的简单代码是: voidCString: : CopyBeforeWrite() { if(GetData()->nRefs>1) { CStringData*pData=GetData(); Release(); AllocBuffer(pData->nDataLength); memcpy(m_pchData,pData->data(), (pData->nDataLength+1)*sizeof(TCHAR)); } } 其中Release就是用来判断该内存的被引用情况的。 voidCString: : Release() { if(GetData()! =_afxDataNil) { if(InterlockedDecrement(&GetData()->nRefs)<=0) FreeData(GetData()); } } 当多个对象共享同一块内存时,这块内存就属于多个对象,而不在属于原来的申请这块内存的那个对象了。 但是,每个对象在其生命结束时,都首先将这块内存的引用减一,然后再判断这个引用值,如果小于等于零时,就将其释放,否则,将之交给另外的正在引用这块内存的对象控制。 CString使用这种数据结构,对于大数据量的字符串操作,可以节省很多频繁申请释放内存的时间,有助于提升系统性能。 通过上面的分析,我们已经对CString的内部机制已经有了一个大致的了解了。 总的说来MFC中的CString是比较成功的。 但是,由于数据结构比较复杂(使用CStringData),所以在使用的时候就出现了很多的问题,最典型的一个就是用来描述内存块属性的属性值和实际的值不一致。 出现这个问题的原因就是CString为了方便某些应用,提供了一些operations,这些operation可以直接返回内存块中的字符串的地址值,用户可以通过对这个地址值指向的地址进行修改,但是,修改后又没有调用相应的operations1使CStringData中的值来保持一致。 比如,用户可以首先通过operations得到字符串地址,然后将一些新的字符增加到这个字符串中,使得字符串的长度增加,但是,由于是直接通过指针修改的,所以描述该字符串长度的CStringData中的nDataLength却还是原来的长度,因此当通过GetLength获取字符串长度时,返回的必然是不正确的。 存在这些问题的operations下面一一介绍。 1.GetBuffer 很多错误用法中最典型的一个就是CString: : GetBuffer()了.查了MSDN,里面对这个operation的描述是: ReturnsapointertotheinternalcharacterbufferfortheCStringobject.ThereturnedLPTSTRisnotconstandthusallowsdirectmodificationofCStringcontents。 这段很清楚的说明,对于这个operation返回的字符串指针,我们可以直接修改其中的值: CStringstr1("Thisisthestring1");――――――――――――――――1 intnOldLen=str1.GetLength();―――――――――――――――――2 char*pstr1=str1.GetBuffer(nOldLen);――――――――――――――3 strcpy(pstr1,"modified");――――――――――――――――――――4 intnNewLen=str1.GetLength();―――――――――――――――――5 通过设置断点,我们来运行并跟踪这段代码可以看出,当运行到三处时,str1的值是”Thisisthe
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- CString GetBuffer 用法 本质 常见问题 解决方法
链接地址:https://www.bdocx.com/doc/28747442.html