Cstring 内存分配机制

CString比起STL的string来说,有很多方便的地方。许多有经验的作者在他们的文章里都写过,string是一个很好用的类型,但是往往MFC程序里的许多BUG就是它引起的,典型的漏洞有:缓冲溢出、内存泄漏等。而且这些BUG都是致命的,会造成系统的瘫痪。因此C++里就专门的做了一个类用来维护字符串指针。标准C++里的字符串类是string,在microsoft MFC类库中使用的是CString类。通过字符串类,可以大大的避免C中的关于字符串指针的那些问题。跟我一起来了解一下你经常用到的CString吧! Microsoft MFC中的CString是如何实现的,最好是拿它的代码过来直接分析。MFC里的关于CString的类的实现大部分在strcore.cpp中。 

 

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类中专门定义的结构体,用来保存CString的必要的元素,从中我们可以看到,CString并非string类型的一个简单的指针。 CString有很多与众不同的特色,它的结构体内有一个用来存放字符串的缓冲区,并有一个指针指向这个缓冲区,既LPTSTR m_pchData。大家都知道,CString不象其他数据类型,它似乎是没有长度限制的,为什么呢?因为CString并非是你赋予多大长度的字符串就占有多大的内存空间。它的空间分配机制是事先申请一个比较大的内存空间来存放字符串,在以后的操作中如果字符串超出了这个内存区域,它才会先释放原先的内存区域,重新申请一个更大的内存空间。同样,如果字符串变短了,它也不是立即释放多余空间,而是累积到了一定程度才释放。这样实现了“无长度限制”,又避免了频繁的申请、释放内存的操作。 CString的另外一个特色就是“写入复制技术(CopyBeforeWrite)”。当使用一个CString对象A来初始化另外一个CString对象B时,B并不会被分配空间,而是将自己的指针指向对象A的存储空间。除非对两个中的某个做修改时,才会为对象B申请内存。如此看来,这些特性又是如何实现的呢?可以肯定的是,这需要很多附加信息的支持,而非仅仅只有一个指针这么简单。 
  根据前面的结构代码,我们可以得知: 
  有一个变量来描述当前内存块的总的大小。一个变量来描述当前内存块已经使用的情况。也就是当前字符串的长度。一个变量来描述该内存块被其他Cstring引用的情况。有一个对象引用该内存块,就将该数值加一。好了,我们了解了很多关于CString的特性了,但是刚刚我们讨论的不过只是CString内部的内存块头部而已。真正的字符串数据存放在这个头部结构体的后面呢!让我们看看实际上整个CString是如何分配到内存空间的吧:

 

pData = (CStringData*) new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)];

pData->nAllocLength = nLen;

 在Cstring内部的内存块头部,放置的是该结构体。从该内存块头部开始的sizeof(CstringData)个BYTE后才是真正的用于存放字符串的内存空间。其中nLen是用于说明需要一次性申请的内存空间的大小的。举个例子说明比较清楚,例如我们要保存一个256*TCHAR长度的CString串,那么它的实际内存空间是: 
              sizeof(CstringData)*BYTE+(256+1)*TCHAR 
   其中,sizeof(CstringData)*BYTE是CString中的那个结构头的大小,后面(256+1)*TCHAR才是真正的字符串所占的空间。呵呵,又有疑问了吧,怎么要加一呢?’/n’用的呢!好了,CString的好处我们都能够看到或者推测到了,必然的,它也肯定存在某些问题或者缺点。从它这种特殊的存贮结构我们就不难推测出它的缺点:当我们修改后面那一部分字符串内容时,同时必须更新其“头”中的CstringData:: nDataLength。会不会有这个变量与字符串实际长度不匹配的时候呢?回答是肯定的。例如:

 

CString str("This is the string A");

int nOldLen = str.GetLength();

char* pstr = str.GetBuffer( nOldLen );

strcpy( pstr, "modified" );

int nNewLen = str.GetLength();

 

 

依次执行上面这几行代码,你发现了什么了吗?第二行和第五行都是取得str的长度,长度都是20,但是,str的字符串内容还一样吗?很明显,nNewLen已经与当时的str的实际长度不符和了。

修改为这样就可以了:

    CString str("This is the string A");

    int nOldLen = str.GetLength();

    char* pstr = str.GetBuffer( nOldLen );

    strcpy( pstr, "modified" );

    str.ReleaseBuffer();

    int nNewLen = str.GetLength();

If you use the pointer returned by GetBuffer to change the string contents, you must callReleaseBuffer before using any other CString member functions.  

 

这句话是MSDN对这个问题的解释,呵呵,所以使用CString要注意了。肯定的,ReleaseBuffer函数里面必然有更新nDataLength的语句。好了,相信CString类型或者其他还有许多另外的优缺点,各位在工作实践中只要多留心就可以发现了。

 

转载声明:本文转自http://blog.csdn.net/liuliu20036/article/details/2997899

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值