C++智能指针详解

来源:互联网 发布:淘宝店铺介绍填写 编辑:程序博客网 时间:2024/06/05 15:06

1.智能指针的产生背景及发展历史:

    在C++中动态内存的管理由一对运算符来管理:new,为动态内存分配空间并返回一个指向该对象的指针;delete,接受一个动态对象的指针,并销毁该对象,释放与之相关联的内存。有时我们忘记释放内存,造成内存泄漏;有时在有指针引用内存的情况下就释放了它,在这种情况下会产生引用非法内存的指针。
RAII(一种解决问题的思想)
     RAII(Resource Acquisition Is Initialization),也称为为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。通过应用RAII思想,就产生了智能指针:    智能纸指针的行为类似常规指针;重要的是它负责自动释放所指的对象。

发展历程:
这里写图片描述

2.模拟实现智能指针

1>模拟实现auto_ptr
template <class T>class AutoPtr  {public:    AutoPtr(T *ptr)        :_ptr(ptr)    {    }    ~AutoPtr()    {        if (_ptr != NULL)        {            delete _ptr;            _ptr = NULL;        }    }    AutoPtr(AutoPtr <T> & ap)        :_ptr(ap._ptr)    {        ap._ptr = NULL;  //只能有一个对象的成员指针指向同一块区域,前一个对象的成员指针置为空指针    }    T& operator  //注意返回值问题不能返回一个临时变量,临时变量具有常性,会导致不能通过解引用更改值    {        return *(this->_ptr);    }    AutoPtr& operator=(AutoPtr &ap)    {        //防止自赋值        if (this != &ap)        {            delete _ptr;            this->_ptr = ap._ptr;            ap._ptr = NULL;        }        return *this;    }    T*  operator->()    {        //this->_ptr->;        return _ptr;    }private:    T *_ptr;};class AA{public:    int _a;    int _b;};//测试用例void TestAutoPtr(){    AutoPtr<int> ap1(new int);    *ap1 = 10;    AutoPtr<int> ap2(ap1);     //*ap1 = 20;      //这句代码会导致程序崩溃,因为当有两个对象的成员指针指向同一块区域    //时,前一个对象的成员指针会被置成空指针     AutoPtr<int> ap3(new int(20));    ap2 = ap3;    AutoPtr<AA> ap4(new AA);    ap4->_a = 10;    ap4->_b = 20;    ap2 = ap3;}
2>模拟实现scoped_ptr(和unique_ptr功能类似)
template<class T>class ScopedPtr   {public:    ScopedPtr(T *ptr)        :_ptr(ptr)    {    }    ~ScopedPtr()    {        if (_ptr)        {            delete _ptr;            _ptr = NULL;        }    }    T& operator *()    {        return  *(_ptr);    }    T* operator ->()    {        return _ptr;    }protected:    //构造和拷贝构造只声明,不定义    ScopedPtr& operator=(const ScopedPtr & sp);    ScopedPtr(const ScopedPtr & sp);private:    T* _ptr;};class AA{public:    int _a;    int _b;};//测试用例void TestScopedPtr(){    ScopedPtr<int> ap1(new int);    *ap1 = 10;    ScopedPtr<int> ap3(new int(10));//  ap3 = ap1;//  ScopedPtr<int> ap4(ap1);//上述两句代码编译不通过,因为ScopedPtr的构造和拷贝构造只声明,没定义,//且为了防止别人进行修改定义为保护或者私有成员    ScopedPtr<AA> ap2(new AA);    ap2->_a = 10;    ap2->_b = 20;}
3>shared_ptr的模拟实现(仿函数版)(实际实现要复杂)

仿函数:

     仿函数(functor),就是使一个类的使用看上去象一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。    没有使用仿函数,就不能实现SharedPtr的多种功能,只使用delete删除的话,如果是new[]的空间,就会造成内存泄漏。

这里写图片描述

template<class T>struct DeleteArray{    void operator()(T *ptr)    {        delete[] ptr;    }};template<class T>struct Delete{    void operator()(T *ptr)    {        delete ptr;    }};struct Fclose{    void operator()(FILE * ptr)    {        fclose(ptr);    }};template<class T,class Del>class SharedPtr  {public:    SharedPtr(T *ptr)        :_ptr(ptr)        , _reference_count(new int (1))    {    }    ~SharedPtr()    {        Clear();    }    void Clear()    {        if (--(*_reference_count) == 0)        {             _del(_ptr);            delete _reference_count;            _reference_count = NULL;        }    }    SharedPtr( SharedPtr<T,Del> & sp)    {        _ptr = sp._ptr;        _reference_count = sp._reference_count;        (*_reference_count)++;    }    //传统写法    //SharedPtr& operator =(const SharedPtr<T,Del> & sp)    //{    //  if (this != &sp)    //  {    //      Clear();    //      _ptr = sp._ptr;    //      _reference_count = sp._reference_count;    //      (*_reference_count)++;    //  }    //  return *this;    //}    //现代写法    SharedPtr& operator =(SharedPtr<T,Del> sp)    {        swap(_ptr, sp._ptr);        swap(_reference_count, sp._reference_count);        return *this;    }    T& operator *()     {        return *(this->_ptr);    }    T*  operator->()    {        return _ptr;    }private:    T *_ptr;    Del _del;    int *_reference_count;};//测试用例void TestSharedPtr(){    SharedPtr<int,Delete<int>> ap3(new int(10));    SharedPtr<int,Delete<int>> ap4(ap3);    SharedPtr<int,DeleteArray<int>> ap5(new int[10]);    SharedPtr<FILE, Fclose>ap6(fopen("text.txt", "w"));}
4>weak_ptr产生原因:
     shared_ptr虽然功能强大,但是存在一些问题,比如说循环引用问题。
struct ListNode{    shared_ptr<ListNode> _prev;    //shared_ptr为库文件中实现的,只需包memory即可使用      shared_ptr<ListNode> _next;    int data;    ListNode(int x)    {        data = x;        _prev = NULL;        _next = NULL;    }    ~ListNode()    {        cout << "~ListNode()" << endl;    }};int main()  {      shared_ptr<ListNode> cur(new ListNode(1));    shared_ptr<ListNode> next(new  ListNode(2));    cur->_next = next;    next->_prev = cur;    return 0;        }  

循环引用:
这里写图片描述
为了解决shared_ptr循环引用所带来的问题,就有了weak_ptr:

  weak_ptr是一种不控制所指对象生存周期的智能指针,它指向一个shared_ptr所管理的对象。将一个weak_ptr绑定到shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。weak_ptr的名字抓住了这种智能指针“弱”共享对象的特点。

3.智能指针的总结

这里写图片描述

0 0