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

發佈了19 篇原創文章 · 獲贊 19 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章