探討C++中string類的完成道理和擴大應用。本站提示廣大學習愛好者:(探討C++中string類的完成道理和擴大應用)文章只能為提供參考,不一定能成為您想要的結果。以下是探討C++中string類的完成道理和擴大應用正文
C++法式員編碼進程中常常會應用string(wstring)類,你能否思慮過它的外部完成細節。好比這個類的迭代器是若何完成的?對象占若干字節的內存空間?外部有無虛函數?內存是若何分派的?結構和析構的本錢有多年夜?筆者綜合這兩天浏覽的源代碼及小我懂得扼要引見之,毛病的處所望讀者指出。
起首看看string和wstring類的界說:
typedef basic_string<char, char_traits<char>, allocator<char> > string; typedef basic_string<wchar_t, char_traits<wchar_t> allocator<wchar_t> > wstring;
從這個界說可以看出string和wstring分離是模板類basic_string對char和wchar_t的特化。
再看看basic_string類的繼續關系(類辦法未列出):
最頂層的類是_Container_base,它也是STL容器的基類,Debug下包括一個_Iterator_base*的成員,指向容器的最開端的元素,如許就可以遍歷容器了,並界說了了兩個函數
void _Orphan_all() const; // orphan all iterators void _Swap_all(_Container_base_secure&) const; // swaps all iterators
Release下_Container_base只是一個空的類。
_String_base類沒稀有據成員,只界說了異常處置的三個函數:
static void _Xlen(); // report a length_error static void _Xran(); // report an out_of_range error static void _Xinvarg();
_String_val包括一個alloctor的對象,這個類也異常簡略,除結構函數沒有界說其它函數。
下面三個基類都界說得很簡略,而basic_string類的完成異常龐雜。不外它的設計和年夜多半尺度庫一樣,把龐雜的功效分紅幾部門去完成,充足表現了模塊的低耦合。
迭代器有關的操作交給_String_iterator類去完成,元素相干的操作交給char_traits類去完成,內存分派交給allocator類去完成。
_String_iterator類的繼續關系以下圖:
這個類完成了迭代器的通用操作,好比:
reference operator*() const; pointer operator->() const _String_iterator & operator++() _String_iterator operator++(int) _String_iterator& operator--() _String_iterator operator--(int) _String_iterator& operator+=(difference_type _Off) _String_iterator operator+(difference_type _Off) const _String_iterator& operator-=(difference_type _Off) _String_iterator operator-(difference_type _Off) const difference_type operator-(const _Mybase& _Right) const reference operator[](difference_type _Off) const
有了迭代器的完成,便可以很便利的應用算法庫外面的函數了,好比將一切字符轉換為小寫:
string s("Hello String"); transform(s.begin(), s.end(), s.begin(), tolower);
char_traits類圖以下:
這個類界說了字符的賦值,拷貝,比擬等操作,假如有特別需求也能夠從新界說這個類。
allocator類圖以下:
這個類應用new和delete完成內存的分派與釋放等操作。你也能夠界說本身的allocator,msdn上有引見哪些辦法是必需界說的。
再看看basic_string類的數據成員:
_Mysize表現現實的元素個數,初始值為0;
_Myres表現以後可以存儲的最年夜元素個數(跨越這個年夜小就要從新分派內存),初始值是_BUF_SIZE-1;
_BUF_SIZE是一個enum類型:
enum { // length of internal buffer, [1, 16] _BUF_SIZE = 16 / sizeof (_Elem) < 1 ? 1: 16 / sizeof(_Elem) };
從這個界說可以得出,針對char和wchar_t它的值分離是16和8。
_Bxty是一個union:
union _Bxty { // storage for small buffer or pointer to larger one _Elem _Buf[_BUF_SIZE]; _Elem *_Ptr; } _Bx;
為何要那樣界說_Bxty呢,看上面這段代碼:
_Elem * _Myptr() { // determine current pointer to buffer for mutable string return (_BUF_SIZE <= _Myres ? _Bx._Ptr : _Bx._Buf); }
這個函數前往basic_string外部的元素指針(c_str函數就是挪用這個函數)。
所以當元素個數小於_BUF_SIZE時不消分派內存,直接應用_Buf數組,_Myptr前往_Buf。不然就要分派內存了,_Myptr前往_Ptr。
不外內存分派戰略又是如何的呢?看上面這段代碼:
void _Copy(size_type _Newsize, size_type _Oldlen) { // copy _Oldlen elements to newly allocated buffer size_type _Newres = _Newsize | _ALLOC_MASK; if (max_size() < _Newres) _Newres = _Newsize; // undo roundup if too big else if (_Newres / 3 < _Myres / 2 && _Myres <= max_size() - _Myres / 2) _Newres = _Myres + _Myres / 2; // grow exponentially if possible //other code }
_ALLOC_MASK的值是_BUF_SIZE-1。這段代碼看起來有點龐雜,簡略描寫就是:最開端_Myres每次增長_BUF_SIZE,當值到達必定年夜小時每次增長一半。
針對char和wchar_t,每次分派內存的臨界值分離是(跨越這些值就要從新分派):
char:15,31,47,70,105,157,235,352,528,792,1188,1782。。。 wchar_t:7, 15, 23, 34, 51, 76, 114, 171, 256, 384, 576, 864, 1296, 1944。。。
從新分派後都邑先將舊的元素拷貝到新的內存地址。所以當處置一個長度會赓續增加而又年夜概曉得最年夜年夜小時可以先挪用reserve函數預分派內存以進步效力。
string類占若干字節的內存空間呢?
_Container_base Debug下含有一個指針,4字節,Release下是空類,0字節。_String_val類含有一個allocator對象。string類應用默許的allocator類,這個類沒稀有據成員,不外按字節對齊的准繩,它占4字節。basic_string類的成員加起來是24,所以總共是32字節(Debug)或28字節(Relase)。wstring也是32或28,至於緣由文中曾經剖析。
綜上所述:string和wstring類借助_String_iterator完成迭代器操作,都占32(Debug)或28(Release)字節的內存空間,沒有虛函數,結構和析構開支較低,內存分派比擬靈巧。
擴大string類
在現實開辟進程中,C++string類應用起來有許多不便利的處所,筆者依據依據這些缺乏簡略的擴大了這個類,如增長與數字之間的互相轉化和格局化字符串。缺乏的處所望斧正。讀者也能夠依據本身需求持續擴大。
頭文件:exstring.h
/* Author: wuqiang Email: [email protected] Description:exstring is a subclass of basic_string.It is added some userful operations,such as toUpper,toLower,toNumber,fromNumber,format,etc.It can also convert between basic_string seamlessly,which is very important for compatibility. And it is almostly a wrapper of some C++ standard library,so there should be no bugs. If you find some,please let me known.It is totally free,you can use it in any way you desire. */ #pragma once #include <string> #include <stdarg.h> #include <algorithm> #include <sstream> #include <iomanip> using namespace std; #ifndef INLINE #define INLINE inline #endif //INLINE static ios_base::fmtflags BaseFlag(int base) { return (base == 16) ? (ios_base::hex) : ( (base == 8) ? (ios_base::oct) : (ios_base::dec) ); } template<class _Elem> struct ex_char_traits { }; template<> struct ex_char_traits<char> { static INLINE int ct_vscprintf(const char* format, va_list argptr ) { return _vscprintf(format, argptr); } static INLINE int ct_vstprintf_s(char* buffer, size_t numberOfElements, const char* format, va_list argptr) { return vsprintf_s(buffer, numberOfElements, format, argptr); } }; template<> struct ex_char_traits<wchar_t> { static INLINE int ct_vscprintf(const wchar_t* format, va_list argptr ) { return _vscwprintf(format, argptr); } static INLINE int ct_vstprintf_s(wchar_t* buffer, size_t numberOfElements, const wchar_t* format, va_list argptr) { return vswprintf_s(buffer, numberOfElements, format, argptr); } }; template<class _Elem, class _Traits, class _Ax, class Type> Type ConvertToNumber(basic_stringstream<_Elem, _Traits, _Ax>& ss, Type t, int base) { ss.setf(BaseFlag(base), ios_base::basefield); ss >> t; return t; } template<class _Elem, class _Traits, class _Ax> float ConvertToNumber(basic_stringstream<_Elem, _Traits, _Ax>& ss, float t, int/*ignore base*/) { ss >> t; return t; } template<class _Elem, class _Traits, class _Ax> double ConvertToNumber(basic_stringstream<_Elem, _Traits, _Ax>& ss, double t, int/*ignore base*/) { ss >> t; return t; } template<class _Elem, class _Traits, class _Ax, class _ExTraits> class basic_exstring : public basic_string<_Elem, _Traits, _Ax> { public: typedef basic_exstring<_Elem, _Traits, _Ax, _ExTraits> _Myt; typedef basic_string<_Elem, _Traits, _Ax> _Mybase; #pragma region "constructor" //一切結構函數的行動同basic_string explicit INLINE _Myt(const _Ax& al = _Ax()) :_Mybase(al) { } INLINE _Myt(const _Myt& rhs) :_Mybase(rhs) { } INLINE _Myt(const _Myt& rhs, size_type pos, size_type n,const _Ax& al = _Ax()) :_Mybase(rhs, pos, n, al) { } INLINE _Myt(const _Elem *s, size_type n, const _Ax& al = _Ax()) :_Mybase(s, n, al) { } INLINE _Myt(const _Elem *s, const _Ax& al = _Ax()) :_Mybase(s, al) { } INLINE _Myt(size_type n, _Elem c, const _Ax& al = _Ax()) :_Mybase(n, c, al) { } INLINE _Myt(const_iterator first, const_iterator last,const _Ax& al = _Ax()) :_Mybase(first, last, al) { } //string(wstring)轉化為exstring(exwstring) INLINE _Myt(const _Mybase& base) :_Mybase(base) { } #pragma endregion //constructor #pragma region "general operation" //一切字符轉為年夜寫,轉變本身 _Myt& toUpper() { transform(begin(), end(), begin(), toupper); return *this; } //一切字符轉為年夜寫,不轉變本身 _Myt toUpper() const { _Myt s; transform(begin(), end(), s.begin(), toupper); return s; } //一切字符轉為小寫,轉變本身 _Myt& toLower() { transform(begin(), end(), begin(), tolower); return *this; } //一切字符轉為年夜寫,不轉變本身 _Myt toLower() const { _Myt s(_Mysize, _Elem()); transform(begin(), end(), s.begin(), tolower); return s; } //將一切oldStr調換為newStr _Myt& replace(const _Myt& oldStr, const _Myt& newStr) { if (oldStr.empty()) return *this; size_type index; while ( (index = find(oldStr)) != npos ) _Mybase::replace(index, oldStr.size(), newStr); return *this; } //刪除右邊一切包括在target中的字符 _Myt& trimLeft(const _Myt& target) { while (!empty() && (target.find(*begin()) != npos)) erase(begin()); return *this; } //刪除左邊一切包括在target中的字符 _Myt& trimRight(const _Myt& target) { while (!empty() && target.find(*rbegin()) != npos) erase(--end()); return *this; } //前往右邊count個字符,count年夜於總長度則前往全部字符串 _Myt left(size_type count) const { return substr( 0, count ); } //前往左邊count個字符,count年夜於總長度則前往全部字符串 _Myt right(size_type count) const { return substr( _Mysize < count ? 0 : _Mysize - count ); } //疏忽年夜小寫斷定兩個字符串能否相等 int compareNoCase(const _Myt& rhs) const { return toLower().compare(rhs.toLower()); } //斷定字符串能否以制訂字符串開首 bool beginWith(const _Myt& rhs) const { return find(rhs) == size_type(0); } //斷定字符串能否以制訂字符串開頭 bool endWith(const _Myt& rhs) const { if(rhs.size() > _Mysize) return false; return compare(_Mysize - rhs.size(), rhs.size(), rhs) == 0; } #pragma endregion //general operation #pragma region "convert between numbers" //將字符串轉為數字 //base:進制數。可認為8,10,16,假如其它值則強迫為10。浮點數則疏忽此參數 template<typename T> T toNumber (int base = 10) const { T t = T(); basic_stringstream<_Elem, _Traits, _Ax> ss(_Myptr()); return ConvertToNumber<_Elem, _Traits, _Ax>(ss, t, base); } //將整數轉化為字符串 //base:進制數。可認為8,10,16,假如其它值則強迫為10 template<typename T> static _Myt fromNumber ( T number, int base = 10 ) { basic_stringstream<_Elem, _Traits, _Ax> ss; ss.setf(BaseFlag(base), ios_base::basefield); ss << number; return ss.str(); } //將float轉化為字符串 //f:格局化參數。可認為'f','e','E','g','G'。'f'為定點數,'e'或'E'表現迷信計數法 // 'g'或‘G'表現格局化為定點數或迷信計數法,看哪個表現便利。 //prec:小數點後的位數(定點數表現法)或總的有用位數(迷信計數法) static _Myt fromNumber ( float number, _Elem f = _Elem('g'), int prec = 6 ) { return fromNumber(static_cast<double>(number), f, prec); } //將double轉化為字符串,參數說明同上 static _Myt fromNumber ( double number, _Elem f = _Elem('g'), int prec = 6 ) { basic_stringstream<_Elem, _Traits, _Ax> ss; ss << setprecision(prec); if ( _Traits::eq(f, _Elem('f')) ) ss << setiosflags(ios_base::fixed); else if ( _Traits::eq(f, _Elem('e')) || _Traits::eq(f, _Elem('E')) ) ss << setiosflags(ios_base::scientific); ss << number; return ss.str(); } #pragma endregion //convert between numbers #pragma region "format string" //將szFormat格局化為字符串,參數說明同sprintf void format(const _Elem* szFormat, ...) { if(!szFormat) return; va_list argList; va_start(argList, szFormat); formatV(szFormat, argList); va_end(argList); } //將szFormat格局化為字符串,參數說明同sprintf void formatV(const _Elem* szFormat, va_list argList) { if(!szFormat) return; int nLength = _ExTraits::ct_vscprintf(szFormat, argList); if(nLength < 0) return; resize(nLength); _ExTraits::ct_vstprintf_s(_Myptr(), nLength + 1, szFormat, argList); va_end(argList); } #pragma endregion //format string }; typedef basic_exstring<char, char_traits<char>, allocator<char>, ex_char_traits<char> > exstring; typedef basic_exstring<wchar_t, char_traits<wchar_t>, allocator<wchar_t>, ex_char_traits<wchar_t> > exwstring;
應用舉例:
#include <iostream> #include <tchar.h> #include "exstring.h" #ifdef _UNICODE typedef exwstring tstring; #define tcout wcout #else typedef exstring tstring; #define tcout cout #endif //_UNICODE int main(int argc, char* argv[]) { tstring s(_T("\t Hello ExString\r\n")); tcout << _T("result of triming left:") << s.trimLeft(_T("\t ")) << endl; tcout << _T("result of triming right:") << s.trimRight(_T("\r\n")) << endl; tcout << _T("result of compare") << s.compareNoCase(_T("hello exstring")) << endl; tcout << _T("result of converting to upper:") << s.toUpper() << endl; tcout << _T("result of converting to lower:") << s.toLower() << endl; tcout << _T("the left 5 chars:") << s.left(5) << endl; tcout << _T("the right 8 chars:") << s.right(8) << endl; tcout << _T("result of appending:") << s.append(_T(",exstring is practical")) << endl; tcout << _T("result of replacing:") << s.replace(_T("exstring"), _T("Exstring")) << endl; s.format(_T("sizeof(%s) is %d(0x%x)"), _T("exstring"), sizeof(exstring), sizeof(exstring)); tcout << _T("result of formating:") << s << endl; tcout << tstring(_T("0xFF")).toNumber<int>(16) << endl; tcout << tstring(_T("-1")).toNumber<unsigned __int64>() << endl; tcout << tstring(_T("12.3456789")).toNumber<float>() << endl; tcout << tstring::fromNumber(255) << endl; tcout << _T("0x") << tstring::fromNumber(__int64(-1), 16).toUpper() << endl; tcout << tstring::fromNumber(12.3456789, _T('f'), 4) << endl; tcout << tstring::fromNumber(12.3456789, _T('E'), 4) << endl; return 0; }
輸入: