面试五 -bind 和 function

         函数对象:函数对象是一个类,它重载了函数调用操作符 operator()。因此,函数对象可以像函数一样被调用,而且可以保存状态。函数对象通常用于STL算法中,例如std::sortstd::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才能对成员变量进行修改 

 2. 使用引用传递

 3. 应用

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值