正文

浅析mfc的Cstring类的copy-on-write(写时复制)功能 2008-04-11 08:16:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/vfdff/34064.html

分享到:

CString类是vc中一个有关字符串处理的类,其中用到了很多好的技法,如写时复制 技法,内存管理技法等,处理字符串的效率是很高的。 这里仅谈谈copy-on-write技法的实现。 我以vc6中的mfc为例。 或许你曾写过如下的代码: CString  str_a = "abcd"; CString  str_b = str_a; 当你调试程序时,你可能发现str_a 和 str_b 这两个对象中的唯一的一个data member  m_pchDATA是指向同样的一个内存地址,即字符串abcd在堆中的首地址,也即字符串abcd在内存中只有一份copy,并不是两份数据。 当你再次写下如下代码时 str_a = “efg”; 会发现str_a的m_pchDATA指向了堆中的另一块内存efg,并没有将str_b中包含的数据覆盖掉。 可是CString的内存布局只有一个data member(因为它没有继承关系,所以没有虚函数,也就没有虚函数表指针)m_pchData,指向字符串数据,我们也没看见什么引用计数的变量呀。 那么CString是如何做到这一点(copy-on-write)的呢? 原来奥秘就在于有一个叫做CStringData的结构体,定义如下(见afx.h) 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); } }; 其中第一个成员变量至关重要,可以说它是copy-on-write的核心,它对它所关联的字符串作引用计数,即当前为此值的CString对象(可能在堆栈也可能在堆中)个数,什么叫’关联’呢?让我用一幅图表示一下,你就清楚了,   从图中可以看出m_pchDATA指向的字符串上方实际上是个12字节的CStringData的结构体变量,也就是说CString类通过m_pchDATA这个仅有一个指针就能即访问字符串数据又能访问引用计数nRefs这个CStringData中的变量。那么CString是如何做到这一点的呢? 侯捷先生说过,源码之下,了无秘密。让我们去看看CString的构造函数的源码吧,(见strcore.cpp)  CString::CString(LPCTSTR lpsz) {        Init();        if (lpsz != NULL && HIWORD(lpsz) == NULL)        {               UINT nID = LOWORD((DWORD)lpsz);               if (!LoadString(nID))                      TRACE1("Warning: implicit LoadString(%u) failed\n", nID);        }        else        {               int nLen = SafeStrlen(lpsz);               if (nLen != 0)               {                      AllocBuffer(nLen);                      memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR));               }        } } 这其中的关键在于AllocBuffer这个函数,下面是它的源码,   void CString::AllocBuffer(int nLen) // always allocate one extra character for '\0' termination // assumes [optimistically] that data length will equal allocation length {        ASSERT(nLen >= 0);        ASSERT(nLen <= INT_MAX-1);    // max size (enough room for 1 extra)          if (nLen == 0)               Init();        else        {               CStringData* pData; #ifndef _DEBUG   //在非调试模式下有4个内存空闲链表链,进行内存池的管理               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;               }               else #endif               {                      pData = (CStringData*)                             new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)];                      pData->nAllocLength = nLen;               }               pData->nRefs = 1;               pData->data()[nLen] = '\0';               pData->nDataLength = nLen;               m_pchData = pData->data();//pData虽然消失了,但信息都可以通过m_pchData //来索取了,呵呵        } } 相信这时你应该对CString的copy-on-write机制了然于胸了吧,下面附上CString的copy constructor     CString::CString(const CString& stringSrc) {        ASSERT(stringSrc.GetData()->nRefs != 0);        if (stringSrc.GetData()->nRefs >= 0)        {               ASSERT(stringSrc.GetData() != _afxDataNil);               m_pchData = stringSrc.m_pchData;               InterlockedIncrement(&GetData()->nRefs);        }        else        {               Init();               *this = stringSrc.m_pchData;        } }

阅读(11103) | 评论(3)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

loading...
您需要登录后才能评论,请 登录 或者 注册