正文

浅析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_am_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

//来索取了,呵呵

       }

}

相信这时你应该对CStringcopy-on-write机制了然于胸了吧,下面附上CStringcopy 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;

       }

}

阅读(7569) | 评论(3)


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

评论

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