在C語言中,string是一個標準庫類(class),用于處理字符串,它提供了一種更高級、更便捷的字符串操作方式,string?類提供了一系列成員函數和重載運算符,以便于對字符串進行操作和處理。
一、string類
在學習 string 前,我們不妨先來了解一下 string 類到底是什么,有什么用呢?我們先來了解一下基本的概念吧
C++標準庫都是英語解釋。我們也應該試著去適應,不懂的可以查閱。當然,在這里我就直接給出翻譯,主要是以下內容:
字符串是表示字符序列的類;
標準的字符串類提供了對此類對象的支持,其接口類似于標準字符容器的接口,但添加了專門用于操作單字節字符字符串的設計特性。
string類是使用char(即作為它的字符類型,使用它的默認char_traits和分配器類型(關于模板的更多信息,請參閱basic_string)。
string類是basic_string模板類的一個實例,它使用char來實例化basic_string模板類,并用char_traits和allocator作為basic_string的默認參數(根于更多的模板信息請參考basic_string)。
注意,這個類獨立于所使用的編碼來處理字節:如果用來處理多字節或變長字符(如UTF-8)的序列,這個類的所有成員(如長度或大小)以及它的迭代器,將仍然按照字節(而不是實際編碼的字符)來操作。
二、string的常用見用法
2.1 string對象的構造
2.1.1?string對象的構造的使用方法
最為常用的無非就是我們用串string來構造一個對象,也就是存儲一個字符,常用的方法有如下幾點:
string()——構造空的?string?類對象,即空字符串;
string(const char* s)——用 char*?來構造?string?類對象;
string(size_t n, char c)——string類對象中包含n個字符c;
string(const string&s)——拷貝構造函數。
下面是使用方法所對應的實例,幫助更好的理解其用法。
三、string常用結構的底層實現
3.1 初建結構
我們通過上述的構造,不難發現也不難理解string的底層其實就是一個字符指針,該指針指向一個數組。當然,我們還需要兩個變量來維護其有效長度(_size)和數組容量(_capacity)。
其次,我們自己實現的string類為了區分std命名空間,我們可自己設置一個命名空間。處型的模擬實現如下:
namespace gtm
{class string{public://string()// :_str(new char[1])// , _size(0)// ,_capacity(0)//{//}//string(const char* str)// :_str(new char[strlen(str) + 1]) //三次strlen函數,效率低。// ,_size(strlen(str))// ,_capacity(strlen(str))//{// strcpy(_str, str);//}// 不再使用strlen函數,初始化列表與變量聲明順序固定string(const char* str = "") //默認空串。注意:空串是以 \0 結尾{_size = strlen(str);_capacity = _size;_str = new char[_size + 1];strcpy(_str, str);}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}private:char* _str;size_t _size;size_t _capacity;};
3.2 返回大小和容量
這兩個部分,是比較容易實現的兩部分。同時也是較為常用的兩部分。具體如下:
size_t size() const{return _size;}size_t capacity() const{return _capacity;}
3.3 拷貝構造和賦值重載
這兩部分較為復雜的兩部分。其中均需要深拷貝去實現完成,而淺拷貝是不可以的。注意:拷貝構造使用一個已定義變量去初始化另一個變量,賦值重載是兩個已定義變量進行賦值。
具體實現如下:
//深拷貝//string(const string& s)// :_str(new char[s._capacity+1])// ,_size(s._size)// ,_capacity(s._capacity)//{// strcpy(_str, s._str);//}void swap(string& tmp){//調用全局的swap::swap(_str, tmp._str);::swap(_size, tmp._size);::swap(_capacity, tmp._capacity);}//借助變量tmpstring(const string& s):_str(nullptr) , _size(0), _capacity(0){string tmp(s._str);swap(tmp);}//賦值//string& operator=(const string& s)//{// if(this == &s)// {// return *this;// }// //先開空間拷貝數據,以防new失敗銷毀原來的空間// char* tmp = new char[s._capacity + 1];// strcpy(tmp, s._str);// delete[] _str;// _str = tmp;// _size = s._size;// _capacity = s._capacity;// return *this;// //delete[] _str;// //_str = new char[s._capacity + 1];// //strcpy(_str, s._str);// //_size = s._size;// //_capacity = s._capacity;// return *this;//}//string& operator=(const string& s)//{// if(this == &s)// {// return *this;// }// string tmp(s._str);// swap(tmp);// return *this;//}string& operator=(string s){if (this == &s){return *this;}swap(s);return *this;}
上述的輔助重載我們巧妙地借助了臨時變量s。當賦值完成后,出了作用域s會自動調用戲后進行銷毀,這里是需要反復理解的。
3.4 擴容(reserve)
我們可簡單的理解reserve為擴容(擴容的前提為要求的容量比原來的大),但是我們要記得把字符數組中原有的內容拷貝過來,并且釋放之前所動態開辟的空間。?具體實現如下:
void reserve(size_t capacity){if (capacity > _capacity){char* tmp = new char[capacity + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = capacity;}}
3.5 插入(push_back、append、operator+=、insert)
插入的實現,主要的點就是是否要進行擴容。其次,當我們實現push_back和append后,其他的均可復用這兩個結構進行實現。具體實現如下:
void push_back(char ch){if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (len + _size > _capacity){reserve(len + _size >= _capacity * 2 ? len + _size : _capacity * 2);}strcpy(_str + _size, str);_size += len;}void append(const string& s){append(s._str);}void append(int n, char ch){reserve(_size + n);for (int i = 0; i < n; i++){push_back(ch);}}string& operator+= (char ch){push_back(ch);return *this;}string& operator+= (const char* str){append(str);return *this;}string& insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//注意,當運算數一個是有符號,另一個是無符號時,有符號的運算數會強制類型轉換為無符號數。pos等于0的位置插入,end--后為超大數據,會出錯。//int end = _size;//while (end >= (int)pos)//{// _str[end + 1] = _str[end];// end--;//}size_t end = _size+1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;return *this;}string& insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (len + _size > _capacity){reserve(len + _size >= _capacity * 2 ? len + _size : _capacity * 2);}size_t end = _size + len;while (end >= pos+len){_str[end] = _str[end - len];end--;}for (int i = pos,j=0; j < len;j++, i++){_str[i] = str[j];}_size += len;return *this;}
string 在C++中算是比較重要的了,也是入門時必須所學的容器。在平常中使用的頻率較高,所以我們不僅要掌握其簡單的用法,更應該去了解其底層的實現。這有助于我們后續的使用和理解。本篇文章列舉出了string中常用的語法和接口底層的底層實現,這些都是我們應該熟練掌握的內容。