STL容器:string類的簡介與使用

傳統的C

  1. 沒有專門的字符串類型,需要使用常量字符串或者字符數組來使用
  2. 在實際運用中,一般使用字符串函數來處理

string類基本操作

  1. string是表示字符串的字符串類
  2. 該類的接口與常規容器的接口基本相同,再添加了一些專門用來操作string的常規操作
  3. string在底層實際是:basic_string模板類的別名
    typedef basic_string<char, char_traits, allocator> string;
  4. 不能操作多字節或者變長字符的序列
  5. 在使用string類時,必須包含#include<string>以及using namespace std;

string類對象的常見構造

函數名稱 功能說明
string() 構造空的string類對象,即空字符串
string(const char* s) 用C-string來構造string類對象
string(size_t n, char c) string類對象中包含n個字符c
string(const string& s) 拷貝構造函數
void TestString()
{
    string s1; // 構造空的string類對象s1
    string s2("hello world"); // 用C格式字符串構造string類對象s2
    string s3(s2); // 拷貝構造s3
}

string類對象的容量操作

函數名稱 功能說明
size 返回字符串有效字符長度
length 返回字符串有效字符長度
capacity 返回空間總大小
empty 檢測字符串釋放爲空串,是返回true,否則返回false
clear 清空有效字符
reserve 爲字符串預留空間即擴容
resize 將有效字符的個數該成n個,多出的空間用字符c填充
//size、length、capacity、resize
void TestString()
{
    string s("hello,world!");
    cout << s.size() << endl;
    cout << s.length() << endl;
    cout << s.capacity() << endl;
    cout << s << endl;

    //將s中的字符串清空,注意清空時只是將size清0,不改變底層空間的大小
    s.clear();
    cout << s.size() << endl;
    cout << s.capacity() << endl;

    //將s中有效字符個數增加到10個,多出位置用'a'進行填充
    //"aaaaaaaaaa"
    s.resize(10, 'a');
    cout << s.size() << endl;
    cout << s.capacity() << endl;
    cout << s << endl;

    //將s中有效字符個數增加到15個,多出位置用缺省值'\0'進行填充
    //"aaaaaaaaaa\0\0\0\0\0"
    //注意此時s中有效字符個數已經增加到15個
    s.resize(15);
    cout << s.size() << endl;
    cout << s.capacity() << endl;
    cout << s << endl;

    //將s中有效字符個數縮小到5個
    s.resize(5);
    cout << s.size() << endl;
    cout << s.capacity() << endl;
    cout << s << endl;
}

注意:

  1. size()與length()方法底層實現原理完全相同,引入size()的原因是爲了與其他容器的接口保持一致,一般情況下基本都是用size()。
  2. clear()只是將string中有效字符清空,不改變底層空間大小。
  3. resize(size_t n) 與 resize(size_t n, char c)都是將字符串中有效字符個數改變到n個,不同的是當字符個數增多時:resize(n)用\0來填充多出的元素空間,resize(size_t n, char c)用字符c來填充多出的元素空間。注意:resize在改變元素個數時,如果是將元素個數增多,可能會改變底層容量的大小,如果是將元素個數減少,底層空間總大小不變。
  4. reserve(size_t res_arg=0):爲string預留空間,不改變有效元素個數,當reserve的參數小於string的底層空間總大小時,reserve不會改變容量大小。

string類對象的訪問及遍歷操作

函數名稱 功能說明
operator[] 返回pos位置的字符
begin + end begin獲取第一個字符的迭代器 + end獲取最後一個字符下一個位置的迭代器
rbegin + rend rbegin返回指向字符串的最後一個字符的反向迭代器 + rend返回指向字符串的第一個字符前一個位置的反向迭代器
範圍for C++11支持更簡潔的範圍for的新遍歷方式
void TestString1()
{
    string s1("hello world");
    const string s2("Hello World");
    cout << s1 << " " << s2 << endl;
    cout << s1[0] << " " << s2[0] << endl;

    s1[0] = 'H';
    cout << s1 << endl;

    //編譯失敗,因爲const類型對象不能修改
    //s2[0] = 'h';
}
void TestString2()
{
    string s("hello world");
    // 3種遍歷方式:
    // 需要注意的以下三種方式除了遍歷string對象,還可以遍歷是修改string中的字符
    // 且以下三種方式對於string而言,第一種使用最多

    // 1. for+operator[]
    for (size_t i = 0; i < s.size(); ++i)
    {
        cout << s[i];
    }
    cout << endl;

    // 2.迭代器
    string::iterator it = s.begin();
    while (it != s.end())
    {
        cout << *it;
        ++it;
    }
    cout << endl;

    // 反向迭代器
    string::reverse_iterator rit = s.rbegin();
    while (rit != s.rend())
    {
        cout << *rit;
        ++rit;
    }
    cout << endl;
        
    // 3.範圍for
    for (auto ch : s)
    {
        cout << ch;
    }
    cout << endl;
}

string類對象的修改操作

函數名稱 功能說明
push_back 在字符串後尾插字符c
append 在字符串後追加一個字符串
operator+= 在字符串後追加字符串str
c_str 返回C格式字符串
find + npos 從字符串pos位置開始往後找字符c,返回該字符在字符串中的位置
rfind 從字符串pos位置開始往前找字符c,返回該字符在字符串中的位置
substr 在str中從pos位置開始,截取n個字符,然後將其返回
void TestString()
{
    string str;
    str.push_back(' ');  // 在str後插入空格
    str.append("hello"); // 在str後追加一個字符"hello"
    str += 'w';          // 在str後追加一個字符'w'
    str += "orld";       // 在str後追加一個字符串"orld"
    cout << str << endl;
    cout << str.c_str() << endl; // 以C語言的方式打印字符串

    // 獲取file的後綴
    string file("string.cpp");
    size_t pos = file.rfind('.');
    string suffix(file.substr(pos, file.size() - pos));
    cout << suffix << endl;

    // npos是string裏面的一個靜態成員變量
    // static const size_t npos = -1;

    // 取出url中的域名
    string url("http://www.cplusplus.com/reference/string/string/find/");
    cout << url << endl;
    size_t start = url.find("://");
    if (start == string::npos)
    {
        cout << "invalid url" << endl;
        return;
    }
    start += 3;
    size_t finish = url.find('/', start);
    string address = url.substr(start, finish - start);
    cout << address << endl;

    // 刪除url的協議前綴
    pos = url.find("://");
    url.erase(0, pos + 3);
    cout << url << endl;
}

注意:

  1. 在string尾部追加字符時,s.push_back(c); s.append(1, c); s += 'c'三種的實現方式差不多,一般情況下string類的+=操作用的比較多,+=操作不僅可以連接單個字符,還可以連接字符串。
  2. 對string操作時,如果能夠大概預估到放多少字符,可以先通過reserve把空間預留好。

string類非成員函數

函數名稱 功能說明
operator+ 儘量少用,因爲傳值返回,導致深拷貝效率低
operator>> 輸入運算符重載
operator<< 輸出運算符重載
getline 獲取一行字符串

string類深淺拷貝

淺拷貝
string淺拷貝
上述string類沒有顯式定義其拷貝構造函數與賦值運算符重載,此時編譯器會合成默認的,當用s1構造s2時,編譯器會調用默認的拷貝構造。最終導致的問題是,s1、s2共用同一塊內存空間,在釋放時同一塊空間被釋放多次而引起程序崩潰,這種拷貝方式,稱爲淺拷貝。

深拷貝
如果一個類中涉及到資源的管理,其拷貝構造函數、賦值運算符重載以及析構函數必須要顯式給出。一般情況都是按照深拷貝方式提供。
string深拷貝

簡單的模擬實現string類(造輪子)

只考慮深淺拷貝。

傳統寫法

namespace MakeString
{
	class string
	{
	public:
		string(const char* str = "")
			: _str(new char[strlen(str) + 1])
		{
			strcpy(_str, str);
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
		}

		string(const string& s)
			: _str(new char[strlen(s._str) + 1])
		{
			strcpy(_str, s._str);
		}

		string& operator=(const string& s)
		{
			if (this != &s)
			{
				delete[] _str;
				_str = new char[strlen(s._str) + 1];
				strcpy(_str, s._str);
				return *this;
			}
		}

		char& operator[](size_t index)
		{
			return _str[index];
		}

		const char& operator[](size_t index) const
		{
			return _str[index];
		}

		const char* c_str() const
		{
			return _str;
		}

		const size_t size() const
		{
			return strlen(_str);
		}

	private:
		char* _str;
	};
}

現代寫法

namespace MakeString
{
	class string
	{
	public:
		string(const char* str = "")
			: _str(new char[strlen(str) + 1])
		{
			strcpy(_str, str);
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
		}

		string(const string& s)
		{
			_str = nullptr;
			string tmp(s._str); //調用構造函數
			std::swap(_str, tmp._str);
		}

		string& operator=(string s)
		{
			std::swap(_str, s._str);
			return *this;
		}

		char& operator[](size_t index)
		{
			return _str[index];
		}

		const char& operator[](size_t index) const
		{
			return _str[index];
		}

		const char* c_str() const
		{
			return _str;
		}

		const size_t size() const
		{
			return strlen(_str);
		}

	private:
		char* _str;
	};
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章