smart_ptr学习之scoped_ptr

 类如其名:scoped_ptr,仅仅只在自己的作用域内才可以使用,出了自己的作用域就自动进行析构。

不同于auto_ptr,不支持所有权的转移,同时把自己的拷贝构造函数、赋值操作符等相关的成员函数设定为私有函数,更进一步的“巩固”了“一切尽在我掌握中”的自信。

因为不支持这些操作,所以对于所有权是完全不会转移,这是与auto_ptr最大的不同点。

同时,因为scoped_ptr在实现上仅仅是指对指针的单纯proxy,因此在使用指针的地方基本上都可以使用它,并且没有比“裸指针”付出更高的代价!

可以在如下几种情况下使用该种智能指针:

(1)异常安全异常重要,因为使用了RAII技术,即使出现异常安全,也能正确的进行资源的释放

(2)动态分配的资源对象必须被严格的限定在一个作用域内,这也是该指针的特点

(3)正是因为该指针的特点,不支持所有权的转移(没有实现的拷贝构造函数和赋值操作符),因此不支持作为STL容器的元素

对于资源类型:支持以new操作符产生的相关资源,同时只支持delete操作符来释放资源。

在利用该指针进行相关的代码编写过程中,要支持完整资源类型的分配和释放<对应的就是非完整类型,只有声明没有定义的,一般在PIMPL中会碰到>,否则会报错误!

 

部分成员函数说明:

explicit scoped_ptr(T* p=0)

构造函数,存储p的一份拷贝。注意,p 必须是用operator new分配的,或者是null. 在构造的时候,不要求T必须是一个完整的类型。当指针p是调用某个分配函数的结果而不是直接调用new得到的时候很有用:因为这个类型不必是完整的,只需要类型T的一个前向声明就可以了。这个构造函数不会抛出异常。

<前向声明是一个比较有用的技术,我们在尽量减少文件的依赖性的时候,对于指针类型、引用类型、函数返回值类型都可以仅仅只需要一个前向声明,不需要包含相关的声明或者是定义的头文件,那样即使相关的头文件发生了修改,那么使用该声明的头文件也不用重新编译>

explicit scoped_ptr( std::auto_ptr<T> p )

构造函数,获得auot_ptr的所有权,属于所有权的终止者。

~scoped_ptr()

删除被指物。类型T在被销毁时必须是一个完整的类型<会进行编译时的错误检查,判断是否是完整类型>。如果scoped_ptr在它被析构时并没有保存资源,它就什么都不做。这个析构函数不会抛出异常。

void reset(T* p=0);

重置一个 scoped_ptr 就是删除它已保存的指针,如果它有的话,并重新保存 p. 通常,资源的生存期管理应该完全由scoped_ptr自己处理,但是在极少数时候,资源需要在scoped_ptr的析构之前释放,或者scoped_ptr要处理它原有资源之外的另外一个资源。这时,就可以用reset,但一定要尽量少用它。(过多地使用它通常表示有设计方面的问题) 这个函数不会抛出异常。

T& operator*() const;

返回一个到被保存指针指向的对象的引用。由于不允许空的引用,所以解引用一个拥有空指针的scoped_ptr将导致未定义行为。如果不能肯定所含指针是否有效,就用函数get替代解引用。这个函数不会抛出异常。

T* operator->() const;

返回保存的指针。如果保存的指针为空,则调用这个函数会导致未定义行为。如果不能肯定指针是否空的,最好使用函数get。这个函数不会抛出异常。

T* get() const;

返回保存的指针。应该小心地使用get,因为它可以直接操作裸指针。但是,get使得你可以测试保存的指针是否为空。这个函数不会抛出异常。get通常用于调用那些需要裸指针的函数<一般是遗留的C程序代码需要“裸指针”>,提醒智能指针的使用者:在使用智能指针的过程中,出了在智能指针的初始构造的时候需要直接使用指针资源,其它时候都尽量或者是不要直接使用裸指针,这个对于引用计数指针更是如此,需要谨记!

operator unspecified_bool_type() const

返回scoped_ptr是否为非空。返回值的类型是未指明的,但这个类型可被用于Boolean的上下文中。在if语句中最好使用这个类型转换函数,而不要用get去测试scoped_ptr的有效性<这个地方对外类型转化可能大家不是很理解,为什么不直接重载operator bool()操作符,因为bool操作符的重载会导致很多其它的类型转化,比如说bool会转换为int等,那么当该智能指针和int内置类型操作数一起的时候,就会产生一些未定义的行为,关于这个操作符的重载,可以参考一些资料,一般都是使用自定义类中的某些函数指针等,因为指针是可以转换为bool的(默认)>

void swap(scoped_ptr& b)

交换两个scoped_ptr的内容。这个函数不会抛出异常。

 

全局swap函数,一般对于自定义类型可以提供该swap函数

template<typename T> void swap(scoped_ptr<T>& a,scoped_ptr<T>& b)

这个函数提供了交换两个scoped pointer的内容的更好的方法。之所以说它更好,是因为 swap(scoped1,scoped2) 可以更广泛地用于很多指针类型,包括裸指针和第三方的智能指针。scoped1.swap(scoped2) 则只能用于它的定义所在的智能指针,而不能用于裸指针。

 

举例:自己显示的一下比较基础的PIMPL

PScoped_ptr.hpp

#include <boost/scoped_ptr.hpp>

class PImp
{
private:
    struct SData;

    boost::scoped_ptr<SData> data;

public:
    PImp( int value );
    ~PImp();
    void set( int value );
    int  get( ) const;
};

PScoped_ptr.cpp

#include <iostream>
#include "PScoped_ptr.hpp"


struct PImp::SData
{
    SData( int v )
        :value(v)
    {}

    int value;
};


PImp::PImp( int value )
:data( new SData(value) )
{}

PImp::~PImp()
{}

void PImp::set( int value )
{
    *data = value;
}

int PImp::get() const
{
    return data->value;
}

PScoped_ptr_main.cpp

#include <iostream>
#include "PScoped_ptr.hpp"


int main()
{
    PImp imp(10);
    std::cout << imp.get() << std::endl;

    imp.set(11);
    std::cout << imp.get() << std::endl;
    return 0;
};

 

需要说明:注意在头文件PScoped_ptr.hpp中,我们使用的SData是一个不完整的类型,只有声明,还没有定义;虽然是PImp的包含类型定义,但是没有完整类型,如果不显示的定义PImp的析构函数,那么main主函数在编译的过程中,会找不到相关的scoped_ptr在使用delete时候的析构函数,这个通过boost的库的一个小工具checked_delete(T * x),在编译的时候就会报错,错误

1>d:\3rdlibs\include\boost\checked_delete.hpp(33) : error C2027: 使用了未定义类型“PImp::SData”
1>        f:\c++\boost\auto_ptrtest\auto_ptrtest\pscoped_ptr.hpp(6) : 参见“PImp::SData”的声明
1>        d:\3rdlibs\include\boost\smart_ptr\scoped_ptr.hpp(81): 参见对正在编译的函数 模板 实例化“void boost::checked_delete<T>(T *)”的引用
1>        with
1>        [
1>            T=PImp::SData
1>        ]
1>        d:\3rdlibs\include\boost\smart_ptr\scoped_ptr.hpp(77): 编译类 模板 成员函数“boost::scoped_ptr<T>::~scoped_ptr(void)”时
1>        with
1>        [
1>            T=PImp::SData
1>        ]
1>        f:\c++\boost\auto_ptrtest\auto_ptrtest\pscoped_ptr.hpp(8): 参见对正在编译的类 模板 实例化“boost::scoped_ptr<T>”的引用
1>        with
1>        [
1>            T=PImp::SData
1>        ]
1>d:\3rdlibs\include\boost\checked_delete.hpp(33) : error C2118: 负下标
1>d:\3rdlibs\include\boost\checked_delete.hpp(35) : warning C4150: 删除指向不完整“PImp::SData”类型的指针;没有调用析构函数
1>        f:\c++\boost\auto_ptrtest\auto_ptrtest\pscoped_ptr.hpp(6) : 参见“PImp::SData”的声明

因此需要显示定义相关的析构函数才可以!

不过对于auto_ptr,则没有这种要求,这也是我个人觉得auto_ptr不够严格的地方,仅仅只会给出类似于warning C4150: 删除指向不完整“PImp::SData”类型的指针;没有调用析构函数警告,不够严格。

输出结果:

其它的使用,大家可以举一反三!

在上面的实现中,是不支持拷贝和赋值操作符的,因为成员变量(智能之争)不支持,如果想实现相关的操作,需要自己手动的去实现相关的拷贝和赋值操作符。

 

scoped_ptr-----------资源泄露不用怕,一切尽在我掌握中,尽量不用“裸指针”,远离资源泄露不是难事情。

当您想获得和裸指针同样的内存布局大小,同样的速度,但是有想异常安全,可以考虑使用它,不过你要自己费点心思写copy construct和assign operator的操作了!

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值