關於Cstring 類

來源:互聯網
上載者:User

關於Cstring 類

著作權
Stevencao@benq.com
2003-11-6

看了很多人寫的程式,包括我自己寫的一些代碼,發現很大的一部分bug是關於MFC類中的Cstring的錯誤用法的.出現這種錯誤的原因主要是對Cstring的實現機制不是太瞭解。

Cstring是對於原來標準c中字串類型的一種的封裝。因為,通過很長時間的編程,我們發現,很多程式的bug多和字串有關,典型的有:緩衝溢出、記憶體流失等。而且這些bug都是致命的,會造成系統的癱瘓。因此c++裡就專門的做了一個類用來維護字串指標。標準c++裡的字串類是string,在microsoft MFC類庫中使用的是Cstring類。通過字串類,可以大大的避免c中的關於字串指標的那些問題。

這裡我們簡單的看看Microsoft MFC中的Cstring是如何?的。當然,要看原理,直接把它的代碼拿過來分析是最好的。MFC裡的關於Cstring的類的實現大部分在strcore.cpp中。

Cstring就是對一個用來存放字串的緩衝區和對施加於這個字串的操作封裝。也就是說,Cstring裡需要有一個用來存放字串的緩衝區,並且有一個指標指向該緩衝區,該指標就是LPTSTR m_pchData。但是有些字串操作會增建或減少字串的長度,因此為了減少頻繁的申請記憶體或者釋放記憶體,Cstring會先申請一個大的記憶體塊用來存放字串。這樣,以後當字串長度增長時,如果增加的總長度不超過預先申請的記憶體塊的長度,就不用再申請記憶體。當增加後的字串長度超過預先申請的記憶體時,Cstring先釋放原先的記憶體,然後再重新申請一個更大的記憶體塊。同樣的,當字串長度減少時,也不釋放多出來的記憶體空間。而是等到積累到一定程度時,才一次性將多餘的記憶體釋放。

還有,當使用一個Cstring對象a來初始化另一個Cstring對象b時,為了節省空間的,新對象b並不分配空間,它所要做的只是將自己的指標指向對象a的那塊記憶體空間,只有當需要修改對象a或者b中的字串時,才會為新對象b申請記憶體空間,這叫做寫入複製技術(CopyBeforeWrite)。

這樣,僅僅通過一個指標就不能完整的描述這塊記憶體的具體情況,需要更多的資訊來描述。

首先,需要有一個變數來描述當前記憶體塊的總的大小。
其次,需要一個變數來描述當前記憶體塊已經使用的情況。也就是當前字串的長度
另外,還需要一個變數來描述該記憶體塊被其他Cstring引用的情況。有一個對象引用該記憶體塊,就將該數值加一。

Cstring中專門定義了一個結構體來描述這些資訊:
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); }
};

實際使用時,該結構體的所佔用的記憶體塊大小是不固定的,在Cstring內部的記憶體塊頭部,放置的是該結構體。從該記憶體塊頭部開始的sizeof(CstringData)個BYTE後才是真正的用於存放字串的記憶體空間。這種結構的資料結構的申請方法是這樣實現的:
pData = (CStringData*) new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)];
pData->nAllocLength = nLen;
其中nLen是用於說明需要一次性申請的記憶體空間的大小的。

從代碼中可以很容易的看出,如果想申請一個256個TCHAR的記憶體塊用於存放字串,實際申請的大小是:
sizeof(CstringData)個BYTE + (nLen+1)個TCHAR

其中前面sizeof(CstringData)個BYTE是用來存放CstringData資訊的。後面的nLen+1個TCHAR才是真正用來存放字串的,多出來的一個用來存放’/0’。

 Cstring中所有的operations的都是針對這個緩衝區的。比如LPTSTR CString::GetBuffer(int nMinBufLength),它的實現方法是:
首先通過Cstring::GetData()取得CstringData對象的指標。該指標是通過存放字串的指標m_pchData先後位移sizeof(CstringData),從而得到了CstringData的地址。
然後根據參數nMinBufLength給定的值重新執行個體化一個CstringData對象,使得新的對象裡的字串緩衝長度能夠滿足nMinBufLength。
然後在重新設定一下新的CstringData中的一些描述值。
最後將新CstringData對象裡的字串緩衝直接返回給調用者。

這些過程用C++代碼描述就是:
if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
{
// we have to grow the buffer
CStringData* pOldData = GetData();
int nOldLen = GetData()->nDataLength;   // AllocBuffer will tromp it
if (nMinBufLength < nOldLen)
nMinBufLength = nOldLen;
AllocBuffer(nMinBufLength);
memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(TCHAR));
GetData()->nDataLength = nOldLen;
CString::Release(pOldData);
}
ASSERT(GetData()->nRefs <= 1);

// return a pointer to the character storage for this string
ASSERT(m_pchData != NULL);
return m_pchData;

很多時候,我們經常的對大批量的字串進行互相拷貝修改等,Cstring 使用了CopyBeforeWrite技術。使用這種方法,當利用一個Cstring對象a執行個體化另一個對象b的時候,其實兩個對象的數值是完全相同的,但是如果簡單的給兩個對象都申請記憶體的話,對於只有幾個、幾十個位元組的字串還沒有什麼,如果是一個幾K甚至幾M的資料量來說,是一個很大的浪費。
因此Cstring 在這個時候只是簡單的將新對象b的字串地址m_pchData直接指向另一個對象a的字串地址m_pchData。所做的額外工作是將對象a的記憶體應用CstringData:: nRefs加一。
CString::CString(const CString& stringSrc)
{
m_pchData = stringSrc.m_pchData;
InterlockedIncrement(&GetData()->nRefs);
}

這樣當修改對象a或對象b的字串內容時,首先檢查CstringData:: nRefs的值,如果大於一(等於一,說明只有自己一個應用該記憶體空間),說明該對象引用了別的對象記憶體或者自己的記憶體被別人應用,該對象首先將該應用值減一,然後將該記憶體交給其他的對象管理,自己重新申請一塊記憶體,並將原來記憶體的內容拷貝過來。

其實現的簡單代碼是:
void CString::CopyBeforeWrite()
{
if (GetData()->nRefs > 1)
{
CStringData* pData = GetData();
Release();
AllocBuffer(pData->nDataLength);
memcpy(m_pchData, pData->data(),
  (pData- >nDataLength+1)*sizeof(TCHAR));
}
}
其中Release 就是用來判斷該記憶體的被引用情況的。
void CString::Release()
{
if (GetData() != _afxDataNil)
{
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
FreeData(GetData());
}
}

當多個對象共用同一塊記憶體時,這塊記憶體就屬於多個對象,而不在屬於原來的申請這塊記憶體的那個對象了。但是,每個對象在其生命結束時,都首先將這塊記憶體的引用減一,然後再判斷這個引用值,如果小於等於零時,就將其釋放,否則,將之交給另外的正在引用這塊記憶體的對象控制。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.