C++STL學習第一篇(什麼是STL以及string的各種功能用法)

来源:https://www.cnblogs.com/ivanlee717/p/18057918
-Advertisement-
Play Games

STL STL提供了六大組件,彼此之間可以組合套用,這六大組件分別是:容器、演算法、迭代器、仿函數、適配器、空間配置器。 數據結構和容器管理:STL 提供了多種數據結構和容器,如向量(vector)、鏈表(list)、集合(set)、映射(map)等。這些容器可以幫助程式員方便地存儲和管理數據,根據需 ...


STL

STL提供了六大組件,彼此之間可以組合套用,這六大組件分別是:容器、演算法、迭代器、仿函數、適配器、空間配置器。

  1. 數據結構和容器管理:STL 提供了多種數據結構和容器,如向量(vector)、鏈表(list)、集合(set)、映射(map)等。這些容器可以幫助程式員方便地存儲和管理數據,根據需求進行動態調整和操作。
  2. 演算法和數據處理:STL 中提供了大量的演算法,如排序、查找、遍歷等,這些演算法可以直接應用於不同類型的容器,幫助程式員高效地對數據進行處理和操作。
  3. 迭代器和訪問控制:STL 中的迭代器提供了統一的訪問介面,使得程式員能夠方便地遍歷容器中的元素併進行讀寫操作。迭代器可以靈活地控制訪問範圍和方式,為數據訪問提供了高度的靈活性。
  4. 泛型編程:STL 的設計基於泛型編程思想,通過模板機制實現了通用的數據結構和演算法。這使得 STL 能夠適用於各種數據類型,並且支持用戶自定義類型的操作,從而提高了代碼的重用性和可擴展性。
  5. 性能優化:STL 中的容器和演算法都經過高度優化,使用 STL 可以藉助這些優化的實現來提高程式的執行效率和性能。

總的來說,STL 適用於需要數據結構、演算法和迭代器等功能的各種場景。它為 C++ 程式員提供了豐富的工具和功能,幫助他們更加高效地處理和操作數據,提高代碼的可讀性、重用性和性能。無論是簡單的數據存儲管理還是複雜的數據處理和演算法實現,STL 都是一個強大而實用的工具庫。

容器:各種數據結構,如vector、list、deque、set、map等,用來存放數據,從實現角度來看,STL容器是一種class template。

演算法:各種常用的演算法,如sort、find、copy、for_each。從實現的角度來看,STL演算法是一種function tempalte.

迭代器:扮演了容器與演算法之間的膠合劑,共有五種類型,從實現角度來看,迭代器是一種將operator* , operator-> , operator++,operator--等指針相關操作予以重載的class template. 所有STL容器都附帶有自己專屬的迭代器,只有容器的設計者才知道如何遍歷自己的元素。原生指針(native pointer)也是一種迭代器。

仿函數:行為類似函數,可作為演算法的某種策略。從實現角度來看,仿函數是一種重載了operator()的class 或者class template

適配器:一種用來修飾容器或者仿函數或迭代器介面的東西。

空間配置器:負責空間的配置與管理。從實現角度看,配置器是一個實現了動態空間配置、空間管理、空間釋放的class tempalte.

STL優點

  • STL 是 C++的一部分,因此不用額外安裝什麼,它被內建在你的編譯器之內。

  • STL 的一個重要特點是數據結構和演算法的分離。儘管這是個簡單的概念,但是這種分離使得 STL 變得非常通用。例如:在 STL 的 vector 容器中,可以放入元素、基礎數據類型變數、元素的地址;STL 的 sort() 排序函數可以用來操作 vector,list 等容器。

  • 程式員可以不用思考 STL 具體的實現過程,只要能夠熟練使用 STL 就 OK 了。這樣他們就可以把精力放在程式開發的別的方面。

  • STL 具有高可重用性,高性能,高移植性,跨平臺的優點。

STL三大組件

容器

常用的數據結構無外乎數組(array),鏈表(list),tree(樹),棧(stack),隊列(queue),集合(set),映射表(map),根據數據在容器中的排列特性,這些數據分為序列式容器關聯式容器兩種。

Ø 序列式容器就是容器元素在容器中的位置是由元素進入容器的時間和地點來決定。Vector容器、Deque容器、List容器、Stack容器、Queue容器。

Ø 關聯式容器是指容器已經有了一定的規則,容器元素在容器中的位置由我的規則來決定。Set/multiset容器 Map/multimap容器

演算法

演算法分為:質變演算法非質變演算法

質變演算法:是指運算過程中會更改區間內的元素的內容。例如拷貝,替換,刪除等等

非質變演算法:是指運算過程中不會更改區間內的元素內容,例如查找、計數、遍歷、尋找極值等等

迭代器

迭代器(iterator)是一種抽象的設計概念,現實程式語言中並沒有直接對應於這個概念的實物。在<>一書中提供了23中設計模式的完整描述,其中iterator模式定義如下:提供一種方法,使之能夠依序尋訪某個容器所含的各個元素,而又無需暴露該容器的內部表示方式。

迭代器的設計思維-STL的關鍵所在,STL的中心思想在於將數據容器(container)和演算法(algorithms)分開,彼此獨立設計,最後再一貼膠著劑將他們撮合在一起。從技術角度來看,容器和演算法的泛型化並不困難,c++的class template和function template可分別達到目標,如果設計出兩這個之間的良好的膠著劑,才是大難題。

迭代器的種類:

輸入迭代器 提供對數據的只讀訪問 只讀,支持++、==、!=
輸出迭代器 提供對數據的只寫訪問 只寫,支持++
前向迭代器 提供讀寫操作,並能向前推進迭代器 讀寫,支持++、==、!=
雙向迭代器 提供讀寫操作,並能向前和向後操作 讀寫,支持++、--,
隨機訪問迭代器 提供讀寫操作,並能在數據中隨機移動 讀寫,支持++、--、[n]、-n、<、<=、>、>=
void test2() {
	vector<int>r;//STL 中的標準容器之一 :動態數組
	r.push_back(1);//vector 容器提供的插入數據的方法
	r.push_back(2);
	r.push_back(7);
	//vector 容器提供了 begin()方法 返回指向第一個元素的迭代器
	//vector 容器提供了 end()方法 返回指向最後一個元素下一個位置的迭代器
	vector<int>::iterator reg = r.begin();
	vector<int>::iterator ina = r.end();//這是一種隨機訪問類型的迭代器
	while (reg != ina) {
		cout << *reg << " ";
		reg++;
	}
	cout << endl;

	//演算法 count 演算法 用於統計元素的個數
	int total = count(reg, ina, 5);
	cout << "total:" << total << endl;
}

image-20240306181244915

STL 容器不單單可以存儲基礎數據類型,也可以存儲類對象

class Regina {
public:
	Regina(int a) : age(a) {};
	int age;
	~Regina(){};
};
vector<Regina>r;
Regina r1(1), r2(2), r3(7);
r.push_back(r1);
r.push_back(r2);
r.push_back(r3);
vector<Regina>::iterator pStart = r.begin();
vector<Regina>::iterator pEnd = r.end();
while (pStart != pEnd) {
	cout << "pStart->age: " << pStart->age << " ";
	pStart++;
}
cout << endl;

image-20240306183444331

還可以存儲類的指針

在 C++ 中,當我們使用迭代器遍歷容器時,迭代器本身是一個指向容器中元素的對象,而不是實際的元素本身。因此,為了訪問容器中存儲的元素,我們需要通過解引用操作符 * 來獲取迭代器指向的實隕對象.具體的vector怎麼用在下一篇

vector<Regina*> v;//存儲 Teacher 類型指針
Regina* r1 = new Regina(1);
Regina* r2 = new Regina(2);
Regina* r3 = new Regina(7);
v.push_back(r1);
v.push_back(r2);
v.push_back(r3);
//拿到容器迭代器
vector<Regina*>::iterator pStart = v.begin();
vector<Regina*>::iterator pEnd = v.end();
//通過迭代器遍歷
while (pStart != pEnd) {
	cout << (*pStart)->age << " ";
	pStart++;
}
cout << endl;

常用容器講解

string

C風格字元串(以空字元結尾的字元數組)太過複雜難於掌握,不適合大程式的開發,所以C++標準庫定義了一種string類,定義在頭文件

String和c風格字元串對比:

  • Char是一個指針,String是一個類

    string封裝了char,管理這個字元串,是一個char型的容器。

  • String封裝了很多實用的成員方法

    查找find,拷貝copy,刪除delete 替換replace,插入insert

  • 不用考慮記憶體釋放和越界

    string管理char*所分配的記憶體。每一次string的複製,取值都由string類負責維護,不用擔心複製越界和取值越界等。

image-20240306184831213

string regina1("regina");
string regina2(7, '$');
string regina3(regina1);
cout << regina1 << endl;  // 輸出 "regina"
cout << regina2 << endl;  // 輸出 "$$$$$$$"
cout << regina3 << endl;  // 輸出 "regina"
regina1 += "baby";
cout << regina1 << endl;  // 輸出 "reginababy"
string regina4 = regina1 + regina3;
char arr[] = "arr loop";
string regina5(arr, 2);
string regina6(arr + 1, arr + 3);
cout << regina1 << endl;  // 輸出 "reginababy"
cout << regina4 << endl;  // 輸出 "reginababyregina"
cout << regina5 << endl;  // 輸出 "ar"
cout << regina6 << endl;  // 輸出 "rr"
string regina7(&regina4[1], &regina4[4]);
string regina8(regina4, 1, 5);
cout << regina7 << endl;  // 輸出 "egi"
cout << regina8 << endl;  // 輸出 "egina"

基本賦值操作

string& operator=(const char* s):將 char* 類型的字元串賦值給當前的字元串。
string& operator=(const string& s):將另一個字元串對象 s 賦值給當前的字元串。
string& operator=(char c):將單個字元 c 賦值給當前的字元串。
string& assign(const char* s):將 char* 類型的字元串 s 賦值給當前的字元串。
string& assign(const char* s, int n):將 char* 類型的字元串 s 的前 n 個字元賦值給當前的字元串。
string& assign(const string& s):將另一個字元串對象 s 賦值給當前的字元串。
string& assign(int n, char c):使用 n 個字元 c 來賦值給當前的字元串。
string& assign(const string& s, int start, int n):將字元串 s 從位置 start 開始的 n 個字元賦值給當前的字元串。
	string s1, s2, s3;

	// 使用賦值操作符重載將 char* 類型的字元串賦值給當前的字元串
	s1 = "regina, baby!";
	cout << "s1: " << s1 << endl;

	// 使用賦值操作符重載將另一個字元串對象賦值給當前的字元串
	s2 = s1;
	cout << "s2: " << s2 << endl;

	// 使用 assign() 成員函數將 char* 類型的字元串賦值給當前的字元串
	s3.assign("regina", 4);
	cout << "s3: " << s3 << endl;

	// 使用 assign() 成員函數將另一個字元串對象賦值給當前的字元串
	s1.assign(s3);
	cout << "s1: " << s1 << endl;

	// 使用 assign() 成員函數使用 n 個字元 c 賦值給當前的字元串
	s2.assign(8, '*');
	cout << "s2: " << s2 << endl;

	// 使用 assign() 成員函數將字元串 s 的子串賦值給當前的字元串
	string s4 = "regina, baby!";
	string s5;
	s5.assign(s4, 7, 5); // 從索引 7 開始的 5 個字元
	cout << "s5: " << s5 << endl;

image-20240306194226311

存取字元串

  1. char& operator[](int n):通過重載 [] 運算符來獲取字元串中索引為 n 的字元。這種方式不進行邊界檢查,如果訪問超出字元串範圍的位置,可能導致未定義行為。
  2. char& at(int n):通過 at 方法來獲取字元串中索引為 n 的字元。與使用 operator[] 不同的是,at 方法會進行邊界檢查,如果訪問超出字元串範圍的位置,會拋出 out_of_range 異常。
string r = "regina";
// 使用 operator[] 獲取字元
cout << "Character at index 7 (using operator[]): " << r[5] << endl;

// 使用 at 方法獲取字元
try {
	cout << "Character at index 3 (using at): " << r.at(3) << endl;
	cout << "Character at index 13 (using at): " << r.at(13) << endl;
}
catch (const out_of_range& e) {
	cerr << "Exception caught: " << e.what() << endl;
}

image-20240306195945777

cerr 是 C++ 中的一個輸出流對象,它與標準錯誤流 (stderr) 相關聯。與標準輸出流 (cout) 不同,標準錯誤流用於輸出程式中的錯誤消息和診斷信息。

cerr 對象通常會直接將消息輸出到終端或日誌文件,而不會進行緩衝操作。這意味著錯誤消息會立即顯示在終端上,而不需要等待緩衝區刷新。因此,cerr 在處理錯誤和調試信息時非常有用。

拼接字元串

string& operator+=(const string& str), string& operator+=(const char* str), string& operator+=(const char c):這些是重載 += 操作符的方法,用於將另一個字元串、C風格字元串或字元連接到當前字元串的末尾。

string& append(const char *s):將 C 風格的字元串 s 追加到當前字元串的末尾。

string& append(const char *s, int n):將 C 風格的字元串 s 的前 n 個字元追加到當前字元串的末尾。

string& append(const string& s):將字元串 s 追加到當前字元串的末尾,與 operator+= 的功能類似。

string& append(const string& s, int pos, int n):將字元串 s 中從索引 pos 開始的 n 個字元追加到當前字元串的末尾。

string& append(int n, char c):在當前字元串的末尾添加 n 個字元 c。
string str = "Hello";

    // 使用 += 操作符連接字元串
    str += ", world!";
    cout << "After +=: " << str << endl;

    // 使用 append(const char*) 連接字元串
    str.append(" This is a test.");
    cout << "After append(const char*): " << str << endl;

    // 使用 append(const string&) 連接字元串
    string additional = " Have a nice day!";
    str.append(additional);
    cout << "After append(const string&): " << str << endl;

    // 使用 append(const char*, int) 連接部分字元串
    str.append(" Testing", 4); // 添加 " Test"
    cout << "After append(const char*, int): " << str << endl;

    // 使用 append(int, char) 添加字元
    str.append(3, '!'); // 添加 "!!!"
    cout << "After append(int, char): " << str << endl;

image-20240306200732669

查找和替換

image-20240306225429552

	string words = "regina baby is my sM baby";
	int pos = words.find("baby");
	cout << pos << '\n';
	pos = words.find("sM", 4, 2);
	cout << pos << '\n';
	pos = words.rfind("baby");
	cout << pos << '\n';

image-20240306230809427

比較操作

/*
compare函數在>時返回 1,<時返回 -1,==時返回 0。
比較區分大小寫,比較時參考字典順序,排越前面的越小。
大寫的A比小寫的a小。
*/
int compare(const string& s) const;//與字元串s比較
int compare(const char *s) const;//與字元串s比較

	string s1 = "regina";
	string s2 = "ivanlee";
	cout << "s1.compare(s2): " << s1.compare(s2) << "\n";
	const char* cs2 = "leeivan";
	cout << "s2.compare(cs2): " << s2.compare(cs2) << "\n";

image-20240306231206035

插入和刪除

string& insert(int pos, constchar* s); //插入字元串
string& insert(int pos, conststring& str); //插入字元串
string& insert(int pos, int n, char c);//在指定位置插入n個字元c
string& erase(int pos, int n = npos);//刪除從Pos開始的n個字元 
	string s = "hello regina";
	s.insert(5, "beautiful");
	cout << s << "\n";
	s.insert(s.length(), 2, '!'); //單引號
	cout << s << "\n";
	s.erase(5, 3);
	cout << s << "\n";

image-20240306231938091

string和c-style字元串轉換

	string s = "regina";
	const char* c = s.c_str();//返回的是一個指向常量字元的指針
	cout << "s.c_str(): " << typeid(c).name() << endl;
	const char* c1 = "regina";
	string s1(s);
	cout << "string s1(s); " << typeid(s1).name() << endl;

image-20240306232810188

在c++中存在一個從const char*到string的隱式類型轉換,卻不存在從一個string對象到C_string的自動類型轉換。對於string類型的字元串,可以通過c_str()函數返回string對象對應的C_string.

通常,程式員在整個程式中應堅持使用string類對象,直到必須將內容轉化為char*時才將其轉換為C_string.

為了修改string字元串的內容,下標操作符[]和at都會返回字元的引用。但當字元串的記憶體被重新分配之後,可能發生錯誤.

自動擴容

在 C++ 的標準庫中,std::string 類具有自動調整大小的功能。當你向 std::string 對象添加字元時,如果當前容量不足以存儲新的字元,std::string 會自動重新分配記憶體以擴展容量,從而確保可以存儲新的字元並保持字元串的有效性。

這種自動調整大小的過程是由 std::string 類內部管理的,開發者無需手動管理字元串的記憶體大小。當添加字元導致字元串超出當前容量時,std::string 會選擇一個新的更大的記憶體塊,將舊數據複製到新的記憶體塊中,並釋放舊的記憶體塊。

這種自動調整大小的機制使得 std::string 更加方便和易用,開發者可以專註於操作字元串內容而不必擔心記憶體管理的細節。

	std::string str = "regina";
	std::cout << "Initial capacity: " << str.capacity() << std::endl;
	// 添加字元,觀察容量的變化
	for (char c = 'a'; c <= 'z'; ++c) {
		str.push_back(c);
		std::cout << "Capacity after adding '" << c << "': " << str.capacity() << std::endl;
	}

std::string 類提供了 capacity() 方法,用於返回當前字元串對象的容量大小。容量表示為當前存儲空間的大小,它可能大於等於字元串的長度,因為 std::string 可能會分配比實際字元串長度更多的記憶體,以便進行後續的添加操作而不需要頻繁地重新分配記憶體。

通常情況下,當你向 std::string 對象添加字元時,如果當前的容量不足以存儲新的字元,std::string 會自動重新分配記憶體以擴展容量,從而確保可以存儲新的字元並保持字元串的有效性。這樣的設計可以提高性能,因為它避免了頻繁的記憶體分配和釋放操作。

image-20240306233544811

本文來自博客園,作者:ivanlee717,轉載請註明原文鏈接:https://www.cnblogs.com/ivanlee717/p/18057918


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 開始之前 Composition API 可以說是Vue3的最大特點,那麼為什麼要推出Composition Api,解決了什麼問題? 通常使用Vue2開發的項目,普遍會存在以下問題: 代碼的可讀性隨著組件變大而變差 每一種代碼復用的方式 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一、Object.defineProperty 定義:Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,並返回此對象 為什麼能實現響應式 通過defineProperty 兩 ...
  • SpringBoot底層預設使用logback日誌框架。 切換使用Log4j2日誌框架。 pom.xml配置 <!-- web場景啟動器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-b ...
  • 在微服務架構或者分散式系統中,客戶端如何捕捉服務端的異常?這裡說的客戶端指調用方、服務端指被調用方,它們通常運行在不同的進程之中,這些進程可能運行在同一臺伺服器,也可能運行在不同的伺服器,甚至不同的數據機房;其使用的技術棧可能相同,也可能存在很大的差異。 ...
  • 什麼是訂單履約系統? 訂單履約系統用來管理從接收客戶訂單到將商品送達客戶手中的全過程。 它連接了上游交易(客戶在銷售平臺下單環)和下游倉儲配送(如庫存管理、物流配送),確保信息流順暢、操作協同,提升整個供應鏈的效率和響應速度。 系統定位 訂單履約系統的目標是讓訂單處理更快、更清晰,提高客戶體驗。 履 ...
  • Spring Boot允許外部化項目配置,以便您可以在不同的環境中使用相同的應用程式代碼。您可以使用各種外部配置源,包括Java屬性文件、YAML文件、環境變數和命令行參數。 屬性值可以通過使用@Value註釋直接註入到bean中,通過Spring的環境抽象進行訪問,或者通過@Configurati ...
  • druid-spring-boot-3-starter目前最新版本是1.2.20,雖然適配了SpringBoot3,但缺少自動裝配的配置文件,會導致載入時報載入驅動異常。 <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-sp ...
  • 獨立樣本T檢驗適用於比較兩組獨立樣本的均值差異,而配對T檢驗則適用於比較同一組樣本在不同條件下的均值差異。在Python中,我們可以利用scipy庫進行T檢驗的實現和結果判斷。通過比較P值與顯著性水平,我們可以判斷兩組樣本均值是否存在顯著差異。T值的大小也對判斷兩組樣本均值差異的統計學意義起著重要作... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...