在使用CString的GetBufferSetLength方法时,遇到了一个问题,代码如下:

CString path;

::GetCurrentDirectory(MAX_PATH, path.GetBufferSetLength(MAX_PATH));

path.Append(TEXT("\\SubDir"));

wprintf(TEXT("%s"), path);

这段代码的输出始终是E:\Projects\Tests\Win32Console,也就是GetCurrentDirectory函数返回的结果,而通过Append方法添加的字符串不见踪影,换用+=操作符也是一样。

如果将GetBufferSetLength方法换成GetBuffer方法:

CString path;

::GetCurrentDirectory(MAX_PATH, path.GetBuffer (MAX_PATH));

path.Append(TEXT("\\SubDir"));

wprintf(TEXT("%s"), path);

那么输出变成了\SubDir,GetCurrentDirectory函数返回的结果倒不见了。

查阅MSDN,发现原来调用了GetBuffer或者GetBufferSetLength方法并且修改了缓冲区里的内容之后,如果要调用其它的方法,就要先调用ReleaseBuffer或者ReleaseBufferSetLength方法。那么就在上面的代码中添加对ReleaseBuffer的调用:

CString path;

::GetCurrentDirectory(MAX_PATH, path.GetBufferSetLength(MAX_PATH));

path.ReleaseBuffer();

path.Append(TEXT("\\SubDir"));

wprintf(TEXT("%s"), path);

CString path;

::GetCurrentDirectory(MAX_PATH, path.GetBuffer (MAX_PATH));

path.ReleaseBuffer();

path.Append(TEXT("\\SubDir"));

wprintf(TEXT("%s"), path);

修改后的输出为E:\Projects\Tests\Win32Console\SubDir,Append方法终于见效了。

那么,GetBuffer和GetBufferSetLength有什么不同?ReleaseBuffer方法又做了什么事情呢?不妨作以下的猜想:

1.在CString对象中,字符串的长度信息保存在一个成员变量中(假设为m_length),需要获取字符串长度的时候直接读取这个值。

2.GetBuffer分配了新的内存,但不改变m_length的值;GetBufferSetLength 也分配新的内存,同时将m_length的值设置为参数中指定的值。

3.GetCurrentDirectory方法将工作目录的路径直接写入path对象的内存,m_length的值没有改变。这时,使用GetBuffer的版本m_length值为0,使用GetBufferSetLength的版本m_length值为MAX_PATH。

4.Append方法根据m_length的值将参数中的字符串复制到path对象的内存中。GetBuffer版本中,由于m_length值为0,所以“\SubDir”被复制到字符串的开头,覆盖了原来的内容;GetBufferSetLength版本中,m_length的值为MAX_PATH,“\SubDir”被复制到从第MAX_PATH个字符开始的位置,所以在输出中看不到。

5.不带参数的ReleaseBuffer方法查找字符串中的’\0’字符,确定字符串的长度,并修改m_length的值,此后m_length的值是正确的,因此Append方法可以正常调用。

为了证明上面的猜想是正确的,我对上面的两段代码进行了跟踪调试。为了突出重点,下面的代码都进行了裁剪,省略了不是很重要的部分。首先是GetBuffer方法的实现:

PXSTR GetBuffer(int nMinBufferLength) {

return( PrepareWrite( nMinBufferLength ) );

}

看到GetBuffer方法调用了PrepareWrite方法,再看PrepareWrite方法的实现:

PXSTR PrepareWrite(int nLength) {

CStringData* pOldData = GetData();

int nShared = 1-pOldData->nRefs;

int nTooShort = pOldData->nAllocLength-nLength;

if( (nShared|nTooShort) < 0 ) {

PrepareWrite2( nLength );

}

return( m_pszData );

}

在进行了检查之后调用了PrepareWrite2方法,继续进入该方法:

void PrepareWrite2(int nLength) {

CStringData* pOldData = GetData();

if( pOldData->nDataLength > nLength ) {

nLength = pOldData->nDataLength;

}

if( pOldData->IsShared() ) {

Fork( nLength );

}

else if( pOldData->nAllocLength < nLength )   {

// 其它代码

}

}

PrepareWrite2 方法又调用了Fork方法,再深入:

void Fork(int nLength)   {

CStringData* pOldData = GetData();

int nOldLength = pOldData->nDataLength;

//分配新的缓冲区

CStringData* pNewData = pOldData->pStringMgr->Clone()->Allocate( nLength, sizeof( XCHAR ) );

//复制字符

int nCharsToCopy = ((nOldLength < nLength) ? nOldLength : nLength)+1;

CopyChars( PXSTR( pNewData->data() ), nCharsToCopy,

PCXSTR( pOldData->data() ), nCharsToCopy );

//设置字符串长度

pNewData->nDataLength = nOldLength;

//释放原来的缓冲区

pOldData->Release();

//保存新的缓冲区指针

Attach( pNewData );

}

可以看到在Fork方法中分配了新的缓冲区,并将原来缓冲区的内容复制到新缓冲区中,以此实现了扩大缓冲区。通过观察Fork的代码,可以证实猜想的第一点是正确的,CString对象确实是通过一个成员变量保存字符串长度。要注意的是Fork方法没有改变字符串的长度。总结GetBuffer方法的调用路径:GetBuffer->PreWrite->PreWrite2->Fork。

接下来再看GetBufferSetLength方法的实现:

PXSTR GetBufferSetLength(int nLength) {

PXSTR pszBuffer = GetBuffer( nLength );

SetLength( nLength );

return( pszBuffer );

}

该方法调用了GetBuffer,然后调用SetLength方法。进入该方法:

void SetLength(int nLength) {

GetData()->nDataLength = nLength;

m_pszData[nLength] = 0;

}

除去一些检查语句,SetLength方法实质上只有两条语句,第一条语句设置字符串的长度,第二条语句在字符串的末尾添加字符串结束标记,也就是’\0’字符。

现在可以看出GetBuffer和GetBufferSetLength的区别了:除了改变缓冲区的大小之外,GetBuffer不改变字符串的长度,而GetBuffefrSetLength会将字符串的长度设置为缓冲区的大小。

接下来再看看Append方法的实现:

void Append(PCXSTR pszSrc) {

Append( pszSrc, StringLength( pszSrc ) );

}

void Append(PCXSTR pszSrc, int nLength) {

//获取原字符串的长度

UINT_PTR nOffset = pszSrc-GetString();

UINT nOldLength = GetLength();

//获取待添加字符串的长度

nLength = StringLengthN(pszSrc, nLength);

//计算新字符串的长度

int nNewLength = nOldLength+nLength;

//分配新的缓冲区

PXSTR pszBuffer = GetBuffer( nNewLength );

//复制字符串

CopyChars( pszBuffer+nOldLength, nLength, pszSrc, nLength );

//设置字符串长度

ReleaseBufferSetLength( nNewLength );

}

可以看到Append方法确实是通过字符串的长度来操作的。

下面再来看看ReleaseBuffer和ReleaseBufferSetLength的实现:

void ReleaseBuffer(int nNewLength = -1) {

if( nNewLength == -1 ) {

int nAlloc = GetData()->nAllocLength;

nNewLength = StringLengthN( m_pszData, nAlloc);

}

SetLength( nNewLength );

}

void ReleaseBufferSetLength(int nNewLength) {

SetLength( nNewLength );

}

如果参数值不为-1的话,ReleaseBuffer和ReleaseBufferSetLength一样只是简单地设置字符串的长度。如果对ReleaseBuffer传入-1或者不传参数的话,它将调用StringLengthN获取字符串的长度,然后通过SetLength设置字符串长度。

现在可以证实猜想是正确的了。通过分析CString的源代码,我对其工作方式有了更深的了解,于是写下本文,希望对大家有所帮助。

FROM: http://www.cnblogs.com/zplutor/archive/2010/10/08/1846181.html

[MFC]对CString::GetBufferSetLength方法的探究,需要ReleaseBuffer,GetLength才正确!相关推荐

  1. MFC中CString.format用法

    MFC中CString.Format的详细用法 收藏  在MFC程序中,使用CString来处理字符串是一个很不错的选择. CString既可以处理Unicode标准的字符串,也可以处理ANSI标准的 ...

  2. VS中MFC访问MySQL的方法

    MFC连接MySQL的方法:首先建立一个MFC项目. 下面进行设置: (1)项目->属性->配置属性->C/C++->附加包含目录:在附加包含目录中添加C:\Program F ...

  3. MFC中CString转换成char数组的问题

    由于结构体中用到联合体(联合体需要确定分配内存分配大小)或其它因素,需要用char数组来保存字符串,但是在MFC中一般都是用CString来存放字条串.关于它们之间的转换,在VS2008中有时会出现异 ...

  4. MFC中CString.Format的用法

    http://www.cnblogs.com/kongtiao/archive/2012/06/13/2548033.html 在MFC程序中,使用CString来处理字符串是一个很不错的选择.CSt ...

  5. VS2013在MFC中使用ADO方法操作Access2013数据库

    1. 首先创建一个基于对话框的MFC应用程序ADO_ACCESS.sln.然后在stdafx.h头文件末尾添加(网上有说在某个#include后面的添加的,也有说在合适位置添加的,我这里直接添加在最末 ...

  6. 【MFC】MFC基础类——CString(使用心得)

    文章目录 01.CString类介绍 02.常见函数表 03.CString类成员函数示例 3.1.CString(构造函数) 3.2.GetLength 3.3.IsEmpty 3.4.Empty ...

  7. MFC中CString.Format的详细用法和进制转换

    在MFC程序中,使用CString来处理字符串是一个很不错的选择.CString既可以处理Unicode标准的字符串,也可以处理ANSI标准的字符串.CString的Format方法给我们进行字符串的 ...

  8. 移动端和PC端弹出遮罩层后,页面禁止滚动的解决方法及探究

    移动端和PC端弹出遮罩层后,页面禁止滚动的解决方法及探究 参考文章: (1)移动端和PC端弹出遮罩层后,页面禁止滚动的解决方法及探究 (2)https://www.cnblogs.com/ranyon ...

  9. MFC中CString,int,string,char * ,char[] 之间互转

    <1> (1)string 转 CString  CString.format("%s", string.c_str());   (2)char 转 CStri ...

最新文章

  1. python读excel字体颜色_无法使用python xlsxwri更改excel中的字体颜色
  2. java swing面试题_Java面试题之AWT、Swing
  3. 软件测试——StringFunction测试
  4. asp.net学习之再论sqlDataSource
  5. ext js如何动态更改xtype_K8S ConfigMap 用于动态应用程序的实践
  6. qt添加资源文件后编译失败,提示Qt:Error:No rule to make target ’ … /…/??.png’,needed by ‘debug/qrc_qrc.cpp’ stop
  7. 百度关键词点击ios_百度推广关键词点击价格高,如何处理?
  8. 利用人工智能“解锁”世界音乐
  9. 深思 JAVA IT 求职
  10. 从bagging到dropout(deep learning笔记Ian)
  11. 元素“UpdateProgress”不是已知元素。原因可能是网站中存在编译错误
  12. linux缺省的shell,Linux操作系统缺省的shell
  13. GitHub 中国区前 100 名到底是什么样的人?
  14. 我的ElasticSearch认证工程师之路
  15. 国庆第三天的一些杂感
  16. 销售额与营收“双增长”背后:金科“稳”字诀的勇气、底气与机遇
  17. 【Lesson 13】万能和弦和弦走向
  18. 三天肝完设计模式的面试题,面试再不怕设计模式的问题了
  19. d如何及为什么探测器
  20. [Go实战]写一个简单的概率算法(抽奖)

热门文章

  1. Django项目实战——用户投票系统(三)
  2. 《羊了个羊》游戏C语言版代码
  3. 计算机office论文,计算机Office办公软件为校园日常管理带来的便利
  4. python的循环语句
  5. 神仙级Python入门教程(非常详细),Python之父鼎力推荐,已有49695人撸过!
  6. 漏洞分析——二进制漏洞
  7. 【1343】平方和与立方和
  8. Swoole入门到实战打造高性能赛事直播平台(完整版)
  9. sql_safe_updates
  10. 前端学习笔记(五):VUE基础学习笔记