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.youkuaiyun.com/liuliu20036/article/details/2997899