智能指针是为了解决内存泄漏问题,与普通指针相比,他能够自动释放malloc或者new的内存的空间。智能指针本质上的实现是函数连带自动释放,先来看段代码:
class smartp { public: smartp(T size) { p=new T(size); } ~smartp() { if(p !=NULL) delete[] p; } private: T *p; }; int main(void) { { smartpa(5); //大括号结束时释放a对象 } while(1); }
其实就是利用了函数执行结束时会自动释放栈上局部变量内存,而我们将对象定义在了栈上(在函数内部定义的局部对象),所以函数结束时不例外的会把对象释放,但是不会管对象在堆上申请空间,在销毁对象时会调用对象的析构函数,利用这个特点,我们把delete堆内存放在析构函数中,则会被自动调用,从而实现自动delete堆内存的目的。
智能指针auto_prtauto_prt已过时,在c++17版弃用,因此在新开发程序中不要使用,但是对我们学习智能指针的使用还是有帮助。auto_ptr是一个类模板,在使用的时候需要指定类型。
头文件:
auto_ptr观察器p1(new people("hu")); //创建智能指针并指向新建的对象; auto_ptr p2=p1; //P1转移到P2,再访问P1会崩溃。
get() people *p3 = p2.get(); //获得智能指针p2管理的对象的指针。 operator-> cout<修改器name; //通过→访问管理的对象内的元素 operator* cout<<(*p3).name; //通过*访问管理的对象内的元素,( )优先级。
reset() p2.reset(new people("dai")); //替换,创建新对象,释放原对象,绑定新对象。 release() people *p4 = p2.release(); //释放管理,释放后,不能再访问p2管理的对象。弊端
弊端不是错误,是规则设计不合理。虽然auto_prt能解决自动释放内存的问题,但是又引入了新问题,导致使用的时候反而要更小心,所以在17版就被废止。如下示例:
auto_ptrp2=p1;
所有权转移,P1所有权转移P2后,如果再 *** 作P1,就会出错。
func(p1);
函数调用传值转移所有权,函数传参时,值传递,导致智能指针所有权被转移。
p2.release();
没有接收返回值,对象指针就会永久丢失,泄漏内存。
unique_ptr由于auot_ptr存在弊端被遗弃,所以c++又发明的新的智能指针来替代, unique_ptr就是其中之一,与auto_ptr不同的是,它不允许使用赋值语句进行权限转移(包括函数传参、返回值),在unique_ptr对象被销毁时,会先去调用与其绑定的删除器(一个对象),该删除器由用户给入,并要在内部实现构造函数、拷贝构造函数、移动构造函数和最重要的operator()(people *p)运算符重载,在销毁前会将管理的对象的指针传入到删除器的operator(),在这里要去删除申请的空间,同时调用管理的对象的析构。典型的删除器如下:
struct Dlete { Dlete(){cout<<"Dlete()"<头文件:
构造函数unique_ptr修改器a; unique_ptr a(nullptr); unique_ptr a(new people,del); //默认构造people对象 unique_ptr a(new people,del); //引用del unique_ptr a(new people,Dlete()); //创建再移动 unique_ptr a(new people,move(del)); //转移 unique_ptr a(new people[5]); //调用5次构造和5次析构 release() people *ptr = aa.release(); //解绑aa管理的对象,并返回对象指针 reset() bb.reset(new people(6)); //替换bb管理的对象 swap () dd.swap(cc); //交换智能指针dd与cc管理的对象观察器get() people *p =dd.get(); //获取dd管理的对象 get_deleter() Dlete &pd = ee.get_deleter(); //获取删除器 operator bool if(ee) {cout<<"y"<unique_prt与auto_prt对比
- auto_prt允许两个对象用赋值语句赋值(a=b), 而unique_prt不允许,包括函数传参和return返回值,因为涉及管理权限转移,容易出错,unique_ptr要直接赋值必须使用a= move(b)。
- 都支持匿名对象赋值auto_ptr
aa = auto_ptr (new int),也包括函数传参和return返回值。 unique释义为独一无二的,强调的就是他只能管理一个对象,当他管理一个新对象时,原来的对象就会被释放。同样也不能多个智能指针同时管理一个对象。会导致在释放的时候出错。
当unique_ptr本身被释放时,他会调用“删除器”,在删除器中去调用被管理的对象的析构,然后在析构自己。
在使用智能指针的时候,尽量不要与普通指针混合使用,因为c++语法并没有规避两个智能指针去管理两个普通指针。如下列:
int *p(new int); unique_ptrshared_ptrkk(p); unique_ptr yy(p); shared_ptr共享指针,意思是同一个对象可以有多个智能指针管理,auto_ptr和unique_ptr是独占式智能指针,语法上只能一个对象一个智能指针,即任何时候对象与智能指针一对一对应。而shared_ptr是基于引用计数式的设计,因此可以多个智能指针绑定1个对象,使得更加接近普通指针,shared的基本使用与前面两个智能指针类似。同样可以使用构造函数初始化,也可以使用make_shared来创建。
shared_ptr引用计数aa(new people); auto bb = make_shared (10); shared_ptr是引用计数来记录同一个对象被几个智能指针所管理,因为内部有计数机制,只有最后一个智能指针被释放时才会去析构管理的对象,所以最后在析构的时候才能避免重复去析构而出错,为了避免重复释放出错的问题,auto_ptr会对管理权限进行转移,来保证析构不出错,而unique_ptr是干脆就不允许左值赋值语句。
shared_ptrweak_ptraa(new people); shared_ptr bb = aa; use_count() int cnt = bb.use_count(); //获取bb所管理的对象有几个智能指针。 unique() if(aa.unique()); //判断aa是否独享对象 reset() aa.reset(new people) //对aa指向的对象更新,bb指针计数值减1; weak_ptr是一个非独立的智能指针,用来给shared_ptr打辅助的,shared_ptr算是一个近乎完美的智能指针,但是他还是有一个由计数而引起缺陷,而这个缺陷则需要使用weak_ptr来配合使用来避免。
weak_ptr本身也是一个模板类,但是不能直接用来创建智能指针对象,只能用来接收shared_ptr智能指针对象,且不会引shared_ptr智能指针计数。weak_ptr能使用的成员少得可怜,甚至连operator*和operator->都没有,不同的是他比其他只能指针多了lock和expired。
weak_ptr中只有函数lock和expired两个函数比较重要,因为它本身不会增加引用计数,所以它指向的对象可能在它用的时候已经被释放了,所以在用之前需要使用expired函数来检测是否过期,然后使用lock函数来获取其对应的shared_ptr对象,然后进行后续 *** 作。
构造函数shared_ptr观察器sptr(new people(12)); weak_ptr wptr=sptr; expired() if(!wptr.expired());//管理的对象被删除返回true,否则false lock() cout<< wptr.lock()->value<迭代器适配器 STL中迭代器分类有输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器等,这些迭代器是STL中标准迭代器,很多时候我们遍历容器的场景比已提供的不同。所以就有了迭代器适配器,迭代器适配器也是一个模板类,是基于前面提到的基础迭代器而实现的,仍属于迭代器,可以理解为是“升级版”迭代器。
反向迭代器反向迭代器(reverse_iterator),又称“逆向迭代器”,其内部重新定义了递增运算符(++)和递减运算符(--),专门用来实现对容器的逆序遍历。
listaa{1,2,3,4,5}; //定义一个list reverse_iterator ::iterator> begin = aa.rbegin(); //读逆序迭代器作为begin reverse_iterator
::iterator> end = aa.rend();//读逆序迭代器作为begin while(begin != end) { cout<<*begin<<" "; ++begin; }
安插型迭代器
安插型迭代器(inserter或者insert_iterator),用于在容器的任何位置添加新的元素,需要注意的是,此类迭代器不能被运用到元素个数固定的容器(比如 array)上。
流迭代器
流迭代器(istream_iterator / ostream_iterator)流缓冲区迭代器(istreambuf_iterator / ostreambuf_iterator),输入流迭代器用于从文件或者键盘读取数据;相反,输出流迭代器用于将数据输出到文件或者屏幕上。
输入流缓冲区迭代器用于从输入缓冲区中逐个读取数据;输出流缓冲区迭代器用于将数据逐个写入输出流缓冲区。移动迭代器
移动迭代器(move_iterator),此类型迭代器是 C++ 11 标准中新添加的,可以将某个范围的类对象移动到目标范围,而不需要通过拷贝去移动。
function函数包装器在c++中有多种可调用的对象,如函数、函数指针、lambda表达式、bind创建的对象、函数对象。对这些东西的调用各有差异,而函数包装器就是来实现将这些东西放在一起,并统一接口后对外预留同样的调用方法。即不同类型的可调用对象共享同一种调用方法。他的底层其实是使用了map来实现,map的格式是map
,我们把key用来做运算符标识,value写成函数,即实现了函数的包装,示例如下 int add(int a,int b){return a+b;}; //定义一个普通函数 auto sub = [](int a,int b){return a-b;}; // 定义一个lambda表达式 struct div //函数对象 { int operator()(int a,int b) { return a / b; } }; map> bing { {"+",add}, {"-",sub}, {"/",div} {"*",[](int a,int b){return a*b;}}, }; cout<< "2+3= "<< bing["+"](3,2)< 欢迎分享,转载请注明来源:内存溢出
评论列表(0条)