C++面向對象程序設計-北京大學-郭煒【課程筆記(十一)】
- 1、string(重要知識點)
- 1.2、string的賦值和鏈接
- 1.3、比較string
- 1.4、子串
- 1.5、交換string
- 1.6、尋找string中的字符
- 1.7、刪除string中的字符
- 1.8、替換string中的字符
- 1.9、在string中插入字符(重要)
- 1.10、轉換成C語言式char * 字符串
- 1.11、字符串流處理
- 1.12、字符串流處理-字符串輸出流istringstream
- 2、標準模板庫STL
- 1.1、STL的基本概念
- 1.2、容器概述
- 2.3、順序容器
- 3、迭代器
- 2.1、迭代器的定義與使用
- 2.2、雙向迭代器
- 2.3、隨機訪問迭代器
- 4、算法簡介
- find()函數原型
- 參數
- 返回值
- 使用方法
- 示例
- 示例1:在 `vector` 中查找元素
- 示例2:在 `list` 中查找元素
- 示例3:在數組中查找元素
- 注意事項
- 小結
- 5、STL中“大” “小”的概念
- 5.1、重要說明
做畢業答辯變PPT脫了幾天,終于要結束讀書生涯了,哈哈。
開始課程:P40 1_1. string類
課程鏈接:程序設計與算法(三)C++面向對象程序設計 北京大學 郭煒
課程PPT:github提供的對應課程PPT
1、string(重要知識點)
學了string類就可以不用char*類當字符串用了。
string類:
- string類是模板類
-
typedef basic_string<char> string;
- 使用string類要包含頭文件
#include<string>
- string對象的初始化:
-
- string s1(“Hello”);
-
- string month = “March”;
-
- string s2(8, ‘x’); // 8個x
注意事項:錯誤的初始化方式:
string error1 = 'c'; // 錯(直接用一個字符初始化一個string對象是不可以的)
可以將字符賦值給string對象;
-
- string s;
-
- s = ‘c’; // OK
- string error2(‘u’); // 錯
- string error3 = 22; // 錯
- string error4(8); // 錯
#include<iostream>
#include<string>
using namespace std;int main(int argc, char * argv[])
{string s1("hello");cout << s1 << endl; // hellostring s2(8, 'x'); cout << s2 << endl; // xxxxxxxxstring month = "March"; cout << month << endl; // Marchstring s;s = 'n';cout << s << endl; // nreturn 0;
}
- string對象的長度用成員函數length()讀取;
-
- string s(“hello”);
- cout << s.length() << endl;
- string支持流讀取運算符
-
string stringObject;
-
cin >> stringObject;
- string支持getline函數
-
- string s;
-
- getline(cin, s);
- 用 = 賦值
-
- string s1(“cat”), s3;
-
- s2 = s1;
- 用assign長遠函數賦值
-
- string s1(“cat”), s3;
-
- s3.assign(s1);
- 用assign成員函數部分復制
-
- string s1(“catpig”), s3;
-
s3.assign(s1, 1, 3);
-
- // 從s1中下標為1的字符開始復制3個字
- 耽擱字符復制
-
- s2
[5]
= s1[3]
= ‘a’;
- s2
- 逐個訪問string對象中的字符
-
- string s1(“Hello”);
-
- for(int i=0; i<s1.length(); i++)
-
- {cout <<
s1.at(i)
<< endl;}
- {cout <<
- 成員函數at會做
范圍檢查
,如果超出范圍,會拋出out_of_renge異常,而下標運算符[]不做范圍檢查。
1.2、string的賦值和鏈接
- 用+運算符連接字符串
-
- string s1(“good”), s2(“morning!”);
-
- s1 += s;
-
- cout << s1;
- 用成員函數append連接字符串
-
- string s1(“good”), s2(“morning!”);
-
- s1.append(s2);
-
- cout << s1;
-
- s2.append(s1, 3, s1.size()); //s1.size(), s1字符數
-
- cout << s2;
-
- // 下標為3開始,s1.size()個字符,如果字符穿內沒有足夠字符,則復制到字符串最后一個字符
1.3、比較string
- 用關系運算符比較string的大小
-
- 返回值都是bool類型,成立返回true,否則返回false
-
- 例如:
#include<iostream>
#include<string>
using namespace std;int main(int argc, char * argv[])
{string s1("hello"), s2("hello"), s3("hello");bool b = (s1 == s2);cout << b << endl;b = (s1 == s3);cout << b << endl;b = (s1 > s3);cout << b << endl;return 0;
}
// OUT
1
1
0
- 用成員函數compare比較string的大小
例題1:
#include <iostream>
#include <string>
#include <cctype>
using std::cout;
using std::endl;
using std::cin;
using std::string;
int main(void){string str1="hi,test,hello";string str2="hi,test";//字符串比較if(str1.compare(str2)>0)printf("str1>str2\n");else if(str1.compare(str2)<0)printf("str1<str2\n");elseprintf("str1==str2\n");//str1的子串(從索引3開始,包含4個字符)與str2進行比較if(str1.compare(3,4,str2)==0)printf("str1的指定子串等于str2\n");elseprintf("str1的指定子串不等于str2\n");//str1指定子串與str2的指定子串進行比較if(str1.compare(3,4,str2,3,4)==0)printf("str1的指定子串等于str2的指定子串\n");elseprintf("str1的指定子串不等于str2的指定子串\n");//str1指定子串與字符串的前n個字符進行比較if(str1.compare(0,2,"hi,hello",2)==0)printf("str1的指定子串等于指定字符串的前2個字符組成的子串\n");elseprintf("str1的指定子串不等于指定字符串的前2個字符組成的子串\n");return 0;
}
// OUT:
str1>str2
str1的指定子串不等于str2
str1的指定子串等于str2的指定子串
str1的指定子串等于指定字符串的前2個字符組成的子串
例題2:
#include<iostream>
#include<string>
using namespace std;int main(int argc, char * argv[])
{string s1("hello"), s2("hello"), s3("hell");int f1 = s1.compare(s2); // 0 // hello == helloint f2 = s1.compare(s3); // 1 // hello > hellint f3 = s3.compare(s1); // -1 // hell < helloint f4 = s1.compare(1, 2, s3); // -3 int f5 = s1.compare(0, s1.size(), s3); // 1cout << f1 << endl << f2 << endl << f3 << endl;cout << f4 << endl << f5 << endl;return 0;
}
注意事項:C++中int f4 = s1.compare(1, 2, s3);結果顯示為-3,為什么
在 C++ 的
std::string
類中,compare
函數的第一個參數是起始位置,第二個參數是要比較的長度,第三個參數是要比較的字符串。當調用s1.compare(1, 2, s3)
時,表示從s1
的索引位置 1 開始,比較長度為 2 的子字符串與s3
的內容。如果s1
的子字符串小于s3
,則返回值為負數,且返回值的絕對值表示兩個字符串第一個不相等字符的 ASCII 碼差值的相反數。因此,結果為 -3 表示在比較的過程中,第一個不相等字符在 ASCII 碼上s1
的子字符串要小于s3
。
1.4、子串
- 成員函數
substr
string s1("hello world"), s2;
s2 = s1.substr(4,5); // 下標4開始5個字符
cout << s2 << endl; // 輸出:o wor
1.5、交換string
- 成員函數
swap
string s1("hello world"), s2("really");
s1.swap(s2);
cout << s1 << endl; // really
cout << s2 << endl; // hello world
1.6、尋找string中的字符
- 成員函數
find()
-
- string s1(‘hello world’);
-
- s1.
find
(“lo”);
- s1.
-
- 在s1中從前向后查找“lo“第一次出現的地方,如果找到,返回”lo“開始的位置,即I所在的位置
下標
。如果找不到,返回string::npos(string 中定義的京塔常量)。
- 在s1中從前向后查找“lo“第一次出現的地方,如果找到,返回”lo“開始的位置,即I所在的位置
1.7、刪除string中的字符
- 成員函數
erase()
-
- string s1(“hello world”);
-
- s1.erase(5);
-
- cout << s1; // hello
-
- cout << s1.length(); // 5
-
- cout << s1.size(); // 5
// 去掉下標5及之后的字符
// 輸出:hello55
- cout << s1.size(); // 5
1.8、替換string中的字符
- 成員函數replace()
-
- string s1(“hello world”);
-
- s1.replace(2,3, “haha”);
-
- cout << s1; // hehaha world
-
- // 將s1中下標2開始的3個字符換成“haha”
1.9、在string中插入字符(重要)
1.10、轉換成C語言式char * 字符串
- 在C++中,
c_str()
函數用于返回一個指向以空字符結尾的數組的指針,該數組包含了作為std::string
對象內容的字符序列的副本。這個函數通常用于將std::string
對象轉換為 C 風格的字符串(以空字符結尾的字符數組),以便與需要使用 C 風格字符串的函數進行交互。注意,返回的指針指向的字符數組是const char*
類型的,因此不能直接修改該數組的內容。
- 在C++中,
data()
函數用于返回一個指向std::string
對象中存儲的字符數據的指針。與c_str()
函數不同,data()
函數不會在字符串的末尾添加空字符(‘\0’),因此返回的指針可以用于修改字符串的內容。需要注意的是,在修改返回的指針所指向的字符串時,要確保不會超出字符串的長度,否則會導致未定義的行為。
1.11、字符串流處理
#include<string>
#include<iostream>
#include<sstream>
using namespace std;int main()
{string input("Input test 123 4.7 A");istringstream inputString(input);string string1, string2;int i;double d;char c;inputString >> string1 >> string2 >> i >> d >> c;cout << string1 << endl << string2 << endl;cout << i << endl << d << endl << c << endl;long L;if(inputString >> L){cout << "long\n";}else cout << "empty\n";
}
// OUT
Input
test
123
4.7
A
empty
這段代碼使用了C++的字符串流類istringstream
來解析一個字符串input
。下面是對代碼的逐行解釋:
#include<string>
:包含了C++標準庫中的string
頭文件,提供了字符串操作的功能。#include<iostream>
:包含了C++標準輸入輸出流的頭文件,提供了輸入輸出功能。#include<sstream>
:包含了C++標準庫中的字符串流頭文件,提供了字符串流的功能。using namespace std;
:使用了命名空間std
,這樣就可以直接使用標準庫中的類和函數,而不需要加上std::
前綴。string input("Input test 123 4.7 A");
:定義了一個字符串input
,內容為"Input test 123 4.7 A"
。istringstream inputString(input);
:定義了一個字符串流inputString
,并將input
作為初始化參數。string string1, string2;
:定義了兩個字符串變量string1
和string2
。int i;
:定義了一個整型變量i
。double d;
:定義了一個雙精度浮點型變量d
。char c;
:定義了一個字符型變量c
。inputString >> string1 >> string2 >> i >> d >> c;
:使用字符串流inputString
依次讀取字符串、字符串、整型、雙精度浮點型和字符,分別賦值給string1
、string2
、i
、d
和c
。cout << string1 << endl << string2 << endl;
:輸出變量string1
和string2
的值,并換行。cout << i << endl << d << endl << c << endl;
:輸出變量i
、d
和c
的值,并換行。long L;
:定義了一個長整型變量L
。if(inputString >> L)
:嘗試從字符串流inputString
中讀取一個長整型值到變量L
中,如果成功讀取則執行下面的代碼塊,否則執行else
中的代碼塊。{ cout << "long\n"; }
:如果成功讀取了長整型值,則輸出"long"
并換行。else cout << "empty\n";
:如果未能成功讀取長整型值,則輸出"empty"
并換行。
這段代碼的功能是將字符串input
中的各個部分分別解析為不同類型的變量,并輸出它們的值。
1.12、字符串流處理-字符串輸出流istringstream
#include<string>
#include<iostream>
#include<sstream>
using namespace std;int main()
{ostringstream outputString;int a = 10;outputString << "This" << a << "ok" << endl;cout << outputString.str(); //This10okreturn 0;
}
// OUT
This10ok
這段代碼演示了如何使用 ostringstream
類來構建一個字符串流,并將各種類型的數據寫入其中,最后將其作為一個字符串輸出到標準輸出流中。
-
ostringstream outputString;
:創建一個ostringstream
對象outputString
,用于構建一個字符串流。 -
outputString << "This" << a << "ok" << endl;
:使用<<
操作符將字符串"This"
、整數a
和字符串"ok"
以及換行符endl
依次寫入到outputString
中。 -
cout << outputString.str();
:使用str()
方法獲取outputString
中的字符串內容,并將其輸出到標準輸出流cout
中。 -
return 0;
:返回主函數的結束。
2、標準模板庫STL
1.1、STL的基本概念
1.2、容器概述
2.3、順序容器
3、迭代器
2.1、迭代器的定義與使用
定義一個容器類的迭代器的方法可以是:
容器類名::iterator 變量名;
容器類名::const_iterator 變量名;
訪問一個迭代器指向的元素:
* 迭代器變量名
迭代器上可以執行++操作,以使其指向容器中的下一個元素。如果迭代器到達了容器中的最后一個元素的后面,此時再使用它,則會報錯,類似于使用NULL或未初始化的指針一樣。
例題:
#include<vector>
#include<iostream>
using namespace std;int main()
{vector<int> v; // 一個存放int元素的數組,一開始里面沒有元素v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4);vector<int>::const_iterator i; // 常用迭代器for(i = v.begin(); i != v.end(); i++){cout << *i << ","; // 1,2,3,4,}cout << endl;vector<int>::reverse_iterator r; // 反向迭代器for(r = v.rbegin(); r!=v.rend(); r++){cout << *r << ","; // 4,3,2,1,}cout << endl;vector<int>::iterator j; // 非常量迭代器for(j=v.begin(); j != v.end(); j++)*j = 100; // 容器中數組每個元素賦值100for(i=v.begin(); i != v.end(); i++)cout << *i << ","; // 100,100,100,100
}
// OUT
STL % ./1
1,2,3,4,
4,3,2,1,
100,100,100,100
2.2、雙向迭代器
2.3、隨機訪問迭代器
p<p1的含義:p指向的這個元素他在p1所指向這個元素的前面。
vector的迭代器是隨機迭代器,遍歷vector可以有以下幾種做法(deque亦然): |
#include<vector>
#include<iostream>
using namespace std;int main()
{vector<int> v(5); // 創建一個包含100個元素的整數向量,默認初始化為0int i;for(i=0;i<v.size();i++) // 使用下標遍歷vectorcout << v[i]; // 根據下標隨機訪問,并輸出每個元素cout << ";" << endl;// 定義一個常量迭代器ii,通過for循環遍歷向量v,并輸出每個元素。vector<int>::const_iterator ii; for(ii=v.begin(); ii != v.end(); ii++) // 使用常量迭代器遍歷vectorcout << *ii; // 輸出迭代器指向的元素cout << ";" << endl;for(ii = v.begin(); ii < v.end(); ii++) // 使用常量迭代器遍歷vector(條件使用<)cout << *ii; // 輸出迭代器指向的元素cout << ";" << endl;// 間隔一個元素輸出ii= v.begin();while(ii < v.end()) // 迭代器方式間隔輸出{cout << *ii; // 輸出當前迭代器指向的元素ii = ii + 2; // 將迭代器向前移動兩個位置}return 0;
}
// OUT
00000;
00000;
00000;
0
0
0
list的迭代器是雙向迭代器,正確遍歷list的方法如下: |
4、算法簡介
C++ 中的 find()
函數用于在容器(如數組、vector
、list
等)中查找特定元素。它是 <algorithm>
頭文件中的一個標準算法。下面是 find()
函數的詳細介紹,包括其使用方法和例子。
find()函數原型
find()
函數有多個重載版本,最常用的版本如下:
template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );
參數
first
:指向輸入范圍的起始位置的迭代器。last
:指向輸入范圍的結束位置的迭代器(不包含在范圍內)。value
:要查找的元素的值。
返回值
- 如果找到匹配的元素,返回指向該元素的迭代器。
- 如果沒有找到匹配的元素,返回
last
。
使用方法
find()
函數遍歷從 first
到 last
范圍內的元素,比較每個元素與 value
是否相等。如果找到相等的元素,則返回一個指向該元素的迭代器;否則,返回一個指向 last
的迭代器。
示例
以下是 find()
函數的幾個使用示例。
示例1:在 vector
中查找元素
#include <iostream>
#include <vector>
#include <algorithm> // 包含 find() 函數的頭文件int main() {std::vector<int> vec = {1, 2, 3, 4, 5};// 查找元素3auto it = std::find(vec.begin(), vec.end(), 3);// std::distance(vec.begin(), it) 是 C++ 標準庫中的一個實用函數,用于計算兩個迭代器之間的距離(即元素的數量)。if (it != vec.end()) {std::cout << "Found element 3 at position: " << std::distance(vec.begin(), it) << std::endl;} else {std::cout << "Element 3 not found." << std::endl;}return 0;
}
輸出:
Found element 3 at position: 2
示例2:在 list
中查找元素
#include <iostream>
#include <list>
#include <algorithm> // 包含 find() 函數的頭文件int main() {std::list<int> lst = {10, 20, 30, 40, 50};// 查找元素30auto it = std::find(lst.begin(), lst.end(), 30);if (it != lst.end()) {std::cout << "Found element 30." << std::endl;} else {std::cout << "Element 30 not found." << std::endl;}return 0;
}
輸出:
Found element 30.
示例3:在數組中查找元素
#include <iostream>
#include <algorithm> // 包含 find() 函數的頭文件int main() {int arr[] = {5, 10, 15, 20, 25};// 查找元素20auto it = std::find(std::begin(arr), std::end(arr), 20);if (it != std::end(arr)) {std::cout << "Found element 20 at position: " << (it - std::begin(arr)) << std::endl;} else {std::cout << "Element 20 not found." << std::endl;}return 0;
}
輸出:
Found element 20 at position: 3
#include<vector>
#include<algorithm>
#include<iostream>using namespace std;int main()
{// find 算法示例int array[10] = {10,20,30,40};vector<int>v;v.push_back(1); v.push_back(2);v.push_back(3); v.push_back(4);vector<int>::iterator p;p = find(v.begin(), v.end(), 3);if(p != v.end()){cout << *p << endl; // 輸出3}p = find(v.begin(), v.end(), 9);if(p==v.end())cout << "not found " << endl;p = find(v.begin()+1, v.end()-2, 1); // 整個容器:[1,2,3,4],查找區間:[2,3)if(p != v.end())cout << *p << endl;int *pp = find(array, array+4, 20); // 數組名是迭代器cout << * pp << endl;
}
輸出:
3
not found
3
20
注意事項
find()
函數使用的是元素的相等比較運算符==
,因此要查找的元素類型必須支持該運算符。find()
函數只能用于支持輸入迭代器的范圍。- 如果要查找的容器是有序的,可以考慮使用更高效的算法,如
std::binary_search
或std::lower_bound
。
小結
find()
函數是一個簡單而強大的工具,用于在各種容器中查找元素。它的使用非常直觀,通過傳遞開始和結束迭代器,以及要查找的值,即可輕松找到目標元素。在實際應用中,可以結合其他算法和數據結構,根據具體需求選擇合適的查找方法。
5、STL中“大” “小”的概念
#include <iostream>
#include <algorithm>
using namespace std;class A
{int v;
public:A(int n) : v(n) {}bool operator < (const A& a2) const{cout << v << "<" << a2.v << "?" << endl;return false;// return v < a2.v; // 修改返回值為比較實際數值}bool operator == (const A& a2) const{cout << v << "==" << a2.v << "?" << endl;return v == a2.v;}
};int main()
{A a[] = {A(1), A(2), A(3), A(4), A(5)};// 修改為搜索整個數組范圍// boolalpha:使 cout 輸出布爾值時,顯示 true 或 false 而不是 1 或 0。cout << boolalpha << binary_search(a, a + 5, A(9)) << endl; // 折半查找return 0;
}
代碼逐行解釋:
下面是對你提供的代碼進行逐行解釋:
#include <iostream>
#include <algorithm>
using namespace std;
#include <iostream>
:包含輸入輸出流的頭文件,用于輸入輸出操作,如cout
。#include <algorithm>
:包含標準庫算法的頭文件,用于調用binary_search
函數。using namespace std;
:使用標準命名空間,避免在使用標準庫對象時每次都加std::
前綴。
class A
{int v;
public:A(int n) : v(n) {}bool operator < (const A& a2) const{cout << v << "<" << a2.v << "?" << endl;return false;// return v < a2.v; // 修改返回值為比較實際數值}bool operator == (const A& a2) const{cout << v << "==" << a2.v << "?" << endl;return v == a2.v;}
};
- 定義一個類
A
,表示一個包含整數成員變量v
的類。 int v;
:私有成員變量,存儲整數值。A(int n) : v(n) {}
:構造函數,初始化成員變量v
為傳入的參數n
。bool operator < (const A& a2) const
:重載小于運算符,用于比較兩個A
對象。輸出比較信息,但總是返回false
(此處注釋掉了實際的比較邏輯return v < a2.v;
)。bool operator == (const A& a2) const
:重載等于運算符,用于比較兩個A
對象。輸出比較信息,并返回兩個對象的v
值是否相等。
int main()
{A a[] = {A(1), A(2), A(3), A(4), A(5)};// 修改為搜索整個數組范圍cout << boolalpha << binary_search(a, a + 5, A(9)) << endl; // 折半查找return 0;
}
int main()
:主函數,程序執行的入口。A a[] = {A(1), A(2), A(3), A(4), A(5)};
:創建一個A
對象數組,包含五個元素,每個元素的v
值分別為1, 2, 3, 4, 5
。cout << boolalpha << binary_search(a, a + 5, A(9)) << endl;
:binary_search(a, a + 5, A(9))
:在數組a
中使用二分查找法查找是否存在值為9
的A
對象。boolalpha
:使cout
輸出布爾值時,顯示true
或false
而不是1
或0
。- 將查找結果輸出到控制臺,并換行。
5.1、重要說明
- 當前的
operator <
函數總是返回false
,這會導致binary_search
函數無法正確比較對象,因此始終返回false
。要使binary_search
函數正常工作,需將operator <
的實現改為實際比較數值,如下所示:
這樣可以正確地進行比較,并使二分查找能夠正確地工作。bool operator < (const A& a2) const {cout << v << "<" << a2.v << "?" << endl;return v < a2.v; }