天天看點

STL容器:string類的簡介與使用傳統的Cstring類基本操作string類深淺拷貝簡單的模拟實作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類深淺拷貝

淺拷貝:

STL容器:string類的簡介與使用傳統的Cstring類基本操作string類深淺拷貝簡單的模拟實作string類(造輪子)

上述string類沒有顯式定義其拷貝構造函數與指派運算符重載,此時編譯器會合成預設的,當用s1構造s2時,編譯器會調用預設的拷貝構造。最終導緻的問題是,s1、s2共用同一塊記憶體空間,在釋放時同一塊空間被釋放多次而引起程式崩潰,這種拷貝方式,稱為淺拷貝。

深拷貝:

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

STL容器:string類的簡介與使用傳統的Cstring類基本操作string類深淺拷貝簡單的模拟實作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;
	};
}
           

繼續閱讀