C++中,由于字符串一開始并非內置的類型,于是,史前時代,江湖上出現了種種的字符串,各有各的優(yōu)點,但自然,也各有各的缺陷,群雄割據,天下大亂,民不聊生。大伙兒盼星星,盼月亮,盼著真正的字符串能出來一統江湖,好不容易,等到1998的C++標準出來了,官方的字符串string終于露面了,自然,它并不是語言內置支持的,而是在STL庫中。當然,字符串這東西,就算要在C++編譯器的層面上給予支持,其實也很不容易。可是,這個官方的string,單純它復雜的模板定義和那一大陀函數成員,就足以嚇退眾多意志不堅定之人。好了,好不容易,鼓起勇氣,再仔細瞅瞅string中的東西,發(fā)現它也不是很美妙,既不強大,部分函數的功能存在重復又或者STL的算法中已有提供,更要命的是,效率也不怎么高。總之,不能說string的設計不如史前的各種stringS,但也強不到那里去。當然,官方的總比非官方的更具權威,但每次使用string時,想到它背地里除了正常字符串操作之外,還可能做了各種的低效的內存分配釋放的操作,又或者線程安全,又或者引用計數,內心就一直惴惴不安。于是,寧愿一再小心翼翼地用著字符數組。但是,用得多了,也很郁悶,字符數組自然高效、靈活,但總是要千編一律地一再寫著容易出錯的代碼,我脆弱的心靈終于頭暈眼花了。于是,我決定按照自己的意愿寫一個字符串,不敢欲與群雄爭鋒,只是,為了能夠在自己的代碼中,替換字符數組。我對它的要求是,字符數組能做到的事情,它也要做到,并且,效率上,絕不妥協。
俗話說,過早的優(yōu)化是萬惡之源。但在此,在設計本家字符串時,一開始,就要考慮到效率的細節(jié)上去了。首先,它要支持堆棧變量的形式,不要它進行內存的分配釋放操作,就好像堆棧上的字符數組那樣。咦,好像很神奇,其實,只要想到TR1中那個經典的array,通過使用神奇的模板技術,就有辦法做到了。所以,此字符串的使用好比這樣子,CStackString <MAX_PATH> sFile,暫時假定這個字符串的名字叫CStackString。
但是,使用模板之后,字符串的字符數組的長度只要不一樣,它們就都屬于不同類型變量,并且之間還都不兼容呢,雖然它們都是字符串。此外,還會編譯器還將生產出一堆重復的代碼。這無論如何,都不能忍受。于是,自然而然,就想到了繼承。CStackString模板類繼承于非模板的mybasestring,mybasestring中實現了CStackString的各種各樣的操作,而CStackString只要仿照array那樣子,定義好自己的數據成員即可。嗯,還是看看代碼,馬上就明白到底是怎么一回事了。

class CMyBaseString


{
public:
typedef size_t size_type;
typedef char *pointer;
typedef const char *const_pointer;
typedef CMyBaseString _Myt;

public:
char operator[](size_type nPos)const

{
assert(nPos < m_nLen);
return m_str[nPos];
}


const_pointer c_str()const
{ return m_str; }

const_pointer right(size_type nLen)const

{
assert(nLen < m_nLen);
return m_str+m_nLen-nLen;
}

int compare(const_pointer str)const

{
return strcmp(m_str, str);
}

_Myt& assign(const char* str)

{
m_nLen = strlen(str);
assert(m_nLen < m_nBuffSize);
strcpy(m_str, str);
return *this;
}

_Myt& append(const_pointer str)

{
size_type nLen = strlen(str);
assert(m_nLen + nLen < m_nBuffSize);
strcpy(m_str+m_nLen, str);
m_nLen += nLen;
return *this;
}

_Myt& format(const_pointer sFormat,
)

{
va_list argList;
va_start( argList, sFormat );
m_nLen = vsprintf(m_str, sFormat, argList);
va_end( argList );
assert(m_nLen < m_nBuffSize);
return *this;
}

//
.

protected:
CMyBaseString(pointer sBuf, size_type nBuffSize)

{
m_nBuffSize = nBuffSize;
m_nLen = 0;
m_str = sBuf;
m_str[0] = 0;
}

private:
size_type m_nBuffSize;
size_type m_nLen;
pointer m_str;
};

template<size_t _size>
class CStackString : public CMyBaseString


{
public:

CStackString(const char* str) : CMyBaseString(m_sMine, _size)
{ assign(str);}

CStackString() : CMyBaseString(m_sMine, _size)
{}

private:
char m_sMine[_size];
};
int main()


{
CStackString<20> sTest("hello");
cout << sTest.c_str() << endl;
cout << sTest.right(3) << endl;
return 0;
}
于是通過基類mybasestring,各種不同類型的template CStackString就又聯系在一塊了。Mybasestring可看成定義了數據接口的基類,其子類的頭部數據必須與它保持一致,嗯,很好。然后,在mybasestring中實現的各種功能,都可以用在mystring身上了,而CStackString中無須實現任何功能,它只負責在堆棧上分配內存。所以,mybasestring不僅可以用在堆棧上,還能用于堆上,只要再繼續(xù)定義一個能在堆上分配內存的mybasestring的子類即可,然后都能相容于堆棧上的CStackString字符串。
……。 經過一番努力,這個字符串類幾乎包含了字符數組的一切基本功能,基本上可代替字符數組了。為了能夠用到STL中的各種算法,再在其上增加一些返回迭代器的函數,好比begin,end,rbegin,rend,它們都很容易實現。還有,為了使用起來更加友好方便更具效率,貌似應該再實現一些全局操作符的重載運算;……;好了,打住。如果打算將這個字符串很好地融入到STL中,需要做出更多的努力。原本只打算代替字符數組而已。代碼在VC2005以上版本編譯時,會出現一些警告,可以用#pregma的指令將其disable掉,或者使用其中的所謂的安全字符串操作函數。
好不容易,終于就實現了一個基于堆棧上的字符串,它是窄字符的。別忘了,還有寬字符的字符串呢。這也沒什么,只須將mybasestring重命名為CMyBaseStringA,CStackString改為CStackStringA。然后再分別實現與CMyBaseStringA與CStackStringA同樣接口的CMyBaseStringW和CStackStringW。然后,再針對UNICODE,Typedef或defined一對CMyBaseStringT和CStackStringT。在這里,并不想模仿STL中的string或MFC中的CString那樣子,template一個basic_string(simple_string),然后分別進行模板特化,近來越看越覺得這種模板特化的方式相當惡心,只是將代碼搞得更加復雜,卻沒帶來多大的好處。
相比于其他的字符串實現,這個mybasestring不過是將內存分配拱手讓人罷了。這樣一來,就帶來一些新的問題。首先,它要假設其子類給它分配了足夠的內存,不過,在C++傳統,經常假設用戶分配了足夠的內存;然后,因為脫離了內存管理,有一些功能自然也就無法實現出來了,C的字符串函數也還不是這樣,當緩沖溢出時,該崩潰就還得崩潰。
再次向C++的無所不能頂禮膜拜。C++,你是電,你是光, 你是唯一的神話, 我只愛你,You are my Super Star!
再次聲明,本字符串只為取代字符數組,至于其它的種種無理要求,均不在本座的考慮范圍之內。