模板
c++另一種編程思想稱為泛型編程,主要利用的技術就是模板
c++提供兩種模板機制:函式模板和類模板
函式模板
建立一個通用函式,函式的回傳值型別和形參型別可以不具體指定,用一個虛擬的型別來代表
語法:
template<typename T>
//或者
template<class T>
函式宣告或定義
當使用class的時候,如果T有子類,編譯器會認為是宣告,所以還是使用typename吧
template<typename T>
void test(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main() {
int a = 1, b = 2;
//自動推導型別
test(a, b);
//指定型別
test<int>(a, b);
cout << a << "==" << b << endl;
}
注意事項:
自動型別推導,必須推匯出一致的資料型別T才可以使用
模板必須要確定出T的資料型別,才可以使用
template<typename T>
void test(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main() {
int a = 1, b = 2;
char c='1';
//自動推導型別錯誤,因為兩個型別不同
test(a, c);
}
template<typename T>
void test() {
cout << "hhhh" << endl;
}
int main() {
test();//錯誤,因為推導不出T是什么型別
}
template<typename T>
void test() {
cout << "hhhh" << endl;
}
int main() {
test<int>(); //明確指定就可以
test<string>();//這個也可以
}
普通模板和函式模板區別
- 普通函式呼叫時可以發生自動型別轉換(隱式型別轉換)
- 函式模板呼叫時,如果利用自動型別推導,不會發生隱式型別轉換
- 函式模板如果利用顯示指定的方法,可以發生型別轉換
普通函式會發生隱式轉換,例如下面的例子中,將char型別的c轉換為了int,對應的就是c的ascll碼
void print1(int a, int b) {
cout << a + b << endl;
}
int main() {
int a = 1;
char b = 'c';
print1(a,b);
}
而使用模板函式時
template<typename T>
void print(T a,T b) {
cout << a+b << endl;
}
int main() {
int a = 1;
char b = 'c';
print(a,b); //報錯
}
template<typename T>
void print(T a,T b) {
cout << a+b << endl;
}
int main() {
int a = 1;
char b = 'c';
print<int>(a,b); //除非我們指定了資料型別為int
}
普通函式和模板函式的呼叫規則
- 如果函式模板和普通函式都可以實作,優先調普通函式
- 可以通過空模板引數來強制呼叫函式模板
- 函式模板也可以發生多載
- 如果函式模板可以產生更好的匹配,優先呼叫函式模板
如果函式模板和普通函式都可以實作,優先調普通函式
template<typename T>
void test(T a) {
cout <<"函式模板" << endl;
}
void test(int a) {
cout << "普通函式" << endl;
}
int main() {
int a = 1;
test(a);
}
結果:普通函式
template<typename T>
void test(T a) {
cout <<"函式模板" << endl;
}
void test(int a);
int main() {
int a = 1;
test(a);
}
如果普通函式只有宣告也是呼叫普通函式,會報錯沒有找到定義
可以通過空模板引數來強制呼叫函式模板
template<typename T>
void test(T a) {
cout <<"函式模板" << endl;
}
void test(int a) {
cout << "普通函式" << endl;
}
int main() {
int a = 1;
test<>(a);
}
這樣就可以強制呼叫函式模板,當然,尖括號里面隨便寫個型別也可以,但沒必要
函式模板也可以發生多載
template<typename T>
void test(T a) {
cout <<"函式模板一個引數" << endl;
}
template<typename T>
void test(T a,T b) {
cout << "函式模板兩個引數" << endl;
}
int main() {
int a = 1;
test<>(a); //函式模板一個引數
test<>(a,a); //函式模板兩個引數
}
如果函式模板可以產生更好的匹配,優先呼叫函式模板
template<typename T>
void test(T a) {
cout <<"函式模板一個引數" << endl;
}
void test(int a) {
cout << "普通函式" << endl;
}
int main() {
char a = '1';
test(a);
}
會優先使用函式模板,因為普通函式確定了型別為int,而char需要轉int,呼叫函式模板則不需要任何轉換
模板的局限性
模板并不是萬能的,例如
template<typename T>
bool test(T& a,T& b) {
return a == b;
}
int main() {
char a = '1';
char b = '2';
cout<<test(a,b);
}
當我們傳基礎的資料類代碼可以正常使用,但是當傳入的資料是自定義型別的時候
class A {
public:
string name;
};
template<typename T>
bool test(T& a,T& b) {
return a == b;
}
int main() {
A a1 = { "小明" };
A a2 = { "大明" };
cout<<test(a1,a2);
}
就會報錯了,因為沒有找到A型別的==判斷
解決辦法有兩個:
1 重寫運算子
class A {
public:
string name;
bool operator==(A& a) {
return (this->name == a.name);
}
};
template<typename T>
bool test(T& a, T& b) {
return a == b;
}
int main() {
A a1 = { "小明" };
A a2 = { "大明" };
cout << test(a1, a2);
}
2 重寫一個針對A類的函式模板
class A {
public:
string name;
};
template<typename T>
bool test(T& a, T& b) {
return a == b;
}
template<> bool test(A& a, A& b) {
return a.name == b.name;
}
int main() {
A a1 = { "小明" };
A a2 = { "大明" };
cout << test(a1, a2);
}
利用具體化的模板,可以解決自定義型別的通用化
類模板
建立一個通用的類,類中的成員資料型別可以不具體指定,用一個虛擬的型別來代表
語法:
template<typename T>
class 類名 {
};
//宣告下面的類中可以使用這兩個型別
template<typename NameType,typename AgeType>
class A {
public:
NameType name;
AgeType age;
};
int main() {
//使用時指定兩個型別
A<string, int> a = { "小明",19 };
cout << a.age << "==" << a.name << endl;
}
類模板和函式模板區別
-
類模板沒有自動推導型別的使用方式
template<typename NameType,typename AgeType> class A { public: A(NameType _name, AgeType _age) { this->name = _name; this->age = _age; } NameType name; AgeType age; }; int main() { //A a("哈哈哈", 1); 報錯,類模板沒有自動推導 A<string,int> a("哈哈哈", 1); //可以正常使用 cout << a.age << "==" << a.name << endl; } -
類模板在模板引數串列中可以有默認引數
template<typename NameType, typename AgeType=int> //這里給AgeType設定了默認型別為int class A { public: A(NameType _name, AgeType _age) { this->name = _name; this->age = _age; } NameType name; AgeType age; }; int main() { //下面在使用時就只需要指定一個string即可 A<string> a("哈哈哈", 1); cout << a.age << "==" << a.name << endl; }
需要注意的是,如果有兩個或多個引數,只能從最右邊開始指定默認型別
例如下面的就是錯誤的
template<typename NameType=string, typename AgeType>
類模板中的成員函式創建時機
類模板的成員函式和普通類的成員函式創建時機是有區別的
- 普通類中的成員函式一開始就可以創建
- 模板類中的成員函式在呼叫的時候才會創建
class A {
public:
void func1() {
cout << "A-func" << endl;
}
A() {
cout << "A-init"<<endl;
}
};
class B {
public:
void func2() {
cout << "A-func" << endl;
}
};
template <typename T>
class MyRun {
public:
T t;
void run1() {
t.func1();
}
void run2() {
t.func2();
}
};
int main() {
MyRun<A> m;
m.run1();
//m.run2();
}
當main方法中什么都不寫的時候,可以正常運行,因為MyRun類中的屬性t并沒有確認是什么型別,只有在運行的時候才能確認t的型別,判斷t有沒有func1或func2的函式
當指定型別為A的時候就已經初始化創建了一個A物件
A-init
A-func
類模板物件做函式引數
三種傳入方式
- 指定傳入型別
- 引數模板化
- 整個類模板化
1 指定傳入型別
template <typename NameType,typename AgeType>
class User {
public:
NameType name;
AgeType age;
User(NameType _name,AgeType _age) :name(_name), age(_age) {
}
};
//直接標明都是什么型別
void print(User<string, int>& u) {
cout << u.name << "==" << u.age << endl;
}
int main() {
User<string, int> user("小明", 20);
print(user);
}
2 引數模板化
template <typename NameType,typename AgeType>
class User {
public:
NameType name;
AgeType age;
User(NameType _name,AgeType _age) :name(_name), age(_age) {
}
};
//模板的名稱不需要和類模板定義名稱一樣,只是單純定義函式模板
template <typename F_NameType,typename F_AgeType>
void print(User<F_NameType, F_AgeType>& u) {
cout << u.name << "==" << u.age << endl;
cout <<"F_NameType的資料型別為:" << typeid(F_NameType).name() << endl;
cout <<"F_AgeType的資料型別為:" << typeid(F_AgeType).name() << endl;
}
int main() {
User<string, int> user("小明", 20);
print(user);
}
3 整個類模板化
template <typename NameType,typename AgeType>
class User {
public:
NameType name;
AgeType age;
User(NameType _name,AgeType _age) :name(_name), age(_age) {
}
};
//再定義有一個模板函式作為接收引數
template <typename T>
void print(T& u) {
cout << u.name << "==" << u.age << endl;
cout <<"T的資料型別dfsdfs為:" << typeid(T).name() << endl;
}
int main() {
User<string, int> user("小明", 20);
print(user);
}
類模板與繼承
- 當子類繼承的父類是一個類模板時,子類在宣告的時候,要指出父類中T的型別
- 如果不指定,編譯器無法給子類分配記憶體
- 如果想靈活指定父類中的T型別,子類也需要變為類模板
1 當子類繼承的父類是一個類模板時,子類在宣告的時候,要指出父類中T的型別
template <typename T>
class Father {
public:
T obj;
};
class Son : public Father<int> {
};
int main() {
Son s;
s.obj = 100;
cout << s.obj << endl;
}
2 如果想靈活指定父類中的T型別,子類也需要變為類模板
template <typename T>
class Father {
public:
T obj;
};
//宣告同時指定T2型別
template <typename T1,typename T2=int>
class Son : public Father<T1> {
public:
T2 t2;
};
int main() {
//傳入的是Father的模板型別,Son的已經有默認型別了
Son<int> s;
s.obj = 100;
s.t2 = 200;
cout << s.obj << endl;
cout << s.t2 << endl;
}
類模板成員函式類外實作
template<typename T>
class A {
public:
A(T n); //宣告構造
void print(); //宣告函式
T number;
};
//類外實作構造
template<typename T>
A<T>::A(T n) {
this->number = n;
}
//類外實作函式
template<typename T>
void A<T>::print() {
cout << this->number;
}
int main() {
A<int> a(1);
a.print();
}
類模板分檔案撰寫
問題: 類模板中成員函式創建時機在呼叫階段,導致分檔案撰寫時鏈接不到
解決方式1: 直接包含.cpp源檔案
解決方式2 將宣告和實作寫到同一個檔案中,并將后綴改為hpp,hpp是約定名稱,不是強制
A.h
#pragma once
#include <iostream>
#include<string>
using namespace std;
template<typename T>
class A {
public:
A(T n);
void print();
T number;
};
A.cpp
#include "A.h"
template<typename T>
A<T>::A(T n) {
this->number = n;
}
template<typename T>
void A<T>::print() {
cout << this->number;
}
主檔案
#include "A.h"
int main() {
A<int> a(1);
a.print();
}
運行報錯,無法決議符號
方式1:把主檔案中的#include "A.h" 改為#include "A.cpp"
方式2:將A.cpp的內容移動到A.h中,然后修改A.h為A.hpp
A.hpp
#pragma once
#include <iostream>
#include<string>
using namespace std;
template<typename T>
class A {
public:
A(T n);
void print();
T number;
};
template<typename T>
A<T>::A(T n) {
this->number = n;
}
template<typename T>
void A<T>::print() {
cout << this->number;
}
主函式
#include "A.hpp"
int main() {
A<int> a(1);
a.print();
}
類模板和友元
全域函式類外實作- 直接在類中宣告友元即可
全域函式類外實作,需要提前讓編譯器知道全域函式的存在
類內實作:
#include <iostream>
#include<string>
using namespace std;
template <typename T>
class G {
//這個就是全域函式,不是成員函式
//還有一種是類內宣告,類外實作,這個是直接實作
//因為要指定那個方法作為當前類的友元,所以會在類中定義全域函式
friend void print(G<T> g) {
cout << g.name << endl;
}
public:
G(T t) {
this->name = t;
}
private:
T name;
};
int main() {
G<string> g("小明");
print(g);
}
類外實作1:
template <typename T>
class G {
//標明這個函式是一個函式模板
template <typename T>
friend void print(G<T>& g);
public:
G(T t) {
this->name = t;
}
private:
T name;
};
//實作
template <typename T>
void print(G<T>& g) {
cout << g.name << endl;
};
int main() {
G<string> g("小明");
print(g);
}
類外實作2:
//因為print方法提前到了G類的定義前
//需要讓編譯器提前知道還有個G類
template <typename T>
class G;
//在G類上方定義函式模板,讓編譯器知道這個方法
template <typename T>
void print(G<T>& g) {
cout << g.name << endl;
};
template <typename T>
class G {
//=======注意! 需要加一個空模板引數串列
friend void print<>(G<T>& g);
public:
G(T t) {
this->name = t;
}
private:
T name;
};
int main() {
G<string> g("小明");
print(g);
}
練習-使用模板類實作一個容器
MyArray.hpp
#pragma once
#include <iostream>
#include <string>
using namespace std;
template <typename T>
class MyArray {
public:
MyArray(int _size) {
//cout << "有參構造" << endl;
this->size = _size;
this->count = 0;
this->p = new T[_size];
}
//重寫拷貝構造,解決淺拷貝問題
MyArray(const MyArray& arr) {
//cout << "拷貝構造" << endl;
this->count = arr.count;
this->size = arr.size;
//深拷貝
this->p = new T[arr.size];
for (int i = 0; i < size; i++) {
this->p[i] = arr.p[i];
}
}
//多載賦值符號,解決淺拷貝問題
MyArray& operator=(const MyArray& arr) {
//cout << "賦值多載" << endl;
if (this->p != NULL) {
delete[] this->p;
p = NULL;
}
this->count = arr.count;
this->size = arr.size;
this->p = new T[arr.size];
for (int i = 0; i < size; i++) {
this->p[i] = arr.p[i];
}
return *this;
}
T& operator[](int index) {
return this->p[index];
}
int getSize() {
return this->size;
}
int getCount() {
return this->count;
}
~MyArray() {
//cout << "解構式" << endl;
if (this->p != NULL)
{
//洗掉陣列
delete[] this->p;
p = NULL;
}
}
void add(const T& t) {
if (this->count == this->size) {
cout << "已經滿了" << endl;
return;
}
this->p[count++] = t;
}
void remove() {
if (this->count == 0) {
return;
}
this->p[--count] = 0;
}
private:
T* p;
//陣列已占用個數
int count;
//陣列大小
int size;
};
主函式
1 測驗基礎資料型別
#include <iostream>
#include "MyArray.hpp" //引入自定義的頭檔案需要使用""而不是<>
#include<string>
using namespace std;
int main() {
MyArray<int>* p = new MyArray<int>(10);
cout << "陣列大小:" << p->getSize() << endl;
cout << "陣列容量:" << p->getCount() << endl;
for (int b = 0; b < 10; b++) {
p->add(b);
}
cout << "陣列容量:" << p->getCount() << endl;
for (int i = 0; i < p->getCount(); i++) {
//因為是指標,所有需要先解參考然后再[下標]取值
cout << (*p)[i] << endl;
}
p->remove();
p->remove();
p->remove();
MyArray<int> arr2 = *p;
cout << "arr2===copy===:" << endl;
for (int i = 0; i < arr2.getCount(); i++) {
cout << arr2[i] << endl;
}
cout << "arr2===end:" << endl;
for (int i = 0; i < p->getCount(); i++) {
cout << (*p)[i] << endl;
}
delete p;
p = NULL;
}
2 測驗自定義資料型別
#include <iostream>
#include "MyArray.hpp"
#include<string>
using namespace std;
class A {
public:
//無參構造,用于自定義容器中的深拷貝的創建物件
// this->p = new T[_size];
A() {}
A(string _name, int _age) :name(_name), age(_age) {
}
string name;
int age;
};
ostream& operator<<(ostream& o, A& a) {
cout <<"年齡:" << a.age<<" 姓名:" << a.name;
return o;
}
int main() {
MyArray<A> arr(10);
A a2 = { "A2",20 };
A a3 = { "A3",30 };
A a4 = { "A4",40 };
arr.add(a1);
arr.add(a2);
arr.add(a3);
arr.add(a4);
for (int i = 0; i < arr.getCount(); i++) {
cout << arr[i]<<"====地址:"<<&(arr[i]) << endl;
}
MyArray<A> arr2(arr);
for (int i = 0; i < arr2.getCount(); i++) {
cout << arr2[i] <<"====地址:" << &(arr[i])<< endl;
}
cout << "兩個陣列地址==arr:" << (int) & arr << " arr2:" << (int)&arr2 << endl;
}
STL容器
-
STL(Standard Template Library) 標準模板庫
-
STL從廣義上分為容器,演算法,迭代器
-
容器和演算法直接通過迭代器進行無縫銜接
-
STL幾乎所有代碼都使用了模板類或模板函式
STL六大組件
容器,演算法,迭代器,仿函式,配接器(配接器),空間配置器
- 容器:各種資料結構,如vector,list,deque,set,map等,用來存放資料
- 演算法:各種常用的演算法,如sort,find,copy,for_each等
- 迭代器:扮演了容器與演算法之間的膠合器
- 仿函式:行為類似函式,可作為演算法的某種策略
- 配接器:一種用來修飾容器或仿函式或迭代器介面的東西
- 空間配置器:賦值空間的配置與管理
容器
常用資料結構:陣列,鏈表,數,堆疊,佇列,集合等
容器分為序列式容器和關聯式容器
- 序列式容器:強調值的排序,序列式容器中每個元素都有固定的位置
- 關聯式容器:二叉樹結構,各元素之間沒有嚴格的物理上的順序關系
演算法
演算法分為質變演算法和非質變演算法
質變演算法:指運算程序中會改變區間內容的元素內容例如拷貝,替換,洗掉等
非質變演算法:指運算程序中不會更改區間的元素內容,例如查找,計數,變數,尋找極值等
迭代器
提供一種方法,能夠依序尋訪某個容器中所包含的每個元素,而又無需暴露該容器的內部表示方式
每個容器都有自己的迭代器,迭代器非常類似于指標,初學階段可以先理解為指標
迭代器種類:
輸入迭代器:對資料的只讀訪問,支持++ == !=
輸出迭代器:對資料的只寫操作,支持++
前向迭代器 讀寫操作,并能向前推進迭代器 支持++ == !=
雙向迭代器 讀寫,并能向前和向后操作 支持++ --
隨機訪問迭代器 讀寫操作 可以跳躍的訪問任何資料 支持++ -- [n] -n < <= > >=
string容器
**string和char ***區別
- char *是一個指標
- string是一個類,內部封裝了char* 管理這個字串,是一個char*型的容器
string 建構式
string()創建一個空字串,例如string s;string(const char* s)使用字串s初始化string(const string& str)使用一個string物件來初始化另一個string(int n,char c)使用n個字符c初始化
int main() {
//默認構造
string s;
const char* str = "hello!";
string s1(str);
cout << "s1" << s1 << endl;
//拷貝構造
string s2(s1);
cout << "s2" << s2 << endl;
//4個h
string s3(4, 'h');
cout << "s3" << s3 << endl;
}
string 賦值操作
string& operator=(const char* s)char*型別字串賦值給當前字串string& operator=(const string& s)字串s賦值給當前字串string& operator=(char c)字符賦值給當前字串string& assign(const char* s)字串s賦值給當前字串string& assign(const char* s,int n)把字串s的前n個字符賦值給當前字串string& assign(const string& s)把字串s賦值給當前字串string& assign(int n,char c)用n個字符c賦值給當前字串
int main() {
//默認構造
string s;
const char* str = "hello!";
s = str;
cout << "s=" << s << endl;
string s1 = s;
cout << "s1=" << s1 << endl;
string s2;
s2 = 'a';
cout << "s2=" << s2 << endl;
string s3;
s3.assign("hellowwww");
cout << "s3=" << s3 << endl;
string s4;
s4.assign("hellowwww",3);
cout << "s4=" << s4 << endl;
string s5;
s5.assign(s4);
cout << "s5=" << s5 << endl;
string s6;
s6.assign(6, 't');
cout << "s6=" << s6 << endl;
}
string 拼接
string& operator+=(const char* str)string& operator+=(const char c)string& operator+=(const string& str)string& append(const char* s)把s字串拼接到當前字串結尾string& append(const char* s,int n)把c字串前n個字符拼接到當前字串結尾string& append(const string& s)同operator+=(const string& str)string& append(const string& s,int pos,int n)字串s中從pos開始的n個字符拼接到字串結尾
int main() {
//默認構造
string s = "hello!";
s += " i ";
s += 'a';
string s1 = "m";
s += s1;
s.append(" j");
s.append("a==", 1);
s.append(s1);
string s2 = "asme";
s.append(s2,3,1);
cout << s << endl;
// hello! i am jame
}
string 查找替換
int find(const string& str,int pos=0) const查找str第一次出現的位置,從pos開始int find(const char* s,int pos=0) const查找s第一次出現的位置,從pos開始查找int find(const char* s,int pos,int n) const從pos位置查找s前n個字符第一次出現的位置int find(const char c,int pos=0) const查找字符c第一次出現的位置int rfind(const string& str,int pos=npos) const查找str最后一次出現的位置,從pos開始查找int rfind(const char* s,int pos=npos) const查找s最后一次出現的位置,從pos開始查找int rfind(const char* s,int pos,int n) const從pos查找s前n個字符最后一次出現的位置int rfind(const char c,int pos=0) const查找字符c最后一次出現的位置string& replace(int pos,int n,const string& str)從pos開始n個字符替換為strstring& replace(int pos,int n const char* s)替換從pos開始n個字符為s
find和rfind區別 find是從左往右,rfind是從右往左
int main() {
string s = "2===2=";
string s1 = "6";
cout<<s.find(s1)<<endl; //s.find(s1,0) 省略
cout << s.find("2") << endl; //s.find("2",0) 省略
cout << s.find("234", 0, 1) << endl; //先把234拆開,從0開始,拆一個,獲取到2,然后再查
cout << s.find('2') << endl;//s.find('2',0)省略
cout << s.rfind(s1,6) << endl;//從下標6開始從右往左查
cout << s.rfind("2",6) << endl;//從下標6開始從右往左查
cout << s.rfind('2',6) << endl;//從下標6開始從右往左查
cout<<s.replace(0, 1, s1)<<endl;//從0開始1個字符替換為6
cout<<s.replace(0, 1, "7")<<endl;//從0開始1個字符替換為6
}
string 比較
字串比較是按字符的ASCII碼進行對比
= 回傳0 > 回傳 1 < 回傳-1
int compare(const string& s) const與字串s比較int compare(const char* s)const與字串s比較
int main() {
string s = "hello";
string s1 = "hello";
string s2 = "xello";
cout << (s.compare(s1) == 0) << endl;
cout << (s.compare(s2) == 0) << endl;
cout << (s.compare("hello") == 0) << endl;
cout << (s.compare("xello") == 0) << endl;
}
string 字串存取
char& operator[](int n)通過[]獲取字符char& at(int n)通過at方法獲取字符
int main() {
string s = "hello";
s[0] = 'y';
for (int i = 0; i < s.length(); i++) {
cout << s[i];
}
s.at(0) = 'y';
for (int i = 0; i < s.length(); i++) {
cout << s.at(i);
}
}
string 插入和洗掉
string& insert(int pos,const char* s);插入字串string& insert(int post,const string& str)插入字串string& insert(int pos,int n,char c)pos位置插入n個字符cstring& erase(int pos,int n=pos)洗掉從pos開始的n個字符
int main() {
string s = "hello";
string s1 = "wu";
cout<<s.insert(1, "+")<<endl; //在下標1后面插入---
cout << s.insert(1, s1) << endl; //在下標1后面插入s1
cout << s.insert(1, 4, 'x') << endl; //在下標1后面插入4個字符x
cout << s.erase(1, 2) << endl;//從下標1后洗掉2個字符
}
string 子串
string substr(int pos=0,int n=pos) const 回傳從pos開始的n個字符組成的字串
int main() {
string s = "hello";
cout << s.substr() << endl;
cout << s.substr(0,1) << endl;
}
vector容器
vector資料結構和陣列非常相似,也稱之為單端陣列,但是不同于陣列是靜態空間,vector是可以動態擴展的
動態擴展
并不是在原空間后繼續開辟空間,而是找一段更大的空間,之后將原資料復制到新空間上,釋放原空間
vector的迭代器是支持隨機訪問的
容器:vector
演算法:for_each
迭代器:vector<int>::iterator
#include <iostream>
#include <vector>
using namespace std; //vector需要匯入
#include<algorithm> //for_each 匯入
void print(int val) {
cout << val;
}
int main() {
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
//直接通過下標訪問
int& a = v[1];
//通過迭代器訪問1
//vector<int>::iterator itBegin = v.begin();
//vector<int>::iterator itEnd = v.end();
//while (itBegin != itEnd) {
// cout << *itBegin << endl;
// itBegin++;
//
//}
//
//通過迭代器訪問2
//for (vector<int>::iterator itBegin = v.begin(); itBegin != v.end(); itBegin++){
// cout << *itBegin << endl;
//
//}
//通過for_each
for_each(v.begin(), v.end(), print);
}
添加自定義的型別
class A {
public :
string name;
};
int main() {
vector<A> v;
A a1 = { "a1" };
A a2 = { "a2" };
A a3 = { "a3" };
v.push_back(a1);
v.push_back(a2);
v.push_back(a3);
//通過迭代器訪問2
for (vector<A>::iterator itBegin = v.begin(); itBegin != v.end(); itBegin++){
cout << (* itBegin).name << endl;
}
}
存放類的指標
int main() {
vector<A*> v;
A a1 = { "a1" };
A a2 = { "a2" };
A a3 = { "a3" };
v.push_back(&a1);
v.push_back(&a2);
v.push_back(&a3);
//通過迭代器訪問2
for (vector<A*>::iterator itBegin = v.begin(); itBegin != v.end(); itBegin++){
cout << (*itBegin)->name << endl;
cout << (**itBegin).name << endl;//或者
}
}
容器記憶體放容器
vector<vector<A>> v;
A a1 = { "a1" };
A a2 = { "a2" };
A a3 = { "a3" };
vector<A> v1;
v1.push_back(a1);
v1.push_back(a2);
v1.push_back(a3);
vector<A> v2;
v2.push_back(a1);
v2.push_back(a3);
v2.push_back(a2);
vector<A> v3;
v3.push_back(a1);
v3.push_back(a3);
v3.push_back(a2);
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
for (vector<vector<A>>::iterator itBegin = v.begin(); itBegin != v.end(); itBegin++){
for (vector<A>::iterator itb = itBegin->begin(); itb != itBegin->end(); itb++) {
cout << itb->name << endl;
}
}
vector 構造
vector<T> v采用模板實作,默認構造vector(v.begin(),v.end())將v[begin(),end()]區間中的元素拷貝給本身vector(n,elem)構造將n個elem拷貝給本身vector(const vector &vec)拷貝構造
int main() {
vector<int> v;
for (int i = 0; i < 10; i++){
v.push_back(i);
}
//把v的begin到end的資料復制給v2
vector<int> v2(v.begin(), v.end());
//創建10個100放到v3
vector<int> v3(10, 100);
//拷貝構造
vector<int> v4(v3);
for (vector<int>::iterator it = v3.begin(); it != v3.end(); it++) {
cout << *it << endl;
}
}
vector 賦值操作
vector& operator=(const vector &vec)多載=assign(beg,end)將v[begin(),end()]區間中的元素拷貝給本身assign(n,elem)將n個elem拷貝賦值給本身
int main() {
vector<int> v;
for (int i = 0; i < 10; i++){
v.push_back(i);
}
//直接=賦值
vector<int> v2 = v;
vector<int> v3;
//把v的begin到end資料賦值給v3
v3.assign(v.begin(), v.end());
//放10個100到v4里面
vector<int> v4;
v4.assign(10, 100);
for (vector<int>::iterator it = v4.begin(); it != v4.end(); it++) {
cout << *it << endl;
}
}
vector 容量和大小
empty()回傳容器是否為空capacity()容器的容量size()回傳容器中的元素個數resize(int num)重新定義容器長度為num,若大于原長度,則新位置填充默認值,若小于原長度,超出部分元素被洗掉resize(int num,elem)重新定義容器長度為num,若大于原長度,則新位置填充elem,若小于原長度,超出部分元素被洗掉
int main() {
vector<int> v;
for (int i = 0; i < 10; i++){
v.push_back(i);
}
cout << v.empty() << endl;
cout << v.capacity() << endl;
cout << v.size() << endl;
v.resize(9);
v.resize(9,4);
}
vector 插入和洗掉
push_back(ele)尾部插入元素elepop_back()洗掉最后一個元素insert(const_iterator pos,ele)迭代器指向位置pos插入元素eleinsert(const_iterator pos,int count,ele)迭代器指向位置pos插入count個元素eleerase(const_iterator pos)洗掉迭代器指向的元素erase(const_iterator start,const_iterator end)洗掉迭代器從start到en之間的元素clear()洗掉容器中所有元素
int main() {
vector<int> v;
for (int i = 0; i < 10; i++){
v.push_back(i);
}
v.push_back(1);
v.pop_back();
v.insert(v.begin(), 1);
v.insert(v.begin(), 1, 2);
v.erase(v.begin());
v.erase(v.begin(), v.end());
v.clear();
}
vector 資料存取
at(int index)回傳索引指向的資料operator[]回傳索引指向的資料front()回傳容器中第一個元素back()回傳最后一個元素
int main() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
cout << v.at(2) << endl;
cout << v[2] << endl;
cout << v.front() << endl;
cout << v.back() << endl;
}
vector 互換容器
swap(vec)與另一個vector容器互換元素
int main() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
vector<int> v2;
for (int i = 0; i < 5; i++) {
v2.push_back(i);
}
v.swap(v2);
for (vector<int>::iterator t = v.begin(); t != v.end(); t++) {
cout << *t;
}
cout << "\n";
for (vector<int>::iterator t = v2.begin(); t != v2.end(); t++) {
cout << *t ;
}
}
還可以使用swap來重構vector的大小
例如有一個容量為1萬的容器,之后洗掉到只剩10個元素,現在希望縮小它的容量
int main() {
vector<int> v;
for (int i = 0; i < 10000; i++) {
v.push_back(i);
}
vector<int>(v).swap(v);
}
首先vector<int>(v)呼叫復制拷貝來初始化一個新的vector物件,會使用v的元素個數來初始化新vector的容量大小,之后交換兩個容器的指標,因為是匿名物件,運行完銷毀原來的容器占用空間
vector 預留空間
減少vector在動態擴展容量時的次數
reserve(int len)給容器預留len個位置,預留位置不可訪問,不初始化
int main() {
vector<int> v;
// v.reserve(100000);
int number = 0;
int* p = NULL;
for (int i = 0; i < 100000; i++) {
v.push_back(i);
if (p != &(v[0])) {
p = &(v[0]);
number++;
}
}
//查看擴容次數
cout << number << endl;
}
deque容器
雙端操作,可以對頭端進行插入洗掉,也支持隨機訪問
deque與vector的區別
-
vector對于頭部的插入洗掉效率低,資料量越大,效率越低
-
deque相對vector頭部的插入洗掉速度要快
-
vector訪問元素時的速度會比deque快
-
deque<T>deqT; 默認構造 -
deque(beg,end)建構式將beg,end區間的元素拷貝給本身 -
deque(n,elem)建構式將n個elem拷貝給本身 -
deque(const deque &deq)拷貝構造
#include <iostream>
#include <deque>
using namespace std;
void pirnt(deque<int> &d) {
for (deque<int>::iterator i = d.begin(); i != d.end(); i++) {
cout << *i << endl;
}
}
int main() {
//默認構造
deque<int> d;
for (int i = 0; i < 10; i++)
{
//尾部添加
//d.push_back(i);
//頭添加
d.push_front(i);
}
//pirnt(d);
cout << "====" << endl;
deque<int> d2(++d.begin(), d.end());
//初始化10個100
deque<int> d3(10, 100) ;
//拷貝構造
deque<int> d4(d3);
pirnt(d4);
}
deque賦值操作
deque&~operator=(const deque &deq)多載等號assign(beg,end)將beg,end區間資料拷貝賦值給本身assign(n,elem)將n個elem賦值給本身
#include <deque>
using namespace std;
void pirnt(const deque<int> &d) {
for (deque<int>::const_iterator i = d.begin(); i != d.end(); i++) {
cout << *i << endl;
}
}
int main() {
//默認構造
deque<int> d;
for (int i = 0; i < 10; i++)
{
//尾部添加
//d.push_back(i);
//頭添加
d.push_front(i);
}
//pirnt(d);
cout << "====" << endl;
deque<int> d2;
//多載=賦值
d2 = d;
deque<int> d3;
d3.assign(d2.begin(), d2.end());
deque<int> d4;
d4.assign(10, 100);
pirnt(d4);
}
deque大小操作
deque.empty()判斷容器是否為空deque.size()回傳容器中的元素個數deque.resize(num)重新指定容器長度num,若容器邊長,則默認值填充新位置,若變短,洗掉末尾元素deque.resize(num,elem)重新指定容器長度num,若容器邊長,則elem填充新位置,若變短,洗掉末尾元素
deque沒有容量概念
void pirnt(const deque<int> &d) {
for (deque<int>::const_iterator i = d.begin(); i != d.end(); i++) {
cout << *i << endl;
}
}
int main() {
//默認構造
deque<int> d;
for (int i = 0; i < 10; i++)
{
//尾部添加
//d.push_back(i);
//頭添加
d.push_front(i);
}
cout << d.empty() << endl;
cout << d.size() << endl;
d.resize(15, 6);
pirnt(d);
cout << "====" << endl;
d.resize(5);
pirnt(d);
}
deque插入洗掉
兩端插入
push_back(e)尾部添加epush_front(e)頭部插入epop_back()洗掉最后的一個元素poh_front()洗掉第一個元素
指定位置插入
insert(pos,elem)在pos位置插入elem元素的拷貝,回傳新資料的位置insert(pos,n,elem)在pos位置插入n個elem資料,無回傳值insert(pos,beg,end)在pos插入beg,end區間的資料,無返clear()清空容器erase(beg,end)洗掉beg-end區間的資料,回傳下一個資料的位置erase(pos)洗掉pos位置的資料,回傳下一個資料的位置
int main() {
//默認構造
deque<int> d;
//尾部添加
d.push_back(1);
//頭添加
d.push_front(2);
//尾刪
d.pop_back();
//頭刪
d.pop_front();
//根據位置插入回傳一個迭代器
deque<int>::iterator i = d.insert(d.begin(), 1);
//插入兩個10
d.insert(d.begin(), 2, 10);
//在頭部添加了容器中所有的元素
d.insert(d.begin(), d.begin(), d.end());
//從第二個刪到倒數第二個
deque<int>::iterator i2=d.erase(++d.begin(), --d.end());
//洗掉第一個
d.erase(d.begin());
//清空
d.clear();
}
deque資料存取
at(int ind)回傳ind位置的資料operator[]回傳索引位置的資料front()回傳第一個元素back()回傳容器最后一個元素
int main() {
//默認構造
deque<int> d;
d.push_front(1);
d.push_front(2);
d.push_front(3);
d.push_front(4);
//多載[]符
cout << d[2] << endl;
//at獲取
cout << d.at(2) << endl;
//取第一個
cout << d.front() << endl;
//取最后一個
cout << d.back() << endl;
}
stack容器
堆疊容器,先進后出,只有頂端元素才能被使用,因此堆疊不允許有遍歷行為
stack構造
stack<T> stk默認構造stack(const stack &stk); 拷貝構造
stack賦值
stack& operator=(const stack &stk)多載=
stack資料存取
push(elem)壓入一個元素pop()彈出一個元素top()獲取堆疊頂元素
stack大小
empty()回傳是否為空size()回傳堆疊大小
#include <stack>
using namespace std;
int main() {
stack<int> s;
//壓入一個元素
s.push(1);
s.push(2);
//拷貝構造
stack<int> s2(s);
//多載=賦值
stack<int> s3 = s2;
//是否為空
cout << s3.empty() << endl;
//彈出一個元素
s3.pop();
//堆疊大小
cout << s3.size() << endl;
//獲取堆疊頂元素
cout << s3.top() << endl;
}
queue容器
佇列,先進先出,只有頭尾能被外界訪問,因此沒有遍歷行為
queue構造
queue<T> que;默認構造queue(const queue & q)拷貝構造
queue賦值操作
queue& operator=(const queue &que)多載=
queue資料存取
push(e)隊尾添加一個元素pop()隊頭移出第一個元素back()回傳最后一個元素front()回傳第一個元素
queue大小操作
empty()回傳是否為空size()回傳大小
#include <queue>
using namespace std;
int main() {
queue<int> q;
queue<int> q2(q);
queue<int> q3 = q2;
q3.push(1);
q3.push(2);
q3.pop();
cout << q3.front() << endl;
cout << q3.back() << endl;
cout << q3.size();
cout << q3.empty();
}
list容器
通過雙向鏈表實作,list的迭代器只支持前移和后移,屬于雙向迭代器
list構造
list<T> list默認構造list(beg,end)建構式將beg-end區間元素拷貝給本身list(n,elem)使用n個elem初始化本身list(const list &list)拷貝構造
#include <iostream>
#include <list>
using namespace std;
int main() {
//默認構造
list<int> l;
//拷貝構造
list<int> l2(l);
//區間賦值
list<int> l3(l2.begin(), l2.end());
//使用4個5初始化
list<int> l4(4, 5);
}
list賦值和交換
assign(beg,end)將beg-end區間資料拷貝給本身assign(n,m)將n個m拷貝賦值給本身list& operatpr=(const list &list)多載=swap(list)交換
#include <iostream>
#include <list>
using namespace std;
int main() {
//默認構造
list<int> l;
l.assign(6, 1);
list<int> l2;
l2.assign(l.begin(), l.end());
//多載=
list<int> l3 = l2;
list<int> l4;
//交換
l4.swap(l3);
for (list<int>::iterator it = l4.begin(); it != l4.end(); it++) {
cout << *it << endl;
}
}
list大小操作
size()回傳容器中元素個數empty()回傳是否為空resize(n)重新指定長度為n,如果邊長,則默認值填充新位置,變短則超出長度的元素被洗掉resize(n,e)重新指定長度為n,如果邊長,則e填充新位置,變短則超出長度的元素被洗掉
int main() {
//默認構造
list<int> l;
l.assign(6, 1);
l.resize(10);
l.resize(15,7);
for (auto a : l)
{
cout << a ;
}
cout << l.size() << endl;
cout << l.empty() << endl;
}
list插入洗掉
push_back(e)尾部加入一個元素pop_back()洗掉容器中最后一個元素push_front(e)頭部插入一個元素pop_front()頭部洗掉一個元素insert(pos,elem)在pos位置插入elem元素的拷貝,回傳新資料位置insert(pos,n,elem)在pos位置插入n個elem,無返insert(pos,beg,end)在pos位置插入beg-end區間資料,無返clear()移除容器所有資料erase(beg,end)移除beg-end區間資料,回傳下一個資料的位置erase(pos)移除pos位置資料,回傳下一個元素的位置remove(elem)洗掉容器中所有和elem值匹配的元素
int main() {
//默認構造
list<int> l;
//尾部添加
l.push_back(1);
l.push_back(2);
l.push_back(3);
//洗掉尾部
l.pop_back();
//洗掉頭部
l.pop_front();
list<int> l2;
l2.insert(l2.begin(), 2);
l2.insert(l2.begin(), 2, 1);
l2.insert(l2.begin(), l.begin(), l.end());
l2.clear();
l2.erase(++l2.begin(), --l2.end());
l2.erase(l2.begin());
l2.remove(2);
}
list資料獲取
front()回傳第一個元素back()回傳最后一個元素
int main() {
//默認構造
list<int> l;
//尾部添加
l.push_back(1);
l.push_back(2);
l.push_back(3);
cout<<l.back();
cout << l.front();
}
list反轉和排序
因為list不支持隨機訪問,所以不能用自帶的sort排序
revers()反轉鏈表sort()鏈表排序 默認從小到大
bool a(int a,int b) {
return a > b;
}
int main() {
//默認構造
list<int> l;
//尾部添加
l.push_back(1);
l.push_back(4);
l.push_back(3);
l.reverse();
//如果想自定義排序規則,傳入方法a
l.sort(a);
cout<<l.back();
cout << l.front();
}
自定義資料型別排序
#include <iostream>
#include <list>
#include <string>
using namespace std;
class User {
public:
User() {
}
User(int _age, int _money, string _name):age(_age), money(_money), name(_name) {
}
int age;
int money;
string name;
};
bool a(User &a, User &b) {
if (a.age >= b.age) {
return a.money >= b.money;
}
return false;
}
int main() {
User u1 = { 18,600,"小明" };
User u2 = { 30,300,"小王" };
User u3 = { 20,400,"小周" };
list<User> l;
l.push_back(u1);
l.push_back(u2);
l.push_back(u3);
l.sort(a);
for (list<User>::iterator it = l.begin(); it != l.end(); it++) {
cout << it->age << "=" << it->money << "=" << it->name << endl;
}
}
set/multiset容器
所有元素都會在插入時自動被排序.set/multiset屬于關聯式容器,底層使用二叉樹實作
set和multiset區別:
- set不允許有重復的元素,插入會回傳插入結果和插入位置的迭代器
- multiset允許存在重復的元素,回傳插入只有插入位置的迭代器
set的構造和賦值
構造:
set<T> st默認構造set(const set& st)拷貝構造
賦值:
set& operator=(const set& set)多載=
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> s;
s.insert(1);
set<int> s2(s);
set<int> s3 = s2;
for (set<int>::iterator it = s3.begin(); it != s3.end(); it++) {
cout << *it << endl;
}
}
set容器大小和交換
size()回傳元素個數empty()回傳是否為空swap(st)交換兩個集合容器
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> s;
s.insert(1);
s.insert(1);
cout << s.empty() << endl;
cout << s.size() << endl;
set<int> s2;
s2.swap(s);
cout << s2.size() << endl;
}
set的插入洗掉
insert(elem)容器插入元素clear()清空所有元素erase(pos)洗掉pos迭代器所指向的元素,回傳下一個元素的迭代器erase(beg,end)洗掉區間beg-end的所有元素,回傳下一個元素的迭代器erase(elem)洗掉容器中值為elem的元素
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> s;
s.insert(1);
s.clear();
s.insert(2);
s.insert(3);
s.insert(4);
s.insert(5);
set<int>::iterator it1 = s.erase(s.begin());
set<int>::iterator it2 = s.erase(s.begin(), --s.end());
int a = s.erase(5);
}
set容器查找和統計
find(key)查找元素是否存在,回傳元素的迭代器,如果不存在回傳set.end()count(key)統計key的元素個數 在set中作用不大,因為不可重復,要么0要么1
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> s;
s.insert(1);
s.insert(2);
set<int>::iterator it=s.find(1);
if (it != s.end()) {
cout << "find!" << endl;
}
else {
cout << "null" << endl;
}
cout << s.count(1) << endl;
}
兩個set插入回傳結果
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> s;
pair<set<int>::iterator, bool> p = s.insert(1);
set<int>::iterator it = p.first;
cout << p.second << endl;
multiset<int> ms;
set<int>::iterator it2= ms.insert(1);
}
pair對組創建
成對出現的資料,利用對組可以回傳兩個資料
pair<type,type p (v1,v2)pair<type,type> p=make_pair(v1,v2)
#include <iostream>
#include <set>
using namespace std;
int main() {
pair<string, int> p("老王", 50);
cout << p.first << endl; //輸出第一個
cout << p.second << endl;//輸出第二個
pair<string, int> p2 = make_pair("小明", 15);
cout << p2.first << endl;
cout << p2.second << endl;
}
set容器排序
利用仿函式,修改排序規則
#include <iostream>
#include <set>
using namespace std;
class MyCompare {
public:
//這里需要加const作用不知道,以后學了在回來解釋
bool operator() (int v1,int v2)const {
return v1 > v2;
}
};
int main() {
set<int,MyCompare> s;
s.insert(4);
s.insert(1);
s.insert(2);
s.insert(3);
for (set<int>::iterator it = s.begin(); it != s.end(); it++) {
cout << *it << endl;
}
}
自定義型別的排序
對于自定義的資料型別,必須指定排序規則才能插入
#include <iostream>
#include <set>
using namespace std;
class People {
public:
People(int _age) :age(_age) {
}
int age;
};
class MyCompare {
public:
// bool operator() (const People& v1,const People& v2)const
//只加& 就會報錯 必須加const修飾
bool operator() (People v1, People v2)const {
return v1.age > v2.age;
}
};
int main() {
set<People,MyCompare> s;
s.insert(People(1));
s.insert(People(4));
s.insert(People(6));
s.insert(People(2));
for (set<People>::iterator it = s.begin(); it != s.end(); it++) {
cout << it->age << endl;
}
}
map/multimap容器
- map的所有元素都是pair
- pair的第一個元素為key,起到索引作用,第二個元素的value
- 所有元素都會根據元素的鍵值自動排序
- map/multimap都屬于關聯式容器,底層是用二叉樹實作
- 可以根據key快速找到value
二者區別: map不允許存在相同的key,mulitmap可以存在
map構造和賦值
map<T1,T2> m默認構造map(const map& m)拷貝構造map& operator=(consst map& m)多載=
#include <iostream>
#include <map>
using namespace std;
int main() {
map<int, int> m;
m.insert(pair<int, int>(1, 10));
m.insert(pair<int, int>(2, 20));
m.insert(pair<int, int>(3, 30));
m.insert(pair<int, int>(4, 40));
map<int, int> m1(m);
map<int, int>m2 = m1;
for (map<int, int>::iterator it = m2.begin(); it != m2.end(); it++) {
cout << it->first << "===" << it->second << endl;
}
}
map大小和交換
size()回傳元素個數empty()回傳是否為空swap(m)交換兩個map的元素
int main() {
map<int, int> m;
m.insert(pair<int, int>(1, 10));
m.insert(pair<int, int>(2, 20));
m.insert(pair<int, int>(3, 30));
m.insert(pair<int, int>(4, 40));
map<int, int> m2;
m2.swap(m);
cout << m2.size() << endl;
cout << m2.empty() << endl;
}
map的插入和洗掉
insert(elem)容器插入元素clear()清空元素erase(pos)洗掉pos迭代器指向的元素,回傳下一個元素的迭代器erase(beg,end)洗掉區間beg-end的元素,回傳下一個元素的迭代器erase(key)根據key洗掉元素
int main() {
map<int, int> m;
m.insert(pair<int, int>(1, 10));
m.clear();
m.insert(pair<int, int>(2, 20));
m.insert(make_pair(3, 30));
m.insert(map<int,int>::value_type(4,40));
//不要使用[]方式來獲取,因為如果沒有這個就會給你創建一個key為5,value為0的元素
m[5] = 50;
map<int, int>::iterator it = m.erase(m.begin());
map<int, int>::iterator it2 = m.erase(m.begin(), --m.end());
int deleted = m.erase(5);
cout << m.size();
}
map查找和統計
find(key)查找key是否存在,回傳元素的迭代器,不存在回傳set.end()count(key)統計key的個數,map中不是0就是1,在multimap中有用
int main() {
map<int, int> m;
m.insert(pair<int, int>(1, 10));
m.insert(make_pair(2, 20));
map<int,int>::iterator it=m.find(1);
cout << it->first<<"====" << it->second << endl;
cout << m.count(1) << endl;
}
int main() {
multimap<int, int> m;
m.insert(pair<int, int>(1, 10));
m.insert(make_pair(1, 20));
multimap<int,int>::iterator it=m.find(1);
cout << it->first<<"====" << it->second << endl;
it++;
cout << it->first<<"====" << it->second << endl;
cout << m.count(1) << endl;
}
map排序
利用仿函式,改變排序規則 兩個引數是key,根據key排
class MyR {
public :
bool operator()( int p1, int p2) const {
return p1>p2;
}
};
int main() {
multimap<int, int, MyR> m;
m.insert(pair<int, int>(1, 10));
m.insert(make_pair(2, 20));
m.insert(make_pair(4, 40));
m.insert(make_pair(3, 30));
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++) {
cout << it->first << "==" << it->second << endl;
}
}
STL函式物件
函式物件
- 多載函式呼叫運算子的類,其物件稱為函式物件
- 函式物件使用多載的()時,行為類似函式的呼叫,也叫仿函式
- 函式物件(仿函式)是一個類,不是一個函式
函式物件的使用
- 函式物件使用可以像普通函式一樣,有引數和回傳值
- 函式物件超出了普通函式的概念呢,函式物件可以有自己的狀態
- 函式物件可以作為引數
class MyR {
public :
bool v() {
this->count++;
return false;
}
bool operator()( int p1, int p2){
this->count++;
cout << "operator" << endl;
return p1<p2;
}
int count = 0;
};
int main() {
MyR mr;
mr(1, 2);
mr(1, 2);
mr(1, 2);
cout << mr.count << endl;
}
class MyR {
public :
bool v() {
this->count++;
return false;
}
bool operator()( int p1, int p2){
this->count++;
cout << "operator" << endl;
return p1<p2;
}
int count = 0;
};
void print(MyR & m,int a,int b) {
m(a, b);
}
int main() {
MyR mr;
print(mr, 1, 2);
}
謂詞
回傳bool型別的仿函式稱為謂詞
如果operator接收一個引數,叫一元謂詞,兩個就叫二元謂詞
class MyR {
public :
bool operator()( int a){
return a>0;
}
};
int main() {
vector<int> v;
v.push_back(-1);
v.push_back(1);
v.push_back(3);
v.push_back(-2);
v.push_back(2);
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
vector<int>::iterator it2 = find_if(it, v.end(), MyR());
it = it2;
cout << *it2 << endl;
}
}
class MyR {
public :
bool operator()( int a,int b){
return a<b;
}
};
int main() {
vector<int> v;
v.push_back(-1);
v.push_back(1);
v.push_back(3);
v.push_back(-2);
v.push_back(2);
sort(v.begin(), v.end(), MyR());
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}
}
內建函式物件
分類:
- 算術仿函式
- 關系仿函式
- 邏輯仿函式
這些仿函式產生的物件,用法和普通函式完全相同
使用內建函式物件,需要引入頭檔案#include <functional>
算術仿函式
其中negate是一元,其他都是二元
template<class T> T plus<T>加法template<class T> T minus<T>減法template<class T> T multiplies<T>乘法template<class T> T divides<T>除法template<class T> T modulus<T>取模template<class T> T negate<T>取反
#include <iostream>
#include <functional>
using namespace std;
int main() {
negate<int> n;
cout << n(2) << endl;
minus<int> m;
cout << m(10, 4) << endl;
}
關系仿函式
template<class T> bool equal_to<T>等于template<class T> bool not_equal_to<T>不等于template<class T> bool greater<T>大于template<class T> bool greater_equal<T>大于等于template<class T> bool less<T>小于template<class T> bool less_equal<T>小于等于
#include <iostream>
#include <functional>
using namespace std;
int main() {
less<int> l;
cout << l(10, 20) << endl;
equal_to<int> e;
cout << e(10, 20) << endl;
}
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
sort(v.begin(), v.end(), greater_equal<int>());
for (vector<int>::iterator i = v.begin(); i != v.end(); i++) {
cout << *i << endl;
}
}
邏輯仿函式
template<class T> bool logical_and與template<class T> bool logical_or或template<class T> bool logical_not非'
移動資料同時取反
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<bool> v;
v.push_back(1);
v.push_back(0);
v.push_back(1);
v.push_back(0);
vector<int> v2;
//需要提前開辟空間
v2.resize(v.size());
transform(v.begin(), v.end(), v2.begin(), logical_not<bool>());
for (vector<int>::iterator it = v2.begin(); it != v2.end(); it++) {
cout << *it << endl;
}
}
STL常用演算法
- 演算法主要由頭檔案
<algorithm>functionalnumberic組成 <algorithm>是所有STL頭檔案中最大的一個,涉及范圍比較,交換查找,遍歷,復制,修改<numberic>體積很小只包括幾個序列上面進行簡單數學運算和模板函式<functional>定義了一些模板類,用以宣告函式物件
遍歷
for_each
for_each(iterator beg,iterator end,_func)
beg 開始迭代器,
end 結束迭代器,
func函式或函式物件
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//普通函式
void print(bool a) {
cout << a << endl;
}
//仿函式
class print2 {
public:
void operator()(int a) {
cout << a << endl;
}
};
int main() {
vector<bool> v;
v.push_back(1);
v.push_back(0);
v.push_back(1);
v.push_back(0);
for_each(v.begin(), v.end(), print);
for_each(v.begin(), v.end(), print2());
}
transform
將容器中元素轉移到另一個容器
轉移的容器需要提前開辟空間
transform(iterator beg, iterator end, iterator beg2, _func)
beg 原始容器開始迭代器
end 原始容器結束迭代器,
beg2 目標容器開始迭代器
func函式或函式物件
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//普通函式
bool tran(bool a) {
return !a;
}
//普通函式
bool print(bool a) {
cout << a << endl;
return a;
}
int main() {
vector<bool> v;
v.push_back(1);
v.push_back(0);
v.push_back(1);
v.push_back(0);
vector<bool> v2;
v2.resize(v.size());
transform(v.begin(),v.end(),v2.begin(), tran);
for_each(v2.begin(), v2.end(), print);
}
查找
find
查找指定元素,找到回傳對應位置的迭代器,否則回傳end()
find(iterator beg,iterator end,value)
beg 開始迭代器
end 結束迭代器
value 元素
基礎資料型別
int main() {
vector<bool> v;
v.push_back(1);
v.push_back(0);
v.push_back(1);
v.push_back(0);
vector<bool>::iterator it = find(v.begin(), v.end(), 0);
while (it != v.end())
{
cout << *it << endl;
it = find(++it, v.end(), 0);
}
}
自定義資料型別
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class A {
public:
A(int _a):a(_a) {
}
int a;
bool operator==(const A& a) {
return this->a == a.a;
}
};
/*
* 底層是呼叫==來判斷,我們需要多載==
for (; _First != _Last; ++_First) {
if (*_First == _Val) {
break;
}
}
*/
int main() {
vector<A> v;
A a(1);
v.psh_back(a);
v.push_back(A(4));
v.push_back(A(2));
v.push_back(A(3));
vector<A>::iterator it = find(v.begin(), v.end(),a);
while (it != v.end())
{
cout << it->a << endl;
it = find(++it, v.end(), 0);
}
}
find_if
條件查找,找到回傳指向元素位置的迭代器,否則回傳end()
find_if(iterator beg, iterator end, _Pred)
beg 開始迭代器
end 結束迭代器
_Pred 函式或謂詞
基礎資料型別
bool myFind(int a) {
return a == 3;
}
int main() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
vector<int>::iterator it = find_if(v.begin(), v.end(), myFind);
while (it != v.end()){
cout << *it << endl;
it = find(++it, v.end(), 0);
}
}
自定義資料型別
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class User {
public:
User(int _age) :age(_age) {
}
int age;
};
class MyG {
public:
bool operator()(const User& u) {
return u.age == 100;
}
};
int main() {
vector<User> v;
v.push_back(User(100));
v.push_back(User(120));
v.push_back(User(100));
vector<User>::iterator it = find_if(v.begin(), v.end(), MyG());
while (it != v.end()) {
cout << it->age << endl;
it = find_if(++it, v.end(), MyG());
}
}
adjavent_find
查找相鄰重復元素,回傳相鄰元素第一個迭代器
adjavent_find(iterator beg, iterator end)
beg 迭代器開始
end 迭代器結束
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(1);
v.push_back(3);
v.push_back(2);
v.push_back(2);
v.push_back(3);
vector<int>::iterator it = adjacent_find(v.begin(), v.end());
cout << *it << endl;
}
binary_search
查找指定元素是否存在,在無序列中不可用,回傳bool型別
binary_search(iterator beg, iterator end, value)
beg 開始迭代器
end 結束迭代器
value 值
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
cout<<binary_search(v.begin(), v.end(), 4);
}
count
統計元素個數
count(iterator beg, iterator end, value)
beg 開始迭代器
end 結束迭代器
value 值
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(2);
cout<<count(v.begin(),v.end(),2);
}
count_if
按條件統計元素個數
count_if(iterator beg,iterator end, _Pred)
_pred 謂詞
class My {
public:
bool operator()(const int a){
return a >= 2;
}
};
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(2);
cout << count_if(v.begin(), v.end(), My());
}
排序
sort
sort(iterator beg, iterator end, _Pred)
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(2);
sort(v.begin(), v.end());
sort(v.begin(), v.end(), greater<int>());
}
random_shuffle
洗牌,指定范圍內元素隨機調整次序
random_shuffle(iterator beg, iterator end)
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <ctime>
void pirnt(int a) {
cout << a << endl;
}
void test1() {
//隨機種子
srand((unsigned int)time(NULL));
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(2);
random_shuffle(v.begin(), v.end());
for_each(v.begin(), v.end(), pirnt);
}
merge
合并兩個容器元素到另一個容器中,注意,兩個容器必須是有序的
merge(iterator beg1, iterator end1, iterator beg2, iterator beg2, iterator dest)
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
vector<int> v2;
v2.push_back(2);
v2.push_back(3);
vector<int> v3;
//先開辟空間
v3.resize(v.size() + v2.size());
//合并不會去重
merge(v.begin(), v.end(), v2.begin(), v2.end(), v3.begin());
for_each(v3.begin(), v3.end(), pirnt);
}
reverse
將容器內元素進行反轉
reverse(iterator beg, iterator end)
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
reverse(v.begin(), v.end());
for_each(v.begin(), v.end(), pirnt);
}
拷貝和替換
copy
容器指定范圍元素拷貝到另一容器中
copy(iterator beg, iterator end, iterator dest)
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int> v2;
v2.resize(v.size());
copy(v.begin(), v.end(), v2.begin());
for_each(v2.begin(), v2.end(), pirnt);
}
replace
將容器指定范圍內的元素替換
replace(iterator beg, iterator end, oldValue, newValue)
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
replace(v.begin(), v.end(), 1, 6);
for_each(v.begin(), v.end(), pirnt);
}
replae_if
在指定區間內判斷如果符合條件則替換元素
replace_if(iterator beg, iterator end, _pred, newValue)
class My {
public:
bool operator()(const int a) {
return a >= 2;
}
};
void pirnt(int a) {
cout << a << endl;
}
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
replace_if(v.begin(), v.end(), My(), 6);
for_each(v.begin(), v.end(), pirnt);
}
swap
互換兩個容器中的元素,兩個容器的型別需要相同
swap(container c1, container c2)
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int> v2;
v2.push_back(444);
swap(v, v2);
for_each(v.begin(), v.end(), pirnt);
cout << "===========" << endl;
for_each(v2.begin(), v2.end(), pirnt);
}
算術生成
包含頭檔案#include <numeric>
accumulate
accumulate(iterator beg, iterator end, value)
value 起始值
計算區間容器元素總和
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
cout<<accumulate(v.begin(), v.end(),0);
}
fill
向容器內填充指定元素
fill(iterator beg,iterator end,value)
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
fill(v.begin(), v.end(),0);
for_each(v.begin(), v.end(), pirnt);
}
集合演算法
set_intersection
求兩個集合的交集,源容器必須是有序序列
set_interaection(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest)
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
vector<int> v1;
v1.push_back(2);
v1.push_back(3);
vector<int> v2;
//先初始化空間,任意一個集合的大小都行,因為交集不可能大于任意一個集合
//使用min取最小的大小
v2.resize(min(v.size(),v1.size()));
vector<int>::iterator itEnd =set_intersection(v.begin(), v.end(), v1.begin(), v1.end(), v2.begin());
for_each(v2.begin(), itEnd, pirnt);
}
set_union
取并集,源容器必須是有序序列
set_union(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest)
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
vector<int> v1;
v1.push_back(2);
v1.push_back(3);
vector<int> v2;
//先初始化空間,最極端的情況就是兩個容器中沒有相同的元素
v2.resize(v.size()+v1.size());
vector<int>::iterator itEnd=set_union(v.begin(), v.end(), v1.begin(), v1.end(), v2.begin());
for_each(v2.begin(), itEnd, pirnt);
}
set_difference
set_difference(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest)
取差集,源容器必須是有序序列
void test1() {
vector<int> v;
v.push_back(1);
v.push_back(2);
vector<int> v1;
v1.push_back(2);
v1.push_back(3);
vector<int> v2;
//先初始化空間,差集不可能大于自己
v2.resize(v.size());
vector<int>::iterator itEnd=set_difference(v.begin(), v.end(), v1.begin(), v1.end(), v2.begin());
for_each(v2.begin(), itEnd, pirnt);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/533478.html
標籤:C++
