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

2023-11-02

在使用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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 聊天软件压力测试,关于昨晚聊天压力测试,后来出现卡顿的原因及解决方案

    昨天晚上的压力测试 在300人以前是毫无压力的 后来为何就卡顿了呢 主要是因为大家发送的内容太大了 有的竟然达到了近百K一条的信息 也就是说单条信息长度达到了十万字 这并不是说不能发送超长文本 而是因为受带宽限制 我们的带宽只有3M 但本质
  • 使用Mybatis完成CRUD操作,并从源码分析执行过程

    目录 一 方式1 使用代理DAO 插入操作 获取插入数据的id 更新操作 删除操作 查询单个 模糊查询 聚合查询 二 查询条件封装 三 查询结果封装 四 主配置文件中的两个技巧 propertie标签的使用及细节 typeAliases 类
  • Mysql进阶(上) -- 存储引擎,索引

    Seasons in the Sun Westlife 单曲 网易云音乐 索引下部分 gt Mysql进阶 中 索引 千帐灯无此声的博客 CSDN博客 目录 查看左栏 目录 存储引擎 Mysql体系结构 简介 InnoDB介绍 MyISAM
  • linux多线程编程详解教程,Linux多线程编程快速入门

    本文主要对Linux下的多线程进行一个入门的介绍 虽然是入门 但是十分详细 希望大家通过本文所述 对Linux多线程编程的概念有一定的了解 具体如下 1 线程基本知识 进程是资源管理的基本单元 而线程是系统调度的基本单元 线程是操作系统能够
  • openGauss学习笔记-01 什么是openGauss

    文章目录 openGauss学习笔记 01 什么是openGauss 1 1 openGauss是一个数据库管理系统 1 2 openGauss数据库是关系型的 1 3 openGauss软件是开源的 1 4 openGauss数据库具有高

随机推荐

  • 一个牛人提供的GIS源码(很好)

    注 下面文字非本人所写 他提供的东西太好了 所以放在这里备忘 浪人 努力 唐丰 Rover Tang2006 08 05 最后的礼物 校园多媒体系统和校园WEBGIS系统 为什么说是最后的礼物 大概是因为我突然想这个blog不更新了 为什么
  • Java应用程序远程调试Remote Debug

    文章目录 Java应用程序远程调试Remote Debug 本地开发工具IDEA的设置 远程服务器的Java应用程序调整JVM启动参数 启动本地IDEA的应用程序 本地IDEA设置断点及调试 Java应用程序远程调试Remote Debug
  • 解决TypeError: object of type 'numpy.float64' has no len()

    使用jupyter notebook过程中遇到这个问题 其实把所有代码从上到下重新执行一遍错误就消失了 也不知道为什么前面执行结果会丢失 更郁闷是这种文不对题的错误信息很是头疼
  • 双系统启动无系统选择界面,直接进入Ubuntu

    安装了Ubuntu和kali双系统后 发现电脑启动的时候直接进入Ubuntu 没有系统选择界面 首先打开终端 Ctrl Alt t 输入 sudo update grub 回车 输入密码即可
  • edge浏览器如何设置始终在新标签页中打开?

    方法1 在IE浏览器里设置 方法2 用鼠标滚轮点击打开 但是这种方法 只能在旁边的新窗口打开 你还需要自己手动切换页面 因为页面不会自动显示新窗口的内容 所以建议使用第一种方法
  • Qt中使用三角函数

    哈哈 今天发现 Qt可以很方便的使用三角函数 只需要 include math h 然后就可以 double a 0 0 double b 0 0 b sin a So easy 决心用Qt进行图像处理了 QImage类很好很强大 转载于
  • Xshell 5 注册码

    留着用 Xftp 5 注册码 101210 450789 147200 Xshell 5 注册码 690313 111999 999313 Xmanager 5 注册码 101210 450789 147200 Xshell4 0
  • linux find 输出文件目录名称到txt文件(yolo图片名称)

    利用find命令得到文件夹内文件名称 find 文件夹路径 name jpg gt txt文件名 输出结果
  • python入门基础

    作者介绍 作者 小刘在C站 每天分享课堂笔记 一起努力 共赴美好人生 夕阳下 在最美的绽放 目录 一 python是什么 二 为什么使用python 1 软件质量 2 提高开发者的效率 3 程序的可移植性 4 标准库的支持 5 组件集成 6
  • Anaconda3如何安装keras

    当下机器学习很火 机器学习编程最流行的就是python语言 yangqiang200608打算自学机器学习 于是与python有了缘 对于初学者来说 配置环境是最让人头痛的事情 一周前参照网上的资料折腾一番 终于安装上了python3 为了
  • 1696:逆波兰表达式——递归(atof)

    描述 逆波兰表达式是一种把运算符前置的算术表达式 例如普通的表达式2 3的逆波兰表示法为 2 3 逆波兰表达式的优点是运算符之间不必有优先级关系 也不必用括号改变运算次序 例如 2 3 4的逆波兰表示法为 2 3 4 本题求解逆波兰表达式的
  • SLAM 轨迹评估工具——EVO安装、使用介绍

    一 概述 evo是一款用于视觉里程计和SLAM问题的轨迹评估工具 核心功能是能够绘制相机的轨迹 或评估轨迹与真值之间的误差 支持多种数据集的轨迹格式 TUM KITTI EuRoC MAV ROS的bag 同时支持这些数据格式之间的相互转换
  • Unsupported character encoding ‘UTF‐8‘

    1 无法识别utf 8 2 在保证链接与mysql链接驱动包都正确时 查看数据库编码 右键编辑数据库即可查看 发现是由于数据库字符集不为utf 8 所以把链接信息的指定utf 8删除即可
  • Android工程师学习路线

    Android工程师学习路线 1 第一行代码 2 Android编程权威指南 3 Android群英传 神兵利器 4 Android开发艺术探索 5 Android项目开发实战 6 Android源码设计模式解析与实战 7 App研发路 相
  • Python 数据分析与数据可视化(一)Python 开发环境搭建与编码规范

    文章目录 1 Python 开发环境搭建与编码规范 1 1 Python 开发环境搭建与使用 1 1 1 Python 语言的特点 1 1 2 Python 开发环境 1 1 3 安装扩展库 1 2 Python 编码规范 1 2 1 缩进
  • Maya模型导入到Unity3d 的秘诀

    看过不少文章谈论这个问题 各种复杂操作 简直想让你 Abandon 到最后还不是再Unity中一个个加材质 解决方案 大家应该都留意到了 Maya自从2016版本开始 有一个功能叫 发送到Unity 发送到UE 第一步 再Maya 中正确打
  • Python修饰符 (一)—— 函数修饰符 “@”

    今天被问到Python函数修饰符 顺手写写 Python函数修饰符 与其说是修饰函数倒不如说是引用 调用它修饰的函数 举个栗子 下面的一段代码 里面两个函数 没有被调用 也会有输出结果 def test f print before f p
  • 分类统计的controller和service

    SpringMVC框架下的 部分代码 Controller控制器 Resource ReviewTitleService reviewTitleService 调用ReviewTitleService 类时 先定义一个对象 Gson gso
  • 构筑无所不在计算的基础 —TRON技术体系结构

    本文转载至 http www zlgmcu com philips ARMStud T Kernel TRON T Kernel pdf 东京大学教授 日本泛在网络计算环境研究所所长 坂村健 日本泛在网络计算环境研究所中国室室长 梁 青 一
  • [MFC]对CString::GetBufferSetLength方法的探究,需要ReleaseBuffer,GetLength才正确!

    在使用CString的GetBufferSetLength方法时 遇到了一个问题 代码如下 CString path GetCurrentDirectory MAX PATH path GetBufferSetLength MAX PATH