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

下载本文档

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

文档简介

1、boost常用库的使用介绍第二讲:boost智能指针库主讲人:步磊峰 UIPower 3D界面引擎负责人第一节: C+程序员对内存管理的苦恼1、内存泄漏(memory leak)2、野指针(wild pointer)3、访问越界(access violation)boost的智能指针库就是内存管理的强劲解决方案通过boost智能指针库,我们能够高效的进行内存管理,解决上述问题,同时彻底让你忘记栈(stack),堆(heap)等内存相关的术语,并且还会发现,boost为c+提供的内存管理解决方案可能比java和c#等其他语言更好,更高效!2第二节:智能指针与RAII机制 为了管理内存等资源,智能

2、指针采用RAII机制(Resource Acquisition Is Initialization,资源获取既初始化) 1、 所有初始化操作移到对象的构造函数中 2、 所有的释放操作都放在对象的析构函数里。 3、 适当的异常处理代码来应付对象构造期间丢出的异常 这种用构造函数申请资源用析构函数释放资源的做法或者说技术通常被称为“资源获取即初始化”。这样做的优点是 而易见的:对象创建后,用户能开始正确使用对象,不用担心对象的有效性或者是否还要作进一步的初始化操作。 3第三节:Boost智能指针的分类:boost 提供下列几种智能指针:4第四节:shared_ptr简介1、boost:shared

3、_ptr 是一个最像指针的“智能指针”: 是boost.smart_ptr中最有价值,最重要,也是最有用的类,boost库的其他许多类库都使用了shared_ptr, 所以毫无悬念的被收入了c+11标准中去。2、boost:shared_ptr实现了计数引用: 它包装了new操作符在堆上分配的动态对象,但它实现了引用计数,可以自由的拷贝和赋值, 在任意地方共享它。当引用计数为0时,它会自动删除被包装的动态分配的对象。5第四节:shared_ptr简介3、boost:shared_ptr不需要手动的调用类似release方法: 它不像侵入式实现的智能指针一样需要手动的调用类似release方法,

4、全部用由shared_ptr内部的计数器自动增减,这一点是非常有用的。(COM的IUnknow接口以及boost:intrusive_ptr都是基于侵入式设计的智能指针,需要手动调用类似release方法)4、boost:shared_ptr支持所有权转移:并且可以安全的存储在stl标准容器中,是在stl容器存储指针的标准解法。例如std:vector IntVec,使用shared_ptr方式为std:vectorboost:shared_ptr IntptrVec.6第五节:shared_ptr类摘要1、 构造函数,拷贝构造函数,赋值操作符以及析构函数Template Class shar

5、ed_ptr /*构造函数*/ shared_ptr(); /创建一个持有空指针的shared_ptr,use_count() = 0& get() = NULL /获得一个指向类型T的指针p的管理权,use_count = 1&get() = p ,Y类型必须能够转换为T类型 template explicit shared_ptr(Y* p); /作用同上,增加了一个构造参数D,是一个仿函数对象,代表删除器,该构造函数非常有用,后面会详解 template shared_ptr(Y* p, D d); /*拷贝构造函数及赋值操作符重载*/ /调用拷贝构造函数或赋值操作后,引用计数加1 &

6、get() = r.get() & use_count() = r.use_count() shared_ptr(shared_ptr const & r); template shared_ptr(shared_ptr const & r); shared_ptr & operator=(shared_ptr const & r); template shared_ptr & operator=(shared_ptr const & r); /*析构函数*/ /引用计数会减1,如果计数器为0,get() != NULL,有删除器的话会调用删除器,否则调用delete操作符 shared_pt

7、r(); 7第五节:shared_ptr类摘要2、public 成员方法和操作符Template class shared_ptr T* get() const ; /返回原始指针 T& operator * () const ; /返回原始指针的引用 T* operator -() cosnt ; /返回原始指针 long use_count() const ; /返回引用计数 bool unique() const ; / 返回 use_count() = 1,是否唯一 operator bool() const ; /bool值转型,可以用于条件判断,例如if(ptr) void swa

8、p(shared_ptr& b) /交换原始指针 void reset() ; template void reset(Y* p); template void reset(Y* p); 8第六节:shared_ptr的一个例子1、#include 2、创建一个shared_ptr要用到的简单类Fooclass Fooprivate: std:string m_strName;Public: Foo(const std:string& strName) : m_strName(strName) Foo() std:cout “Destructing Foo with name = “ m_st

9、rName std:endl;9第六节:shared_ptr的一个例子3、实现void Test_Boost_Shared_Ptr()typedef boost:share_ptr FooPtr;void Test_Boost_Shared_Ptr() /ptr1获得Foo_1指针的所有权 FooPtr ptr1(new Foo(“Foo1”);/引用计数为1assert(ptr1.use_count() = 1); /ptr2指向ptr1FooPtr ptr2 = ptr1; /调用shared_ptr的赋值操作符,引用计数加1assert(ptr1.use_count() = ptr2.u

10、se_count(); /两者引用计数相同assert(ptr1 = ptr2); /shared_ptr重载=操作符,等同于ptr1.get() = ptr2.get()。assert(ptr1.use_count() = 2); /现在ptr1和ptr2都指向了Foo_1,因此计数器都为210第六节:shared_ptr的一个例子 /ptr3获得Foo_1指针的所有权 FooPtr ptr3 = ptr2;/引用计数加1assert(ptr1.use_count() = ptr2.use_count() & ptr1.use_count()= ptr3.use_count(); asser

11、t(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

12、_count() & ptr1.use_count() = 2); assert(ptr1 = ptr2 & ptr1 != ptr3); /前面ptr1和ptr2都指向了同一个对象,他们的引用计数都为2,现在创建一个ptr4,让其指向ptr1。 FooPtr ptr4 = ptr1; /引用计数加1 assert(ptr1 = ptr2 & ptr4 = ptr1); assert(ptr4.use_count = 3 & ptr1.use_count = 3 & ptr2.use_count() = 3);11第六节:shared_ptr的一个例子 /现在我们转移ptr2的所有权到另外一个

13、新分配的名为Foo_2的Foo指针上去 /在ptr2转移所有权后,ptr2的计数器应该是1 & ptr1和ptr4的计数器为2 & ptr1 != ptr2 ptr2.reset(new Foo(“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,并且ptr

14、5.get()返回值应该也为NULL FooPtr ptr5 = ptr3; /引用计数为0 assert(ptr5.use_count() = 0 & ptr5.get() = NULL); /运行到此时,程序即将结束,在退出作用域前会调用析构函数 /首先会调用ptr2的析构函数,打印出Destructing a Foo with name = Foo_2 /然后会调用ptr1 和ptr4析构函数进行引用计数递减 /最终会打印出Destructing a Foo with name = Foo_1 /这样就没有任何内存泄露了哈哈哈哈/结束test_boost_shared_ptr函数 12第

15、七节:shared_ptr使用注意点1、shared_ptr多次引用同一内存数据导致程序崩溃 例如下面代码会导致堆内存破坏而引起程序奔溃 void test_boost_shared_ptr_crash() Foo* pFoo = new Foo(Foo1); boost:shared_ptr ptr1(pFoo); boost:shared_ptr ptr2(pFoo); 上述代码两次释放同一内存而破坏堆,导致程序奔溃。13第七节:shared_ptr使用注意点1、shared_ptr多次引用同一内存数据导致程序崩溃 解决方案: 1 )直接使用 boost:shared_ptr ptr1(n

16、ew int(100); 使用匿名内存分配限制其他shared_ptr多次指向同一内存数据。 多次指向时使用shared_ptr的赋值操作符或拷贝构造函数,例如shared_ptr ptr2 = ptr1。 上面的匿名方式就是资源初始化既分配技术,直接在构造函数中分配内存 2) 使用boost的make_shared模板函数,例如shared_ptr ptr1 = boost:make_shared(100); shared_Ptr ptr2 = ptr1; 14第七节:shared_ptr使用注意点2、shared_ptr 循环引用导致内存泄露,代码如下: class Parent; cla

17、ss Child; class Parent public: Parent() std:cout “父类析构函数被调用n”; public: boost:shared_ptr child_ptr; ;15第七节:shared_ptr使用注意点2、shared_ptr 循环引用导致内存泄露,代码如下(续): class Child public: Child() std:cout “子类析构函数被调用n”; public: boost:shared_ptr Parent_ptr; ; 16第七节:shared_ptr使用注意点2、shared_ptr 循环引用导致内存泄露,代码如下(续): in

18、t main() boost:shared_ptr father(new Parent();/father引用计数为1 boost:shared_ptr son(new Child();/son引用计数为1 / 父子互相引用。 father-children = son;/son的引用计数为2 son-parent = father;/father的引用计数为2 return 0; /退出作用域前,father和son的引用计数都减1,此时father和son的引用计数都是为1 /因此各自指向的内存无法释放,导致内存泄露 17第七节:shared_ptr使用注意点 2、shared_ptr 循

19、环引用导致内存泄露 上述代码运行后,不会调用father和son的析构函数,因为循环引用,导致father和son的引用计数都为2,退出作用 域时候,引用计数减1,因此father和son在退出作用域时候引用计数都为1,无法调用析构函数,于是造成father和 son所指向的内存得不到释放,导致内存泄露。 18第七节:shared_ptr使用注意点2、shared_ptr 循环引用导致内存泄露的解决方案代码: 针对循环引用,使用boost:weak_ptr可以很方便的解决该问题。代码如下: class Parent; class Child; class Parent public: Pare

20、nt() std:cout “父类析构函数被调用n”; public: / boost:shared_ptr child_ptr;改为如下代码: boost:weak_ptr child_ptr; ;19第七节:shared_ptr使用注意点2、shared_ptr 循环引用导致内存泄露的解决方案代码: 针对循环引用,使用boost:weak_ptr可以很方便的解决该问题。代码如下(续): class Child public: Child() std:cout “子类析构函数被调用n”; public: /boost:shared_ptr Parent_ptr;改为如下代码: boost:w

21、eak_ptr Parent_ptr; ; 20第八节:Boost:weak_ptr的介绍 1 、weak_ptr是用来解决循环引用和自引用对象从前面的例子我们可以看出,引用计数是一种很便利的内存管理机制,但是有一个很大的缺点,那就是不能管理循环引用或自引用对象(例如链表或树节点),为了解决这个限制,因此weak_ptr被引入到boost的智能指针库中。2、weak_ptr并不能单独存在它是与shared_ptr同时使用的,它更像是shared_ptr的助手而不是智能指针,因为它不具备普通指针的行为,没有重载operator *和-操作符,这是特意的。这样它就不能共享指针,不能操作资源,这正是

22、它“弱”的原因。它最大的作用是协助shared_ptr工作,像旁观者那样观察资源的使用情况。21第八节:Boost:weak_ptr的总结3、 weak_ptr获得资源的观察权weak_ptr可以从一个shared_ptr或另外一个weak_ptr构造,从而获得资源的观察权,但weak_ptr并没有共享资源,它的构造并不会引起引用计数的增加,同时它的析构也不会引起引用计数的减少,它仅仅是观察者。4、 weak_ptr可以被用于标准容器库中的元素weak_ptr实现了拷贝构造函数和重载了赋值操作符,因此weak_ptr可以被用于标准容器库中的元素,例如:在一个树节点中声明子树节点std:vect

23、orboost:weak_ptr children;22第九节:shared_ptr一些使用技巧:1、将shared_ptr用于标准容器库 有两种方式:1) 将标准容器库作为shared_ptr管理的对象例如boost:shared_ptrstd:vector ,使容器可以被安全的共享,用法与普通shared_ptr没区别,我们不再讨论。 2) 将shared_ptr作为容器的元素例如std:vectorboost:shared_ptr ,因为shared_ptr支持拷贝构造和赋值操作以及比较操作的语意,符合标准容器对元素的要求,所以可以在容器中安全的容纳元素的指针而不是拷贝。标准容器可以容纳

24、原始指针,例如std:vector,但是这就丧失了容器的许多好处,因为标准容器库无法自动管理类型为指针的元素,必须编写额外的代码来保证指针最终被正确的删除,而保存shared_ptr作为标准容器库的元素,既保证与存储原始指针几乎一样的功能,而且不用担心资源泄露。 23第九节:shared_ptr的使用技巧2、以函数方式封装现有的c函数 例如crt的FILE操作函数,fopen/fclose/fread等函数,我们可以使用一些技巧,利用shared_ptr进行封装,从而使 我们不需要调用fclose,让其自动进行内存管理,代码如下: 1) 实现FileClose函数(实际不需要实现该函数,这里实

25、现是为了在资源释放时候打印出相关信息): typedef boost:shared_ptr FilePtr; void FileClose(FILE* f) fclose(f); std:cout “调用fclose函数释放FILE资源n”; 24第九节:shared_ptr使用技巧2、以函数方式封装现有的c函数 2 ) 实现FileOpen函数 FilePtr FileOpen(const char* path,const char* mode) /FilePtr fptr(fopen(path,mode),fclose);/直接使用fclose作为shared_ptr的删除器 FilePt

26、r fptr(fopen(path,mode),FileClose);/使用我们的包装删除器,在释放资源时候打印出相关信息 return fptr; 25第九节:shared_ptr使用技巧3、用c+桥接设计模式来封装现有的c函数,隐藏实现细节 在c+的.h文件中声明如下类: class FileSharedPtr private: class impl;/很重要一点,前向申明实现类,具体实现在.cpp文件中,隐藏实现细节boost:shared_ptr pimpl; /shared_ptr作为私有成员变量 public: FileSharedPtr(char const * name, ch

27、ar const * mode); void Read(void * data, size_t size); ;26第九节:shared_ptr使用技巧:3、用c+桥接设计模式来封装现有的c函数在c+的.cpp文件中实现如下类:class FileSharedPtr:implprivate: impl(impl const &) impl & operator=(impl const &) FILE* f;public: impl(char const * name, char const * mode)f = fopen(name,mode); impl() int result = fcl

28、ose(f); printf(invoke FileSharedPtr:impl 析构函数result = %dn,result); void read(void * data, size_t size) fread(data,1,size,f) ;27第九节:shared_ptr使用技巧3、用c+桥接设计模式来封装现有的c函数FileSharedPtr:FileSharedPtr(const char *name, const char *mode) : pimpl(new FileSharedPtr:impl(name,mode)void FileSharedPtr:Read(void*

29、data,size_t size) pimpl-read(data,size); void Test_CPP_File_Ptr()FileSharedPtr ptr(“memory.log”,“r”);/引用计数为1FileSharedPtr ptr2 = ptr;/引用计数为2char data100;ptr.Read(data,100);printf(%sn,data); /析构ptr2引用计数为1,再析构ptr,引用计数为0,释放内存,无泄漏28第九节:shared_ptr使用技巧4、使用面向接口编程方式隐藏实现 在c+的.h文件中声明如下接口:class IPrinter public

30、: virtual void Print() = 0;protected:/受保护的虚拟析构函数,导致本类必须被继承,也不能调用delete操作符。 virtual IPrinter() printf(invoke IPrinter virtual 析构函数n); ;typedef boost:shared_ptr PrinterPtr;PrinterPtr CreatePrinter();/工厂方法,创建IPrinter智能指针。29第九节:shared_ptr使用技巧4、使用面向接口编程方式隐藏实现 在c+的.cpp文件中声明如下实现类 :(很重要一点,实现类都是在cpp文件中的,必须要注

31、意这一点) class Printer : public IPrinterprivate:FILE* f;public: /使用createPrinter时候,调用实现类的构造函数,返回的是IPrinter的shared_ptrPrinter(const char* path,const char* mode)f = fopen(path,mode); /实现类的析构是public,但是接口类的是protected,但是shared_ptr在析构时候会自动调用实现类的析构 /且再次调用基类受保护的虚拟析构函数。通过这种机制我们可以完全封闭掉new和delete操作符,只能使用 /公开的工厂方法

32、函数返回接口智能指针,而且不需要也无法调用析构函数,完全由shared_ptr来管理内存。 /这样我们在整个程序中都没有原始指针的概念,从而不会忘记调用delte操作而导致内存泄露。30第九节:shared_ptr使用技巧4、使用面向接口编程方式隐藏实现 Printer() fclose(f); printf(invoke Printer 析构函数result = %dn,result); void Print() char data100; fread(data,1,100,f); printf(%sn,data); rewind(f); ;PrinterPtr CreatePrinter(

33、) PrinterPtr ptr(new Printer(memory.log,r) ; return ptr; 31第九节:shared_ptr使用技巧5、使用shared_ptr持有一个具有侵入式的引用计数的对象 如Com对象或intrusive_ptr等需要手动增减引用计数的对象。 假设我们要将shared_ptr用于一个COM对象。 1)我们在头文件中引入 头文件前定义宏: #define BOOST_MEM_FN_ENABLE_STDCALL 2) #include 3) 我们定义一个函数,例如 shared_ptr make_shared_from_COM(IWhatever *

34、p)、 p-AddRef(); /注意mem_fn仿函数的用法,它用于成员函数,&类名:成员函数名 shared_ptr pw(p, mem_fn(&IWhatever:Release); return pw; 一旦使用shared_ptr享有com的接口指针所有权后,com的引用计数被shared_ptr所接管,因此所有引用计数 增减都是由shared_ptr来进行. 32第九节:shared_ptr一些使用技巧:6、使用shared_ptr持有一个win32 handle 例如win32使用HANDLE来代表内核对象,所有内核对象都使用CreateXXX创建一个句柄,而使用CloseHan

35、dle来释 放一个句柄。 typedef void* HANDLE;/win32 HANDLE的内部定义,在windows中可以看到 HANDLE CreateMutex(); BOOL CloseHandle(HANDLE); 使用shared_ptr来管理上述类型的HANDLE,代码如下: typedef shared_ptr handle;33第九节:shared_ptr一些使用技巧:6、使用shared_ptr持有一个win32 handle BOOL MyCloseHandleWrap(HANDLE h) std:cout “调用win32 CloseHandle APIn”; re

36、turn CloseHandle(h); handle createMutexHandle() shared_ptr ptr(CreateMutex(NULL,FALSE,NULL),MyCloseHandleWrap/*CloseHandle*/); 34第九节:shared_ptr使用技巧7、使用shared_ptr持有一个静态分配的对象 有时候我们需要使用shared_ptr来管理一个静态分配的对象或全局对象,例如 static X xobj; 由于静态或全局对象的析构是在程序结束时候自动进行的,我们不能够在shared_ptr中自动调用delete操作符,因此 我们可以实现一个NULL

37、析构器来实现该目的 struct null_deleter/仿函数对象 /重载函数调用操作符() void operator()(void* const ) const /没有任何代码 ; shared_ptr CreateX() shared_ptr px(&xobj,null_deleter(); return px; 35第九节:shared_ptr使用技巧8、使用shared_ptr 持有任意对象的所有权 1) 使用shared_ptr来获得任何对象的所有权:在该shared_ptr离开作用域时候,会自动调用该shared_ptr所享有的实际对象的析构函数,2) 如果要使用实际对象的相关成员函数:需要使用:boost:static_pointer_castboost:dynamic_pointer_castboost:const_pointer_castboost:reinterpret_pointer_cast这四个转型模板函数。36第九节:shared_ptr一些使用技巧:8、使用shared_ptr 持有任意对象的所有权 void Test

温馨提示

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

评论

0/150

提交评论