浅谈字符串

ajin00 贡献于2012-03-19

作者 Bob wen  创建于2003-06-06 07:53:00   修改者Bob wen  修改于2003-06-10 02:09:00字数22752

文档摘要:浅谈字符串
关键词:

浅谈字符串 CString和string的对比 2 概括 3 构造 3 CString构造定义 3 以CString(LPCSTR lpsz)方式构造 3 以CString(LPCWSTR lpsz)方式构造 4 以CString()方式构造 4 以CString(const CString& stringSrc)方式构造 4 以CString(LPCSTR lpch, int nLength)方式构造 4 以CString(LPCWSTR lpch, int nLength)方式构造 4 以CString(TCHAR ch, int nRepeat = 1)方式构造 4 以CString(const unsigned char* psz)方式构造 5 常见错误 5 String构造定义 5 以basic_string(const A& al = A())方式构造 6 以basic_string(const basic_string& rhs)方式构造 6 以basic_string(const E *s, const A& al = A())方式构造 6 以basic_string(const basic_string& rhs, size_type pos, size_type n,const A& al = A())方式构造 6 以basic_string(const E *s, size_type n, const A& al = A())方式构造 6 以basic_string(size_type n, E c, const A& al = A())方式构造 6 以basic_string(const_iterator first, const_iterator last,const A& al = A())方式构造 6 赋值 7 CString的赋值定义 7 以const CString& operator=(const CString& stringSrc)方式赋值 7 以const CString& operator=(TCHAR ch)方式赋值 7 以const CString& operator=(char ch)方式赋值 8 以const CString& operator=(LPCSTR lpsz)方式赋值 8 以const CString& operator=(LPCWSTR lpsz)方式赋值 8 以const CString& operator=(const unsigned char* psz)方式赋值 8 string赋值定义 8 以basic_string& operator=(const basic_string& rhs)方式赋值 9 以basic_string& operator=(const E *s)方式赋值 9 以basic_string& operator=(E c)方式赋值 9 结构、原理 9 CString的数据存放结构 9 CString引用计数原理 10 string的管理结构 10 string的赋值流程 11 实际应用 13 CString引用计数探秘 13 string的困惑 14 问题描述 15 问题分析 15 问题解决 15 关于CString string char* BSTR之间的类型转换 15 char*类型转成string类型 15 char*类型转成CString类型 16 char*类型转成BSTR类型 16 BSTR类型转成char*类型 17 CString类型转成char*类型 17 string类型转成char*类型 18 BSTR类型转成CString类型 18 CString类型转成BSTR类型 18 附录 18 附录一: CString的部分函数实现 18 引用计数相关函数实现 18 CString申请空间函数(AllocBuffer)的实现 19 赋值操作相关函数 20 附录二:string相关函数的部分实现 21 Microsoft提供string的析构函数 21 string的赋值的部分实现 23 SGI的实现代码片断 23 Microsoft的实现代码片断 25 附录三:关于string模板的相关模板定义 27 char_traits的定义 27 allocator定义 27 CString和string的对比 概括 CString string 普通类 模板类 微软自定义 C++标准定义 构造 从定义一个实例的角度分析CString和string。 列出两个类的构造部分的定义(这两种类都是微软编写的): CString构造定义 class CString { public: // Constructors // constructs empty CString CString(); // copy constructor CString(const CString& stringSrc); // from a single character CString(TCHAR ch, int nRepeat = 1); // from an ANSI string (converts to TCHAR) CString(LPCSTR lpsz); // from a UNICODE string (converts to TCHAR) CString(LPCWSTR lpsz); // subset of characters from an ANSI string (converts to TCHAR) CString(LPCSTR lpch, int nLength); // subset of characters from a UNICODE string (converts to TCHAR) CString(LPCWSTR lpch, int nLength); // from unsigned characters CString(const unsigned char* psz); } 以CString(LPCSTR lpsz)方式构造 1)CString str(“Hello”); 2)char str1[] = “Hello”; CString str2 = str1; 以CString(LPCWSTR lpsz)方式构造 1) CString str(L “Hello”); 2) WCHAR str1[] = L“Hello”; CString str2 = str1; 以CString()方式构造 CString str1; 以CString(const CString& stringSrc)方式构造 1) CString str1(“Hello”); CString str2 = str1; 2) CString str1(“Hello”); CString str2(str1); 以CString(LPCSTR lpch, int nLength)方式构造 1) CString str1(“Hello”,3); 3) WCHAR str1[] = L“Hello”; CString str2(str1,3); 以上方式是从一个串中构造一个字串。 以CString(LPCWSTR lpch, int nLength)方式构造 1) CString str1(L“Hello”,3); 2) har str1[] = “Hello”; CString str2(str1,3); 以上方式是从一个串中构造一个字串。 以CString(TCHAR ch, int nRepeat = 1)方式构造 1) CString s = 'C'; 2) Char c = ‘C’; CString s = c; 3) CString s('C',10); 4) CString s('C'); 5) Char c = ‘C’; CString s(c); 该方式以一个字符,重复几次来构造一个字符串。 以CString(const unsigned char* psz)方式构造 1)const unsigned char c[] = "Hello"; CString s = c; 常见错误 1) CString s = (‘C’,10); 2) CString s = (“Hello”); 3) CString s = (“Hello”,3); 这些方式都会以CString(TCHAR ch, int nRepeat = 1)方式构造,但是构造的都不正确。但是,编译时不会提出警告。 String构造定义 typedef basic_string string; typedef basic_string wstring; string在定义时,规定传入的class E的类型是char。所以string只针对const char*类型和string本身的类型进行识别。 wstring是针对宽体字的类型。 template, class A = allocator > class basic_string { public: explicit basic_string(const A& al = A()); basic_string(const basic_string& rhs); basic_string(const basic_string& rhs, size_type pos, size_type n,const A& al = A()); basic_string(const E *s, size_type n, const A& al = A()); basic_string(const E *s, const A& al = A()); basic_string(size_type n, E c, const A& al = A()); basic_string(const_iterator first, const_iterator last,const A& al = A()); } 以basic_string(const A& al = A())方式构造 explicit表示只能在class内部构造时使用。所以该种方式构造string对外界来说是不允许的。 以basic_string(const basic_string& rhs)方式构造 1)string str(“Hello”); string str1 = str; 2)string str(“Hello”); string str1(str); 以basic_string(const E *s, const A& al = A())方式构造 1) string str(“Hello”); 2) string str = “Hello”; 以basic_string(const basic_string& rhs, size_type pos, size_type n,const A& al = A())方式构造 1) string str(“Hello”); string str1(str,2,3); 2)string str(“Hello”,2,3); 该方式是根据一个字符串构造一个子串。 以basic_string(const E *s, size_type n, const A& al = A())方式构造 1) string str(“Hello”,3); 该方式是根据一个字符串构造一个子串。 以basic_string(size_type n, E c, const A& al = A())方式构造 1) string str(3,’C’); 2) char c = ‘C’; string str(3,c); 该方式以一个字符重复n次的方式构造字符串。 以basic_string(const_iterator first, const_iterator last,const A& al = A())方式构造 1) string str(“Hello”); string str1(str.begin() + 1,str.end()); 2) string str(“Hello”); string::iterator pointer = str.begin() +1; string str1(pointer,pointer+3); 该方式以第一个迭代子指向的位置开始,到第二个迭代子指向的位置结束,构造一个字符串。 赋值 CString的赋值定义 class CString { public: // ref-counted copy from another CString const CString& operator=(const CString& stringSrc); // set string content to single character const CString& operator=(TCHAR ch); #ifdef _UNICODE const CString& operator=(char ch); #endif // copy string content from ANSI string (converts to TCHAR) const CString& operator=(LPCSTR lpsz); // copy string content from UNICODE string (converts to TCHAR) const CString& operator=(LPCWSTR lpsz); // copy string content from unsigned chars const CString& operator=(const unsigned char* psz); } 以const CString& operator=(const CString& stringSrc)方式赋值 CString str(“Hello”); CString str1; str1 = str; 以const CString& operator=(TCHAR ch)方式赋值 1)char c = ‘C’; CString str; Str = c; 2) CString str; Str = ‘C’; 以const CString& operator=(char ch)方式赋值 该方式只有当_UNICODE被定义时,才有效。 以const CString& operator=(LPCSTR lpsz)方式赋值 1) CString str; str = “Hello”; 2) char* s = “Hello”; CString str; str = s; 以const CString& operator=(LPCWSTR lpsz)方式赋值 1) CString str; str = L“Hello”; 2) WCHAR* s = L“Hello”; CString str; str = s; 以const CString& operator=(const unsigned char* psz)方式赋值 1) const unsigned char s[] = “Hello”; CString str; str = s; string赋值定义 typedef basic_string string; template, class A = allocator > class basic_string { public: basic_string& operator=(const basic_string& rhs); basic_string& operator=(const E *s); basic_string& operator=(E c); } 以basic_string& operator=(const basic_string& rhs)方式赋值 string str(“Hello”); string str1; str1 = str; 以basic_string& operator=(const E *s)方式赋值 1) string str; str = “Hello”; 2)char s[] = “Hello”; string str; str = s; 以basic_string& operator=(E c)方式赋值 1) string str; str = ‘C’; 2) string str; char s = ‘C’; str = s; 结构、原理 CString的数据存放结构 CString在存储数据的结构上采用了字符数据前放置管理数据CStringData的机制。 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的内存结构如下: 引用计数(long) 数据长度(int) 申请空间的长度(int) 数据开始 在字符串buf存储之前,CString放置了一个结构体CstringData大小的数据,用以记录该字符串被几次引用。 CString引用计数原理 CString在做完同类型等号赋值(const CString& CString::operator=(const CString& stringSrc))后,引用计数nRefs会加1。表示它再次被引用。 在做完析构函数后,会将引用计数减1。在引用计数为0时,才会释放为字符串申请的空间。 如下例所示: 初始赋值: CString str1 = “Hello”; 在32位Windows 2000操作系统内存中数据结构如图所示: 01 00 00 00 05 00 00 00 05 00 00 00 H E L L O 00 str1 operator=赋值 CString str2 = str1; 在32位Windows 2000操作系统内存中数据结构如图所示: 02 00 00 00 05 00 00 00 05 00 00 00 H E L L O 00 str1 str2 AllocBuffer函数是CString的一个保护函数。是为字符串申请空间的。它将字符串分为64、128、256、512、>512等几种方式。每种方式都会将一个CstringData大小的数据放在字符串buf之前。 string的管理结构 string会在构造的时候建立一个空间。通过记录M_start、M_finish、M_end_of_storage来区分当前string的最大空间、和使用空间。 template class basic_string : private _String_base<_CharT,_Alloc> { public: // Size, capacity, etc. size_type size() const { return _M_finish - _M_start; } size_type capacity() const { return (_M_end_of_storage - _M_start) - 1; } protected: using _Base::_M_start; using _Base::_M_finish; using _Base::_M_end_of_storage; } string是通过class _Alloc来进行空间的分配和释放的。_Alloc是STL内部用来管理空间的一种管理类。不同厂商的实现机制不同。通常,不同的内部实现机制不会影响外部的使用。 string的赋值流程 SGI实现的string的赋值过程如下: Microsoft实现的string的赋值过程如下: 实际应用 CString引用计数探秘 CString内部的实现使用了引用计数。为了有一个直观的印象,编写了一段程序来验证CString的引用计数。 以下是程序片断: CStringData_COPY的结构体定义是参照CstringData定义的。目的是为了能都读取引用计数。 struct CStringData_COPY { 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); } }; struct CStringData_COPY* record; { CString str1("This is a test!"); long length = sizeof(struct CStringData_COPY); record = (struct CStringData_COPY*)((*((long*)(&str1)))-length); printf("%d\t",(&str1)); printf("%d\n",*(&str1)); printf("str1's data = %s\n",str1); printf("nRefs = %d\n",record->nRefs); { CString str2; str2 = str1; printf("%d\t",(&str2)); printf("%d\n",*(&str2)); printf("str2's data = %s\n",str2); printf("nRefs = %d\n",record->nRefs); { CString str3; str3 = str1; printf("%d\t",(&str3)); printf("%d\n",*(&str3)); printf("str3's data = %s\n",str3); printf("nRefs = %d\n",record->nRefs); } printf("nRefs = %d\n",record->nRefs); } printf("nRefs = %d\n",record->nRefs); } printf("nRefs = %d\n",record->nRefs); } 结果输出: 1245016 3425436 str1's data = This is a test! nRefs = 1 1245008 3425436 str2's data = This is a test! nRefs = 2 1245004 3425436 str3's data = This is a test! nRefs = 3 nRefs = 2 nRefs = 1 nRefs = -572662307 string的困惑 曾经有一个问题困扰了我很长时间,我在长达2年的时间内,4个项目中都遇到过这个令我十分困惑的问题。 问题描述 我们在使用动态连接库提供接口函数,如下: void GetUserName(string& name) { name = m_username; // m_username是成员变量 } 。。。 另外一个DLL调用如下: string username; GetUserName(&username); 使用SGI提供的STL,没有遇到问题,使用Microsoft提供的STL,有时就会出现问题。VC提示的试图非法释放空间。 问题分析 对问题进行分析,如果通过DLL接口函数,得到一个string对象,如果碰巧满足某种条件,那么两个string对象指向同一块内存(针对Microsoft提供的string库)。这时,如果DLL先被FreeLibrary后,DLL中的成员变量m_username被释放。调用~basic_string()函数。 这时引用计数不为0,该内存没有被释放。紧接着变量username被释放,调用~basic_string()函数。引用计数为0,调用allocator.deallocate(_Ptr - 1, _Res + 2);在释放空间时发生异常。VC提示试图非法释放空间。我们一直怀疑是DLL被释放后,他的数据空间也被释放,这是另外一个DLL试图对这个数据空间进行读写操作时,会出现问题。(没有得到证实,只是猜测) 但是,SGI的operator=操作,没有将内存指针直接赋值过来。所以不会出现试图非法访问内存的提示信息。 问题解决 我们采用append函数方式替代了operator=方式,append直接会将字符串赋值到本身的空间内。 关于CString string char* BSTR之间的类型转换 char*类型转成string类型 string是一个模板类。所以char*给string赋值时,string把char当作一个类型来处理。 string fisrt; char second[] = “this is a test!”; first = second; 这时,string会调用basic_string& operator=(const E *s);把char*当作E*类型的处理。 _Myt& operator=(const _E *_S) {return (assign(_S)); } char*类型转成CString类型 CString定义了一个const CString& CString::operator=(LPCTSTR lpsz)函数,当char*给CString赋值时,会调用该函数。 LPCTSTR实际上是const char*类型。 typedef char CHAR; #define CONST const typedef CONST CHAR *LPCSTR, *PCSTR; typedef LPCSTR LPCTSTR; char*类型转成BSTR类型 BSTR bstrtemp = SysAllocString(OLESTR(“great”)); BSTR类型在WIN32并且没有定义OLE2ANSI的情况下是unsigned short*类型,不然就是char*类型。 #ifndef _WCHAR_T_DEFINED typedef unsigned short wchar_t; #define _WCHAR_T_DEFINED #endif #ifndef _MAC typedef wchar_t WCHAR; // wc, 16-bit UNICODE character #else // some Macintosh compilers don't define wchar_t in a convenient location, or define it as a char typedef unsigned short WCHAR; // wc, 16-bit UNICODE character #endif #if defined(WIN32) && !defined(OLE2ANSI) typedef WCHAR OLECHAR; #else typedef char OLECHAR; #endif typedef OLECHAR* BSTR; OLESTR是一个宏,为的是将字符串转换成OLECHAR * #define OLESTR(str) L##str L是为了把字符或字符串转化成相对应Unicode的字符或字符串。 <注:Unicode是一种用16位的值来表示字符的宽字节字符集。> L (converts following character or string to its Unicode counterpart) SysAllocString是将OLECHAR *转换成BSTR的函数。 BSTR SysAllocString( const OLECHAR * sz ); 它需要调用SysFreeString函数将利用SysAllocString生成的BSTR字符串释放掉。 另外,mbstowcs函数也可以做到这一点。因为BSTR和wchar_t *是同一种类型。 size_t mbstowcs( wchar_t *wcstr, const char *mbstr, size_t count ); BSTR类型转成char*类型 BSTR bstrtemp = SysAllocString(OLESTR(“great”)); char ctemp[10]; wcstombs(ctemp,bstrtemp,wcslen(bstrtemp)); ctemp[wcslen(bstrtemp)] = ‘\0’; wcstombs实际上是将宽体字符集转换成multibyte字符集。 (Converts a sequence of wide characters to a corresponding sequence of multibyte characters.) CString类型转成char*类型 CString cs(“bstr”); char *pch = (char*)((LPCTSTR)cs); char*是一种基本类型,它只能接受char*的赋值。 CString提供了一个()操作。它返回const LPTSTR类型的成员变量; LPTSTR m_pchData; // pointer to ref counted string data _AFX_INLINE CString::operator LPCTSTR() const { return m_pchData; } LPTSTR类型是char*类型的。然后将const char* 原换成char*。 typedef char CHAR; typedef CHAR *LPSTR, *PSTR; typedef LPSTR PTSTR, LPTSTR; string类型转成char*类型 string str("bstr"); char* pch = (char*)str.data(); string作为模板类,提供了一个函数const E data() const;它可以返回构造string变量的类型的常量指针。然后,使用char*将其转换成char*类型变量。 另外,string提供的另外一个函数const E c_str() const;也可以转换char*类型。用法同上。 BSTR类型转成CString类型 可以直接将BSTR赋值给CString。 BSTR bstrtemp = SysAllocString(OLESTR(“great”)); CString cs = bstrtemp; CString提供了函数const CString& operator=(LPCWSTR lpsz);它可以直接将宽体字转成CString类型。 LPCWSTR类型实际上是const unsigned short*类型。 typedef CONST WCHAR *LPCWSTR, *PCWSTR; CString类型转成BSTR类型 把CString赋值给BSTR,需要使用CString的成员函数AllocSysString() BSTR bstrtemp = cs.AllocSysString(); CString提供了函数BSTR AllocSysString() const;可以直接将CString内存储的字符数据转换成BSTR类型的字符数据。 附录 附录一: CString的部分函数实现 引用计数相关函数实现 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; } CString::~CString() // free any attached data { if (GetData() != _afxDataNil) { if (InterlockedDecrement(&GetData()->nRefs) <= 0) FreeData(GetData()); } } CString申请空间函数(AllocBuffer)的实现 #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))); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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 = (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(); } } 赋值操作相关函数 const CString& CString::operator=(LPCTSTR lpsz) { ASSERT(lpsz == NULL || AfxIsValidString(lpsz)); AssignCopy(SafeStrlen(lpsz), lpsz); return *this; } 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; } BSTR CString::AllocSysString() const { #if defined(_UNICODE) || defined(OLE2ANSI) BSTR bstr = ::SysAllocStringLen(m_pchData, GetData()->nDataLength); if (bstr == NULL) AfxThrowMemoryException(); #else int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, NULL, NULL); BSTR bstr = ::SysAllocStringLen(NULL, nLen); if (bstr == NULL) AfxThrowMemoryException(); MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, bstr, nLen); #endif return bstr; } 附录二:string相关函数的部分实现 Microsoft提供string的析构函数 ~basic_string() {_Tidy(true); } void _Tidy(bool _Built = false) {if (!_Built || _Ptr == 0) ; else if (_Refcnt(_Ptr) == 0 || _Refcnt(_Ptr) == _FROZEN) allocator.deallocate(_Ptr - 1, _Res + 2); else --_Refcnt(_Ptr); _Ptr = 0, _Len = 0, _Res = 0; } _E *_Ptr; size_type _Len, _Res; }; void deallocate(void _FARQ *_P, size_type) {operator delete(_P); } void __cdecl operator delete(void *p) _THROW0() { // free an allocated object free(p); } _CRTIMP void __cdecl free( void * pUserData ) { _free_dbg(pUserData, _NORMAL_BLOCK); } _CRTIMP void __cdecl _free_dbg( void * pUserData, int nBlockUse ) { /* lock the heap */ _mlock(_HEAP_LOCK); __try { /* allocate the block */ _free_dbg_lk(pUserData, nBlockUse); } __finally { /* unlock the heap */ _munlock(_HEAP_LOCK); } } string的赋值的部分实现 SGI的实现代码片断 1.调用operator=操作 basic_string& operator=(const basic_string& __s) { if (&__s != this) assign(__s.begin(), __s.end()); return *this; } 2.调用assign操作 template basic_string<_CharT,_Traits,_Alloc>& basic_string<_CharT,_Traits,_Alloc>::assign(const _CharT* __f, const _CharT* __l) { const ptrdiff_t __n = __l - __f; if (static_cast(__n) <= size()) { _Traits::copy(_M_start, __f, __n); erase(_M_start + __n, _M_finish); } else { _Traits::copy(_M_start, __f, size()); append(__f + size(), __l); } return *this; } 3.如果传入的字符串长度没有超过了本身使用的长度,将调用_Traits::copy将传入字符串复制到本身的字符串中。 static _CharT* copy(_CharT* __s1, const _CharT* __s2, size_t __n) { memcpy(__s1, __s2, __n * sizeof(_CharT)); return __s1; } 4.然后调用erase将本身剩余的部分清空。 iterator erase(iterator __first, iterator __last) { if (__first != __last) { // The move includes the terminating null. _Traits::move(__first, __last, (_M_finish - __last) + 1); const iterator __new_finish = _M_finish - (__last - __first); destroy(__new_finish + 1, _M_finish + 1); _M_finish = __new_finish; } return __first; } 5.如果传入字符串长度超过本身长度,先调用_Traits::copy将传入字符串的前部分复制下来,然后调用append将后半部分添加到字符串尾部。 template basic_string<_Tp, _Traits, _Alloc>& basic_string<_Tp, _Traits, _Alloc>::append(const _Tp* __first, const _Tp* __last) { if (__first != __last) { const size_type __old_size = size(); ptrdiff_t __n = __last - __first; if (__n > max_size() || __old_size > max_size() - __n) _M_throw_length_error(); if (__old_size + __n > capacity()) { const size_type __len = __old_size + max(__old_size, (size_t) __n) + 1; pointer __new_start = _M_allocate(__len); pointer __new_finish = __new_start; __STL_TRY { __new_finish = uninitialized_copy(_M_start, _M_finish, __new_start); __new_finish = uninitialized_copy(__first, __last, __new_finish); _M_construct_null(__new_finish); } __STL_UNWIND((destroy(__new_start,__new_finish), _M_deallocate(__new_start,__len))); destroy(_M_start, _M_finish + 1); _M_deallocate_block(); _M_start = __new_start; _M_finish = __new_finish; _M_end_of_storage = __new_start + __len; } else { const _Tp* __f1 = __first; ++__f1; uninitialized_copy(__f1, __last, _M_finish + 1); __STL_TRY { _M_construct_null(_M_finish + __n); } __STL_UNWIND(destroy(_M_finish + 1, _M_finish + __n)); _Traits::assign(*_M_finish, *__first); _M_finish += __n; } } return *this; } 6.如果字符串的容量大于传入字符串的长度(__old_size + __n < capacity()),调用uninitialized_copy将后续字符串加入到本身字符串中。 7.如果字符串的容量小于传入字符串的长度(__old_size + __n > capacity()),调用M_allocate申请一块更大的空间(__old_size + max(__old_size, (size_t) __n) + 1)。然后,调用uninitialized_copy将本身的字符串(4.1已经复制的)复制到新的空间中,将剩余的字符串接续复制。 8.最后调用destroy(_M_start, _M_finish + 1);函数销毁原来的空间。 Microsoft的实现代码片断 1. 调用operator=操作 _Myt& operator=(const _Myt& _X) {return (assign(_X)); } 2. 调用assign(const _Myt& _X)操作 _Myt& assign(const _Myt& _X) {return (assign(_X, 0, npos)); } //不知道为什么是0?,直接导致等号赋值变成了两个string类型的对象直接指向同一块buf。 3.调用_Myt& assign(const _Myt& _X, size_type _P, size_type _M)操作。 _Myt& assign(const _Myt& _X, size_type _P, size_type _M) {if (_X.size() < _P) _Xran(); size_type _N = _X.size() - _P; if (_M < _N) _N = _M; if (this == &_X) erase((size_type)(_P + _N)), erase(0, _P); else if (0 < _N && _N == _X.size() && _Refcnt(_X.c_str()) < _FROZEN - 1 && allocator == _X.allocator) {_Tidy(true); _Ptr = (_E *)_X.c_str(); _Len = _X.size(); _Res = _X.capacity(); ++_Refcnt(_Ptr); } else if (_Grow(_N, true)) {_Tr::copy(_Ptr, &_X.c_str()[_P], _N); _Eos(_N); } return (*this); } 4.果满足传入的字符串长度大于0(0 < _N),_N == _X.size()(_N =X.size() - _P;P传入为0)为真,引用计数小于254(_Refcnt(_X.c_str()) < _FROZEN – 1),allocator == _X.allocator不是很清楚,推测是判断是否采用同一种申请空间的方式。满足以上条件后,就会执行_Ptr = (_E *)_X.c_str();将传入的字符串指针复制到本身,_Len = _X.size()长度不变,_Res = _X.capacity()容量不变,++_Refcnt(_Ptr)引用计数加1。 5.如果条件不满足,就会调用_Grow(_N, true)函数申请内存空间。 bool _Grow(size_type _N, bool _Trim = false) {if (max_size() < _N) _Xlen(); if (_Ptr != 0 && _Refcnt(_Ptr) != 0 && _Refcnt(_Ptr) != _FROZEN) if (_N == 0) {--_Refcnt(_Ptr), _Tidy(); return (false); } else {_Copy(_N); return (true); } if (_N == 0) {if (_Trim) _Tidy(true); else if (_Ptr != 0) _Eos(0); return (false); } else {if (_Trim && (_MIN_SIZE < _Res || _Res < _N)) {_Tidy(true); _Copy(_N); } else if (!_Trim && _Res < _N) _Copy(_N); return (true); }} 6.max_size()的返回值最大为0xFFFFFFFD, _Xlen()会提示字符串过长。 size_type max_size() const {size_type _N = allocator.max_size();//0xFFFFFFFF return (_N <= 2 ? 1 : _N - 2); } _CRTIMP2 void __cdecl _Xlen() {_THROW(length_error, "string too long"); } 7. 后调用_Tr::copy(_Ptr, &_X.c_str()[_P], _N),将字符串复制到新的字符空间内。 附录三:关于string模板的相关模板定义 char_traits的定义 struct char_traits { typedef E char_type; typedef T1 int_type; typedef T2 pos_type; typedef T3 off_type; typedef T4 state_type; static void assign(E& x, const E& y); static E *assign(E *x, size_t n, const E& y); static bool eq(const E& x, const E& y); static bool lt(const E& x, const E& y); static int compare(const E *x, const E *y, size_t n); static size_t length(const E *x); static E *copy(E *x, const E *y, size_t n); static E *move(E *x, const E *y, size_t n); static const E *find(const E *x, size_t n, const E& y); static E to_char_type(const int_type& ch); static int_type to_int_type(const E& c); static bool eq_int_type(const int_type& ch1, const int_type& ch2); static int_type eof(); static int_type not_eof(const int_type& ch); }; 这个模板类描述了类型E的多种的特征。模板类basic_string以及几个 iostreams模板类,包括basci_io,使用这些信息来操纵E类型元素。 allocator定义 template class allocator { typedef size_t size_type; typedef ptrdiff_t difference_type; typedef T *pointer; typedef const T *const_pointer; typedef T& reference; typedef const T& const_reference; typedef T value_type; pointer address(reference x) const; const_pointer address(const_reference x) const; allocator(); allocator& operator=(const allocator); pointer allocate(size_type n, const void *hint); void deallocate(pointer p, size_type n); void construct(pointer p, const T& val); void destroy(pointer p); size_type max_size() const; }; The template class describes an object that manages storage allocation and freeing for arrays of objects of type T. An object of class allocator is the default allocator object specified in the constructors for several container template classes in the Standard C++ library. Template class allocator supplies several type definitions that are rather pedestrian. They hardly seem worth defining. But another class with the same members might choose more interesting alternatives. Constructing a container with an allocator object of such a class gives individual control over allocation and freeing of elements controlled by that container. For example, an allocator object might allocate storage on a private heap. Or it might allocate storage on a far heap, requiring nonstandard pointers to access the allocated objects. Or it might specify, through the type definitions it supplies, that elements be accessed through special accessor objects that manage shared memory, or perform automatic garbage collection. Hence, a class that allocates storage using an allocator object should always use these types for declaring pointer and reference objects (as do the containers in the Standard C++ library). Thus, an allocator defines the types (among others): · pointer -- behaves like a pointer to T · const_pointer -- behaves like a const pointer to T · reference -- behaves like a reference to T · const_reference -- behaves like a const reference to T These types specify the form that pointers and references must take for allocated elements. (allocator::types::pointer is not necessarily the same as T * for all allocator objects, even though it has this obvious definition for class allocator.) In this implementation, if a translator does not support member template functions, it omits the type-mapping member template class: template struct rebind { typedef allocator other; }; Thus, how you write an allocator is constrained. A container may need to allocate and free objects other than type T, but cannot use the rebind mechanism to derive a suitable allocator object. This means you cannot write an allocator that uses any pointer or reference types that differ from those used by allocator, and you must supply the member function: char *_Charalloc(size_type n); which allocates an object of size n bytes and returns a pointer to its first byte.

下载文档到电脑,查找使用更方便

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享文档获得金币 ] 1 人已下载

下载文档