模拟实现boost库里的智能指针

来源:互联网 发布:java 文件上传过程 编辑:程序博客网 时间:2024/05/19 13:07
  • 智能指针
    什么是智能指针呢,它是行为类似于指针的类对象,但这种对象还有其他功能。我们为什么要封装智能指针类对象呢?这是因为C++中的动态内存需要用户自己来维护,动态开辟的空间,在出函数作用域或者程序正常退出前必须释放掉,否则会造成内存泄漏,所以我们会定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。
    接下来我们模拟实现以下boost库里的一些智能指针了解他们的使用。

  • Auto_ptr
    第一种实现方法

#include<iostream>#include<stdlib.h>using namespace std;//模拟实现auto_ptr,非常坑爹什么情况下都不要使用template <typename T>class Auto_ptr{public:    Auto_ptr(T *ptr = NULL)        :_ptr(ptr)    {}    Auto_ptr(Auto_ptr &a)//拷贝构造函数    {        _ptr = a._ptr;        a._ptr = NULL;//有新的指针使用这块空间为了避免程序崩溃原先的指针        //这快空间脱离关系    }    Auto_ptr & operator=(Auto_ptr& a)    {        if(this != &a)//防止自身赋值        {            if(NULL !=_ptr)            {                delete _ptr;                _ptr = NULL;            }            _ptr = a._ptr;            a._ptr = NULL;        }        return *this;    }    ~Auto_ptr()    {        if(NULL != _ptr)        {            delete _ptr;            _ptr = NULL;            cout<<"_ptr has been deleted";        }    }private:    T *_ptr;};

第二种实现方法-添加bool变量

template <typename T>class Auto_ptr{public:    Auto_ptr(T *ptr = NULL)        :_ptr(ptr)        ,Onlyone(true)    {        if(NULL == ptr)        {            Onlyone = false;        }    }    Auto_ptr(Auto_ptr &a)//拷贝构造函数    {        _ptr = a._ptr;        Onlyone = true;        a.Onlyone = false;//有新的指针使用这块空间为了避免程序崩溃原先管理空间的指针Onlyone置为false    }    Auto_ptr & operator=(Auto_ptr& a)    {        if(this != &a)//防止自身赋值        {            if(false != Onlyone)            {                delete _ptr;                _ptr = NULL;            }            _ptr = a._ptr;            a.Onlyone = false;            Onlyone = true;        }        return *this;    }    ~Auto_ptr()    {        if(true == Onlyone)        {            delete _ptr;            _ptr = NULL;            cout<<"_ptr has been deleted";        }    }private:    T *_ptr;    bool Onlyone;};

两种方法各有优缺点,这里我们来看一下第二种的Bug.

void FunTest(){    Auto_ptr<int>ap1(new int)    if(true)    {        Auto_ptr<int>ap2(new int)    }//出了if作用域空间就会被释放,此时使用ap1指针就会出现问题}
  • ScopedPtr
    ScopedPtr这个智能指针采取比较暴力的手段,让空间只由自己一个来管理,我们来看看具体的实现
#define  _CRT_SECURE_NO_WARNINGS 1#include<iostream>#include<stdlib.h>using namespace std;template <typename T>class ScopedPtr{public:    ScopedPtr(T *p = NULL)        :_p(p)    {}    ~ScopedPtr()    {        if(NULL != _p)        {            delete _p;            _p = NULL;        }    }private:    //为了防止浅拷贝的问题出现,使这个类无法被拷贝和赋值    //采用的方法就是将拷贝构造函数和赋值运算符重载函数    //访问权限设为私有,并且只给出声明。    ScopedPtr(const ScopedPtr& s);    T &operator=(const ScopedPtr& s);private:    T *_p;};void FunTest(){    ScopedPtr<int> sp(new int);    //ScopedPtr<int> sp1(sp);//错误无法完成拷贝    ScopedPtr<int> sp1(new int);    //sp1 = sp;//无法成功赋值}int main(){    FunTest();    system("pause");    return 0;}

一个类如何防拷贝呢?
1.声明为私有的:这样可以通过友元函数和成员函数拷贝成成功(不可取)
2.声明为公有:可能在类外被定义(不可取)
3.声明为私有(只给出声明)(可以实现)

  • SharedPtr

最后我们来模拟boost库中shared_ptr,但这个函数线程不是很安全,一般建议使用scoped_ptr

#define  _CRT_SECURE_NO_WARNINGS 1#include<iostream>#include<stdlib.h>using namespace std;//共享的智能指针//使用引用计数template <typename T>struct Delete{    void operator()(T* ptr)    {        if(NULL != ptr)        {            delete ptr;            ptr = NULL;        }    }};struct Fclose{    void operator()(FILE* ptr)    {        if(NULL != ptr)        {            fclose(ptr);        }    }};template <typename T,typename _Del = Delete<T>>class SharedPtr{public:    SharedPtr(T *p = NULL,_Del del = Delete<T>())//构造函数        :_p(p)        ,_pCount(NULL)//不可以在此处初始化为1,如果p为NULL        ,_del(del)    {        if(NULL != _p)        {            _pCount = new int(1);        }    }    SharedPtr(const SharedPtr& sp)//拷贝构造函数        :_p(sp._p)        ,_pCount(sp._pCount)    {        if(NULL != _pCount)//注意_pCount为NULL的情况,此时不能解引用,不用++        {            ++(*_pCount);        }    }    SharedPtr& operator=(const SharedPtr& sp)    {        if(_p != sp._p)//注意自身赋值,有时候两个不同的对象但里面的的指针指向            //相同不能用(this != &sp)判断出来        {            if(NULL != _pCount)//被复制的对象本身为空            {                if(0 == --(*_pCount))//被赋值的对象自己管理一段空间,需要释放                {                    Release();                }            }                _p = sp._p;//和其他对象共同管理                _pCount = sp._pCount;                if(NULL != sp._pCount)//注意判断赋值对象是否为空                {                    ++(*_pCount);                }            }        return *this;    }    ~SharedPtr()    {        Release();    }private:    void Release()    {        if(0 == --(*_pCount))        {            _del(_p);            delete _pCount;//记得要释放引用计数的空间            _pCount = NULL;        }    }private:    T *_p;    int *_pCount;    _Del _del;};

SharedPtr存在线程安全问题,并且也存在循环引用的问题,我们之后会详细讨论,所以建议使用ScopedPtr。

原创粉丝点击