Getbuffer跟ReleaseBuffer的作用,查了一些资料,还是搞不明白,麻烦大家了
Getbuffer和ReleaseBuffer的作用,查了一些资料,还是搞不明白,麻烦大家了
Getbuffer和ReleaseBuffer的作用,查了一些资料,还是搞不明白,麻烦大家了
------解决思路----------------------
CString 类内部有一个成员变量也就是指针,他指向一个动态开辟的空间来存放字符串,并且不管你是初始化还是调用其成员函数变动其内容,该类都将按照字符串所需空间重新分配内存,将原来的内容拷贝过来,将原来的内存释放掉。但是对于一个初始化的 CString 来说,其并未指向任何字符串,其指针指向的内存空间长如为 1 个 TCHAR 的大小。如果将 CString 直接传递给 C 风格的函数作为字符指针(字符串)参数和数组元素个数参数时,直接将其转换为 LPTSTR(TCHAR *)类型传递给函数的参数,可能访问越界。通过 GetBuffer 则先预分配一定大小的空间,待调用完毕后,ReleaseBuffer 则会重新按照 CString 的方式,按照字符串的内容所需空间分配内存拷贝存储内容。按我的理解,ReleaseBuffer 其实也可以不调用,不会有太大影响。
------解决思路----------------------
------解决思路----------------------
总之一句话,GetBuffer如同new一样,把原CString的数据做了一个副本,因为是new出来的,因此要delete,这里是用ReleaseBuffer来做这项工作的
------解决思路----------------------
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 新的内存,拷贝字符串并删除原有内容)。
------解决思路----------------------
这个具体细节你得自己去研究CString的源码,我这里有个说法版本:
对于任何一个默认构造初始化的CString来说(譬如CString str;),它的字符串指针都指向一个全局的地址,也就是说数据区指针不为空,并且指针指向的内容为空
我们来稍微仿真一下吧:
1、CString 头部信息
2、CString类
3、全局数据区:
4、默认构造函数:
然而现实中我看过很多人的代码都这样初始化一个空的CString: CSting str = _T(""); 实在是令人汗颜!
------解决思路----------------------
空字符串肯定要分配內存了,空字符串和 NULL 不是一個意思。
如同 C 語言裡的:
const char *str = NULL;
和:
const char *str = "";
的區別。只不過 NULL 這種情況不可能發生在 CString 裡。
第一種根本不指向任何空間,第二種只是指向一個 1 字節的 0 值而已。
------解决思路----------------------
参数都填0就行了。
------解决思路----------------------
简单的说,GetBuffer 为 CString(就是上面说的他)分配一定的空间,用来填字符。ReleaseBuffer 把填完字符后多余的空间释放掉。请看下面的试验:
其实,一般情况下不用ReleaseBuffer,除非长时间占用了很大的空余空间。
值得一提的是,如果一个CString 反复赋不同长度的值,应该先用GetBuffer申请一个较大的空间,中间千万不能用ReleaseBuffer,否则会反复销毁和分配内存,降低效率,增加内存碎片。
Getbuffer和ReleaseBuffer的作用,查了一些资料,还是搞不明白,麻烦大家了
------解决思路----------------------
CString 类内部有一个成员变量也就是指针,他指向一个动态开辟的空间来存放字符串,并且不管你是初始化还是调用其成员函数变动其内容,该类都将按照字符串所需空间重新分配内存,将原来的内容拷贝过来,将原来的内存释放掉。但是对于一个初始化的 CString 来说,其并未指向任何字符串,其指针指向的内存空间长如为 1 个 TCHAR 的大小。如果将 CString 直接传递给 C 风格的函数作为字符指针(字符串)参数和数组元素个数参数时,直接将其转换为 LPTSTR(TCHAR *)类型传递给函数的参数,可能访问越界。通过 GetBuffer 则先预分配一定大小的空间,待调用完毕后,ReleaseBuffer 则会重新按照 CString 的方式,按照字符串的内容所需空间分配内存拷贝存储内容。按我的理解,ReleaseBuffer 其实也可以不调用,不会有太大影响。
------解决思路----------------------
CString str;LPCTSTR(str) 指向的空间是多大呢?CString 如果没有初始化字符串,那么他内部的指针就是指向一个 TCHAR(1 或 2 字节)且内容为零,表示一个空字符串。所以这样的一个调用是根本无法成功的。
::GetModuleFileName(NULL, LPCTSTR(str), str.GetLength() + 1);
TCHAR szPath[MAX_PATH];这样的缓冲区大小是确定了,260 个 TCHAR。调用成功后,其中也只是保存了本程序的完整路径。如果本程序的路径为 C:\test.exe,那么其中只有 12 个字符是有用的,其他的控件就浪费掉了。
::GetModuleFileName(NULL, szPath, _countof(szPath));
CString str;这样就让 CString 内部新分配 MAX_PATH 个 TCHAR 大小返回这段内存的指针,可以成功调用。当函数调用成功后,和第二种情况一样,假如文件名只有 12 个字符有用,剩下的 260 - 12 个字符就有些浪费了。CString 类的意义在于他能根据字符串的长度来分配内存,因此才有 ReleaseBuffer。
::GetModuleFileName(NULL, str.GetBuffer(MAX_PATH), MAX_PATH);
CString str;ReleaseBuffer 实际上就是完成将字符串指向的空间重新按照 CString 类存储数据的方式分配够用的新空间来储存字符串,并非必需。而 MFC 中那些以 CString 作为参数的成员函数,其内部也是把如上的步骤重新实现了一次而已。下面是 CWnd::GetWindowText 的源码(CWnd::GetWindowText 有两个版本,这是其中一个):
::GetModuleFileName(NULL, str.GetBuffer(MAX_PATH), MAX_PATH);
str.ReleaseBuffer();
void CWnd::GetWindowText(CString &rString) const可见其内部也是帮你实现 GetBuffer/GetBufferSetLength 而已。如果你用另外一个版本,那么自然就需要手动分配空间。
{
int nLen = ::GetWindowTextLength(m_hWnd);
::GetWindowText(m_hWnd, rString.GetBufferSetLength(nLen), nLen+1);
rString.ReleaseBuffer();
}
------解决思路----------------------
总之一句话,GetBuffer如同new一样,把原CString的数据做了一个副本,因为是new出来的,因此要delete,这里是用ReleaseBuffer来做这项工作的
------解决思路----------------------
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 新的内存,拷贝字符串并删除原有内容)。
------解决思路----------------------
这个具体细节你得自己去研究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(""); 实在是令人汗颜!
------解决思路----------------------
空字符串肯定要分配內存了,空字符串和 NULL 不是一個意思。
如同 C 語言裡的:
const char *str = NULL;
和:
const char *str = "";
的區別。只不過 NULL 這種情況不可能發生在 CString 裡。
第一種根本不指向任何空間,第二種只是指向一個 1 字節的 0 值而已。
------解决思路----------------------
参数都填0就行了。
------解决思路----------------------
简单的说,GetBuffer 为 CString(就是上面说的他)分配一定的空间,用来填字符。ReleaseBuffer 把填完字符后多余的空间释放掉。请看下面的试验:
测试环境VC2010
// 声明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,地址不变,释放多分配的内存
其实,一般情况下不用ReleaseBuffer,除非长时间占用了很大的空余空间。
值得一提的是,如果一个CString 反复赋不同长度的值,应该先用GetBuffer申请一个较大的空间,中间千万不能用ReleaseBuffer,否则会反复销毁和分配内存,降低效率,增加内存碎片。