我的邮箱是 wuxfei@gmail.com,今天记录一下写时复制相关的类容。如有错误,请指正!
众所周知,CString 是 MFC 中最风光的类之一,它给我们提供了很实用的字符串处理功能,其有一个成员变量(m_pchData)是用来存放字符串数据的。我们在使用的过程中,免不了会有 CString 对象间的相互赋。但你有没有注意到,CString 对象间相互赋值时,被赋值对象并没有立即申请内存空间来拷贝数据,而是在本 CString 对象有数据改变时,才会申请内存空间,这时才真正执行拷贝动作,拷贝完成后再执行修改动作。
请看下面一段代码的执行
CString str1 = _T("Hello");
CString str2 = str1;
CString str3 = str1;
TCHAR *p;
p = (LPTSTR)(LPCTSTR)str1;
p = (LPTSTR)(LPCTSTR)str2;
p = (LPTSTR)(LPCTSTR)str3;
str2.SetAt(0, 'W');
p = (LPTSTR)(LPCTSTR)str1;
p = (LPTSTR)(LPCTSTR)str2;
p = (LPTSTR)(LPCTSTR)str3;
通过调试发现,str2 在未改变自身类容时,其数据地址和 str1 数据地址是相同的,在 str2.SetAt(0, 'W'); 过后,str2 的数据地址才发生改变,即才真正的申请内存空间执行拷贝。即文章开头所说的写时拷贝。CString 的这种设计方法使得其在大量对象赋值时,提高了运行效率、节省了内存空间,较为灵活。
另外,CString 提供了两种获取内部数据地址的方法,一种是 GetBuffer()、和LPCTSTR。需要注意的是前者提供的是可写属性的,即调用此方法后,就执行了申请空间和拷贝动作;后者提供的是只读属性,它是不会触发申请空间和执行拷贝的。所以千万不要有对后者获取的地址空间进行写的动作,这样会给程序带来灾难性破坏。
请看下面一段代码:
CString str1 = _T("Hello");
CString str2 = str1;
CString str3 = str1;
_tcscpy((LPTSTR)(LPCTSTR)str1, _T("abc"));
通过调试,你就会发现在执行 _tcscpy((LPTSTR)(LPCTSTR)str1, _T("abc")); 过后,str1、str2、str3 的数据内容都是“abc”了。正确的做法是这样的:
CString str1 = _T("Hello");
CString str2 = str1;
CString str3 = str1;
_tcscpy(str1.GetBuffer(100), _T("abc"));
str1.ReleaseBuffer();
通过这篇文章,我们就不难理解,为什么有时候我并没有改变某个 CString 对象的数据呀,但它为什么它就随着另一个 CString 对象在变化呢!