今天遇到一個很有意思的bug,當程式開發完成後打包到伺服器運行,總是會出現棧溢出異常 ...
綁定器bind1st,bind2nd
vector<int> vec;
for(int i=0;i<20;i++){
vec.push_back(rand()%100);
}
showContainer(vec);
sort(vec.begin(),vec.end(),greater<int>());
showContainer(vec);
/*
* greater a>b
* less a<b
* */
//把70按順序插入到vector容器中
auto it= find_if(vec.begin(), vec.end(), bind1st(greater<int>(),70));//兩種綁定器的用法
//auto it= find_if(vec.begin(), vec.end(), bind2nd(less<int>(),70));
if(it!=vec.end()){
vec.insert(it,70);
}
showContainer(vec);
綁定器的實現原理
綁定器其實是函數對象的一個應用!!綁定器+二元函數對象+值=一元函數對象。底層還是靠二元函數對象做事
自己實現一個綁定器:
/**
*
* @tparam Compare 函數對象類型
* @tparam T 對象類型
*/
template<typename Compare,typename T>
class CMyBind1st{
public:
CMyBind1st(Compare com,T val):_comp(com),_val(val){}
/**
* 綁定器函數對象的實現,實際上是傳入的函數對象在幹活
* @param second 傳入要綁定的數據
* @return 函數對象,這個函數對象幹完傳回的數據
*/
bool operator()(const T &second){
return _comp(_val,second);
}
private:
Compare _comp;
T _val;
};
/**
* 實現綁定器
* @tparam Compare 返回函數對象類型
* @tparam T 數據對象
* @param comp 函數對象
* @param val 綁定數據
* @return
*/
template<typename Compare,typename T>
CMyBind1st<Compare,T> my_bind1st(Compare comp,const T &val){
return CMyBind1st<Compare,T>(comp,val);
}
/**
*
* @tparam Compare 函數對象類型
* @tparam T 對象類型
*/
template<typename Compare,typename T>
class CMyBind2nd{
public:
CMyBind2nd(Compare com,T val):_comp(com),_val(val){}
/**
* 綁定器函數對象的實現,實際上是傳入的函數對象在幹活
* @param second 傳入要綁定的數據
* @return 函數對象,這個函數對象幹完傳回的數據
*/
bool operator()(const T &first){
return _comp(first,_val);
}
private:
Compare _comp;
T _val;
};
/**
* 實現綁定器
* @tparam Compare 返回函數對象類型
* @tparam T 數據對象
* @param comp 函數對象
* @param val 綁定數據
* @return 返回的是一個函數對象
*/
template<typename Compare,typename T>
CMyBind2nd<Compare,T> my_bind2nd(Compare comp,const T &val){
return CMyBind2nd<Compare,T>(comp,val);
}
/////////////////
/**
* 實現find_if
* @tparam Iterator 迭代器
* @tparam Compare 函數對象
* @param begin
* @param end
* @param comp 函數對象
* @return 迭代器
*/
template<typename Iterator,typename Compare>
Iterator my_find_if(Iterator begin,Iterator end,Compare comp){
for(;begin!=end;++begin){
if(comp(*begin)){//comp.operator()(*begin)
return begin;
}
}
return end;
}
function函數對象的應用
首先講一下什麼是函數指針:
如果在程式中定義了一個函數,那麼在編譯時系統就會為這個函數代碼分配一段存儲空間,這段存儲空間的首地址稱為這個函數的地址。而且函數名錶示的就是這個地址。既然是地址我們就可以定義一個指針變數來存放,這個指針變數就叫作函數指針變數,簡稱函數指針。
那麼這個指針變數怎麼定義呢?雖然同樣是指向一個地址,但指向函數的指針變數同我們之前講的指向變數的指針變數的定義方式是不同的。例如:
int(*p)(int, int);
這個語句就定義了一個指向函數的指針變數 p。首先它是一個指針變數,所以要有一個“”,即(p);其次前面的 int 表示這個指針變數可以指向返回值類型為 int 型的函數;後面括弧中的兩個 int 表示這個指針變數可以指向有兩個參數且都是 int 型的函數。所以合起來這個語句的意思就是:定義了一個指針變數 p,該指針變數可以指向返回值類型為 int 型,且有兩個整型參數的函數。p 的類型為 int(*)(int,int)。
所以函數指針的定義方式為:函數返回值類型 (* 指針變數名) (函數參數列表);
函數指針的使用:
int Func(int x); /*聲明一個函數*/
int (*p) (int x); /*定義一個函數指針*/
p = Func; /*將Func函數的首地址賦給指針變數p*/
# include <stdio.h>
int Max(int, int); //函數聲明
int main(void)
{
int(*p)(int, int); //定義一個函數指針
int a, b, c;
p = Max; //把函數Max賦給指針變數p, 使p指向Max函數
printf("please enter a and b:");
scanf("%d%d", &a, &b);
c = (*p)(a, b); //通過函數指針調用Max函數
printf("a = %d\nb = %d\nmax = %d\n", a, b, c);
return 0;
}
int Max(int x, int y) //定義Max函數
{
int z;
if (x > y)
{
z = x;
}
else
{
z = y;
}
return z;
}
輸出結果是:
please enter a and b:3 4
a = 3
b = 4
max = 4
function:
void hello1(){
cout<<"hello1"<<endl;
}
void hello2(string str){
cout<<str<<endl;
}
int sum(int a,int b){
return a+b;
}
class TT{
public:
TT()=default;
void hello(string str) const {
cout<<"call TT::hello"<<str<<endl;
}
};
int main(){
function<void()> func1=hello1;
function<void(string)> func2=hello2;
func1();
func2("sashkgf");
function<int(int,int)> func3=sum;
cout<<func3(20,22)<<endl;
function<void(TT*,string)> func4=&TT::hello;
TT t;
func4(&t,"asgasgxb");
return 0;
}
使用function需要註意兩點:
- 用函數類型實例化function
- 通過function調用operator()函數的時候,需要根據函數類型傳入相應的參數
一個function應用的例子:
void doShow(){cout<<"查看所有書籍"<<endl;}
void doBorrow(){cout<<"借書"<<endl;}
void doBack(){cout<<"還書"<<endl;}
void doQuery(){cout<<"查詢書籍"<<endl;}
void doLogout(){cout<<"註銷"<<endl;}
int main(){
int choice=0;
map<int,function<void()>> actionMap;
actionMap.insert({1,doShow});
actionMap.insert({2,doBorrow});
actionMap.insert({3,doBack});
actionMap.insert({4,doQuery});
actionMap.insert({5,doLogout});
for(;;){
cout<<"————————————————————"<<endl;
cout<<"1_查看所有書籍"<<endl;
cout<<"2_借書"<<endl;
cout<<"3_還書"<<endl;
cout<<"4_查詢書籍"<<endl;
cout<<"5_註銷"<<endl;
cout<<"————————————————————"<<endl;
cin>>choice;
auto it= actionMap.find(choice);
if(it==actionMap.end()){
cout<<"輸入數字無效,請重新選擇"<<endl;
}else{
it->second();
}
}
return 0;
}
輸出:
1_查看所有書籍
2_借書
3_還書
4_查詢書籍
5_註銷
4
查詢書籍
如何實現function
首先介紹兩個前置知識:
模板的完全特例化和非完全特例化
模板的實參推演
實現function
template<typename Fty>//必須要有
class MyFunction{};
template<typename R,typename... A>//非完全特例化
class MyFunction<R(A...)>{//function其實就是函數指針的封裝
public:
using PFUNC=R(*)(A...);//函數指針
MyFunction(PFUNC pfunc):_pfunc(pfunc){}
R operator()(A... arg){
return _pfunc(arg...);
}
private:
PFUNC _pfunc;
};
int main(){
MyFunction<void(string)> func2=hello2;
func2("sashkgf");
MyFunction<int(int,int)> function= sum;
cout<<function(20,30)<<endl;
return 0;
}
使用bind和function實現線程池
C++11里的綁定器bind返回的還是是一個函數對象
//TODO 學完線程再回來寫這個筆記
lambda表達式
lambda表達式的語法:[捕獲外部變數](形參列表)->返回值{操作代碼}
如果lambda表達式的返回值不需要,那麼->返回值
可以省略
[捕獲外部變數]
lambda表達式生成的函數對象的()重載預設是const常量形的,不能對內部的值進行修改,需要在lambda表達式的後面加mutable標識符才可以修改(只有是傳值的情況下需要加這個標識符)。如果是[&]的情況就不需要加這個標識符
使用lambda表達式實現優先順序隊列:
class Data{
public:
Data(int val1=10,int val2=10):ma(val1),mb(val2){}
int ma;
int mb;
};
int main(){
using FUNC=function<bool(Data&,Data&)>;
priority_queue<Data,vector<Data>,FUNC> que([](Data& d1,Data& d2)->bool{//在構造函數中使用lambda表達式傳入函數對象
return d1.ma>d2.ma;
});
que.push({10,20});
que.push({20,10});
que.push({30,40});
return 0;
}