boost常用库使用介绍第二讲_第1页
boost常用库使用介绍第二讲_第2页
boost常用库使用介绍第二讲_第3页
boost常用库使用介绍第二讲_第4页
boost常用库使用介绍第二讲_第5页
已阅读5页,还剩36页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

boost常用库使用介绍第二讲第一页,共41页。第一节:C++程序员对内存管理的苦恼1、内存泄漏(memoryleak)2、野指针(wildpointer)3、访问越界(accessviolation)boost的智能指针库就是内存管理的强劲解决方案通过boost智能指针库,我们能够高效的进行内存管理,解决上述问题,同时彻底让你忘记栈(stack),堆(heap)等内存相关的术语,并且还会发现,boost为c++提供的内存管理解决方案可能比java和c#等其他语言更好,更高效!2第二页,共41页。第二节:智能指针与RAII机制为了管理内存等资源,智能指针采用RAII机制(ResourceAcquisitionIsInitialization,资源获取既初始化)1、所有初始化操作移到对象的构造函数中2、所有的释放操作都放在对象的析构函数里。3、适当的异常处理代码来应付对象构造期间丢出的异常这种用构造函数申请资源用析构函数释放资源的做法或者说技术通常被称为“资源获取即初始化”。这样做的优点是而易见的:对象创建后,用户能开始正确使用对象,不用担心对象的有效性或者是否还要作进一步的初始化操作。

3第三页,共41页。第三节:Boost智能指针的分类:boost提供下列几种智能指针:4第四页,共41页。第四节:shared_ptr简介1、boost::shared_ptr是一个最像指针的“智能指针”:是boost.smart_ptr中最有价值,最重要,也是最有用的类,boost库的其他许多类库都使用了shared_ptr,所以毫无悬念的被收入了c++11标准中去。2、boost::shared_ptr实现了计数引用:

它包装了new操作符在堆上分配的动态对象,但它实现了引用计数,可以自由的拷贝和赋值,在任意地方共享它。当引用计数为0时,它会自动删除被包装的动态分配的对象。5第五页,共41页。第四节:shared_ptr简介3、boost::shared_ptr不需要手动的调用类似release方法:

它不像侵入式实现的智能指针一样需要手动的调用类似release方法,全部用由shared_ptr内部的计数器自动增减,这一点是非常有用的。(COM的IUnknow接口以及boost::intrusive_ptr<T>都是基于侵入式设计的智能指针,需要手动调用类似release方法)4、boost::shared_ptr支持所有权转移:

并且可以安全的存储在stl标准容器中,是在stl容器存储指针的标准解法。例如std::vector<int*>IntVec,使用shared_ptr方式为std::vector<boost::shared_ptr<int>>IntptrVec.6第六页,共41页。第五节:shared_ptr类摘要1、构造函数,拷贝构造函数,赋值操作符以及析构函数Template<classT>Classshared_ptr{/*******************构造函数*******************************************/

shared_ptr();//创建一个持有空指针的shared_ptr,use_count()=0&&get()==NULL//获得一个指向类型T的指针p的管理权,use_count==1&&get()==p,Y类型必须能够转换为T类型

template<classY>explicitshared_ptr(Y*p);

//作用同上,增加了一个构造参数D,是一个仿函数对象,代表删除器,该构造函数非常有用,后面会详解

template<classY,classD>shared_ptr(Y*p,Dd);/******************拷贝构造函数及赋值操作符重载***********************/

//调用拷贝构造函数或赋值操作后,引用计数加1&&get()==r.get()&&use_count()==r.use_count()shared_ptr(shared_ptrconst&r);template<classY>shared_ptr(shared_ptr<Y>const&r);

shared_ptr&operator=(shared_ptrconst&r);template<classY>shared_ptr&operator=(shared_ptr<Y>const&r);/******************析构函数*******************************************/

//引用计数会减1,如果计数器为0,get()!=NULL,有删除器的话会调用删除器,否则调用delete操作符~shared_ptr();}

7第七页,共41页。第五节:shared_ptr类摘要2、public成员方法和操作符Template<classT>classshared_ptr{T*get()const;//返回原始指针T&operator*()const;//返回原始指针的引用T*operator->()cosnt;//返回原始指针longuse_count()const;//返回引用计数boolunique()const;//返回use_count()==1,是否唯一operatorbool()const;//bool值转型,可以用于条件判断,例如if(ptr){…}voidsb)//交换原始指针

voidreset();template<classY>voidreset(Y*p);template<classY,classD>voidreset(Y*p);}

8第八页,共41页。第六节:shared_ptr的一个例子1、#include<boost/shared_ptr.hpp>2、创建一个shared_ptr要用到的简单类FooclassFoo{private:std::stringm_strName;Public:Foo(conststd::string&strName):m_strName(strName){}~Foo(){std::cout<<“DestructingFoowithname=“<<m_strName<<std::endl}};9第九页,共41页。第六节:shared_ptr的一个例子3、实现voidTest_Boost_Shared_Ptr()typedefboost::share_ptr<Foo>FooPtr;voidTest_Boost_Shared_Ptr(){//ptr1获得Foo__1指针的所有权FooPtrptr1(newFoo(“Foo1”));//引用计数为1 assert(ptr1.use_count()==1);//ptr2指向ptr1 FooPtrptr2=ptr1;//调用shared_ptr的赋值操作符,引用计数加1 assert(ptr1.use_count()==ptr2.use_count());//两者引用计数相同 assert(ptr1==ptr2);//shared_ptr重载==操作符,等同于ptr1.get()==ptr2.get()。 assert(ptr1.use_count()==2);//现在ptr1和ptr2都指向了Foo__1,因此计数器都为2

10第十页,共41页。第六节:shared_ptr的一个例子//ptr3获得Foo__1指针的所有权FooPtrptr3=ptr2;//引用计数加1 assert(ptr1.use_count()==ptr2.use_count()&&ptr1.use_count()==ptr3.use_count()); assert(ptr1.use_count()==3&&ptr2.use_count()==3&&ptr3.use_count()==3); assert(ptr1==ptr2&&ptr1==ptr3);//现在我们重置ptr3,测试reset()函数 ptr3.reset(); assert(ptr3.use_count()==0&&ptr3.get()==NULL); std::cout<<"ptr3引用计数为0,get()指针指向NULL,但是不会调用析构函数,因为ptr1和ptr2都指向了原生指针"<<std::endl;assert(ptr1.use_count()==ptr2.use_count()&&ptr1.use_count()==2);assert(ptr1==ptr2&&ptr1!=ptr3);//前面ptr1和ptr2都指向了同一个对象,他们的引用计数都为2,现在创建一个ptr4,让其指向ptr1。FooPtrptr4=ptr1;//引用计数加1assert(ptr1==ptr2&&ptr4==ptr1);assert(ptr4.use_count==3&&ptr1.use_count==3&&ptr2.use_count()==3);11第十一页,共41页。第六节:shared_ptr的一个例子//现在我们转移ptr2的所有权到另外一个新分配的名为Foo__2的Foo指针上去//在ptr2转移所有权后,ptr2的计数器应该是1&&ptr1和ptr4的计数器为2&&ptr1!=ptr2ptr2.reset(newFoo(“Foo2”);assert(ptr2.use_count()==1&&ptr1.use_count()==2&&ptr4.use_count()==2);assert(ptr1!=ptr2&&ptr1==ptr4);//前面ptr3因为被reset()为0,并且get()=NULL了,我们再用ptr5来指向ptr3,此时ptr5的引用计数也应该//为0而不是加1,并且ptr5.get()返回值应该也为NULLFooPtrptr5=ptr3;//引用计数为0assert(ptr5.use_count()==0&&ptr5.get()==NULL);//运行到此时,程序即将结束,在退出作用域前会调用析构函数//首先会调用ptr2的析构函数,打印出DestructingaFoowithname=Foo__2//然后会调用ptr1和ptr4析构函数进行引用计数递减//最终会打印出DestructingaFoowithname=Foo__1//这样就没有任何内存泄露了哈哈哈哈}//结束test_boost_shared_ptr函数

12第十二页,共41页。第七节:shared_ptr使用注意点1、shared_ptr多次引用同一内存数据导致程序崩溃

例如下面代码会导致堆内存破坏而引起程序奔溃voidtest_boost_shared_ptr_crash(){Foo*pFoo=newFoo("Foo1");boost::shared_ptr<Foo>ptr1(pFoo);boost::shared_ptr<Foo>ptr2(pFoo);}上述代码两次释放同一内存而破坏堆,导致程序奔溃。13第十三页,共41页。第七节:shared_ptr使用注意点1、shared_ptr多次引用同一内存数据导致程序崩溃

解决方案: 1)直接使用boost::shared_ptr<int>ptr1(newint(100));使用匿名内存分配限制其他shared_ptr多次指向同一内存数据。多次指向时使用shared_ptr的赋值操作符或拷贝构造函数,例如shared_ptr<int>ptr2=ptr1。上面的匿名方式就是资源初始化既分配技术,直接在构造函数中分配内存 2)使用boost的make_shared模板函数,例如shared_ptr<int>ptr1=boost::make_shared<int>(100);shared_Ptr<int>ptr2=ptr1;

14第十四页,共41页。第七节:shared_ptr使用注意点2、shared_ptr循环引用导致内存泄露,代码如下:

classParent;classChild;classParent{public:~Parent(){std::cout<<“父类析构函数被调用\n”;}public:boost::shared_ptr<Child>child_ptr;};15第十五页,共41页。第七节:shared_ptr使用注意点2、shared_ptr循环引用导致内存泄露,代码如下(续):classChild{public:~Child(){std::cout<<“子类析构函数被调用\n”;}public:boost::shared_ptr<Parent>Parent_ptr;};

16第十六页,共41页。第七节:shared_ptr使用注意点2、shared_ptr循环引用导致内存泄露,代码如下(续):

intmain(){

boost::shared_ptr<Parent>father(newParent());//father引用计数为1

boost::shared_ptr<Child>son(newChild());//son引用计数为1

//

父子互相引用。

father->children=son;//son的引用计数为2

son->parent=father;//father的引用计数为2

return0;//退出作用域前,father和son的引用计数都减1,此时father和son的引用计数都是为1//因此各自指向的内存无法释放,导致内存泄露}

17第十七页,共41页。第七节:shared_ptr使用注意点

2、shared_ptr循环引用导致内存泄露上述代码运行后,不会调用father和son的析构函数,因为循环引用,导致father和son的引用计数都为2,退出作用域时候,引用计数减1,因此father和son在退出作用域时候引用计数都为1,无法调用析构函数,于是造成father和son所指向的内存得不到释放,导致内存泄露。

18第十八页,共41页。第七节:shared_ptr使用注意点2、shared_ptr循环引用导致内存泄露的解决方案代码:

针对循环引用,使用boost::weak_ptr可以很方便的解决该问题。代码如下:

classParent;classChild;classParent{public:~Parent(){std::cout<<“父类析构函数被调用\n”;}public://boost::shared_ptr<Child>child_ptr;改为如下代码:boost::weak_ptr<Child>child_ptr;};19第十九页,共41页。第七节:shared_ptr使用注意点2、shared_ptr循环引用导致内存泄露的解决方案代码:针对循环引用,使用boost::weak_ptr可以很方便的解决该问题。代码如下(续):classChild{public:~Child(){std::cout<<“子类析构函数被调用\n”;}public://boost::shared_ptr<Parent>Parent_ptr;改为如下代码:boost::weak_ptr<Parent>Parent_ptr;};

20第二十页,共41页。第八节:Boost::weak_ptr的介绍1、weak_ptr是用来解决循环引用和自引用对象

从前面的例子我们可以看出,引用计数是一种很便利的内存管理机制,但是有一个很大的缺点,那就是不能管理循环引用或自引用对象(例如链表或树节点),为了解决这个限制,因此weak_ptr被引入到boost的智能指针库中。2、weak_ptr并不能单独存在

它是与shared_ptr同时使用的,它更像是shared_ptr的助手而不是智能指针,因为它不具备普通指针的行为,没有重载operator*和->操作符,这是特意的。这样它就不能共享指针,不能操作资源,这正是它“弱”的原因。它最大的作用是协助shared_ptr工作,像旁观者那样观察资源的使用情况。21第二十一页,共41页。第八节:Boost::weak_ptr的总结3、weak_ptr获得资源的观察权

weak_ptr可以从一个shared_ptr或另外一个weak_ptr构造,从而获得资源的观察权,但weak_ptr并没有共享资源,它的构造并不会引起引用计数的增加,同时它的析构也不会引起引用计数的减少,它仅仅是观察者。4、weak_ptr可以被用于标准容器库中的元素

weak_ptr实现了拷贝构造函数和重载了赋值操作符,因此weak_ptr可以被用于标准容器库中的元素,例如:在一个树节点中声明子树节点std::vector<boost::weak_ptr<Node>>children;22第二十二页,共41页。第九节:shared_ptr一些使用技巧:1、将shared_ptr用于标准容器库有两种方式:

1)将标准容器库作为shared_ptr管理的对象 例如boost::shared_ptr<std::vector<T>>,使容器可以被安全的共享,用法与普通shared_ptr没区别,我们不再讨论。 2)将shared_ptr作为容器的元素 例如std::vector<boost::shared_ptr<T>>,因为shared_ptr支持拷贝构造和赋值操作以及比较操作的语意,符合标准容器对元素的要求,所以可以在容器中安全的容纳元素的指针而不是拷贝。标准容器可以容纳原始指针,例如std::vector<T*>,但是这就丧失了容器的许多好处,因为标准容器库无法自动管理类型为指针的元素,必须编写额外的代码来保证指针最终被正确的删除,而保存shared_ptr作为标准容器库的元素,既保证与存储原始指针几乎一样的功能,而且不用担心资源泄露。

23第二十三页,共41页。第九节:shared_ptr的使用技巧2、以函数方式封装现有的c函数例如crt的FILE操作函数,fopen/fclose/fread等函数,我们可以使用一些技巧,利用shared_ptr进行封装,从而使我们不需要调用fclose,让其自动进行内存管理,代码如下:1)实现函数(实际不需要实现该函数,这里实现是为了在资源释放时候打印出相关信息):typedefboost::shared_ptr<FILE>;void(FILE*f){fclose(f);std::cout<<“调用fclose函数释放FILE资源\n”;}24第二十四页,共41页。第九节:shared_ptr使用技巧2、以函数方式封装现有的c函数2)实现函数(constchar*path,constchar*mode){//fptr(fopen(path,mode),fclose);//直接使用fclose作为shared_ptr的删除器fptr(fopen(path,mode),);//使用我们的包装删除器,在释放资源时候打印出相关信息returnfptr;}25第二十五页,共41页。第九节:shared_ptr使用技巧3、用c++桥接设计模式来封装现有的c函数,隐藏实现细节

在c++的.h文件中声明如下类:class{private:classimpl;//很重要一点,前向申明实现类,具体实现在.cpp文件中,隐藏实现细节 boost::shared_ptr<impl>pimpl;//shared_ptr作为私有成员变量public:(charconst*name,charconst*mode);voidRead(void*data,size_tsize);};26第二十六页,共41页。第九节:shared_ptr使用技巧:3、用c++桥接设计模式来封装现有的c函数在c++的.cpp文件中实现如下类:class{private:impl(implconst&){}impl&operator=(implconst&){}FILE*f;public:impl(charconst*name,charconst*mode){f=fopen(name,mode);}~impl(){intresult=fclose(f);printf("invoke析构函数result=%d\n",result);}voidread(void*data,size_tsize){fread(data,1,size,f)}};27第二十七页,共41页。第九节:shared_ptr使用技巧3、用c++桥接设计模式来封装现有的c函数(constchar*name,constchar*mode):pimpl(new(name,mode)){}void(void*data,size_tsize){pimpl->read(data,size);}voidTest_CPP_(){ ptr(“memory.log”,“r”);//引用计数为1 ptr2=ptr;//引用计数为2 chardata[100]; ptr.Read(data,100); printf("%s\n",data);}//析构ptr2引用计数为1,再析构ptr,引用计数为0,释放内存,无泄漏28第二十八页,共41页。第九节:shared_ptr使用技巧4、使用面向接口编程方式隐藏实现

在c++的.h文件中声明如下接口:classIPrinter{public:virtualvoidPrint()=0;protected://受保护的虚拟析构函数,导致本类必须被继承,也不能调用delete操作符。virtual~IPrinter(){printf("invokeIPrintervirtual析构函数\n");}};typedefboost::shared_ptr<IPrinter>PrinterPtr;PrinterPtrCreatePrinter();//工厂方法,创建IPrinter智能指针。29第二十九页,共41页。第九节:shared_ptr使用技巧4、使用面向接口编程方式隐藏实现在c++的.cpp文件中声明如下实现类:(很重要一点,实现类都是在cpp文件中的,必须要注意这一点)classPrinter:publicIPrinter{private: FILE*f;public://使用createPrinter时候,调用实现类的构造函数,返回的是IPrinter的shared_ptr Printer(constchar*path,constchar*mode){f=fopen(path,mode); }//实现类的析构是public,但是接口类的是protected,但是shared_ptr在析构时候会自动调用实现类的析构//且再次调用基类受保护的虚拟析构函数。通过这种机制我们可以完全封闭掉new和delete操作符,只能使用//公开的工厂方法函数返回接口智能指针,而且不需要也无法调用析构函数,完全由shared_ptr来管理内存。//这样我们在整个程序中都没有原始指针的概念,从而不会忘记调用delte操作而导致内存泄露。30第三十页,共41页。第九节:shared_ptr使用技巧4、使用面向接口编程方式隐藏实现 ~Printer(){fclose(f);printf("invokePrinter析构函数result=%d\n",result);} voidPrint(){chardata[100];fread(data,1,100,f);printf("%s\n",data);rewind(f);}};PrinterPtrCreatePrinter(){PrinterPtrptr(newPrinter("memory.log","r");returnptr};

31第三十一页,共41页。第九节:shared_ptr使用技巧5、使用shared_ptr持有一个具有侵入式的引用计数的对象如Com对象或intrusive_ptr等需要手动增减引用计数的对象。假设我们要将shared_ptr用于一个COM对象。1)我们在头文件中引入<boost/mem_fn.hpp>头文件前定义宏:#defineBOOST_MEM_FN_ENABLE_STDCALL

2)#include<boost/mem_fn.hpp>3)我们定义一个函数,例如shared_ptr<IWhatever>make_shared_from_COM(IWhatever*p)、{p->AddRef();//注意mem_fn仿函数的用法,它用于成员函数,&类名::成员函数名shared_ptr<IWhatever>pw(p,mem_fn(&IWhatever::Release));returnpw;}一旦使用shared_ptr享有com的接口指针所有权后,com的引用计数被shared_ptr所接管,因此所有引用计数增减都是由shared_ptr来进行.32第三十二页,共41页。第九节:shared_ptr一些使用技巧:6、使用shared_ptr持有一个win32handle例如win32使用HANDLE来代表内核对象,所有内核对象都使用CreateXXX创建一个句柄,而使用CloseHandle来释放一个句柄。typedefvoid*HANDLE;//win32HANDLE的内部定义,在windows中可以看到HANDLECreateMutex();BOOLCloseHandle(HANDLE);使用shared_ptr来管理上述类型的HANDLE,代码如下:typedefshared_ptr<void>handle;33第三十三页,共41页。第九节:shared_ptr一些使用技巧:6、使用shared_ptr持有一个win32handleBOOLMyCloseHandleWrap(HANDLEh){std::cout<<“调用win32CloseHandleAPI\n”;returnCloseHandle(h);}handlecreateMutexHandle(){shared_ptr<void>ptr(CreateMutex(NULL,FALSE,NULL),MyCloseHandleWrap/*CloseHandle*/);}34第三十四页,共41页。第九节:shared_ptr使用技巧7、使用shared_ptr持有一个静态分配的对象有时候我们需要使用shared_ptr来管理一个静态分配的对象或全局对象,例如staticXxobj;由于静态或全局对象的析构是在程序结束时候自动进行的,我们不能够在shared_ptr中自动调用delete操作符,因此我们可以实现一个NULL析构器来实现该目的

structnull_deleter//仿函数对象{//重载函数调用操作符()voidoperator()(void*const)const{}//没有任何代码};shared_ptr<X>CreateX(){shared_ptr<X>px(&xobj,null_deleter());returnpx;}

35第三十五页,共41页。第九节:shared_ptr使用技巧8、使用shared_ptr<void>持有任意对象的所有权 1)使用shared_ptr<void>来获得任何对象的所有权: 在该shared_ptr<void>离开作用域时候,会自动调用该shared_ptr所享有的实际对象的析构函数, 2)如果要使用实际对象的相关成员函数: 需要使用: boost::static_pointer_cast boost::dynamic_pointer_cast boost::const_pointer_cast boost::reinterpret_pointer_cast 这四个转型模板函数。36第三十六页,共41页。第九节:shared

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论