这是STL中 绑定器
分别用于绑定二元函数的第一个或第二个参数
c++11 中的 bind绑定器和 function函数对象机制
lambda表达式—底层依赖函数对象的机制实现
代码:
#include
#include
#include
#include
using namespace std;
template
void showContainer(Container& con)
{
typename Container::iterator it = con.begin(); //需要加 typename,编译时并不知道什么类型
for (; it != con.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
vector 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);
// greater 二元函数对象
sort(vec.begin(), vec.end(), greater());
showContainer(vec);
//把70按顺序 插入到vec中 使用bind1st 70>b
auto it1 = find_if(vec.begin(), vec.end(), bind1st(greater(), 70));
if (it1 != vec.end())
{
vec.insert(it1, 70);
}
showContainer(vec);
}
代码:
#include
#include
#include // 用于 std::greater, std::less 等
// 自定义的 mybindist 类,模拟 bind1st 的功能
template
class _mybindist
{
public:
// 构造函数,接受一个比较函数对象和一个值
_mybindist(Compare comp, T val)
: _comp(comp), _val(val)
{}
// 重载函数调用运算符,使其成为一个函数对象
bool operator()(const T& x) const
{
// 使用绑定的值和传入的参数进行比较
return _comp(x,_val); // 这里要根据使用 greater和less 来调整顺序
}
private:
Compare _comp; // 比较函数对象
T _val; // 绑定的值
};
// 辅助函数,用于创建 mybindist 对象
template
_mybindist mybindist(Compare comp, const T& val)
{
// 返回 mybindist 对象
return _mybindist(comp, val);
}
// 自定义的 my_find_if 函数,模拟 std::find_if 的功能
template
Iterator my_find_if(Iterator first, Iterator last, Predicate pred)
{
for (; first != last; ++first)
{
if (pred(*first)) // 如果满足条件
{
return first; // 返回当前迭代器
}
}
return last; // 如果没有找到,返回 last
}
int main()
{
// 创建一个整数向量
std::vector vec = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
// 使用 mybindist 创建一个函数对象,检查元素是否大于 70
auto greater_than_70 = mybindist(std::greater(), 70);
// 使用 my_find_if 查找第一个大于 70 的元素
auto it = my_find_if(vec.begin(), vec.end(), greater_than_70);
// 输出结果
if (it != vec.end())
{
std::cout << "First element greater than 70 is: " << *it << std::endl;
}
else
{
std::cout << "No element greater than 70 found." << std::endl;
}
return 0;
}
直接使用函数模板, 好处是 可以进行 类型的 推演
处理能力有限, 只能处理二元函数对象, 多了就不行了
c++11的绑定器bind 与 STL中的bind1st 和 bind2nd不同, 能用, 但已被弃用
STL中的bind1st 和 bind2nd ---- 本质还是函数对象
绑定器, 函数对象, lambda 表达式, 只能使用在一条语句中, 使用了function, 可以解决这个问题
补充知识点: 函数类型 和 函数指针类型 区分
函数类型
函数类型指的是函数的签名(signature),包括函数的返回类型和参数类型。函数类型本身并不是一个可以直接使用的对象,而是描述函数的特征。
int add(int a, int b); // 函数类型是 int(int, int)
void print(const std::string& str); // 函数类型是 void(const std::string&)
函数类型通常用于模板参数或类型推导中。
std::function func = add; // std::function 使用函数类型 int(int, int)
函数指针类型
函数指针类型是指向函数的指针类型。它是一个具体的对象,可以存储函数的地址,并通过指针调用函数。
int (*funcPtr)(int, int); // 函数指针类型是 int(*)(int, int)
funcPtr = add; // 将函数 add 的地址赋值给 funcPtr
int result = funcPtr(2, 3); // 通过函数指针调用函数
函数指针类型的语法包括:
*
表示)function 应用实例代码:
用函数类型 实例化 function
通过function调用operator()函数的时候, 需要根据函数类型 传入 相应的参数
可以保留 lambda 表达式, 到处都可以使用
最大的作用: 保留可以看到的函数, 成员方法, lambda表达式, 其他函数对象
#include
#include
#include // 用于 std::greater, std::less 等
using namespace std;
void hello1()
{
cout << "hello world!" << endl;
}
void hello2(string str) // void(*pfunc)(string)
{
cout << str << endl;
}
int sum(int a, int b)
{
return a + b;
}
class Test
{
// 成员方法和普通函数不同, 调用必须 依赖对象,通过函数指针调用 void(Test::*pfunc)(string)
public:
void hello(string str) { cout << str << endl; }
};
int main()
{
// 从function的类模板定义处,看到希望用一个函数类型实例化function
function func1 = hello1;
func1(); // func1.operator()=>hello1()
function func2 = hello2; //
func2("hello2"); // func2.operator()(string str)=>hello2(string)
function func3 = sum;
cout << func3(4, 5) << endl;
// function + lambda
function func4 = [](int a, int b)->int {return a + b; };
cout << func4(40, 50) << endl;
// function+成员方法
// 课里讲的 这个在高版本 编译不了, & 必须跟左值
//function func5 = &Test::hello;
//func5(&Test(), "call Tes::hello!"); // &Test() 临时对象地址
function func5 = &Test::hello;
func5(Test(), "call Tes::hello!"); // &Test() 临时对象地址
return 0;
}
特别注意: 成员方法第一个参数, 都是 this指针!!!, 不要忽略, 这不像python, python会标出self, cpp不会标this指针
实际的 应用案例之一:
这样的代码 无法闭合, 无法做到 “开-闭原则”, 无法通过添加代码完成修改, 而是需要 修改原本的代码
#include
using namespace std;
int main() {
int choice;
cout << "1.查看所有书籍信息" << endl;
cout << "2.借书" << endl;
cout << "3.还书" << endl;
cout << "4.查询书籍" << endl;
cout << "5.注销" << endl;
cout << "---" << endl;
cout << "请选择:";
cin >> choice;
switch (choice) {
case 1:
// 查看所有书籍信息的代码
cout << "查看所有书籍信息" << endl;
break;
case 2:
// 借书的代码
cout << "借书" << endl;
break;
case 3:
// 还书的代码
cout << "还书" << endl;
break;
case 4:
// 查询书籍的代码
cout << "查询书籍" << endl;
break;
case 5:
// 注销的代码
cout << "注销" << endl;
break;
default:
cout << "无效的选择,请重新选择。" << endl;
break;
}
return 0;
}
优化:----function应用
#include
#include
#include
有完全特例化 版本, 优先匹配 完全特例化版本
#include
#include // 用于 strcmp 函数
using namespace std;
// 通用模板函数
template
bool compare(T a, T b)
{
return a > b;
}
// 特化版本,用于 const char* 类型
template<>
bool compare(const char* a, const char* b)
{
return strcmp(a, b) > 0;
}
int main()
{
// 使用通用模板比较整数
compare(10, 20);
// 使用特化版本比较 C 风格字符串
compare("aaa", "bbb");
return 0;
}
#include
using namespace std;
// 通用模板类
template
class Vector
{
public:
Vector() { cout << "call Vector template init" << endl; }
};
// 完全特例化版本,针对 char* 类型
template<>
class Vector
{
public:
Vector() { cout << "call Vector init" << endl; }
};
// 部分特例化版本,针对所有指针类型
template
class Vector
{
public:
Vector() { cout << "call Vector init" << endl; }
};
// 部分特例化版本,针对两个参数的函数指针类型
template
class Vector
{
public:
Vector() { cout << "call Vector init" << endl; }
};
int sum(int a, int b)
{
return a + b;
}
int main()
{
// 使用通用模板类
Vector intVector; // 调用通用模板构造函数
// 使用完全特例化版本
Vector charPtrVector; // 调用 char* 特例化构造函数
// 使用部分特例化版本
Vector intPtrVector; // 调用指针类型的部分特例化构造函数
//函数指针, 也是指针
Vector funcPtrVector; // 没有函数指针特例化, 就用匹配的部分特例化
//这是函数类型, 不是函数指针
Vector funcVector;
typedef int(*PFUNCPTR)(int, int);
PFUNCPTR func1 = sum;
cout << func1(10, 20) << endl;
return 0;
}
注意区分函数类型和函数指针类型
实例–细分:
#include
#include // 用于 typeid 和 type_info
using namespace std;
// 模板函数,用于打印类型信息
template
void func(T a)
{
cout << "Type of T: " << typeid(T).name() << endl;
}
// 模板函数,用于打印类型信息
template
void func2(T* a)
{
cout << "Type of T: " << typeid(T).name() << endl;
}
template
void func3(R(*a)(A1, A2))
{
cout << "Type of T: " << typeid(R).name() << endl;
cout << "Type of A1: " << typeid(A1).name() << endl;
cout << "Type of A2: " << typeid(A2).name() << endl;
}
class Test
{
public:
int sum(int a, int b) { return a + b; }
};
template
void func4(R(T::*a)(A1, A2)) // a这种, 存不存在都行, 因为没使用
{
cout << "Type of R: " << typeid(R).name() << endl;
cout << "Type of T: " << typeid(T).name() << endl;
cout << "Type of A1: " << typeid(A1).name() << endl;
cout << "Type of A2: " << typeid(A2).name() << endl;
}
// 普通函数,计算两个整数的和
int sum(int a, int b) {
return a + b;
}
int main()
{
// 调用 func 并传入整数
func(10); // T 推导为 int
// 调用 func 并传入字符串字面量
func("aaa"); // T 推导为 const char*
// 调用 func 并传入函数指针
func(sum); // T 推导为 int (*)(int, int)
func2(sum); // int __cdecl(int,int)
//细分类型
func3(sum);
func(&Test::sum); // int (__thiscall Test::*)(int,int)
func4(&Test::sum);
return 0;
}
一般的 自己写的 function 会因为形参个数不同, 导致需要特例化 太多, 如下:
#include
#include
using namespace std;
// 模板类,封装函数指针
template
class myfunction {
public:
using PFUNC = R(*)(A1); // 定义函数指针类型
// 构造函数,接受一个函数指针
myfunction(PFUNC pfunc) : _pfunc(pfunc) {}
// 重载 operator(),使对象可以像函数一样调用
R operator()(A1 arg) {
return _pfunc(arg);
}
private:
PFUNC _pfunc; // 存储函数指针
};
// 示例函数,用于测试
void hello(string str) {
cout << "Hello, " << str << endl;
}
int main() {
// 创建 myfunction 对象,封装 hello 函数
myfunction func1(hello);
// 调用 func1,输出 "Hello, hello world!"
func1("hello world!");
return 0;
}
但是,c++11提供了 可变参的模板
typename… A
template
class myfunction {
public:
using PFUNC = R(*)(A...); // 定义函数指针类型
// 构造函数,接受一个函数指针
myfunction(PFUNC pfunc) : _pfunc(pfunc) {}
// 重载 operator(),使对象可以像函数一样调用
R operator()(A... arg) {
return _pfunc(arg);
}
private:
PFUNC _pfunc; // 存储函数指针
};
返回的结果 还是 一个函数对象
bind 是 函数模板 – 也就能 自动推演类型了
不要忘记 bind(…)()最后这个括号
#include
#include
#include
using namespace std;
// 示例函数,用于测试
void hello(string str) {
cout << "Hello, " << str << endl;
}
int sum(int a, int b) {
return a + b;
}
class Test
{
// 成员方法和普通函数不同, 调用必须 依赖对象,通过函数指针调用 void(Test::*pfunc)(string)
public:
int sum(int a, int b) {
return a + b;
}
};
int main() {
bind(hello, "hello bind!")();
cout<
//参数占位符
bind(hello, placeholders::_1)("hello placeholders");
//placeholders 是一个命名空间
using namespace placeholders;
bind(hello, _1)("hello placeholders");
绑定器 出了 语句无法继续使用
function func1 = bind(hello, placeholders::_1);
func1("hello shanxi");
func1("hello shanxi-2");
bind原本是 boost库里的, c++11 放到了 标准库
只学过c的线程池, 可能看不明白, 后续会写 c++的多线程 笔记
#include
#include
#include
#include // 包含 std::bind
#include // 包含 std::unique_ptr
using namespace std;
class Thread {
public:
Thread(function func) : _func(func) {} // function用于接收 bind
thread start() {
return thread(_func);
}
private:
function _func;
};
class ThreadPool {
public:
// 开启线程池
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());
}
for (thread& t : _handler) {
if (t.joinable()) {
t.join();
}
}
}
// 清理线程池
~ThreadPool() {
for (int i = 0; i < _pool.size(); ++i) {
delete _pool[i];
}
}
private:
vector _pool; // 使用智能指针管理线程对象
vector _handler; // 存储线程对象
// 线程函数
void runInThread(int id) {
cout << "Thread " << id << " is running!" << endl;
}
};
int main() {
ThreadPool pool;
pool.startPool(10); // 开启一个包含 10 个线程的线程池
return 0;
}
可以使用智能指针, 自动释放
make_unique 是c++14加入的工具函数
在容器中存储 unique_ptr
时,推荐使用 make_unique
容器中 不能使用 new 传给 智能指针
#include
#include
#include
#include // 包含 std::bind
#include // 包含 std::unique_ptr
using namespace std;
class Thread {
public:
Thread(function func) : _func(func) {} // function用于接收 bind
thread start() {
return thread(_func);
}
private:
function _func;
};
class ThreadPool {
public:
// 开启线程池
void startPool(int size) {
for (int i = 0; i < size; ++i) {
_pool.push_back(make_unique(bind(&ThreadPool::runInThread, this, i))); //不使用占位符, 将不用再次传参
}
for (int i = 0; i < size; ++i) {
_handler.push_back(_pool[i]->start());
}
for (thread& t : _handler) {
if (t.joinable()) {
t.join();
}
}
}
// 清理线程池
~ThreadPool() {
}
private:
vector> _pool; // 使用智能指针管理线程对象
vector _handler; // 存储线程对象
// 线程函数
void runInThread(int id) {
cout << "Thread " << id << " is running!" << endl;
}
};
int main() {
ThreadPool pool;
pool.startPool(10); // 开启一个包含 10 个线程的线程池
return 0;
}
函数对象相较于 lambda
的缺点
lambda
只需一行代码。lambda
可自动捕获。lambda
可随用随写,函数对象需额外定义类。lambda
直接传入算法,函数对象需单独定义。operator()
需显式指定返回类型,lambda
可自动推导。lambda表达式语法:
[捕获外部变量](形参列表)->返回值 {操作代码}
如果返回值不需要, 可以省略->返回值
[] : 表示 不捕获外部变量
[=] : 以传值的方式 捕获的所有变量
[&] : 以引用的方式 捕获的所有变量
[this] : 捕获外部 this指针
[=, &a] : 以传值的方式 捕获的所有变量, 但是a是引用方式捕获
[a, b] : 以传值的方式 捕获 外部 a,b
[a,&b] : 以传值的方式 捕获 a, 但是b是引用方式捕获
mutable 关键字
在 C++ 中,mutable
是一个关键字,用于修饰类的成员变量。它的主要作用是允许在 const
成员函数中修改被 mutable
修饰的成员变量。mutable
通常用于那些逻辑上不改变对象状态,但技术上需要修改某些成员变量的场景。
class Counter {
public:
Counter() : _count(0) {}
void increment() const {
++_count; // 允许在 const 成员函数中修改 _count
}
int getCount() const {
return _count;
}
private:
mutable int _count; // 使用 mutable 修饰
};
int main() {
const Counter counter;
counter.increment(); // 调用 const 成员函数
std::cout << "Count: " << counter.getCount() << std::endl; // 输出: Count: 1
return 0;
}
lambda代码示例: — 使用auto
可以回顾一下 STL中使用lambda的点
注意 成员变量是左值引用时, 不在定义出初始化, 而是在构造函数里
#include
using namespace std;
template // 注意这个 void lambda的原理
class TestLambda01 {
public:
void operator()() {
cout << "hello world!" << endl;
}
};
template // 注意这个 void lambda的原理
class TestLambda02 {
public:
TestLambda02(int a, int b):ma(a),mb(b){}
void operator()() const{
int tmp = mb;
mb = ma;
ma = tmp;
}
private:
int ma;
int mb;
};
template // 注意这个 void lambda的原理
class TestLambda03 {
public:
TestLambda03(int &a, int &b) :ma(a), mb(b) {}
void operator()() const {
int tmp = mb;
mb = ma;
ma = tmp;
}
private:
int &ma; // C++ 规定引用成员变量必须在构造函数的初始化列表中初始化。 所以这里不需要初始化
int &mb;
};
int main() {
auto func1 = []()->void { cout << "hello world!" << endl; };
func1();
auto func2 = []() { cout << "hello world!" << endl; };
func2();
int a = 10;
int b = 20;
//auto func3 = [=]() { // 类似于 TestLambda02
// int tmp = b;
// b = a; // lambda 默认是 是使用常方法, 因此不能修改值
// a = tmp;
// cout << "hello world!" << endl; };
auto func3 = [=]()mutable { // mutable 表示 可以在常方法中修改成员变量
int tmp = b;
b = a;
a = tmp;
cout << "hello world!" << endl; };
func3();
cout << "a " << a << endl; // 因为是值传递, 外边的没改变
TestLambda01<> t1; //这种太冗余了
t1();
/* TestLambda02 t2(20, 30);
t2();*/
TestLambda03 t3(a, b);
t3();
return 0;
}
需要多多练习 代码使用方式
#include
#include
#include