CString::GetBuffer/ReleaseBuffer 技术内幕

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"}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值