參考資源:1 黑馬程式員
參考資源:2 《C++標準庫 - 侯捷》中的 5.2 節-容器
文章目錄
- 1 各容器的特點總結及使用場景
- 2 各個容器的使用場景
- 3 函式物件
- 3.1 函式物件的基本概念
- 3.2 函式物件實體演示+解釋
- 3.3 函式物件的優勢
- 4 謂詞
- 4.1 謂詞的基本概念
- 4.2 一元謂詞實體使用
- 4.2 二元謂詞實體使用
- 5 內建函式物件
- 5.1 內建函式的基本概念
- 5.2 內建函式分類
- 6 函式物件配接器
- 6.1 函式物件配接器的基本概念
- 6.2 bind1st 和 bind2nd系結配置器的實體使用
- 6.2.1 bind1st和bin2nd的區別
- 6.3 not1和not2取反配置器的實體使用
- 6.4 ptr_func 函式指標配接器
- 6.5 mem_fun mem_func_ref 成員函式配接器
- 6.6函式配置器使用總結
- 7 演算法
- 7.1 常用遍歷演算法
- 7.2 常用查找演算法
- 7.3 常用排序演算法
- 7.4 常用拷貝和替換演算法
1 各容器的特點總結及使用場景
- vector 頭部與中間插入和洗掉效率較低,在尾部插入和洗掉效率高,支持隨機訪問,
- deque 是在頭部和尾部插入和洗掉效率較高,支持隨機訪問,但效率沒有 vector 高,
- list 在任意位置的插入和洗掉效率都較高,但不支持隨機訪問,
- set 由紅黑樹實作,其內部元素依據其值自動排序,每個元素值只能出現一次,不允許重復,且插入和洗掉效率比用其他序列容器高,
- map 可以自動建立 Key - value 的對應,key 和 value 可以是任意你需要的型別,根據 key 快速查找記錄,
2 各個容器的使用場景
- 如果需要高效的隨機存取,不在乎插入和洗掉的效率,使用 vector,
- 如果需要大量的插入和洗掉元素,不關心隨機存取的效率,使用 list,
- 如果需要隨機存取,并且關心兩端資料的插入和洗掉效率,使用 deque,
- 如果打算存盤資料字典,并且要求方便地根據 key 找到 value,一對一的情況使用 map,一對多的情況使用 multimap,
- 如果打算查找一個元素是否存在于某集合中,唯一存在的情況使用 set,不唯一存在的情況使用 multiset,
3 函式物件
3.1 函式物件的基本概念
多載函式呼叫運算子的類,其物件常稱為函式物件(function object),即它們是行為類似函式的物件,也叫仿函式(functor),其實就是多載“()”運算子,使得類物件可以像函式那樣呼叫,
注意:
- 函式物件(仿函式)是一個類,不是一個函式,
- 函式物件(仿函式)多載了”() ”運算子使得它可以像函式一樣呼叫,
假定某個類有一個多載的operator(),而且多載的operator()要求獲取一個引數,我們就將這個類稱為“一元仿函式”(unary functor);相反,如果多載的operator()要求獲取兩個引數,就將這個類稱為“二元仿函式”(binary functor),
3.2 函式物件實體演示+解釋
不難看出下面代碼用了兩種方式去實作記錄函式呼叫次數,
- 方法一:全域變數,但是這種方式可能帶來的是1 無意間的修改2干擾了模塊化3 并發(Concurrency)的問題…等等問題
- 方法二:我們用函式物件 ,內部定義一個num的成員,避免的這一問題,利用了
函式物件可以保存函式呼叫的狀態的特性,補充:這邊用到的是struct 而不是 class ,并不是不能用class,只是為了方便,class和struct的區別只是在于
struct默認為public,
//全域變數
int num = 0;
//函式物件多載了()
struct MyPrint
{
MyPrint()
{
num = 0;
}
void operator()(int val)
{
num++;
cout << val << endl;
}
int num;
};
//普通函式 用來測驗比對
void MyPrint1(int val)
{
num++;
cout<<val<<endl;
}
int main(void)
{
MyPrint prin1;//常見一個實體類
prin1(10);
prin1(20);
prin1(30);
cout << prin1.num<<endl;
cout<<"----分割線----"<<endl;
MyPrint1(10);
MyPrint1(20);
MyPrint1(30);
cout << num << endl;
return 0;
}
3.3 函式物件的優勢
-
函式物件可以像普通函式一樣呼叫,
-
函式物件可以像函式那樣接受引數,
-
函式物件 超出函式的概念 函式物件可以保存函式呼叫的狀態,
-
函式物件課可行內編譯,性能好,用函式指標幾乎不可能,
-
模板函式物件具有通用性,優勢之一,
4 謂詞
4.1 謂詞的基本概念
謂詞是指普通函式或 多載的operator() 回傳值是bool型別的函式物件(仿函式),如果operator接受一個引數,那么叫做一元謂詞,如果接受兩個引數,那么叫做二元謂詞,謂詞可作為一個判斷式.
4.2 一元謂詞實體使用
前提說明:
- find_if是一個模板函式,接受兩個資料型別:InputItearator迭代器,Predicate用于比較數值的函式或者函式物件(仿函式),查詢成功回傳對應元素的迭代器,查詢失敗回傳v.end();(最后的迭代器)
// 函式物件
struct compare
{
bool operator()(int val)
{
return val > 5;
}
};
void test101()
{
vector<int> v;
v.push_back(1);
v.push_back(4);
v.push_back(7);
v.push_back(11);
vector<int>::iterator var = find_if(v.begin(), v.end(), compare());//第三個引數這邊用的是匿名函式物件
if (var == v.end())
{
cout << "查詢失敗"<< endl;
}
else
{
cout << "找到了" <<"當前值:"<<*var<<endl;
}
}

4.2 二元謂詞實體使用
前提說明:sort 是排序演算法 引數一 和 引數二 通過迭代器指定排序區間,引數三可以用函式物件改變演算法策略,
struct MyCompare1
{
bool operator()(int val, int val1)
{
return val > val1;
}
};
void test111()
{
vector<int > v;
v.push_back(10);
v.push_back(1);
v.push_back(30);
v.push_back(13);
//利用函式物件 改變演算法策略 原本是默認從小到大
sort(v.begin(), v.end(), MyCompare1());//匿名函式物件
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout<<*it<<endl;
}
}

寫法二,不用匿名函式物件 直接使用實體物件
struct MyCompare1
{
bool operator()(int val, int val1)
{
return val > val1;
}
};
void test111()
{
vector<int > v;
v.push_back(10);
v.push_back(1);
v.push_back(30);
v.push_back(13);
MyCompare1 m1;//創建實體函式物件
//利用函式物件 改變演算法策略 原本是默認從小到大
sort(v.begin(), v.end(), m1);//使用實體函式物件
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout<<*it<<endl;
}
}
5 內建函式物件
5.1 內建函式的基本概念
STL內建了一些函式物件,分為:算數類函式物件,關系運算類函式物件,邏輯運算類仿函式,這些仿函式所產生的物件,用法和一般函式完全相同,當然我們還可以產生無名的臨時物件來履行函式功能,使用內建函式物件,需要引入頭檔案#include<functional>.
類名<引數型別> 實體類名;
5.2 內建函式分類
6個算數類函式物件,除了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>//取反仿函式
6個關系運算類函式物件,每一種都是二元運算,
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>//小于等于
邏輯運算類運算函式,not為一元運算,其余為二元運算
template<class T> bool logical_and<T>//邏輯與
template<class T> bool logical_or<T>//邏輯或
template<class T> bool logical_not<T>//邏輯非
plus<int> p;//相加
int a=p(10, 20);
cout << a<<endl;
equal_to<string> e;//判斷是否等于 回傳 bool
bool e1=e("aaa", "bbb");
cout << e1<<endl;
6 函式物件配接器
6.1 函式物件配接器的基本概念
函式物件配接器是完成一些配接作業,這些作業包括系結(bind),否定(negate),以及對一般成員或成員函式的修飾,使其成為函式物件,重點掌握以下四種重要的以及三種不常用的,
6.2 bind1st 和 bind2nd系結配置器的實體使用
需求分析: 在遍歷容器的時候,我希望將容器中的值全部加上x(我們設定的增值)之后顯示出來,
問題分析:從for_each的定義可以看出,第三個引數位置的函式物件每次只能傳入一個引數,如果要使用for_each去直接實作需求顯然是不可能的,
配接器作用:利用bind1st 或者bind2nd配接器將二元函式物件轉成一元函式物件( ),
class MyPrint :public binary_function<int, int, void>
{
public:
void operator()(int a, int b) const
{
cout <<"和為:"<< a + b <<" 第一個值為:"<<a<<" 第二個值為:"<<b<< endl;
}
};
void test01()
{
vector<int> v;
for (int i = 0; i < 5; i++)
{
v.push_back(i);
}
int x=100;
for_each(v.begin(), v.end(), bind1st( MyPrint(),x ));
}

class MyPrint :public binary_function<int, int, void>
{
public:
void operator()(int a, int b) const
{
cout <<"和為:"<< a + b <<" 第一個值為:"<<a<<" 第二個值為:"<<b<< endl;
}
};
void test01()
{
vector<int> v;
for (int i = 0; i < 5; i++)
{
v.push_back(i);
}
int x=100;
for_each(v.begin(), v.end(), bind2nd( MyPrint(),x ));
}

6.2.1 bind1st和bin2nd的區別
bind1st,將addnum系結為函式物件的第一個引數
bin2nd,將addnum系結為函式物件的第二個引數
6.3 not1和not2取反配置器的實體使用
not1作用:對一元函式物件取反 ,從輸出大于5的第一個值轉變成 小于5的第一個值
class MyGreater:public unary_function<int, bool>
{
public:
bool operator()(int val)const
{
return val > 5;
}
};
void test909()
{
vector<int> v;
v.push_back(1);
v.push_back(3);
v.push_back(10);
v.push_back(2);
vector<int>::iterator it = find_if(v.begin(),v.end(), not1(MyGreater()));
cout<<*it<<endl;
}

not2作用:對二元函式物件取反 ,從大到小排序,設定成從小到大排序
class Compare1:public binary_function<int, int, void>
{
public:
bool operator()(int val,int val1)const
{
return val > val1;
}
};
void test111()
{
vector<int> v;
v.push_back(1);
v.push_back(78);
v.push_back(10);
v.push_back(2);
sort(v.begin(),v.end(),not2(Compare1()));
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout<<(*it)<<endl;
}
}
6.4 ptr_func 函式指標配接器
//如何給一個普通函式使用系結配接器(bind1st bind2nd)系結一個引數?(拓展)
//ptr_fun
void myprint04(int v1, int v2){
cout << v1 + v2 << " ";
}
void test04(){
vector<int> v;
v.push_back(2);
v.push_back(1);
v.push_back(5);
v.push_back(4);
//1 將普通函式適配成函式物件
//2 然后通過系結器系結引數
for_each(v.begin(), v.end(), bind2nd(ptr_fun(myprint04), 100));
cout << endl;
//總結: ptr_fun 將普通函式轉變為函式物件
}
6.5 mem_fun mem_func_ref 成員函式配接器
//mem_fun mem_fun_ref
//如果我們容器中存盤的是物件或者物件指標,如果能指定某個成員函式處理成員資料,
class student{
public:
student(string name, int age) :name(name), age(age){}
void print(){
cout << "name:" << name << " age:" << age << endl;;
}
void print2(int a){
cout << "name:" << name << " age:" << age << " a:" << a << endl;
}
int age;
string name;
};
void test05(){
//mem_fun : 如果存盤的是物件指標,需要使用mem_fun
vector<student*> v;
student* s1 = new student("zhaosi", 10);
student* s2 = new student("liuneng", 20);
student* s3 = new student("shenyang", 30);
student* s4 = new student("xiaobao", 40);
v.push_back(s1);
v.push_back(s2);
v.push_back(s3);
v.push_back(s4);
for_each(v.begin(), v.end(), mem_fun(&student::print));
cout << "-----------------------------" << endl;
//mem_fun_ref : 如果容器中存盤的是物件,需要使用mem_fun_ref
vector<student> v2;
v2.push_back(student("zhaosi", 50));
v2.push_back(student("liuneng", 60));
v2.push_back(student("shenyang", 70));
v2.push_back(student("xiaobao", 80));
for_each(v2.begin(), v2.end(), mem_fun_ref(&student::print));
}
6.6函式配置器使用總結
- 需要繼承一個類 :
binary_function< >二元函式物件使用;unary_function<>一元函式使用,例如 :public binary_function < int int bool >前兩個表示傳入引數的型別 第三個表示回傳的型別, - 需要加上
const限定符 例如:bool operator(int a,int b)const operator名稱不要寫錯,不好找,
7 演算法
這部分具體使用具體查詢,內容過多,不一一列舉,
7.1 常用遍歷演算法
for_each 和 transform用法簡單,唯一需要注意的就是利用transform把一個容器元素搬到另外一個容器,需要預先開辟空間,不能是
reserve而是使用resize如下
Target.resize(Source.size());
/*
遍歷演算法 遍歷容器元素
@param beg 開始迭代器
@param end 結束迭代器
@param _callback 函式回呼或者函式物件
@return 函式物件
*/
for_each(iterator beg, iterator end, _callback);
/*
transform演算法 將指定容器區間元素搬運到另一容器中
注意 : transform 不會給目標容器分配記憶體,所以需要我們提前分配好記憶體
@param beg1 源容器開始迭代器
@param end1 源容器結束迭代器
@param beg2 目標容器開始迭代器
@param _cakkback 回呼函式或者函式物件
@return 回傳目標容器迭代器
*/
transform(iterator beg1, iterator end1, iterator beg2, _callbakc)
for_each:
/*
template<class _InIt,class _Fn1> inline
void for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
{
for (; _First != _Last; ++_First)
_Func(*_First);
}
*/
//for_each 和 transform用法簡單 唯一注意的就是
7.2 常用查找演算法
/*
find演算法 查找元素
@param beg 容器開始迭代器
@param end 容器結束迭代器
@param value 查找的元素
@return 回傳查找元素的位置
*/
find(iterator beg, iterator end, value)
/*
find_if演算法 條件查找
@param beg 容器開始迭代器
@param end 容器結束迭代器
@param callback 回呼函式或者謂詞(回傳bool型別的函式物件)
@return bool 查找回傳true 否則false
*/
find_if(iterator beg, iterator end, _callback);
/*
adjacent_find演算法 查找相鄰重復元素
@param beg 容器開始迭代器
@param end 容器結束迭代器
@param _callback 回呼函式或者謂詞(回傳bool型別的函式物件)
@return 回傳相鄰元素的第一個位置的迭代器
*/
adjacent_find(iterator beg, iterator end, _callback);
/*
binary_search演算法 二分查找法
注意: 在無序序列中不可用
@param beg 容器開始迭代器
@param end 容器結束迭代器
@param value 查找的元素
@return bool 查找回傳true 否則false
*/
bool binary_search(iterator beg, iterator end, value);
/*
count演算法 統計元素出現次數
@param beg 容器開始迭代器
@param end 容器結束迭代器
@param value回呼函式或者謂詞(回傳bool型別的函式物件)
@return int回傳元素個數
*/
count(iterator beg, iterator end, value);
/*
count演算法 統計元素出現次數
@param beg 容器開始迭代器
@param end 容器結束迭代器
@param callback 回呼函式或者謂詞(回傳bool型別的函式物件)
@return int回傳元素個數
*/
count_if(iterator beg, iterator end, _callback);
7.3 常用排序演算法
/*
merge演算法 容器元素合并,并存盤到另一容器中
@param beg1 容器1開始迭代器
@param end1 容器1結束迭代器
@param beg2 容器2開始迭代器
@param end2 容器2結束迭代器
@param dest 目標容器開始迭代器
*/
merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest)
/*
sort演算法 容器元素排序
注意:兩個容器必須是有序的
@param beg 容器1開始迭代器
@param end 容器1結束迭代器
@param _callback 回呼函式或者謂詞(回傳bool型別的函式物件)
*/
sort(iterator beg, iterator end, _callback)
/*
sort演算法 對指定范圍內的元素隨機調整次序
@param beg 容器開始迭代器
@param end 容器結束迭代器
*/
random_shuffle(iterator beg, iterator end)
/*
reverse演算法 反轉指定范圍的元素
@param beg 容器開始迭代器
@param end 容器結束迭代器
*/
reverse(iterator beg, iterator end)
7.4 常用拷貝和替換演算法
/*
copy演算法 將容器內指定范圍的元素拷貝到另一容器中
@param beg 容器開始迭代器
@param end 容器結束迭代器
@param dest 目標起始迭代器
*/
copy(iterator beg, iterator end, iterator dest)
/*
replace演算法 將容器內指定范圍的舊元素修改為新元素
@param beg 容器開始迭代器
@param end 容器結束迭代器
@param oldvalue 舊元素
@param oldvalue 新元素
*/
replace(iterator beg, iterator end, oldvalue, newvalue)
/*
replace_if演算法 將容器內指定范圍滿足條件的元素替換為新元素
@param beg 容器開始迭代器
@param end 容器結束迭代器
@param callback函式回呼或者謂詞(回傳Bool型別的函式物件)
@param oldvalue 新元素
*/
replace_if(iterator beg, iterator end, _callback, newvalue)
/*
swap演算法 互換兩個容器的元素
@param c1容器1
@param c2容器2
*/
swap(container c1, container c2)
學習筆記總結,如有錯誤,歡迎指出!,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/271550.html
標籤:其他

