CString采用copy-on-write机制(可以实现多个对象共享一块内存),高效而节省内存。
CString只有一个成员变量,长度为4,即 sizeof(CString) == 4:
class CString
{
// functions ...
protected:
LPTSTR m_pchData; // pointer to ref counted string data
// functions ...
};
CString 使用结构体CStringData来管理数据:
struct CStringData
{
long nRefs; // reference count
int nDataLength; // length of data (including terminator)
int nAllocLength; // length of allocation
// TCHAR data[nAllocLength]
TCHAR* data() // TCHAR* to managed data
{ return (TCHAR*)(this+1); }
};
该结构体长度为12,有三个变量:
1、nRefs,CString的引用个数;
2、nDataLength,为字符串的长度,长度不算最后一个结束符'\0',也不包括CStringData结构体的长度12字节;
3、nAllocLength,为字符串开辟缓存的长度,不包括CStringData结构体的长度12字节;
源码说明一切:
nLen = strlen(...);
new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)];
nAllocLength = nLen;
而CString的成员变量m_pchData指针并不指向缓存的起始位置,而是指向该连续缓存的第13个字节处,恰巧就是字符串区域。
进一步,从CString的成员变量m_pchData向前偏移12个字节,恰巧就是CStringData结构体。这个结构体的nRefs为copy-on-write机制提供了保障,nDataLength仅仅就是记录字符串的长度,nAllocLength为了管理短小字符串,提高效率,如下:
if (nLen <= 64)
{
pData = (CStringData*)_afxAlloc64.Alloc();
pData->nAllocLength = 64;
}
else if (nLen <= 128)
{
pData = (CStringData*)_afxAlloc128.Alloc();
pData->nAllocLength = 128;
}
else if (nLen <= 256)
{
pData = (CStringData*)_afxAlloc256.Alloc();
pData->nAllocLength = 256;
}
else if (nLen <= 512)
{
pData = (CStringData*)_afxAlloc512.Alloc();
pData->nAllocLength = 512;
}
GetBuffer函数遇到下面两种情况之一时会创建一个新对象给调用者:
1、有大于1个的引用时(也就是有多个对象共享内存时);
2、GetBuffer传递的参数nMinBufLength大于原始原始分配长度时,删除原来缓存,开辟更大的缓存。
GetBuffer返回一个字符串指针,可以像操作 TCHAR* 一样操作,并立即反映到CString对象中。
ReleaseBuffer可以理解为重新设置CString的字符串有效长度。(当然ReleaseBuffer也考虑到了多个对象共享的情况,它不会设置其共享对象的长度。最好不要在TCHAR* p = GetBuffer之后再引用该对象,此时p指向了所有共享对象的字符串内存。)
ReleaseBuffer做了两个动作:
1、多个对象共享时,启用copy-on-write机制,新建一个对象。
2、重新设置CString的有效长度。
所以,在调用GetBuffer之后,如果没有共享,并且不需要重新设置有效长度(没有更改字符),完全可以不调用ReleaseBuffer。
exemple:
CString str0 = "abcdefghijklmnopqrstuvwxyz";
CString str1 = str0;
char* p = str0.GetBuffer(0);
CString str2 = str0;
p[0] = '1';
p[1] = '2';
p[2] = '3';
p[3] = '4';
p[4] = '5';
str0.ReleaseBuffer(5);
执行完之后的结果:
str0 {"12345"}
str1 {"abcdefghijklmnopqrstuvwxyz"}
str2 {"12345fghijklmnopqrstuvwxyz"}