在傳統的C/C++語言中,對字符串的處理比較麻煩,基本都是用char*來操作,而指針又往往是一個初學者的噩夢。STL為了解決這個問題,提供了std::string這個數據結構,其實它就是一個類,不過其提供了常見的對字符串的
操作符的重載,實現在實際工程中經常遇到的字符串的長度計算,拼接和裁剪以及和C類型字符串的轉換。它不算是STL的容器,它只是一個類。
在面試時經常有這種題,讓自己實現一個簡單的字符串類,下面是比較常見的實現的方式:
class mystring
{
public:
mystring() : str_(new char[1])
{
* str_ = '\0';
}
mystring(const char* str): str_(new char[strlen(str) + 1])
{
strcpy(str_, str);
}
//C++11的右值語義支持
mystring(mystring&& mstr) noexcept: str_(mstr.str_)
{
mstr.str_ = nullptr;
}
mystring(const mystring& mstr): str_(new char[mstr.str_size() + 1])
{
strcpy(str_, mstr.to_char());
}
//C++11可以代替上面函數
//mystring(const mystring& mstr): mystring(mstr.str_)
//{}
//c++11異常控制
~mystring() noexcept
{
delete[] str_;
}
mystring & operator=(mystring mstr)
{
//這裡如果使用直接拷貝可能更容易理解
swap(mstr);
return *this;
}
size_t str_size() const
{
return strlen(str_);
}
const char* to_char() const
{
return str_;
}
void swap(mystring& mstr)
{
std::swap(str_, mstr.str_);
}
private:
char* str_;
};
這個是參考《Effect c++》和網上c++大牛陳碩的相關資料完善的。基本的方法都類似,主要就在於對內存空間的利用和優化。這個在網上的爭論很多,如果有興趣可以去網上搜索一下。這裡就不再展開了。
庫中的實際實現:
template <class _Elem, class _Traits = char_traits<_Elem>, class _Alloc = allocator<_Elem>>
class basic_string { // null-terminated transparent array of elements
private:
friend _Tidy_deallocate_guard<basic_string>;
using _Alty = _Rebind_alloc_t<_Alloc, _Elem>;
using _Alty_traits = allocator_traits<_Alty>;
using _Scary_val = _String_val<conditional_t<_Is_simple_alloc_v<_Alty>, _Simple_types<_Elem>,
_String_iter_types<_Elem, typename _Alty_traits::size_type, typename _Alty_traits::difference_type,
typename _Alty_traits::pointer, typename _Alty_traits::const_pointer, _Elem&, const _Elem&>>>;
.
public:
basic_string(initializer_list<_Elem> _Ilist, const _Alloc& _Al = allocator_type())
: _Mypair(_One_then_variadic_args_t{}, _Al) {
auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal());
_Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2);
_Tidy_init();
assign(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
_Proxy._Release();
}
basic_string& operator=(initializer_list<_Elem> _Ilist) {
return assign(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
}
basic_string& operator+=(initializer_list<_Elem> _Ilist) {
return append(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
}
basic_string& assign(initializer_list<_Elem> _Ilist) {
return assign(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
}
basic_string& append(initializer_list<_Elem> _Ilist) {
return append(_Ilist.begin(), _Convert_size<size_type>(_Ilist.size()));
}
basic_string& assign(const basic_string& _Right) {
*this = _Right;
return *this;
}
basic_string& assign(const basic_string& _Right, const size_type _Roff, size_type _Count = npos) {
// assign _Right [_Roff, _Roff + _Count)
_Right._Mypair._Myval2._Check_offset(_Roff);
_Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count);
return assign(_Right._Mypair._Myval2._Myptr() + _Roff, _Count);
}
.
}
你會發現庫的實現要複雜很多,但仔細一看,除了各種安全機制和內存控制機制,其實和上面的常見的編寫原理完全一致。這恐怕也是面試者出這個題目的一個主要原因,只要掌握了基本原理,再搞其它的基本都是觸類旁通。
三、字符串的操作1、插入和連接
void TestString()
{
std::string s1 = "abcd";
std::string s2 = "efghi";
std::string s_add = s1 + s2;
std::cout << "string content:" << s_add<< std::endl;
s_add += "123";
std::cout << "string content:" << s_add << std::endl;
s_add.insert(3, "0");
std::cout << "string content:" << s_add << std::endl;
std::cout << "string size:" << s1.size()<<" "<<s2.size() << std::endl;
}
2、查找和替換
void FindR()
{
std::string s = "are you ok!";
std::cout << "y pos:" << s.find('y') << std::endl;
s.replace(2,3,"tt");//從索引2,替換三個字符長度為tt
std::cout << "s content:" << s << std::endl;
std::cout << s.substr(2, 5) << std::endl;
}
int main()
{
TestString();
FindR();
return 0;
}
運行結果是:
string content:abcdefghi
string content:abcdefghi123
string content:abc0defghi123
string size:4 5
y pos:4
s content:arttou ok!
ttou
這個類的功能有很多,除了上面的這些,還包括追加、擦除、流操作、比較、複製等等。但是相比於其它高級語言缺少太多的相關功能如trim,upper,lower,strip以及向json、xml等轉換的函數。
這裡仍然提醒注意的是,新標準在快速迭代,要注意更新標準的知識。比如在c++11中增加了大量的數字和字符串的直接轉換操作,增加類似棧操作的POP和相關的front等。具體的細節這裡就不再一一展開,用到哪個後,直接查詢一下相關的幫助或者c++開發網站,即可正確使用。
總體來說,string這個類在c++的開發應用中是非常普遍的,不管是小白和老鳥,都會經常用到。不過這個類的功能還是有些簡單,比如對字符串的一些分割split,仍然需要自己手動處理。對正則的支持也是一點點開始有的,所以網上有很多個人和公司開發的強大的string類,用來在c++開發中替換標準庫的std::string,這個在實際使用中要注意區別。
另外這個類有一個比較致命的缺點,它沒有區別寬字符集和ASCII字符集等,所以在實際應用中,一定要看清楚應用的場景,小心對待。這個在實際開發中,很多人進過坑。總之,這個類功能不算強大,但是很常用,能在這個類的基礎上用好,並能自己進行二次擴展,就說明你基本掌握了這個類,不要急,慢慢來。