CString的实现机制

看了很多人写的程序,包括我自己写的一些代码,发现很大的一部分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());
 }
}

当多个对象共享同一块内存时,这块内存就属于多个对象,而不在属于原来的申请这块内存的那个对象了。但是,每个对象在其生命结束时,都首先将这块内存的引用减一,然后再判断这个引用值,如果小于等于零时,就将其释放,否则,将之交给另外的正在引用这块内存的对象控制。

CString使用这种数据结构,对于大数据量的字符串操作,可以节省很多频繁申请释放内存的时间,有助于提升系统性能。

通过上面的分析,我们已经对CString的内部机制已经有了一个大致的了解了。总的说来MFC中的CString是比较成功的。但是,由于数据结构比较复杂(使用CStringData),所以在使用的时候就出现了很多的问题,最典型的一个就是用来描述内存块属性的属性值和实际的值不一致。出现这个问题的原因就是CString为了方便某些应用,提供了一些operations,这些operation可以直接返回内存块中的字符串的地址值,用户可以通过对这个地址值指向的地址进行修改,但是,修改后又没有调用相应的operations使CStringData中的值来保持一致。比如,用户可以首先通过operations得到字符串地址,然后将一些新的字符增加到这个字符串中,使得字符串的长度增加,但是,由于是直接通过指针修改的,所以描述该字符串长度的CStringData中的nDataLength却还是原来的长度,因此当通过GetLength获取字符串长度时,返回的必然是不正确的。

存在这些问题的operations下面一一介绍。

1. GetBuffer

很多错误用法中最典型的一个就是CString:: GetBuffer ()了.查了MSDN,里面对这个operation的描述是:
 Returns a pointer to the internal character buffer for the CString object. The returned LPTSTR is not const and thus allows direct modification of CString contents。
这段很清楚的说明,对于这个operation返回的字符串指针,我们可以直接修改其中的值:
 CString str1("This is the string 1");――――――――――――――――1
 int nOldLen = str1.GetLength();―――――――――――――――――2
 char* pstr1 = str1.GetBuffer( nOldLen );――――――――――――――3
 strcpy( pstr1, "modified" );――――――――――――――――――――4
 int nNewLen = str1.GetLength();―――――――――――――――――5

通过设置断点,我们来运行并跟踪这段代码可以看出,当运行到三处时,str1的值是”This is the string 1”,并且nOldLen的值是20。当运行到5处时,发现,str1的值变成了”modified”。也就是说,对GetBuffer返回的字符串指 针,我们将它做为参数传递给strcpy,试图来修改这个字符串指针指向的地址,结果是修改成功,并且CString对象str1的值也响应的变成了” modified”。但是,我们接着再调用str1.GetLength()时却意外的发现其返回值仍然是20,但是实际上此时str1中的字符串已经变 成了” modified”,也就是说这个时候返回的值应该是字符串” modified”的长度8!而不是20。现在CString工作已经不正常了!这是怎么回事?

很显然,str1工作不正常是在对通过GetBuffer返回的指针进行一个字符串拷贝之后的。

再看MSDN上的关于这个operation的说明,可以看到里面有这么一段话:
If you use the pointer returned by GetBuffer to change the string contents, you must call ReleaseBuffer before using any other CString member functions.

 原来在对GetBuffer返回的指针使用之后需要调用ReleaseBuffer,这样才能使用其他CString的operations。上 面的代码中,我们在4-5处增建一行代码:str2.ReleaseBuffer(),然后再观察nNewLen,发现这个时候已经是我们想要的值8了。

从CString的机理上也可以看出:GetBuffer返回的是CStringData对象里的字符串缓冲的首地址。根据这个地址,我们对这个地 址里的值进行的修改,改变的只是CStringData里的字符串缓冲中的值, CStringData中的其他用来描述字符串缓冲的属性的值已经不是正确的了。比如此时CStringData:: nDataLength很显然还是原来的值20,但是现在实际上字符串的长度已经是8了。也就是说我们还需要对CStringData中的其他值进行修 改。这也就是需要调用ReleaseBuffer()的原因了。

正如我们所预料的,ReleaseBuffer源代码中显示的正是我们所猜想的:
 CopyBeforeWrite();  // just in case GetBuffer was not called

 if (nNewLength == -1)
  nNewLength = lstrlen(m_pchData); // zero terminated

 ASSERT(nNewLength <= GetData()->nAllocLength);
 GetData()->nDataLength = nNewLength;
 m_pchData[nNewLength] = '/0';
其中CopyBeforeWrite是实现写拷贝技术的,这里不管它。

下面的代码就是重新设置CStringData对象中描述字符串长度的那个属性值的。首先取得当前字符串的长度,然后通过GetData()取得CStringData的对象指针,并修改里面的nDataLength成员值。

但是,现在的问题是,我们虽然知道了错误的原因,知道了当修改了GetBuffer返回的指针所指向的值之后需要调用ReleaseBuffer才 能使用CString的其他operations时,我们就能避免不在犯这个错误了。答案是否定的。这就像虽然每一个懂一点编程知识的人都知道通过new 申请的内存在使用完以后需要通过delete来释放一样,道理虽然很简单,但是,最后实际的结果还是有由于忘记调用delete而出现了内存泄漏。
实际工作中,常常是对GetBuffer返回的值进行了修改,但是最后却忘记调用ReleaseBuffer来释放。而且,由于这个错误不象new和 delete人人都知道的并重视的,因此也没有一个检查机制来专门检查,所以最终程序中由于忘记调用ReleaseBuffer而引起的错误被带到了发行 版本中。

要避免这个错误,方法很多。但是最简单也是最有效的就是避免这种用法。很多时候,我们并不需要这种用法,我们完全可以通过其他的安全方法来实现。
比如上面的代码,我们完全可以这样写:
 CString str1("This is the string 1");
 int nOldLen = str1.GetLength();
 str1 = "modified";
 int nNewLen = str1.GetLength();

但是有时候确实需要,比如:
我们需要将一个CString对象中的字符串进行一些转换,这个转换是通过调用一个dll里的函数Translate来完成的,但是要命的是,不知道什么原因,这个函数的参数使用的是char*型的:
DWORD Translate( char* pSrc, char *pDest, int nSrcLen, int nDestLen );
这个时候我们可能就需要这个方法了:
CString strDest;
Int nDestLen = 100;
DWORD dwRet = Translate( _strSrc.GetBuffer( _strSrc.GetLength() ),
 strDest.GetBuffer(nDestLen),
 _strSrc.GetLength(), nDestlen );
_strSrc.ReleaseBuffer();
strDest.ReleaseBuffer();
if ( SUCCESSCALL(dwRet)  )
{
}
if ( FAILEDCALL(dwRet) )
{
}

的确,这种情况是存在的,但是,我还是建议尽量避免这种用法,如果确实需要使用,请不要使用一个专门的指针来保存GetBuffer返回的值,因为 这样常常会让我们忘记调用ReleaseBuffer。就像上面的代码,我们可以在调用GetBuffer之后马上就调用ReleaseBuffer来调 整CString对象。

2. LPCTSTR

关于LPCTSTR的错误常常发生在初学者身上。
例如在调用函数
DWORD Translate( char* pSrc, char *pDest, int nSrcLen, int nDestLen );
时,初学者常常使用的方法就是:
int nLen = _strSrc.GetLength();
DWORD dwRet = Translate( (char*)(LPCTSTR)_strSrc),
 (char*)(LPCTSTR)_strSrc),
 nLen,
 nLen);
if ( SUCCESSCALL(dwRet)  )
{
}
if ( FAILEDCALL(dwRet) )
{
}

他原本的初衷是将转换后的字符串仍然放在_strSrc中,但是,当调用完Translate以后之后再使用_strSrc时,却发现_strSrc已经工作不正常了。检查代码却又找不到问题到底出在哪里。

其实这个问题和第一个问题是一样的。CString类已经将LPCTST重载了。在CString中LPCTST实际上已经是一个 operation了。对LPCTST的调用实际上和GetBuffer是类似的,直接返回CStringData对象中的字符串缓冲的首地址。
其C++代码实现是:
_AFX_INLINE CString::operator LPCTSTR() const
 { return m_pchData; }

因此在使用完以后同样需要调用ReleaseBuffer()。
但是,这个谁又能看出来呢?

其实这个问题的本质原因出在类型转换上。LPCTSTR返回的是一个const char*类型,因此使用这个指针来调用Translate编译是不能通过的。对于一个初学者,或者一个有很长编程经验的人都会再通过强行类型转换将 const char*转换为char*。最终造成了CString工作不正常,并且这样也很容易造成缓冲溢出。

通过上面对于CString机制和一些容易出现的使用错误的描述,可以使我们更好的使用CString。

 

 

=======================

 

CString str = "abcde\0cde";
输出字符串的值为: abcde

而字符串的长度为 s.GetLength() 的值为: 5

这是因为CString对象在赋值时只检查到'\0',后面的忽略了, 也就是说实际对象str内容为"abcde".

而str真正的存储空间为6(字符串以'\0'结尾).

所以说在字符长度和实际的空间是不一样的. 好!别跑!

请看下面有趣的程序:

CString str = "hello";

LPSTR pf = (LPSTR)(LPCSTR)s;

LPSTR pa = s.GetBuffer(0);

        你可以测得 pf == pa;

LPSTR pb = s.GetBuffer(10);

        你可以测得 pf != pb;

   

为什么:

我们都知道(LPSTR)(LPCSTR)s 实际指向对象str的实际字符串的内存地址, GetBuffer() 函数中的参数(其实就是重新申请的字符串的长度)如果小于等于先前的字符串长度, 则不会重新分配内存使用原来的内存所以 pf == pa, 如果大于先前的字符串长度, 则重新追加内存(也就是要复制原来的内容),

所以pf != pb.

   

注意GetBuffer()函数中的参数为重新申请的字符串的长度, 实际内存的大小应再加1.

  

CString s = "hello";

LPSTR pf = s.GetBuffer(0);

strcpy(pf,"hi");

这时对象str 的内容为 "hi"

但是s.GetLength()的值为5, 如果加上一条语句:

s.ReleaseBuffer();

则s.GetLength()的值为2

解释:
CString对象在内存中用一个计数器来维持可用缓冲区的大小

void ReleaseBuffer( int nNewLength = -1 )
     {
          if( nNewLength == -1 )
          {
               nNewLength = StringLength( m_pszData );
          }
          SetLength( nNewLength );
     }

很明显ReleaseBuffer的作用就是更新字符串的长度。 CString内,GetLength获取字符串长度并不是动态计算的,而是在赋值操作后计算并保存在一个int变量内的,当通过GetBuffer直接 修改CString时,那个int变量并不可能自动更新,于是便有了ReleaseBuffer.

  

CString s = "hello";

LPSTR pf = s.GetBuffer(0);

strcpy(pf,"hi");

LPSTR ps =  (LPSTR)(LPCSTR)s;    字符串缓冲区的首地址

*(ps+2) = 'x';

  则字符串的实际内容为:    "hixlo"

*(ps+6) = 'a';        出错, 因为对象s的实际空间为 6

  

CString s = "hello";

LPSTR pf = s.GetBuffer(10);

strcpy(pf,"hi");

LPSTR ps =  (LPSTR)(LPCSTR)s;    字符串缓冲区的首地址

*(ps+2) = 'x';

*(ps+5)= '\0';

 则字符串的实际内容还是为:    "hixlo"

*(ps+6) = 'a';         可以因为s对象的实际空间为11

  

说白了  ReleaseBuffer就是更新赋值之后的字符串的长度, 而实际空间没有根本的变化, GetBuffer才是使内存空间大小变化的罪魁祸首.

// ============================================================================= // FILE: StdString.h // AUTHOR: Joe O'Leary (with outside help noted in comments) // // If you find any bugs in this code, please let me know: // // jmoleary@earthlink.net // http://www.joeo.net/stdstring.htm (a bit outdated) // // The latest version of this code should always be available at the // following link: // // http://www.joeo.net/code/StdString.zip (Dec 6, 2003) // // // REMARKS: // This header file declares the CStdStr template. This template derives // the Standard C++ Library basic_string<> template and add to it the // the following conveniences: // - The full MFC CString set of functions (including implicit cast) // - writing to/reading from COM IStream interfaces // - Functional objects for use in STL algorithms // // From this template, we intstantiate two classes: CStdStringA and // CStdStringW. The name "CStdString" is just a #define of one of these, // based upone the UNICODE macro setting // // This header also declares our own version of the MFC/ATL UNICODE-MBCS // conversion macros. Our version looks exactly like the Microsoft's to // facilitate portability. // // NOTE: // If you you use this in an MFC or ATL build, you should include either // afx.h or atlbase.h first, as appropriate. // // PEOPLE WHO HAVE CONTRIBUTED TO THIS CLASS: // // Several people have helped me iron out problems and othewise improve // this class. OK, this is a long list but in my own defense, this code // has undergone two major rewrites. Many of the improvements became // necessary after I rewrote the code as a template. Others helped me // improve the CString facade. // // Anyway, these people are (in chronological order): // // - Pete the Plumber (???) // - Julian Selman // - Chris (of Melbsys) // - Dave Plummer // - John C Sipos // - Chris Sells // - Nigel Nunn // - Fan Xia // - Matthew Williams // - Carl Engman // - Mark Zeren // - Craig Watson // - Rich Zuris // - Karim Ratib // - Chris Conti // - Baptiste Lepilleur // - Greg Pickles // - Jim Cline // - Jeff Kohn // - Todd Heckel // - Ullrich Poll?hne // - Joe Vitaterna // - Joe Woodbury // - Aaron (no last name) // - Joldakowski (???) // - Scott Hathaway // - Eric Nitzche // - Pablo Presedo // - Farrokh Nejadlotfi // - Jason Mills // - Igor Kholodov // - Mike Crusader // - John James // - Wang Haifeng // - Tim Dowty // - Arnt Witteveen // - Glen Maynard // - Paul DeMarco // - Bagira (full name?) // - Ronny Schulz // - Jakko Van Hunen // - Charles Godwin // - Henk Demper // - Greg Marr // - Bill Carducci // - Brian Groose // - MKingman // - Don Beusee // // REVISION HISTORY // // 2005-JAN-10 - Thanks to Don Beusee for pointing out the danger in mapping // length-checked formatting functions to non-length-checked // CRT equivalents. Also thanks to him for motivating me to // optimize my implementation of Replace() // // 2004-APR-22 - A big, big thank you to "MKingman" (whoever you are) for // finally spotting a silly little error in StdCodeCvt that // has been causing me (and users of CStdString) problems for // years in some relatively rare conversions. I had reversed // two length arguments. // // 2003-NOV-24 - Thanks to a bunch of people for helping me clean up many // compiler warnings (and yes, even a couple of actual compiler // errors). These include Henk Demper for figuring out how // to make the Intellisense work on with CStdString on VC6, // something I was never able to do. Greg Marr pointed out // a compiler warning about an unreferenced symbol and a // problem with my version of Load in MFC builds. Bill // Carducci took a lot of time with me to help me figure out // why some implementations of the Standard C++ Library were // returning error codes for apparently successful conversions // between ASCII and UNICODE. Finally thanks to Brian Groose // for helping me fix compiler signed unsigned warnings in // several functions. // // 2003-JUL-10 - Thanks to Charles Godwin for making me realize my 'FmtArg' // fixes had inadvertently broken the DLL-export code (which is // normally commented out. I had to move it up higher. Also // this helped me catch a bug in ssicoll that would prevent // compilation, otherwise. // // 2003-MAR-14 - Thanks to Jakko Van Hunen for pointing out a copy-and-paste // bug in one of the overloads of FmtArg. // // 2003-MAR-10 - Thanks to Ronny Schulz for (twice!) sending me some changes // to help CStdString build on SGI and for pointing out an // error in placement of my preprocessor macros for ssfmtmsg. // // 2002-NOV-26 - Thanks to Bagira for pointing out that my implementation of // SpanExcluding was not properly handling the case in which // the string did NOT contain any of the given characters // // 2002-OCT-21 - Many thanks to Paul DeMarco who was invaluable in helping me // get this code working with Borland's free compiler as well // as the Dev-C++ compiler (available free at SourceForge). // // 2002-SEP-13 - Thanks to Glen Maynard who helped me get rid of some loud // but harmless warnings that were showing up on g++. Glen // also pointed out that some pre-declarations of FmtArg<> // specializations were unnecessary (and no good on G++) // // 2002-JUN-26 - Thanks to Arnt Witteveen for pointing out that I was using // static_cast<> in a place in which I should have been using // reinterpret_cast<> (the ctor for unsigned char strings). // That's what happens when I don't unit-test properly! // Arnt also noticed that CString was silently correcting the // 'nCount' argument to Left() and Right() where CStdString was // not (and crashing if it was bad). That is also now fixed! // // 2002-FEB-25 - Thanks to Tim Dowty for pointing out (and giving me the fix // for) a conversion problem with non-ASCII MBCS characters. // CStdString is now used in my favorite commercial MP3 player! // // 2001-DEC-06 - Thanks to Wang Haifeng for spotting a problem in one of the // assignment operators (for _bstr_t) that would cause compiler // errors when refcounting protection was turned off. // // 2001-NOV-27 - Remove calls to operator!= which involve reverse_iterators // due to a conflict with the rel_ops operator!=. Thanks to // John James for pointing this out. // // 2001-OCT-29 - Added a minor range checking fix for the Mid function to // make it as forgiving as CString's version is. Thanks to // Igor Kholodov for noticing this. // - Added a specialization of std::swap for CStdString. Thanks // to Mike Crusader for suggesting this! It's commented out // because you're not supposed to inject your own code into the // 'std' namespace. But if you don't care about that, it's // there if you want it // - Thanks to Jason Mills for catching a case where CString was // more forgiving in the Delete() function than I was. // // 2001-JUN-06 - I was violating the Standard name lookup rules stated // in [14.6.2(3)]. None of the compilers I've tried so // far apparently caught this but HP-UX aCC 3.30 did. The // fix was to add 'this->' prefixes in many places. // Thanks to Farrokh Nejadlotfi for this! // // 2001-APR-27 - StreamLoad was calculating the number of BYTES in one // case, not characters. Thanks to Pablo Presedo for this. // // 2001-FEB-23 - Replace() had a bug which caused infinite loops if the // source string was empty. Fixed thanks to Eric Nitzsche. // // 2001-FEB-23 - Scott Hathaway was a huge help in providing me with the // ability to build CStdString on Sun Unix systems. He // sent me detailed build reports about what works and what // does not. If CStdString compiles on your Unix box, you // can thank Scott for it. // // 2000-DEC-29 - Joldakowski noticed one overload of Insert failed to do a // range check as CString's does. Now fixed -- thanks! // // 2000-NOV-07 - Aaron pointed out that I was calling static member // functions of char_traits via a temporary. This was not // technically wrong, but it was unnecessary and caused // problems for poor old buggy VC5. Thanks Aaron! // // 2000-JUL-11 - Joe Woodbury noted that the CString::Find docs don't match // what the CString::Find code really ends up doing. I was // trying to match the docs. Now I match the CString code // - Joe also caught me truncating strings for GetBuffer() calls // when the supplied length was less than the current length. // // 2000-MAY-25 - Better support for STLPORT's Standard library distribution // - Got rid of the NSP macro - it interfered with Koenig lookup // - Thanks to Joe Woodbury for catching a TrimLeft() bug that // I introduced in January. Empty strings were not getting // trimmed // // 2000-APR-17 - Thanks to Joe Vitaterna for pointing out that ReverseFind // is supposed to be a const function. // // 2000-MAR-07 - Thanks to Ullrich Poll?hne for catching a range bug in one // of the overloads of assign. // // 2000-FEB-01 - You can now use CStdString on the Mac with CodeWarrior! // Thanks to Todd Heckel for helping out with this. // // 2000-JAN-23 - Thanks to Jim Cline for pointing out how I could make the // Trim() function more efficient. // - Thanks to Jeff Kohn for prompting me to find and fix a typo // in one of the addition operators that takes _bstr_t. // - Got rid of the .CPP file - you only need StdString.h now! // // 1999-DEC-22 - Thanks to Greg Pickles for helping me identify a problem // with my implementation of CStdString::FormatV in which // resulting string might not be properly NULL terminated. // // 1999-DEC-06 - Chris Conti pointed yet another basic_string<> assignment // bug that MS has not fixed. CStdString did nothing to fix // it either but it does now! The bug was: create a string // longer than 31 characters, get a pointer to it (via c_str()) // and then assign that pointer to the original string object. // The resulting string would be empty. Not with CStdString! // // 1999-OCT-06 - BufferSet was erasing the string even when it was merely // supposed to shrink it. Fixed. Thanks to Chris Conti. // - Some of the Q172398 fixes were not checking for assignment- // to-self. Fixed. Thanks to Baptiste Lepilleur. // // 1999-AUG-20 - Improved Load() function to be more efficient by using // SizeOfResource(). Thanks to Rich Zuris for this. // - Corrected resource ID constructor, again thanks to Rich. // - Fixed a bug that occurred with UNICODE characters above // the first 255 ANSI ones. Thanks to Craig Watson. // - Added missing overloads of TrimLeft() and TrimRight(). // Thanks to Karim Ratib for pointing them out // // 1999-JUL-21 - Made all calls to GetBuf() with no args check length first. // // 1999-JUL-10 - Improved MFC/ATL independence of conversion macros // - Added SS_NO_REFCOUNT macro to allow you to disable any // reference-counting your basic_string<> impl. may do. // - Improved ReleaseBuffer() to be as forgiving as CString. // Thanks for Fan Xia for helping me find this and to // Matthew Williams for pointing it out directly. // // 1999-JUL-06 - Thanks to Nigel Nunn for catching a very sneaky bug in // ToLower/ToUpper. They should call GetBuf() instead of // data() in order to ensure the changed string buffer is not // reference-counted (in those implementations that refcount). // // 1999-JUL-01 - Added a true CString facade. Now you can use CStdString as // a drop-in replacement for CString. If you find this useful, // you can thank Chris Sells for finally convincing me to give // in and implement it. // - Changed operators << and >> (for MFC CArchive) to serialize // EXACTLY as CString's do. So now you can send a CString out // to a CArchive and later read it in as a CStdString. I have // no idea why you would want to do this but you can. // // 1999-JUN-21 - Changed the CStdString class into the CStdStr template. // - Fixed FormatV() to correctly decrement the loop counter. // This was harmless bug but a bug nevertheless. Thanks to // Chris (of Melbsys) for pointing it out // - Changed Format() to try a normal stack-based array before // using to _alloca(). // - Updated the text conversion macros to properly use code // pages and to fit in better in MFC/ATL builds. In other // words, I copied Microsoft's conversion stuff again. // - Added equivalents of CString::GetBuffer, GetBufferSetLength // - new sscpy() replacement of CStdString::CopyString() // - a Trim() function that combines TrimRight() and TrimLeft(). // // 1999-MAR-13 - Corrected the "NotSpace" functional object to use _istpace() // instead of _isspace() Thanks to Dave Plummer for this. // // 1999-FEB-26 - Removed errant line (left over from testing) that #defined // _MFC_VER. Thanks to John C Sipos for noticing this. // // 1999-FEB-03 - Fixed a bug in a rarely-used overload of operator+() that // caused infinite recursion and stack overflow // - Added member functions to simplify the process of // persisting CStdStrings to/from DCOM IStream interfaces // - Added functional objects (e.g. StdStringLessNoCase) that // allow CStdStrings to be used as keys STL map objects with // case-insensitive comparison // - Added array indexing operators (i.e. operator[]). I // originally assumed that these were unnecessary and would be // inherited from basic_string. However, without them, Visual // C++ complains about ambiguous overloads when you try to use // them. Thanks to Julian Selman to pointing this out. // // 1998-FEB-?? - Added overloads of assign() function to completely account // for Q172398 bug. Thanks to "Pete the Plumber" for this // // 1998-FEB-?? - Initial submission // // COPYRIGHT: // 2002 Joseph M. O'Leary. This code is 100% free. Use it anywhere you // want. Rewrite it, restructure it, whatever. If you can write software // that makes money off of it, good for you. I kinda like capitalism. // Please don't blame me if it causes your $30 billion dollar satellite // explode in orbit. If you redistribute it in any form, I'd appreciate it // if you would leave this notice here. // ============
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值