函数对象:函数对象是一个类,它重载了函数调用操作符 operator()
。因此,函数对象可以像函数一样被调用,而且可以保存状态。函数对象通常用于STL算法中,例如std::sort
、std::transform
等,以及C++11中的Lambda表达式。
绑定器:绑定器是一种特殊的函数对象,它用于将部分参数绑定到函数上,从而创建一个新的函数对象。
一、vector的绑定器bind1st 和bind2nd
#include <iostream>
template<typename Container>
void showContainer(Container& con) {
typename Container::iterator it = con.begin(); // 定义迭代器并初始化为容器的起始位置
for (; it != con.end(); ++it) { // 遍历容器
std::cout << *it << " "; // 输出当前元素值
}
std::cout << std::endl; // 输出换行
}
int main() {
std::vector<int> vec ;
srand(time(nullptr));
for(int i=0;i<20;i++){
vec.push_back(rand() % 100+1);
}
showContainer(vec);
sort(vec.begin(),vec.end()); // 默认从小到大
showContainer(vec);
sort(vec.begin(),vec.end(),greater<int>()); // greater 为二元函数对象
showContainer(vec); // 调用函数显示容器内容
return 0;
}
auto it1 = find_if(vec.begin(),vec.end(),bind1st(greater<int>(),70));
if(it1 != vec.end())[
vec.insert(it1 ,70);
}
showContainer(vec);
find_if
函数用于在 vec
容器中查找第一个大于70的元素,bind1st
函数适配器用于将 greater<int>()
函数对象的第一个参数绑定为70,创建了一个新的一元函数对象。找到该元素后,如果不是容器的末尾迭代器,就在找到的位置之前插入一个值为70的元素,并显示修改后的容器。
底层实现:
my_find_if 需要一个一元函数对象,绑定器是函数对象的一个应用
template<typename Iterator,typename Compare>
Iterator my_find_if(Iterator first,Iterator last,Compare comp){
for(;first != last;++first){
if(comp(*first)){
return first;
}
}
return last;
}
// mybind1st(greater<int>(),70)
template<typename Compare,typename T>
_mybind1st<Compare,T> mybind1st(Compare comp,const T&val)
{
// 直接使用函数模板,好处是,可以进行类型的推演
return _mybind1st<Compare,T>(comp,val);
}
template<typename Compare ,typename T>
class _mybind1st{
public:
_mybind1st(Compare comp,T val): _comp(comp),_val(val){}
bool operator() (const T &second){
return _comp(_val ,second);
}
private:
Compare _comp;
T _val;
};
二、bind绑定器
1.function的使用
C++ 中的 std::function
是一个通用的函数封装器,它可以用来存储、传递和调用任何可调用目标(如函数、函数指针、lambda 表达式、成员函数等)。保留类型。
2. 模板的完全特例化和非完全(部分)特例化 及 模板的实参推演
1.完全特例化(Full Specialization): 在完全特例化中,模板被特化为一个特定的类型或参数,而不是通用的模板。这意味着对于给定的类型或参数,完全特例化版本将提供完整的实现,而不是使用通用模板的泛化实现。
2.部分特例化(Partial Specialization): 部分特例化允许我们对模板的一部分进行特化,而不是整个模板。通常用于类模板,部分特例化允许我们根据模板参数的某些属性来特化模板。
/*
模板的完全特例化和部分特例化
模板的实参推演
*/
template<typename T>
bool compare(T a ,T b){
return a >b;
}
// 完全特列化
template<>
bool compare<const char*>(const char *a ,const char *b){
cout<< "compare <const char*>" <<endl;
return strcmp(a,b) >0;
}
int main(){
compare(10,20); // 首先进行模板的实参推演将传入参数推断类型,然后进行实例化出一个模板函数进行编译
compare("aaa","bbb"); // 传入函数模板中,T const char* 这样比较的是地址,需要我们提供特列化的函数
}
// 模板的实参推演 #3
template<typename T>
class Vector{
public:
Vector(){
cout<< " call vector template init "<< endl;
}
}
// 完全特列化 #1
template<>
class Vector<char *>{
public:
Vector(){
cout<< " call vector <char *> init "<< endl;
}
}
// 对于指针类型提供部分特列化 #2
template <typename Ty>
class Vector<Ty *>{
public:
Vector(){
cout<< " call vector <Ty*> init "<< endl;
}
}
int main(){
Vector<int(*)(int ,int)>vec4; // 调用部分特利化
}
// 函数指针(有返回值,有两个形参变量)提供的部分特列化
template<typename R,typename A1,typename A2>
class Vector<R(*) (A1,A2)>
{
publuc:
}
// 针对函数(有一个返回值,有两个形参变量)提供的部分特列化
template<typename T,typename A1,typename A2>
class Vector<T(A1,A2)>
{
publuc:
}
// 注意区分一下函数类型和函数指针类型
typedef int(*PFUNC1)(int ,int);
PFUNC1 pfunc1 =sum;
cout << pfunc1(10,20) <<endl;
typedef int PFUNC2(int ,int)
PFUNC2 *pfunc2=sum;
cout << (*pfunc2)(10,20)<< endl;
模板的实参推演
模板的实参推演是指在使用模板时,编译器根据传递给模板的参数来推断模板参数的过程。这个过程发生在编译时,它使得模板能够在不指定具体类型的情况下进行工作。编译器会实例化一个的版本来处理这个调用。
3.fuction实现原理
使用类模板+ 部分特例化 +operator()运算符,将函数指针传入,然后使用部分特例化将函数类型分解,然后重载(),底层调用传入的函数,其实还是函数对象
4. bind和function实现线程池
function绑定类型,而bind可以给函数绑定参数。
绑定器只能使用在语句当中,当语句结束了,下条语句需要重新写绑定器。绑定器的类型留下了可以使用function
1. bind返回一个函数对象,可以直接通过()调用
2. 参数占位符,最多绑定20个 ,绑定器出了语句,无法继续使用
3. 使用function存储参数,返回一个函数对象
4. 实现线程池
class Thread{
public:
Thread(function<void()> func): _func(func){}
thread start(){
thread t(_func); // _func()
return t;
}
private:
function<void()>_func;
}
class ThreadPool{
public:
ThreadPool(){}
~ThreadPool(){
// 释放Thread对象占用的堆资源(如果vector容器中存放了指向堆上分配内存的指针时候,这些指针指向的内存需要手动释放
for(int i=0;i<_pool.size();i++){
delete _pool[i];
}
}
// 开启线程池
void startPool(int size){
for(int i-0;i<size;i++){
_pool.push_back(new Thread( bind(&ThreadPool::runInThread, this ,i)));
}
for(int i=0;i<size;++i){
_handler.push_back(_pool[i]->start());
}
}
private:
vector<Thread *>_pool;
vector<thread>_handler;
// 将runInThread这个成员方法充当线程函数 thread pthread_create 需要的线程函数都是c函数,不能直接使用成员函数,必须使用bind给成员函数绑定对象
void runInThread(int id){
cout << "call runInThread id:" << id <<endl;
}
}
三、 lambda表达式
函数对象的缺点之一是需要定义一个特定的类型或类来实现它。这种定义增加了代码的复杂性,并且可能会导致代码的可读性和维护性降低。
另一个缺点是函数对象可能会引入额外的性能开销。虽然现代编译器和优化器可以对函数对象进行内联优化,但是仍然存在一些额外的开销,比如函数调用和对象构造销毁的开销。
闭包类型(Closure Type)是指Lambda表达式所生成的匿名类型。当你定义一个Lambda表达式时,编译器会自动生成一个匿名的类,这个类重载了operator()
,并且可以捕获其周围的变量。这个匿名类就是Lambda表达式的闭包类型。闭包类型通常是一个特定Lambda表达式的类型,每个Lambda表达式都会生成一个不同的闭包类型。
1. 使用值传递
类的常成员方法不能修改成员变量,只能读,mutable修饰成员变量之后,常成员方法就能访问
在lambda表达式中,需要加入mutable才能对成员变量进行修改