Getbuffer和ReleaseBuffer的作用,查了一些资料,还是搞不明白,麻烦大家了

himetric 2014-12-03 03:40:36
Getbuffer和ReleaseBuffer的作用,查了一些资料,还是搞不明白,麻烦大家了
...全文
458 15 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
用户 昵称 2014-12-04
  • 打赏
  • 举报
回复
参数都填0就行了。
「已注销」 2014-12-04
  • 打赏
  • 举报
回复
空字符串肯定要分配內存了,空字符串和 NULL 不是一個意思。 如同 C 語言裡的: const char *str = NULL; 和: const char *str = ""; 的區別。只不過 NULL 這種情況不可能發生在 CString 裡。 第一種根本不指向任何空間,第二種只是指向一個 1 字節的 0 值而已。
himetric 2014-12-04
  • 打赏
  • 举报
回复
引用 10 楼 SXJIAKE 的回复:
1 . 一个空字符串也要占用 1 个 TCHAR 的空间来存放尾 0。其实我说得有些不对,不是并未指向任何字符串,应该说指向一个空字符串,对于 CString 而言他就是指向一个 TCHAR 大小且值为 0 的区域而已。 2. 由于一个初始化的 CString 转换为 LPTSTR 等这样的指针类型,其有效的空间仅有一个 TCHAR 大小,如果你去调用函数,并且传递了长度(通常也就是 2 个 TCHAR 大小),那么肯定调用不成功。如果你给那些不需要传递长度的函数,比如 SHGetFolderPath,MSDN 只是说缓冲区至少要有 MAX_PATH,但并未检查长度,这种情况下肯定会越界。 3. 对于 CString 而言,他的字符串空间本来就是 new 出来的,只不过大小仅足够储存字符串内容和尾 0。即使你对其进行操作变更了长度,他都会 new 出来新的大小(不仅仅是 GetBuffer),来存放新字符串,删除掉原来的内存。GetBuffer 的作用只不过是你自己指定一个大小,而不是有 CString 替你计算字符串所需大小。ReleaseBuffer 只是重新的按照字符串长度计算所需大小(new 新的内存,拷贝字符串并删除原有内容)。
非常感谢,但是,如你所说“1 .不是并未指向任何字符串,应该说指向一个空字符串,对于 CString 而言他就是指向一个 TCHAR 大小且值为 0 的区域而已。”,这个空字符串在内存中也应该有具体分配到的空间吧? 又麻烦您了,真的是不好意思了。
信阳毛尖 2014-12-04
  • 打赏
  • 举报
回复
引用 7 楼 himetric 的回复:
[quote=引用 4 楼 SXJIAKE 的回复:] CString 类内部有一个成员变量也就是指针,他指向一个动态开辟的空间来存放字符串,并且不管你是初始化还是调用其成员函数变动其内容,该类都将按照字符串所需空间重新分配内存,将原来的内容拷贝过来,将原来的内存释放掉。但是对于一个初始化的 CString 来说,其并未指向任何字符串,其指针指向的内存空间长如为 1 个 TCHAR 的大小。如果将 CString 直接传递给 C 风格的函数作为字符指针(字符串)参数和数组元素个数参数时,直接将其转换为 LPTSTR(TCHAR *)类型传递给函数的参数,可能访问越界。通过 GetBuffer 则先预分配一定大小的空间,待调用完毕后,ReleaseBuffer 则会重新按照 CString 的方式,按照字符串的内容所需空间分配内存拷贝存储内容。按我的理解,ReleaseBuffer 其实也可以不调用,不会有太大影响。
如您所说:对于一个初始化的 CString 来说,其并未指向任何字符串,其指针指向的内存空间长如为 1 个 TCHAR 的大小。 这话显然有些矛盾,先是说“其并未指向任何字符串”,而后又说“其指针指向的内存空间长如为 1 个 TCHAR 的大小”,这后面它的指针不是有所指吗,指针指向的内存不管有没有赋过值,都是有内容的呀,还是再就是指针指向存放字符内容的内存还是指向存放数值型内容的内存,都是可以的呀,这儿怎么强调是其指针指向的内存空间长如为 1 个 TCHAR 的大小? 谢谢,期待您的回复。[/quote] 这个具体细节你得自己去研究CString的源码,我这里有个说法版本: 对于任何一个默认构造初始化的CString来说(譬如CString str;),它的字符串指针都指向一个全局的地址,也就是说数据区指针不为空,并且指针指向的内容为空 我们来稍微仿真一下吧: 1、CString 头部信息

struct CStringData
{
	long lRefs;			// 引用记数
	long lDataLength; 	// 字符使用长度
	long lMallocLength;	 // 分配长度
	// 数据区,存放字符串的地方
	TCHAR* data() 
	{ 
		return (TCHAR*)(this+1); 
	} 
};
2、CString类

class MyCString
{
public:
	MyCString(/*LPCTSTR lpStr=NULL*/);
	~MyCString(void);
        void Initialize();
private:
	LPTSTR	m_lpszData;	//指向CStringData的数据区
};
3、全局数据区:

long g_InitData[] = { -1, 0, 0, 0 };
CStringData* g_Data = (CStringData*)g_InitData; //全局CStringData指针,所有初始化为空的MyCString对象的CStringData指针都指向这里
LPCTSTR g_pCStr = (LPCTSTR)(((BYTE*)g_InitData)+sizeof(CStringData));	//相当于LPCTSTR g_pCStr = (LPCTSTR)g_Data->data(),值为空

//生成一个空的CString
const MyCString& GetEmptyString()  
{ 
	return *(MyCString*)&g_pCStr; 
}
4、默认构造函数:

MyCString::MyCString(void)
{
	Initialize();
}

void MyCString::Initialize()
{
	m_lpszData = g_pCStr;
}
然而现实中我看过很多人的代码都这样初始化一个空的CString: CSting str = _T(""); 实在是令人汗颜!
「已注销」 2014-12-04
  • 打赏
  • 举报
回复
1 . 一个空字符串也要占用 1 个 TCHAR 的空间来存放尾 0。其实我说得有些不对,不是并未指向任何字符串,应该说指向一个空字符串,对于 CString 而言他就是指向一个 TCHAR 大小且值为 0 的区域而已。 2. 由于一个初始化的 CString 转换为 LPTSTR 等这样的指针类型,其有效的空间仅有一个 TCHAR 大小,如果你去调用函数,并且传递了长度(通常也就是 2 个 TCHAR 大小),那么肯定调用不成功。如果你给那些不需要传递长度的函数,比如 SHGetFolderPath,MSDN 只是说缓冲区至少要有 MAX_PATH,但并未检查长度,这种情况下肯定会越界。 3. 对于 CString 而言,他的字符串空间本来就是 new 出来的,只不过大小仅足够储存字符串内容和尾 0。即使你对其进行操作变更了长度,他都会 new 出来新的大小(不仅仅是 GetBuffer),来存放新字符串,删除掉原来的内存。GetBuffer 的作用只不过是你自己指定一个大小,而不是有 CString 替你计算字符串所需大小。ReleaseBuffer 只是重新的按照字符串长度计算所需大小(new 新的内存,拷贝字符串并删除原有内容)。
mlqxj35674 2014-12-04
  • 打赏
  • 举报
回复
总之一句话,GetBuffer如同new一样,把原CString的数据做了一个副本,因为是new出来的,因此要delete,这里是用ReleaseBuffer来做这项工作的
himetric 2014-12-04
  • 打赏
  • 举报
回复
引用 4 楼 SXJIAKE 的回复:
CString 类内部有一个成员变量也就是指针,他指向一个动态开辟的空间来存放字符串,并且不管你是初始化还是调用其成员函数变动其内容,该类都将按照字符串所需空间重新分配内存,将原来的内容拷贝过来,将原来的内存释放掉。但是对于一个初始化的 CString 来说,其并未指向任何字符串,其指针指向的内存空间长如为 1 个 TCHAR 的大小。如果将 CString 直接传递给 C 风格的函数作为字符指针(字符串)参数和数组元素个数参数时,直接将其转换为 LPTSTR(TCHAR *)类型传递给函数的参数,可能访问越界。通过 GetBuffer 则先预分配一定大小的空间,待调用完毕后,ReleaseBuffer 则会重新按照 CString 的方式,按照字符串的内容所需空间分配内存拷贝存储内容。按我的理解,ReleaseBuffer 其实也可以不调用,不会有太大影响。
还是一问是:如果将 CString 直接传递给 C 风格的函数作为字符指针(字符串)参数和数组元素个数参数时,直接将其转换为 LPTSTR(TCHAR *)类型传递给函数的参数,可能访问越界。 可否讲的详细些,怎么会产生越界呢,谢谢。
himetric 2014-12-04
  • 打赏
  • 举报
回复
引用 4 楼 SXJIAKE 的回复:
CString 类内部有一个成员变量也就是指针,他指向一个动态开辟的空间来存放字符串,并且不管你是初始化还是调用其成员函数变动其内容,该类都将按照字符串所需空间重新分配内存,将原来的内容拷贝过来,将原来的内存释放掉。但是对于一个初始化的 CString 来说,其并未指向任何字符串,其指针指向的内存空间长如为 1 个 TCHAR 的大小。如果将 CString 直接传递给 C 风格的函数作为字符指针(字符串)参数和数组元素个数参数时,直接将其转换为 LPTSTR(TCHAR *)类型传递给函数的参数,可能访问越界。通过 GetBuffer 则先预分配一定大小的空间,待调用完毕后,ReleaseBuffer 则会重新按照 CString 的方式,按照字符串的内容所需空间分配内存拷贝存储内容。按我的理解,ReleaseBuffer 其实也可以不调用,不会有太大影响。
如您所说:对于一个初始化的 CString 来说,其并未指向任何字符串,其指针指向的内存空间长如为 1 个 TCHAR 的大小。 这话显然有些矛盾,先是说“其并未指向任何字符串”,而后又说“其指针指向的内存空间长如为 1 个 TCHAR 的大小”,这后面它的指针不是有所指吗,指针指向的内存不管有没有赋过值,都是有内容的呀,还是再就是指针指向存放字符内容的内存还是指向存放数值型内容的内存,都是可以的呀,这儿怎么强调是其指针指向的内存空间长如为 1 个 TCHAR 的大小? 谢谢,期待您的回复。
himetric 2014-12-04
  • 打赏
  • 举报
回复
引用 5 楼 SXJIAKE 的回复:
CString str;
::GetModuleFileName(NULL, LPCTSTR(str), str.GetLength() + 1);
LPCTSTR(str) 指向的空间是多大呢?CString 如果没有初始化字符串,那么他内部的指针就是指向一个 TCHAR(1 或 2 字节)且内容为零,表示一个空字符串。所以这样的一个调用是根本无法成功的。
TCHAR szPath[MAX_PATH];
::GetModuleFileName(NULL, szPath, _countof(szPath));
这样的缓冲区大小是确定了,260 个 TCHAR。调用成功后,其中也只是保存了本程序的完整路径。如果本程序的路径为 C:\test.exe,那么其中只有 12 个字符是有用的,其他的控件就浪费掉了。
CString str;
::GetModuleFileName(NULL, str.GetBuffer(MAX_PATH), MAX_PATH);
这样就让 CString 内部新分配 MAX_PATH 个 TCHAR 大小返回这段内存的指针,可以成功调用。当函数调用成功后,和第二种情况一样,假如文件名只有 12 个字符有用,剩下的 260 - 12 个字符就有些浪费了。CString 类的意义在于他能根据字符串的长度来分配内存,因此才有 ReleaseBuffer。
CString str;
::GetModuleFileName(NULL, str.GetBuffer(MAX_PATH), MAX_PATH);
str.ReleaseBuffer();
ReleaseBuffer 实际上就是完成将字符串指向的空间重新按照 CString 类存储数据的方式分配够用的新空间来储存字符串,并非必需。而 MFC 中那些以 CString 作为参数的成员函数,其内部也是把如上的步骤重新实现了一次而已。下面是 CWnd::GetWindowText 的源码(CWnd::GetWindowText 有两个版本,这是其中一个):
void CWnd::GetWindowText(CString &rString) const
{
    int nLen = ::GetWindowTextLength(m_hWnd);
    ::GetWindowText(m_hWnd, rString.GetBufferSetLength(nLen), nLen+1);
    rString.ReleaseBuffer();
}
可见其内部也是帮你实现 GetBuffer/GetBufferSetLength 而已。如果你用另外一个版本,那么自然就需要手动分配空间。
非常感谢
hhhh63 2014-12-04
  • 打赏
  • 举报
回复
简单的说,GetBuffer 为 CString(就是上面说的他)分配一定的空间,用来填字符。ReleaseBuffer 把填完字符后多余的空间释放掉。请看下面的试验:

	// 声明4个CString
	CString s1, s2, s3, s4;
	s4.Format( L"0x%X 0x%X 0x%X\n", (PCTSTR)s1, (PCTSTR)s2, (PCTSTR)s3 );
	TRACE( s4 );	// 显示0xF5B8F8 0xF5B8F8 0xF5B8F8,三个一样的地址,内容是0

	// GetBuffer();
	s1.GetBuffer();
	s2.GetBuffer();
	s3.GetBuffer();
	s4.Format( L"0x%X 0x%X 0x%X\n", (PCTSTR)s1, (PCTSTR)s2, (PCTSTR)s3 );
	TRACE( s4 );	// 显示0x12CD830 0x12CD9A0 0x12CD9F0,重分配内存,地址不一样,长度是1,内容是0

	// 赋值
	s1 = "a";
	s2 = "bb";
	s3 = "ccc";
	s4.Format( L"0x%X 0x%X 0x%X\n", (PCTSTR)s1, (PCTSTR)s2, (PCTSTR)s3 );
	TRACE( s4 );	// 0x12CD830 0x12CD7A8 0x12CDAE0,s1在原来的基础上扩大,s2和s3重分配别的内存,新分配的内存比字符串长,便于以后再加内容。

	// ReleaseBuffer
	s1.ReleaseBuffer();
	s2.ReleaseBuffer();
	s3.ReleaseBuffer();
	s4.Format( L"0x%X 0x%X 0x%X\n", (PCTSTR)s1, (PCTSTR)s2, (PCTSTR)s3 );
	TRACE( s4 );	// 0x12CD830 0x12CD7A8 0x12CDAE0,地址不变,释放多分配的内存
测试环境VC2010 其实,一般情况下不用ReleaseBuffer,除非长时间占用了很大的空余空间。 值得一提的是,如果一个CString 反复赋不同长度的值,应该先用GetBuffer申请一个较大的空间,中间千万不能用ReleaseBuffer,否则会反复销毁和分配内存,降低效率,增加内存碎片。
「已注销」 2014-12-04
  • 打赏
  • 举报
回复
CString str;
::GetModuleFileName(NULL, LPCTSTR(str), str.GetLength() + 1);
LPCTSTR(str) 指向的空间是多大呢?CString 如果没有初始化字符串,那么他内部的指针就是指向一个 TCHAR(1 或 2 字节)且内容为零,表示一个空字符串。所以这样的一个调用是根本无法成功的。
TCHAR szPath[MAX_PATH];
::GetModuleFileName(NULL, szPath, _countof(szPath));
这样的缓冲区大小是确定了,260 个 TCHAR。调用成功后,其中也只是保存了本程序的完整路径。如果本程序的路径为 C:\test.exe,那么其中只有 12 个字符是有用的,其他的控件就浪费掉了。
CString str;
::GetModuleFileName(NULL, str.GetBuffer(MAX_PATH), MAX_PATH);
这样就让 CString 内部新分配 MAX_PATH 个 TCHAR 大小返回这段内存的指针,可以成功调用。当函数调用成功后,和第二种情况一样,假如文件名只有 12 个字符有用,剩下的 260 - 12 个字符就有些浪费了。CString 类的意义在于他能根据字符串的长度来分配内存,因此才有 ReleaseBuffer。
CString str;
::GetModuleFileName(NULL, str.GetBuffer(MAX_PATH), MAX_PATH);
str.ReleaseBuffer();
ReleaseBuffer 实际上就是完成将字符串指向的空间重新按照 CString 类存储数据的方式分配够用的新空间来储存字符串,并非必需。而 MFC 中那些以 CString 作为参数的成员函数,其内部也是把如上的步骤重新实现了一次而已。下面是 CWnd::GetWindowText 的源码(CWnd::GetWindowText 有两个版本,这是其中一个):
void CWnd::GetWindowText(CString &rString) const
{
    int nLen = ::GetWindowTextLength(m_hWnd);
    ::GetWindowText(m_hWnd, rString.GetBufferSetLength(nLen), nLen+1);
    rString.ReleaseBuffer();
}
可见其内部也是帮你实现 GetBuffer/GetBufferSetLength 而已。如果你用另外一个版本,那么自然就需要手动分配空间。
「已注销」 2014-12-04
  • 打赏
  • 举报
回复
CString 类内部有一个成员变量也就是指针,他指向一个动态开辟的空间来存放字符串,并且不管你是初始化还是调用其成员函数变动其内容,该类都将按照字符串所需空间重新分配内存,将原来的内容拷贝过来,将原来的内存释放掉。但是对于一个初始化的 CString 来说,其并未指向任何字符串,其指针指向的内存空间长如为 1 个 TCHAR 的大小。如果将 CString 直接传递给 C 风格的函数作为字符指针(字符串)参数和数组元素个数参数时,直接将其转换为 LPTSTR(TCHAR *)类型传递给函数的参数,可能访问越界。通过 GetBuffer 则先预分配一定大小的空间,待调用完毕后,ReleaseBuffer 则会重新按照 CString 的方式,按照字符串的内容所需空间分配内存拷贝存储内容。按我的理解,ReleaseBuffer 其实也可以不调用,不会有太大影响。
himetric 2014-12-03
  • 打赏
  • 举报
回复
引用 2 楼 hubo86915531 的回复:
哪里搞不明白?
有个资料是这么说的: GetBuffer() 他会create出所指定大小的空间出来 这个空间是可以让我们修改的。 很多时候 有的 API 会要一个(char*)的指标作为输出。 如果我们就因为这样(这样是指哪样,难道不是指GetBuffer() create出所指定大小的空间?)去产生一个(char*)的buffer 给他(此处的给他,是指给谁?) 等到资料取出来之後便无法使用CString 的种种方便功能(怎么就无法使用CString 的种种方便功能?)。 因此 比较好的做法 便是用GetBuffer()来产生一个buffer空间给他(这个他,又是指什么?),等到取出来之後 我们便可以直接使用CString来对他操作。GetBuffer() 使用完後 最好是呼叫一下ReleaseBuffer()做为结束(为什么要ReleaseBuffer()做为结束?)。
hubo86915531 2014-12-03
  • 打赏
  • 举报
回复
哪里搞不明白?
himetric 2014-12-03
  • 打赏
  • 举报
回复
顶一下,谢谢大家!

16,548

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • AIGC Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧