我的C++实践(10):智能指针

    1、独占型智能指针: HolderPtr<T>。智能指针对象独占被管理对象的所有权,不允许对智能指针进行拷贝和赋值。可以提供一个release()函数来让出所有权。

   

    2、转移型智能指针: AutoPtr<T>。允许对智能指针进行拷贝或赋值,但拷贝或赋值后对象所有权就会自动转移到其他的智能指针,原来的智能指针指向NULL。例如标准库中的std::auto_ptr就属于这一类智能指针。

    注意在拷贝和赋值时,函数的参数不能加const修饰符,因为转移对象的所有权需要修改参数的指针成员的值。另外要注意针对其他兼容类型的拷贝构造函数和赋值操作符使用了成员函数模板,它并不会覆盖默认的拷贝构造函数和赋值运算,因此我们还要显示提供默认的拷贝构造函数和赋值操作符,因为它们都是深拷贝和深赋值。
    3、共享型智能指针: CountingPtr<T,CP,OP>。多个智能指针可以指向一个对象。这要对共享的指针个数进行计数,当计数器变为0时,就释放指向的对象。有多种计数策略(比如侵入式和非侵入式)和对象释放策略(比如对普通对象和数组对象释放策略不一样,有时可能必须要用free()来释放内存等),因此通常可以把计数功能和对象释放功能抽离出来设计成独立的计数policy和对象释放policy,通过模板参数传递给智能指针。我们这里把policy设计成具有成员函数模板的普通类,而不是模板,这样传递policy时就不需要模板模板参数,用普通的类型参数就可以了。
    非侵入式计数器:计数器并不存储在所指向的对象内部,而是用独立的空间来存储和管理计数器。由于计数器对象比较小,又要被多个共享型智能指针所共享,为了提高性能,通常设计一个独立的分配器来分配大小固定的计数器对象。计数器policy类的操作包括分配、释放、加1、减1、判0等,设计成成员函数模板,形参为模板参数的指针类型,表示要计数的指针。代码如下:

    侵入式计数器:将计数器放到被管理对象本身的类型中,是这个类型的一个普通成员变量,因此无需独立的分配器。侵入式计数器一般专用于管理某一种具体类型。计数器类应设计成模板,用一个模板参数来传递用作为计数器的成员变量指针。代码如下:

    对象释放器:对普通对象用delete,对数组对象用delete[],有时我们可能必须要使用其他的方式来释放对象,比如用C函数free()。我们把它们设计不同的policy类。如下:

    使用了计数policy和对象释放policy的共享型智能指针实现如下:

 

    有几点要注意:
    (1)在detach()中,在减少计数值之前必须要检查智能指针是否为空,因为空指针并没有可关联的计数器。
    (2)CountingPtr<T,CP,OP>直接继承了两种policy类,这样当policy类为空类时,就可以执行空基类优化。比如前面的侵入式计数器MemberReferenceCount就是一个空类,在CountingPtr中使用它时可以被优化掉,但非侵入式计数器SimpleReferenceCount不是空类(有一个指针成员),不能被优化。而对象释放器均为空类。可见一般非侵入式计数器会增加智能指针的大小(当然我们可以使用压缩存储技术来优化,这需要改变该policy类的设计),侵入式计数器则会增加被管理的对象的大小。
    (3)计数器类中,函数模板的形参表示要进行计数的指针,但函数内部却并不需要用到这个指针,因为它只需要改变计数值即可。
    (4)智能指针的常数性:与内建指针相比,X const*(即const X*)与CountingPtr<X const>对应,表示所指对象为常数。X* const与CountingPtr<X> const对应,表示指针本身为常数。因此在->和*重载中,const版本的返回类型是T*或T&,而不是T const*或T const&,因为T本身可以是const类型。
    (5)智能指针的转型:内建指针可以隐式转型为基类指针、void*类型或bool类型。对智能指针,我们可以添加转型成员函数,以允许CountingPtr<T>转型为bool类型。我们也可以转型为CountingPtr<B>(通过单参数的拷贝构造函数完成),其中B为void类型或者为T的基类,使用这种转型时就不能用侵入式计数器,因为void类型没有计数器,且基类的计数器与子类的计数器通常也不同(各有各的计数器)。注意我们并不允许智能指针直接转型为内建指针,因为我们设计智能指针就是为了避免使用内建指针,而且如果允许的话,对智能指针进行运算(如cp[n],cp2-cp1),将很难确定其结果。对于bool转型,可直接提供operator bool()运算符,但这会有一定的副作用,比如导致两个智能指针可以相加了。我们可以换一种解决方案,提供一个到成员指针的转型运算符(这需要增加一个私有的嵌套类,注意嵌套类并不会增加智能指针CountingPtr的大小),当转型到成员指针后,可以自动地隐式转型为bool类型。注意成员指针与内建指针不同,并不能对成员指针进行算术运算,因此没有副作用。但是,我在gcc上测试出这种方案不能通过(代码中的这部分我注释掉了),有可能是因为gcc并不支持到成员指针的转型运算符。
    (6)智能指针的比较:内建指针支持相等比较(==,!=)和排序比较(,<,<=等)。对智能指针,可以添加相应的比较运算符函数。要注意有智能指针和内建指针的直接比较,也有两个智能指针之间的比较。对于两个智能指针之间的比较,我们把它设计成了一个独立的全局函数模板(在类外面),允许兼容类型的智能指针进行比较。由于是全局的非成员函数,因此要显式加inline表示内联,以提高性能。
    4、测试代码。 当智能指针使用侵入式计数器时,由于计数器是在目标类型对象的内部,用于专门对这个类型的对象进行计数。因此为了使用方便,我们通常把智能指针组合到目标类型中,成为它的一个类型成员(这并没有增加目标类型的大小),这样通过这个类型成员就可以方便的定义我们需要的智能指针。

 

    从代码中可以看出,类型MyClass使用了侵入式计数器,因此我把管理这个类型的智能指针CountingPtr组合进来,定义成一个简洁的名字。在下面的测试代码中我们就可以看出,使用MyClass::SmartPtr要比使用CountingPtr<MyClass,MemberReferenceCount<MyClass,size_t,&MyClass::ref_count> >简洁得多。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值