前言
說起string類,首先需要了解的是 - string類是什么?
std::string是類模板std::basic_string的一個元素類型為char的實例化,而basic_string則是對元素指針的封裝。由于basic_string的實現對字符串操作進行了優化,所以它不能用來表示除char以外的對象串。但是使用string類也避免了和'\0'的糾纏不休,也就是說,string并不關心它所代表的字符串有無'\0'作為字符串的結束標志,相反'\0'是它的一個合法元素。
string類的接口與常規容器的接口基本相同,但在此基礎上也添加了一些專門用來操作string的常規接口。在使用string類時,必須包含#include頭文件以及using namespace std;另外,想要更加深入的了解string類,請點擊string類的文檔介紹~~
string類的常用接口
因為string類的接口特別多(106個接口函數),所以這里只介紹幾個常用且重要的接口。
1.string類對象的常見構造
函數說明 | 功能說明 | 代碼演示 |
string() | 構造空的string類對象,即空字符串 | string s1; |
string(const char* str) | 用C-string來構造string類對象 | string s2("hello"); |
string(const char& str) | 拷貝構造函數 | string s3(s2); |
2.string類對象的容量操作
函數名稱 | 功能說明 | 函數原型 |
reserve | 為字符串預留空間 | void reserve(size_t n = 0); |
resize | 將有效字符個數改成n個,多出的空間用char c填充 | void resize(size_t n); void resize(size_t n, char c); |
clear | 清空有效字符 | void clear(); |
特別說明:
- 使用reserve擴容時,只會影響capacity,并不會影響size;默認不會縮容
- 使用resize擴容,不僅會影響capacity,也會影響size,并且會將開出的空間初始化為'\0'(不顯示寫char c的時候);縮容的時候只會影響size
3. string類對象的訪問及遍歷操作
函數名稱 | 功能說明 | 函數原型 |
operator[] | 返回pos位置的字符 | char& operator[] (size_t pos); |
begin + end | begin獲取一個字符的迭代器? end獲取最后一個字符下一個位置的迭代器 | iterator begin(); iterator end(); |
4. string類對象的修改操作
函數名稱 | 功能說明 | 函數原型 |
operator+= | 在字符串后追加字符串str | string& operator+= (char c); string& operator+= (const char* s); |
c_str | 返回指向C格式字符串的指針 | const char* c_str() const; |
find | 從字符串pos位置開始往后找字符c, 返回該字符在字符串中的位置 | size_t find(char c,size_t pos = 0) const; size_t find (const char* s, size_t pos = 0) const; |
特別說明:
- 在string尾部追加字符時,s.push_back(c) / s.append(1, c) / s += 'c'三種的實現方式差不多,一般情況下string類的+=操作用的比較多,因為+=操作不僅可以連接單個字符,還可以連接字符串。
5. string類非成員函數
函數名稱 | 功能說明 | 函數原型 |
operator>> | 輸入運算符重載 | istream& operator>> (istream& is, string& str); |
operator<< | 輸出運算符重載 | ostream& operator<< (ostream& os, const string& str); |
getline | 獲取一行字符串 | istream& getline (istream& is, string& str); |
?特別說明:
- ????????getline –> 獲取一行數據(包括空格),cin不能獲取空格,遇到空格就結束
vs和g++下string結構的說明
vs下string的結構
union _Bxty
{ // storage for small buffer or pointer to larger onevalue_type _buff[_BUF_SIZE];pointer _Ptr;char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;
?string在vs下總共占28個字節,內部結構稍微復雜一點,先是有一個聯合體,聯合體用來定義string中字符串的存儲空間:其實當size()的長度小于_buff的長度時,會優先存儲在buff里面;當size()的長度大于_buff的長度時,才會存儲在堆上新開辟的空間(擴容)里面。
這種設計也是有一定道理的,大多數情況下字符串的長度都小于16,那string對象創建好之后,內部已經有了16個字符數組的固定空間,不需要通過堆創建空間,效率高。
g++下string的結構
struct _Rep_base
{size_t _M_size;size_t _M_capacity;_Atomic_word _M_refcount;
}
?g++下,string是通過寫時拷貝實現的,string對象總共占4個字節,內部只包含了一個指針,該指針將來指向一塊堆空間,內部包含了如下字段:
- 空間總大小
- 字符串有效長度
- 引用計數
- 指向堆空間的指針,用來存儲字符串
string類的模擬實現
與常用接口類似,這里只實現一些常用的
1.string類的默認成員函數
//構造函數
string(const char* str = "") //字符串后面默認有一個\0,所以這里缺省值不需要 \0: _size(strlen(str))
{_str = new char[_size + 1];_capacity = _size;strcpy(_str, str);
}
//拷貝構造
//傳統寫法
string(const string& s)
{char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);_str = tmp;_size = s._size;_capacity = s._capacity;
}//現代寫法
string(const string& s)
{string tmp(s._str); //調用構造swap(tmp); //std::swap(tmp._str, _str);std::swap(tmp._size, _size);std::swap(tmp._capacity, _capacity);
}
//賦值重載
//傳統寫法
string& operator=(const string& s)
{if (this != &s) //防止出現自己給自己賦值的情況{char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;
}
//現代寫法1
string& operator=(const string& s)
{if (this != &s){string tmp(s._str); //調用構造swap(tmp); //std::swap(tmp._str, _str);std::swap(tmp._size, _size);std::swap(tmp._capacity, _capacity);}return *this;
}
//現代寫法2
string& operator=(string tmp)
{swap(tmp); //調用拷貝構造return *this;
}
?2string類對象的修改相關函數
//查找一個字符串里是否有相應的字符
size_t find(char ch, size_t pos = 0)
{assert(pos < _size);for (int i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;
}
//查找一個字符串里是否有相應的子串
size_t find(const char* sub, size_t pos = 0)
{assert(pos < _size);char* ptr = strstr(_str + pos, sub);return ptr - _str;
}
//查找子串
string substr(size_t pos = 0, size_t len = npos)
{assert(pos < _size);if (len > _size - pos){string sub(_str + pos); //構造一個子串return sub;}else{string sub;sub.reserve(len); //為構造的子串開空間for (int i = 0; i < len; i++){sub += _str[pos + i]; //尾插}return sub;}
}
?3.string類非成員函數
//流提取
ostream& operator<<(ostream& os, string& str)
{for (size_t i = 0; i < str.size(); i++){os << str[i];}return os;
}
//流插入
istream& operator>>(istream& is, string& str)
{str.clear(); //清空有效數據char buff[128]; //開一個char類型的最大數組int i = 0;char ch = is.get();while (ch != ' ' && ch != '\n'){buff[i++] = ch; //讀到的數據放到數組buff里if (i == 127) {buff[i] = '\0';str += buff;//i = 0;}ch = is.get();}if (i != 0){buff[i] = '\0';str += buff;}return is;
}