c++基礎2

来源:https://www.cnblogs.com/sunankang/archive/2022/11/14/16890678.html
-Advertisement-
Play Games

模板 c++另一種編程思想稱為泛型編程,主要利用的技術就是模板 c++提供兩種模板機制:函數模板和類模板 函數模板 建立一個通用函數,函數的返回值類型和形參類型可以不具體指定,用一個虛擬的類型來代表 語法: template<typename T> //或者 template<class T> 函數 ...


模板

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六大組件

容器,演算法,迭代器,仿函數,適配器(配接器),空間配置器

  1. 容器:各種數據結構,如vector,list,deque,set,map等,用來存放數據
  2. 演算法:各種常用的演算法,如sort,find,copy,for_each等
  3. 迭代器:扮演了容器與演算法之間的膠合器
  4. 仿函數:行為類似函數,可作為演算法的某種策略
  5. 適配器:一種用來修飾容器或仿函數或迭代器介面的東西
  6. 空間配置器:賦值空間的配置與管理

容器

常用數據結構:數組,鏈表,數,棧,隊列,集合等

容器分為序列式容器和關聯式容器

  • 序列式容器:強調值的排序,序列式容器中每個元素都有固定的位置
  • 關聯式容器:二叉樹結構,各元素之間沒有嚴格的物理上的順序關係

演算法

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

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

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

迭代器

提供一種方法,能夠依序尋訪某個容器中所包含的每個元素,而又無需暴露該容器的內部表示方式

每個容器都有自己的迭代器,迭代器非常類似於指針,初學階段可以先理解為指針

迭代器種類:

輸入迭代器:對數據的只讀訪問,支持++ == !=

輸出迭代器:對數據的只寫操作,支持++

前向迭代器 讀寫操作,並能向前推進迭代器 支持++ == !=

雙向迭代器 讀寫,並能向前和向後操作 支持++ --

隨機訪問迭代器 讀寫操作 可以跳躍的訪問任何數據 支持++ -- [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個字元替換為str
  • string& 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個字元c
  • string& 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) 尾部插入元素ele
  • pop_back() 刪除最後一個元素
  • insert(const_iterator pos,ele) 迭代器指向位置pos插入元素ele
  • insert(const_iterator pos,int count,ele) 迭代器指向位置pos插入count個元素ele
  • erase(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) 尾部添加e
  • push_front(e) 頭部插入e
  • pop_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.pu

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

-Advertisement-
Play Games
更多相關文章
  • 在瀏覽器訪問網站,想在瀏覽器最新化的情況下,也能收到右下角的消息通知 這個時候就會用到H5 Notifications 具體效果可以參照演示頁面 演示頁面-唯一線上客服系統 實現代碼js function notify(title, options, callback) { // 先檢查瀏覽器是否支 ...
  • TypeScript 是對 JavaScript 的補充,將 JavaScript 由動態類型、弱類型語言轉為靜態類型、強類型的語言 簡介 TypeScript 由三個部分組成: 類型:為 JavaScript 代碼添加類型與類型檢查來確保健壯性,進入學習 語法:提前使用新語法或新特性來簡化代碼,進 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 vm.$forceUpdate (1)作用 迫使Vue.js實例重新渲染。註意它僅僅影響實例本身以及插入插槽內容的子組件,而不是所有子組件。 (2)實現 只需要執行watcher的update方法,就可以讓實例重新渲染。 Vue.js的每 ...
  • 該系列已更新文章: 分享一個實用的 vite + vue3 組件庫腳手架工具,提升開發效率 開箱即用 yyg-cli 腳手架:快速創建 vue3 組件庫和vue3 全家桶項目 Vue3 企業級優雅實戰 - 組件庫框架 - 1 搭建 pnpm monorepo Vue3 企業級優雅實戰 - 組件庫框架 ...
  • css裡面有個背景色漸變色的效果,我們能拿來做什麼呢 現在就演示下,我在開發此頁面時所實際實現的樣子 演示頁面-唯一線上客服系統 實現代碼很簡單,效果還是很不錯: background: linear-gradient(90deg, #EE884C 0%, #FFBA8E 100%); 首頁里也有個 ...
  • 摘要:Ajax是非同步JavaScript和XML可用於前後端交互。 本文分享自華為雲社區《Flask框架:運用Ajax輪詢動態繪圖》,作者:LyShark。 Ajax是非同步JavaScript和XML可用於前後端交互,在之前《Flask 框架:運用Ajax實現數據交互》簡單實現了前後端交互,本章將通 ...
  • 5. 文件服務開發 全套代碼及資料全部完整提供,點此處下載 5.1 環境搭建 5.1.1 資料庫環境搭建 第一步:創建pd_files資料庫 create database pd_files character set utf8mb4; 第二步:在pd_files資料庫中創建pd_attachmen ...
  • 前提 Tomcat 10.1.x Tomcat線程池介紹 Tomcat線程池,源於JAVA JDK自帶線程池。由於JAVA JDK線程池策略,比較適合處理 CPU 密集型任務,但是對於 I/O 密集型任務,如資料庫查詢,rpc 請求調用等,不是很友好,所以Tomcat在其基礎上進行了擴展。 任務處理 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...