二十一、 std::bind
21.1 含义
bind 用来将可调用对象和参数一起进行绑定。可调用对象包括普通函数、全局函
数、静态函数、类静态函数甚至是类成员函数,参数包括普通参数和类成员。
语义
std::bind(funcName,argType,…);//绑定以存在的函数名,以及传入的实参
placeholders::_x 实参占位
placeholders::_1 表示第一个实参暂时不填实参数值,依次可推。
21.2 作用
21.2.1 绑定普通函数与参数及占位
double myDivide (double x, double y); auto fn_five = std::bind (myDivide, 10, 2); cout << fn_five() << endl;
21.2.2 绑定对象与成员及占位
class MyPair2 { public: int add(int x, int y) { return x + y; } }; auto bindObjfunc = bind(&MyPair2::add,mp2 , 2, 3);
21.2.3 函数重载情形
int add(int x, int y); double add(double x, double y); auto bindGlobalFunc = bind((int(*)(int,int))add,_1,_2); auto bindGlobalFunc2 = bind(static_cast<double(*)(double,double)>(add),_1,_2);
注意
1.预先绑定的参数值通过 值传递进去;通过placehodler传递进去的 传递引用进去
2.bind 返回的是可调用的函数
3.绑定的参数 调用之前 需要确认是可用的
21.3 多态之 bind +fucntion
21.3.1.可实现多态使用
std::function<void(void)> f; void foo(); void func(int a) f = std::bind(foo); f(); f= std::bind(func,1); f();
21.3.2.function 本是不可以包装类成员函数,bind实现类成员函数的绑定, 然后赋给 fucntion 对象,亦即实现了间接性的包装
std::function<void(void)> f; class Foo { public: void method() { cout<<"Foo::void method()"<<endl; } void method2(string s) { cout<<"Foo:void method2()"<<endl; } }; Foo foo; f = std::bind(&Foo::method,&foo); f(); f = std::bind(&Foo::method2,&foo,"china"); f();
二十二、 Unordered Contrainer 无序容器
22.1 unordered_map
采用hash_map 数据结构,无序
二十三、 Auto Memeory Manage 自动内存管理
23.1 RAII(Resource Acquisition Is Initialization)
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制
程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。 RAII 的一般做法是这样的:在对象构造时获取资源,接着控制对资源的访问使之
在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际 上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
①不需要显式地释放资源。 ②采用这种方式,对象所需的资源在其生命期内始终保持有效。
此时,所托管的资源,随对象的创建而获取,随对象的消失而消失,即所谓的 RAII 思想:资源获取即初始化。
原理上,是代理了被托管的对象指针,管理对象的生命周期,即实现自动释放。其 行为类似于所托管的对象指针,原因是,重载了 operator->和 operator*。 如下是智能指针的模板(template)化实现。
template <class T> class SmartPtr { public: explicit SmartPtr(T* pointee) : pointee_(pointee){} ~SmartPtr() { delete pointee_; } T& operator*() const { //... return *pointee_; } T* operator->() const { //... return pointee_; } private: T* pointee_; //... };
23.2 auto_ptr(deprecated)
代理new出来的对象,自动化释放对象,作用域处结束,释放对象。
存在问题
- 两个auto_ptr对象拥有同一个对象时,析构时都试图删除p,会出现崩溃问题
#include <iostream> #include <memory> using namespace std; int main() { int *p = new int(10); { auto_ptr<int> ap(p); cout<<*ap<<endl; } auto_ptr<int> ap2(p); cout<<*ap2<<endl; return 0; }
- 作参数传递的时,亦是会出现同样的情况,两个指针,对同一段资源产生了引用行为。
23.3 unique_ptr
uniqu_ptr 的使用方法,基本上等同于 auto_ptr, 不同之处,就在于实现了资源的 唯一, 既不可以拷贝也不可以赋值,正如其名字一样。
- 解决了作为 参数拷贝时,无法拷贝和赋值的特性使 只能使用同一个对象,解决auto_ptr造成的释放崩溃
- 判断是否有资源
unique_ptr<Copy> up; if(!up) { cout<<"无资源托管"<<endl; } unique_ptr<Copy> up2(new Copy(10)); if(up2) { cout<<"有资源托管"<<endl; }
- 允许托管左值 unique_ptr up2 =std::move(up); 相当于资源转移,被转移的资源不允许再使用
int main() { unique_ptr<Copy> up(new Copy(99)); cout<<up.get()<<endl; unique_ptr<Copy> up2 =std::move(up); cout<<up2.get()<<endl; //up.get();//被转移资源 再使用 会造成崩溃 }
23.3.1 常用函数
- get() 返回所托管资源的指针
- release() 取消代理(放弃托管) ,返回资源句柄,注意:对象释放仍需手动维护
- reset() 重置 1.参数为空 释放之前的资源对象 2.参数为对象指针,先释放之前的资源对象,重新托管新的对象
- std::move() 允许托管左值 相当于资源转移,被转移的资源不允许再使用
23.4 shared_ptr
shared_ptr之间共享资源,共享同一个计数器,每次引用,引用计数+1
int main() { shared_ptr<int> sp(new int(10)); cout<<sp.get()<<endl; if(sp) { cout<<"有资源托管中"<<endl; } cout<<sp.use_count()<<endl; { shared_ptr<int> sp2 = sp; cout<<sp2.use_count()<<endl; } cout<<sp.use_count()<<endl; return 0; } 输出结果: 有资源托管中 1 2 1
比较:
- uinque_ptr 解决了 auto_ptr 中引用同一个对象的问题,方式就是不可引用同一个 对象。
- shared_ptr 解决了 auto_ptr 中引用同一个对象,共同拥有一个资源, 但不会重 析构的问题,原理是,在内部保持一个引用计数,并且仅当引用计数为 0 才能被删除, 不可以用数组。
都无法避免作用域重复析构崩溃
23.5 用法
- 基本类型计数测试
#include <iostream> #include <memory> using namespace std; void func(shared_ptr<int> sp) { // sp.reset(); //此处仅将自己所累积的计数减 1 cout<<sp.use_count()<<endl; sp.reset(); //此时 reset 等价于 sp 对象消失,若己为零,则不再减 1. } int main() { shared_ptr<int> sp(new int(10)); cout<<sp.get()<<endl; if(sp){ cout<<"有资源托管中"<<endl; } cout<<sp.use_count()<<endl; shared_ptr<int> sp2 = sp; cout<<sp2.use_count()<<endl; cout<<sp.use_count()<<endl; func(sp); cout<<sp.use_count()<<endl; return 0; }
- 对象计数测试
reset 跟参数,会托管新对象,释放旧对象,如若不跟参数话,会将当前对象的引 用计数减 1
#include <iostream> #include <memory> using namespace std; class A { public: A() { cout<<"A()"<<this<<endl; } ~A() { cout<<"~A()"<<this<<endl; } void dis() { cout<<"A::void dis()"<<endl; } }; int main1() { { shared_ptr<A> sp(new A); sp.reset(new A()); cout<<"+++++++++++++++++++++"<<endl; } cout<<"======================"<<endl; } int main() { { shared_ptr<A> sp(new A); sp.reset();sp.reset();sp.reset(); cout<<"+++++++++++++++++++++"<<endl; } cout<<"======================"<<endl; }
- 对象传参测试
#include <iostream> #include <memory> using namespace std; class A {public: A() { cout<<"A()"<<this<<endl; } ~A() { cout<<"~A()"<<this<<endl; } void dis() { cout<<"A::void dis()"<<endl; } }; void func(shared_ptr<A> &sp) //shared_ptr<A> &sp { cout<<sp.use_count()<<endl; sp.reset(); sp.reset(); cout<<"======================"<<endl; } int main() { shared_ptr<A> sp(new A); cout<<sp.use_count()<<endl; func(sp); cout<<sp.use_count()<<endl; shared_ptr<A> sp2 = std::move(sp); //移动会将计数也一起移走 cout<<sp2.use_count()<<endl; cout<<sp.use_count()<<endl; //此时资源为 0 return 0; }
对同一个对象,多次 reset 的结果,是仅对自己增加的计数减 1。要保证,当前的 对象的使用安全性。也不会对其它象的使用造成影响。
void func(shared_ptr<Copy> spc) //&spc 若传递的是引用,则引用计数不会加 1。离开函数也不会减 1 shared_ptr<A> sp2 = std::move(sp); //移动会将计数也一起移走
23.5.1 常用函数
- reset() 只可自杀 不可他杀
- std::move() 计数不会增加
- operator bool 判空
- operator= 支持赋值,复制
23.6 weak_ptr
#include <memory> std::weak_ptr<int> wp1;
只能和 shared_ptr 类型指针搭配使用。甚至于,我们可以将 weak_ptr 类型指针视为 shared_ptr 指针的一种辅助工具。
借助 weak_ptr 类型指针, 我们可以获取 shared_ptr 指针的一些状态信息:
- 有多少指向相同的 shared_ptr 指针、
- shared_ptr 指针指向的堆内存是否已经被释放等等。
注意:
当 weak_ptr 类型指针的指向和某一 shared_ptr 指针相同时,weak_ptr 指针并不会使所指堆内存的引用计数加 1;
同样,当 weak_ptr 指针被释放时,之前所指堆内存的引用计数也不会因此而减 1。
也就是说,weak_ptr 类型指针并不会影响所指堆内存空间的引用计数。