未完待续

我的笔记本

导航

CString点滴

Posted on 2005-10-30 17:19  普罗大众  阅读(2683)  评论(0编辑  收藏  举报

问题一:
CString str;
str += '\0';
str += "\0";
会有什么样的结果?
跟踪:str += '\0', 能真正的加一个'\0'在字符串尾;str += "\0", 因为"\0"被解释成一个LPCTSTR类型,当此LPCTSTR长度为0时,什么都没做。
分别调用+=(TCHAR), +=(LPCTSTR )。
思考一:
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); }
};

中为什么不用一个TCHAR * str; 的成员呢?
思考二:
char *ch = "";
 char *ch2 = "\0";
 char ch3[] = "\0";
用strlen得到的都是0;
用sizeof得到分别是4, 4, 2. 奇怪!
char *str = new char[10]; 
int len = sizeof(str);
double *d = new double[10];
len = sizeof(d);
得到的结果都是4!

思考三:
CString到LPTSTR的隐式转换是怎么完成的?
重载了LPCSTR"运算符"

思考四:
判断一个CString是否为空,是用GetLength()<=0好,还是==""好?
CString str;
str += '\0';
int len = str.GetLength();
得到len=1;
但str=="", 返回的是true;
因为真正比较的时候只是单纯的逐个字符比较, '\0'作为结束符.
CString str2;
str==str2返回也是true, 因为str2被隐式转换为LPTCSTR, 比较也是一样的.
所以说应避免str += '\0'这样的写法.
在CString的使用过程中应该忘记'\0'的存在?
但声明一个char str[]; 必须memset(...)为0值,不然麻烦多.


思考五:
CString 的对象作为CString 的方法的参数时候, 先查找适合CString 类型的函数, 找不到就隐式转换为LPCSTR?

//以下来自于AFX.H
//CString 最基本的数据结构
struct CStringData
{
//引用计数
 long nRefs;             // reference count
//GetLength()返回的是它的值
//字符串长度
 int nDataLength;        // length of data (including terminator)
//实际分配的内存 
int nAllocLength;       // length of allocation
 // TCHAR data[nAllocLength]

//数据内存。动态分配。
//this是指向CStringData类型的指针
//this+1就是地址+12
// (TCHAR *)(&(*this)+12)
TCHAR* data()           // TCHAR* to managed data
  { return (TCHAR*)(this+1); }
};

class CString
{
......
protected:
//实际上被赋值TCHAR* data() 返回值
 LPTSTR m_pchData;   // pointer to ref counted string data

 // implementation helpers
 CStringData* GetData() const;
......
}
// Compare helpers
bool AFXAPI operator==(const CString& s1, const CString& s2);
bool AFXAPI operator==(const CString& s1, LPCTSTR s2);
bool AFXAPI operator==(LPCTSTR s1, const CString& s2);
bool AFXAPI operator!=(const CString& s1, const CString& s2);
bool AFXAPI operator!=(const CString& s1, LPCTSTR s2);
bool AFXAPI operator!=(LPCTSTR s1, const CString& s2);
bool AFXAPI operator<(const CString& s1, const CString& s2);
bool AFXAPI operator<(const CString& s1, LPCTSTR s2);
bool AFXAPI operator<(LPCTSTR s1, const CString& s2);
bool AFXAPI operator>(const CString& s1, const CString& s2);
bool AFXAPI operator>(const CString& s1, LPCTSTR s2);
bool AFXAPI operator>(LPCTSTR s1, const CString& s2);
bool AFXAPI operator<=(const CString& s1, const CString& s2);
bool AFXAPI operator<=(const CString& s1, LPCTSTR s2);
bool AFXAPI operator<=(LPCTSTR s1, const CString& s2);
bool AFXAPI operator>=(const CString& s1, const CString& s2);
bool AFXAPI operator>=(const CString& s1, LPCTSTR s2);
bool AFXAPI operator>=(LPCTSTR s1, const CString& s2);

// conversion helpers
int AFX_CDECL _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count);
int AFX_CDECL _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count);

//来自于STRCORE.CPP的声明
// Globals
extern AFX_DATA TCHAR afxChNil;
//动态连接MFC
#ifdef _AFXDLL
const CString& AFXAPI AfxGetEmptyString();
#define afxEmptyString AfxGetEmptyString()
#else
//静态连接MFC
extern LPCTSTR _afxPchNil;
#define afxEmptyString ((CString&)*(CString*)&_afxPchNil)
#endif

//以下这一段是来自AFX.INL,CString的有些实现只在这里出现
//
_AFX_INLINE CStringData* CString::GetData() const
 { ASSERT(m_pchData != NULL); return ((CStringData*)m_pchData)-1; }
//返回空的CString, afxEmptyString由前面的宏定义
//在strcore.cpp里的构造函数也是调用这个函数
// m_pchData指向_afxPchNil中的m_pchData
//_afxPchNil是静态的,所以CString str;这样的声明都是指向同一个内存地址。
_AFX_INLINE void CString::Init()
 { m_pchData = afxEmptyString.m_pchData; }
//静态连接的时候
#ifndef _AFXDLL
//返回空的CString
_AFX_INLINE CString::CString()
 { m_pchData = afxEmptyString.m_pchData; }
#endif
_AFX_INLINE CString::CString(const unsigned char* lpsz)
 { Init(); *this = (LPCSTR)lpsz; }
_AFX_INLINE const CString& CString::operator=(const unsigned char* lpsz)
 { *this = (LPCSTR)lpsz; return *this; }
#ifdef _UNICODE
_AFX_INLINE const CString& CString::operator+=(char ch)
 { *this += (TCHAR)ch; return *this; }
_AFX_INLINE const CString& CString::operator=(char ch)
 { *this = (TCHAR)ch; return *this; }
_AFX_INLINE CString AFXAPI operator+(const CString& string, char ch)
 { return string + (TCHAR)ch; }
_AFX_INLINE CString AFXAPI operator+(char ch, const CString& string)
 { return (TCHAR)ch + string; }
#endif

_AFX_INLINE int CString::GetLength() const
 { return GetData()->nDataLength; }
_AFX_INLINE int CString::GetAllocLength() const
 { return GetData()->nAllocLength; }
_AFX_INLINE BOOL CString::IsEmpty() const
 { return GetData()->nDataLength == 0; }
_AFX_INLINE CString::operator LPCTSTR() const
 { return m_pchData; }
_AFX_INLINE int PASCAL CString::SafeStrlen(LPCTSTR lpsz)
 { return (lpsz == NULL) ? 0 : lstrlen(lpsz); }

// CString support (windows specific)
_AFX_INLINE int CString::Compare(LPCTSTR lpsz) const
 { ASSERT(AfxIsValidString(lpsz)); return _tcscmp(m_pchData, lpsz); }    // MBCS/Unicode aware
_AFX_INLINE int CString::CompareNoCase(LPCTSTR lpsz) const
 { ASSERT(AfxIsValidString(lpsz)); return _tcsicmp(m_pchData, lpsz); }   // MBCS/Unicode aware
// CString::Collate is often slower than Compare but is MBSC/Unicode
//  aware as well as locale-sensitive with respect to sort order.
_AFX_INLINE int CString::Collate(LPCTSTR lpsz) const
 { ASSERT(AfxIsValidString(lpsz)); return _tcscoll(m_pchData, lpsz); }   // locale sensitive
_AFX_INLINE int CString::CollateNoCase(LPCTSTR lpsz) const
 { ASSERT(AfxIsValidString(lpsz)); return _tcsicoll(m_pchData, lpsz); }   // locale sensitive

_AFX_INLINE TCHAR CString::GetAt(int nIndex) const
{
 ASSERT(nIndex >= 0);
 ASSERT(nIndex < GetData()->nDataLength);
 return m_pchData[nIndex];
}
_AFX_INLINE TCHAR CString::operator[](int nIndex) const
{
 // same as GetAt
 ASSERT(nIndex >= 0);
 ASSERT(nIndex < GetData()->nDataLength);
 return m_pchData[nIndex];
}
_AFX_INLINE bool AFXAPI operator==(const CString& s1, const CString& s2)
 { return s1.Compare(s2) == 0; }
_AFX_INLINE bool AFXAPI operator==(const CString& s1, LPCTSTR s2)
 { return s1.Compare(s2) == 0; }
_AFX_INLINE bool AFXAPI operator==(LPCTSTR s1, const CString& s2)
 { return s2.Compare(s1) == 0; }
_AFX_INLINE bool AFXAPI operator!=(const CString& s1, const CString& s2)
 { return s1.Compare(s2) != 0; }
_AFX_INLINE bool AFXAPI operator!=(const CString& s1, LPCTSTR s2)
 { return s1.Compare(s2) != 0; }
_AFX_INLINE bool AFXAPI operator!=(LPCTSTR s1, const CString& s2)
 { return s2.Compare(s1) != 0; }
_AFX_INLINE bool AFXAPI operator<(const CString& s1, const CString& s2)
 { return s1.Compare(s2) < 0; }
_AFX_INLINE bool AFXAPI operator<(const CString& s1, LPCTSTR s2)
 { return s1.Compare(s2) < 0; }
_AFX_INLINE bool AFXAPI operator<(LPCTSTR s1, const CString& s2)
 { return s2.Compare(s1) > 0; }
_AFX_INLINE bool AFXAPI operator>(const CString& s1, const CString& s2)
 { return s1.Compare(s2) > 0; }
_AFX_INLINE bool AFXAPI operator>(const CString& s1, LPCTSTR s2)
 { return s1.Compare(s2) > 0; }
_AFX_INLINE bool AFXAPI operator>(LPCTSTR s1, const CString& s2)
 { return s2.Compare(s1) < 0; }
_AFX_INLINE bool AFXAPI operator<=(const CString& s1, const CString& s2)
 { return s1.Compare(s2) <= 0; }
_AFX_INLINE bool AFXAPI operator<=(const CString& s1, LPCTSTR s2)
 { return s1.Compare(s2) <= 0; }
_AFX_INLINE bool AFXAPI operator<=(LPCTSTR s1, const CString& s2)
 { return s2.Compare(s1) >= 0; }
_AFX_INLINE bool AFXAPI operator>=(const CString& s1, const CString& s2)
 { return s1.Compare(s2) >= 0; }
_AFX_INLINE bool AFXAPI operator>=(const CString& s1, LPCTSTR s2)
 { return s1.Compare(s2) >= 0; }
_AFX_INLINE bool AFXAPI operator>=(LPCTSTR s1, const CString& s2)
 { return s2.Compare(s1) <= 0; }

//以下是STRCORE.CPP的所有东西,是CString 的主要实现
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include "fixalloc.h"

#ifdef AFX_CORE1_SEG
#pragma code_seg(AFX_CORE1_SEG)
#endif

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define new DEBUG_NEW

/////////////////////////////////////////////////////////////////////////////
// static class data, special inlines

// afxChNil is left for backward compatibility
AFX_DATADEF TCHAR afxChNil = '\0';

// For an empty string, m_pchData will point here
// (note: avoids special case of checking for NULL m_pchData)
// empty string data (and locked)
// 静态的
// 内存:FF FF FF FF 00 00 00 00  00 00 00 00  00 00 00 00
AFX_STATIC_DATA int _afxInitData[] = { -1, 0, 0, 0 };
AFX_STATIC_DATA CStringData* _afxDataNil = (CStringData*)&_afxInitData;
//内存:00 00 00 00
//地址:_afxInitData的地址+12
AFX_COMDAT LPCTSTR _afxPchNil = (LPCTSTR)(((BYTE*)&_afxInitData)+sizeof(CStringData));
// special function to make afxEmptyString work even during initialization
const CString& AFXAPI AfxGetEmptyString()
 { return *(CString*)&_afxPchNil; }

//////////////////////////////////////////////////////////////////////////////
// Construction/Destruction
//动态连接的时候

#ifdef _AFXDLL
CString::CString()
{
 Init();
}
#endif

//拷贝构造函数
CString::CString(const CString& stringSrc)
{
 ASSERT(stringSrc.GetData()->nRefs != 0);
//引用计数加1
 if (stringSrc.GetData()->nRefs >= 0)
 {
  ASSERT(stringSrc.GetData() != _afxDataNil);
  m_pchData = stringSrc.m_pchData;
  InterlockedIncrement(&GetData()->nRefs);
 }
 else
 {
  Init();
  *this = stringSrc.m_pchData;
 }
}

#ifndef _DEBUG

#pragma warning(disable: 4074)
#pragma init_seg(compiler)

#define ROUND(x,y) (((x)+(y-1))&~(y-1))
#define ROUND4(x) ROUND(x, 4)
AFX_STATIC CFixedAlloc _afxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CStringData)));
AFX_STATIC CFixedAlloc _afxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CStringData)));

#endif //!_DEBUG

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
  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指向的是真实分配的内存,BYTE数组
//长度:12+(nLen+1)*sizeof(TCHAR)
   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中的数据
  m_pchData = pData->data();
 }
}

void FASTCALL CString::FreeData(CStringData* pData)
{
#ifndef _DEBUG
 int nLen = pData->nAllocLength;
 if (nLen == 64)
  _afxAlloc64.Free(pData);
 else if (nLen == 128)
  _afxAlloc128.Free(pData);
 else if (nLen == 256)
  _afxAlloc256.Free(pData);
 else  if (nLen == 512)
  _afxAlloc512.Free(pData);
 else
 {
  ASSERT(nLen > 512);
  delete[] (BYTE*)pData;
 }
#else
 delete[] (BYTE*)pData;
#endif
}

void CString::Release()
{
 if (GetData() != _afxDataNil)
 {
  ASSERT(GetData()->nRefs != 0);
  if (InterlockedDecrement(&GetData()->nRefs) <= 0)
   FreeData(GetData());
  Init();
 }
}

void PASCAL CString::Release(CStringData* pData)
{
 if (pData != _afxDataNil)
 {
  ASSERT(pData->nRefs != 0);
  if (InterlockedDecrement(&pData->nRefs) <= 0)
   FreeData(pData);
 }
}

void CString::Empty()
{
 if (GetData()->nDataLength == 0)
  return;
 if (GetData()->nRefs >= 0)
  Release();
 else
  *this = &afxChNil;
 ASSERT(GetData()->nDataLength == 0);
 ASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
}

void CString::CopyBeforeWrite()
{
 if (GetData()->nRefs > 1)
 {
  CStringData* pData = GetData();
  Release();
  AllocBuffer(pData->nDataLength);
  memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(TCHAR));
 }
 ASSERT(GetData()->nRefs <= 1);
}

void CString::AllocBeforeWrite(int nLen)
{
 if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
 {
  Release();
  AllocBuffer(nLen);
 }
 ASSERT(GetData()->nRefs <= 1);
}

CString::~CString()
//  free any attached data
{
 if (GetData() != _afxDataNil)
 {
  if (InterlockedDecrement(&GetData()->nRefs) <= 0)
   FreeData(GetData());
 }
}

//////////////////////////////////////////////////////////////////////////////
// Helpers for the rest of the implementation

void CString::AllocCopy(CString& dest, int nCopyLen, int nCopyIndex,
  int nExtraLen) const
{
 // will clone the data attached to this string
 // allocating 'nExtraLen' characters
 // Places results in uninitialized string 'dest'
 // Will copy the part or all of original data to start of new string

 int nNewLen = nCopyLen + nExtraLen;
 if (nNewLen == 0)
 {
  dest.Init();
 }
 else
 {
  dest.AllocBuffer(nNewLen);
  memcpy(dest.m_pchData, m_pchData+nCopyIndex, nCopyLen*sizeof(TCHAR));
 }
}

//////////////////////////////////////////////////////////////////////////////
// More sophisticated construction

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));
  }
 }
}

/////////////////////////////////////////////////////////////////////////////
// Special conversion constructors

#ifdef _UNICODE
CString::CString(LPCSTR lpsz)
{
 Init();
 int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
 if (nSrcLen != 0)
 {
  AllocBuffer(nSrcLen);
  _mbstowcsz(m_pchData, lpsz, nSrcLen+1);
  ReleaseBuffer();
 }
}
#else //_UNICODE
CString::CString(LPCWSTR lpsz)
{
 Init();
 int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
 if (nSrcLen != 0)
 {
  AllocBuffer(nSrcLen*2);
  _wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1);
  ReleaseBuffer();
 }
}
#endif //!_UNICODE

//////////////////////////////////////////////////////////////////////////////
// Diagnostic support

#ifdef _DEBUG
CDumpContext& AFXAPI operator<<(CDumpContext& dc, const CString& string)
{
 dc << string.m_pchData;
 return dc;
}
#endif //_DEBUG

//////////////////////////////////////////////////////////////////////////////
// Assignment operators
//  All assign a new value to the string
//      (a) first see if the buffer is big enough
//      (b) if enough room, copy on top of old buffer, set size and type
//      (c) otherwise free old string data, and create a new one
//
//  All routines return the new string (but as a 'const CString&' so that
//      assigning it again will cause a copy, eg: s1 = s2 = "hi there".
//

void CString::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData)
{
 AllocBeforeWrite(nSrcLen);
 memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR));
 GetData()->nDataLength = nSrcLen;
 m_pchData[nSrcLen] = '\0';
}

const CString& CString::operator=(const CString& stringSrc)
{
 if (m_pchData != stringSrc.m_pchData)
 {
  if ((GetData()->nRefs < 0 && GetData() != _afxDataNil) ||
   stringSrc.GetData()->nRefs < 0)
  {
   // actual copy necessary since one of the strings is locked
   AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
  }
  else
  {
   // can just copy references around
   Release();
   ASSERT(stringSrc.GetData() != _afxDataNil);
   m_pchData = stringSrc.m_pchData;
   InterlockedIncrement(&GetData()->nRefs);
  }
 }
 return *this;
}

const CString& CString::operator=(LPCTSTR lpsz)
{
 ASSERT(lpsz == NULL || AfxIsValidString(lpsz));
 AssignCopy(SafeStrlen(lpsz), lpsz);
 return *this;
}

/////////////////////////////////////////////////////////////////////////////
// Special conversion assignment

#ifdef _UNICODE
const CString& CString::operator=(LPCSTR lpsz)
{
 int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
 AllocBeforeWrite(nSrcLen);
 _mbstowcsz(m_pchData, lpsz, nSrcLen+1);
 ReleaseBuffer();
 return *this;
}
#else //!_UNICODE
const CString& CString::operator=(LPCWSTR lpsz)
{
 int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
 AllocBeforeWrite(nSrcLen*2);
 _wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1);
 ReleaseBuffer();
 return *this;
}
#endif  //!_UNICODE

//////////////////////////////////////////////////////////////////////////////
// concatenation

// NOTE: "operator+" is done as friend functions for simplicity
//      There are three variants:
//          CString + CString
// and for ? = TCHAR, LPCTSTR
//          CString + ?
//          ? + CString

void CString::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data,
 int nSrc2Len, LPCTSTR lpszSrc2Data)
{
  // -- master concatenation routine
  // Concatenate two sources
  // -- assume that 'this' is a new CString object

 int nNewLen = nSrc1Len + nSrc2Len;
 if (nNewLen != 0)
 {
  AllocBuffer(nNewLen);
  memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(TCHAR));
  memcpy(m_pchData+nSrc1Len, lpszSrc2Data, nSrc2Len*sizeof(TCHAR));
 }
}

CString AFXAPI operator+(const CString& string1, const CString& string2)
{
 CString s;
 s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData,
  string2.GetData()->nDataLength, string2.m_pchData);
 return s;
}

CString AFXAPI operator+(const CString& string, LPCTSTR lpsz)
{
 ASSERT(lpsz == NULL || AfxIsValidString(lpsz));
 CString s;
 s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData,
  CString::SafeStrlen(lpsz), lpsz);
 return s;
}

CString AFXAPI operator+(LPCTSTR lpsz, const CString& string)
{
 ASSERT(lpsz == NULL || AfxIsValidString(lpsz));
 CString s;
 s.ConcatCopy(CString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength,
  string.m_pchData);
 return s;
}

//////////////////////////////////////////////////////////////////////////////
// concatenate in place

void CString::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData)
{
 //  -- the main routine for += operators

 // concatenating an empty string is a no-op!
 if (nSrcLen == 0)
  return;

 // if the buffer is too small, or we have a width mis-match, just
 //   allocate a new buffer (slow but sure)
 if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
 {
  // we have to grow the buffer, use the ConcatCopy routine
  CStringData* pOldData = GetData();
  ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData);
  ASSERT(pOldData != NULL);
  CString::Release(pOldData);
 }
 else
 {
  // fast concatenation when buffer big enough
  memcpy(m_pchData+GetData()->nDataLength, lpszSrcData, nSrcLen*sizeof(TCHAR));
  GetData()->nDataLength += nSrcLen;
  ASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
  m_pchData[GetData()->nDataLength] = '\0';
 }
}

const CString& CString::operator+=(LPCTSTR lpsz)
{
 ASSERT(lpsz == NULL || AfxIsValidString(lpsz));
 ConcatInPlace(SafeStrlen(lpsz), lpsz);
 return *this;
}

const CString& CString::operator+=(TCHAR ch)
{
 ConcatInPlace(1, &ch);
 return *this;
}

const CString& CString::operator+=(const CString& string)
{
 ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
 return *this;
}

///////////////////////////////////////////////////////////////////////////////
// Advanced direct buffer access

LPTSTR CString::GetBuffer(int nMinBufLength)
{
 ASSERT(nMinBufLength >= 0);

 if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
 {
#ifdef _DEBUG
  // give a warning in case locked string becomes unlocked
  if (GetData() != _afxDataNil && GetData()->nRefs < 0)
   TRACE0("Warning: GetBuffer on locked CString creates unlocked CString!\n");
#endif
  // 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;
}

void CString::ReleaseBuffer(int nNewLength)
{
 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';
}

LPTSTR CString::GetBufferSetLength(int nNewLength)
{
 ASSERT(nNewLength >= 0);

 GetBuffer(nNewLength);
 GetData()->nDataLength = nNewLength;
 m_pchData[nNewLength] = '\0';
 return m_pchData;
}

void CString::FreeExtra()
{
 ASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
 if (GetData()->nDataLength != GetData()->nAllocLength)
 {
  CStringData* pOldData = GetData();
  AllocBuffer(GetData()->nDataLength);
  memcpy(m_pchData, pOldData->data(), pOldData->nDataLength*sizeof(TCHAR));
  ASSERT(m_pchData[GetData()->nDataLength] == '\0');
  CString::Release(pOldData);
 }
 ASSERT(GetData() != NULL);
}

LPTSTR CString::LockBuffer()
{
 LPTSTR lpsz = GetBuffer(0);
 GetData()->nRefs = -1;
 return lpsz;
}

void CString::UnlockBuffer()
{
 ASSERT(GetData()->nRefs == -1);
 if (GetData() != _afxDataNil)
  GetData()->nRefs = 1;
}

///////////////////////////////////////////////////////////////////////////////
// Commonly used routines (rarely used routines in STREX.CPP)

int CString::Find(TCHAR ch) const
{
 return Find(ch, 0);
}

int CString::Find(TCHAR ch, int nStart) const
{
 int nLength = GetData()->nDataLength;
 if (nStart >= nLength)
  return -1;

 // find first single character
 LPTSTR lpsz = _tcschr(m_pchData + nStart, (_TUCHAR)ch);

 // return -1 if not found and index otherwise
 return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
}

int CString::FindOneOf(LPCTSTR lpszCharSet) const
{
 ASSERT(AfxIsValidString(lpszCharSet));
 LPTSTR lpsz = _tcspbrk(m_pchData, lpszCharSet);
 return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
}

void CString::MakeUpper()
{
 CopyBeforeWrite();
 _tcsupr(m_pchData);
}

void CString::MakeLower()
{
 CopyBeforeWrite();
 _tcslwr(m_pchData);
}

void CString::MakeReverse()
{
 CopyBeforeWrite();
 _tcsrev(m_pchData);
}

void CString::SetAt(int nIndex, TCHAR ch)
{
 ASSERT(nIndex >= 0);
 ASSERT(nIndex < GetData()->nDataLength);

 CopyBeforeWrite();
 m_pchData[nIndex] = ch;
}

#ifndef _UNICODE
void CString::AnsiToOem()
{
 CopyBeforeWrite();
 ::AnsiToOem(m_pchData, m_pchData);
}
void CString::OemToAnsi()
{
 CopyBeforeWrite();
 ::OemToAnsi(m_pchData, m_pchData);
}
#endif

///////////////////////////////////////////////////////////////////////////////
// CString conversion helpers (these use the current system locale)

int AFX_CDECL _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count)
{
 if (count == 0 && mbstr != NULL)
  return 0;

 int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1,
  mbstr, count, NULL, NULL);
 ASSERT(mbstr == NULL || result <= (int)count);
 if (result > 0)
  mbstr[result-1] = 0;
 return result;
}

int AFX_CDECL _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
{
 if (count == 0 && wcstr != NULL)
  return 0;

 int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1,
  wcstr, count);
 ASSERT(wcstr == NULL || result <= (int)count);
 if (result > 0)
  wcstr[result-1] = 0;
 return result;
}

LPWSTR AFXAPI AfxA2WHelper(LPWSTR lpw, LPCSTR lpa, int nChars)
{
 if (lpa == NULL)
  return NULL;
 ASSERT(lpw != NULL);
 // verify that no illegal character present
 // since lpw was allocated based on the size of lpa
 // don't worry about the number of chars
 lpw[0] = '\0';
 VERIFY(MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars));
 return lpw;
}

LPSTR AFXAPI AfxW2AHelper(LPSTR lpa, LPCWSTR lpw, int nChars)
{
 if (lpw == NULL)
  return NULL;
 ASSERT(lpa != NULL);
 // verify that no illegal character present
 // since lpa was allocated based on the size of lpw
 // don't worry about the number of chars
 lpa[0] = '\0';
 VERIFY(WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpa, nChars, NULL, NULL));
 return lpa;
}

///////////////////////////////////////////////////////////////////////////////