先来看看我下面的这段代码 使用上有什么问题:
m_WeatherZone = "北京";
m_szWeatherInfo = "多云转晴";
m_szWeatherglass = "23~35度";
......
......
CString WeatherZone = "";
int strLen = this->m_WeatherZone.GetLength();
memcpy(WeatherZone.GetBuffer(strLen),this->m_WeatherZone.GetBuffer(strLen),strLen);
CString WeatherInfo = "";
strLen = this->m_szWeatherInfo.GetLength();
memcpy(WeatherInfo.GetBuffer(strLen),this->m_szWeatherInfo.GetBuffer(strLen),strLen);
CString Weatherglass= "";
strLen = this->m_szWeatherglass.GetLength();
memcpy(Weatherglass.GetBuffer(strLen),this->m_szWeatherglass.GetBuffer(strLen),strLen);
......
......
CString weather = WeatherZone +" " +WeatherInfo +" "+Weatherglass;
为啥得带的这个weather 是" "呢?而不是 "北京多云转晴 23~35度"呢?
眼尖的你一定发现问题了吧,其实是我在写代码的时候太着急了 忘记了ReleaseBuffer().....
为什么没有ReleaseBuffer就会出现这样的问题呢?我都已经进行了memcpy了呀?
下面就来详细探讨下GetBuffer 和 ReleaseBuffer 的原理吧~~~~
GetBuffer:
LPTSTR CString::GetBuffer(int nMinBufLength)
{
ASSERT(nMinBufLength >= 0);
if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
{
// we have to grow the buffer
CStringData* pOldData = GetData();
int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it
if (nMinBufLength < nOldLen)
{
nMinBufLength = nOldLen;
}
AllocBuffer(nMinBufLength);
memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(TCHAR));
GetData()->nDataLength = nOldLen;
CString::Release(pOldData);
}
ASSERT(GetData()->nRefs <= 1);
// return a pointer to the character storage for this string
ASSERT(m_pchData != NULL);
return m_pchData;
}
参数:
指定:表示要重新申请的缓冲大小,重新申请的字符串的长度如果小于等于先前的字符串长度, 则不会重新分配内存使用原来的内存;如果大于当前字符串长度,则要重新分配缓冲,然后把旧缓冲给Release掉。
不指定:默认为0,表示缓冲大小为当前值,而且不会申请新的缓冲。
返回值:
指定并大于当前长度,返回的是新申请的缓冲的指针;
不指定或指定但小于当前长度,返回的是当前缓冲的指针。
可以基于GetBuffer返回的指针,对缓冲进行操作,从而修改CSting的内容。
由上可知,在确定对CString对象当前缓存的操作不会越界的前提下,不必指定GetBuffer的参数,之后对CString的操作都是在当前缓存上进行的;而如果可能造成越界,则必须指定一个值,此时,CString会在内部重新申请一块缓存,作为新的当前缓存。
ReleaseBuffer:
void CString::ReleaseBuffer(int nNewLength)
{
CopyBeforeWrite(); // just in case GetBuffer was not called
if (nNewLength == -1)
nNewLength = lstrlen(m_pchData); // zero terminated
ASSERT(nNewLength <= GetData()->nAllocLength);
GetData()->nDataLength = nNewLength;
m_pchData[nNewLength] = '\0';
}
ReleaseBuffer的作用只是负责消除多余的缓存内容,比如通过GetBuffer(100)申请了100个字符的空间,但后续只使用了其中的50个,则此时调用ReleaseBuffer()就会将未使用的50个空间释放掉。它并不是说释放掉通过GetBuffer分配的全部缓存空间(实际上,CString目前占用的空间在CString对象释放的时候才由CString对象自动负责释放)。
同时 ReleaseBuffer还有一个很重要的作用,就是“ GetData()->nDataLength = nNewLength;”
CString对象在内存中用一个计数器来维持可用缓冲区的大小
ReleaseBuffer的作用就是更新字符串的长度。 CString内,GetLength获取字符串长度并不是动态计算的,而是在赋值操作后计算并保存在一个int变量内的,当通过GetBuffer直接修改CString时,那个int变量并不可能自动更新,于是便有了ReleaseBuffer.
SO..
1)不管是用哪种GetBuffer获得的缓存,在CString对象销毁的时候,这些缓存都会自动销毁。
2)在GetBuffer和ReleaseBuffer之间,不要调用CString的成员函数。此时得到的结果,将是不准确、不可预知的,甚至会发生访问错误。
但使用时需要注意以下问题:
如果要保存的内容不是以0结尾, 比如是读取文件数据, 则GetBuffer参数如果大于文件长度时, ReleaseBuffer参数一定要为文件长度(如果GetBuffer参数为文件长度的话不存在问题, ReleaseBuffer参数可以为默认-1)!
通过以上学习我明白啦~ 没有经过releaseBuffer的时候虽然我已经分配了内存拷贝了值,但是 WeatherZone 、WeatherInfo、Weatherglass 的长度都还是老样子 是0呢,所以做CString的+运算 得到的值就是“” 。
===============================以下为另一篇================================================
CString s="hello";
LPTSTR ps=s.GetBuffer();
strcpy(ps,"hi");
s.ReleaseBuffer();
此时调用s.GetLength()获取的值是2,正确无误。但如果注释掉s.ReleaseBuffer()这一行,s.GetLength()获取的值则是5,哈哈,错了。
怎么会这样呢,我们来看看MFC中ReleaseBuffer的代码:
void ReleaseBuffer( int nNewLength = -1 )
{
if( nNewLength == -1 )
{
nNewLength = StringLength( m_pszData );
}
SetLength( nNewLength );
}
很明显ReleaseBuffer只有一个作用,就是更新字符串的长度。CString内,GetLength获取字符串长度并不是动态计算的,而是在赋值操作后计算并保存在一个int变量内的,当通过GetBuffer直接修改CString时,那个int变量并不可能自动更新,于是便有了ReleaseBuffer。
其实,计算长度还能用strlen(),这个就算不ReleaseBuffer也不会出错,但如果不ReleaseBuffer,在+=这种赋值时字符串很可能会跟想要得到的不同。