智能指针

26 篇文章 0 订阅

一.RAII(资源分配即初始化)

就是定义一个类来进行资源的分配和释放,在构造函数完成资源的初始化,在析构函数完成资源的清理和释放。可以保证资源的正确初始化和释放。
using namespace std;
template<class T>
class AA
{
public:
    AA()
    {
        cout << "AA()" << endl;
    }
    ~AA()
    {
        cout << "~AA()" << endl;
    }

protected:
    int _aa;
};
int main()
{
    AA <int> a;
    int *a1 = new int(10);
    bool IsEnd = true;
        if (IsEnd)
        {
            delete a1;
            return 1;
        }
        delete a1;
    system("pause");
    return 0;
}


二.异常

打断执行流

template<class T>
class AA
{
public:
    AA()
    {
        cout << "AA()" << endl;
    }
    ~AA()
    {
        cout << "~AA()" << endl;
    }
protected:
    int _aa;
};
int main()
{
    AA <int> a;
    int *a1 = new int(0xefffffff);
    bool IsEnd = true;

    throw bad_alloc();
    if (IsEnd)
    {
        delete a1;
        return 1;
    }

    //delete a1;
    system("puase");
    return 0;
}
当我们加上throw bad_alloc()时直接抛异常,程序跳过判断语句终止。

在C++中异常显示的抛出可以被检测和捕捉到,这里面我们常用到try(检测)和catcy(捕获);

这样就可以帮我们检测到,但有的时候如果忘记释放内存就去开辟新的空间就会造成内存泄露的问题,有没有一种简单的方法帮助我们检测到提前预防呢?

三.智能指针:

所谓的智能指针就是自动管理指针所指的动态内存的释放

第一种:AutoPtr(管理权转移)

AutoPtr的类模板实现
template<class T>
class AutoPtr
{
public:
    AutoPtr(T*ptr)
        :_ptr(ptr)
    {}
    //p2(p1)
    AutoPtr(AutoPtr<T>&ap)
        :_ptr(ap._ptr)
    {
        ap._ptr = NULL;
    }
    //p1 = p2
    AutoPtr<T>&operator = (AutoPtr<T> &ap)
    {
        if (this != &ap)
        {
            delete _ptr;
            _ptr = ap._ptr;
            _ptr = NULL;
        }
        return *this;
    }
    ~AutoPtr()
    {
        delete _ptr;
    }
    T&operator*()
    {
        return *_ptr;
    }
    T*operator->()
    {
        return _ptr;
    }
    T*Get()
    {
        return _ptr;
    }
protected:
    T*_ptr;
};
void TestAutoPtr()
{

    AutoPtr <int> p1(new int(3));
    AutoPtr <int> p2 = p1;
    throw bad_alloc();
    cout << (*p1) << endl;
    cout << (*p2) << endl;

}
我们再画出对象模型:


我们会发现_ptr1和_ptr2指针只是发生了管理所有权的变化,但是会存在潜在的内存崩溃的问题。

第二种:ScopedPtr(防拷贝)适用于部分场景

template<class T>
class ScopedPtr
{
public:
    ScopedPtr(T*ptr)
        :_ptr(ptr)
    {}
    ~ScopedPtr()
    {
        if (_ptr)
        delete _ptr;
    }
    T&operator *()
    {
        return *_ptr;
    }
    T*operator->()
    {
        return _ptr;
    }
protected:
    ScopedPtr(const ScopedPtr<T>&sp);
    ScopedPtr<T> & operator = (const ScopedPtr <T>&sp);
protected:
    T* _ptr;
};
void TestScopedPtr()
{
    ScopedPtr<int> sp1 (new int(2));
    *sp1 = 30;
   // ScopedPtr<int> sp2(sp1);
}

void TestScopedPtr()
{
    ScopedPtr<int> sp1 (new int(2));
    *sp1 = 30;
    ScopedPtr<int> sp2(sp1);
}

我们看到当进行拷贝时会无法访问,这种智能指针就是简单粗暴,把拷贝函数放到私有或者保护,这样就不能直接访问。这只实用部分场景。
缺点是:不能拷贝。

第三种:SharedPtr(应用计数)

template<class T>
class SharedPtr
{
public:
    SharedPtr(T*ptr)
        :_ptr(ptr)
        , _refcount(new int(1))
    {}
    //拷贝一次引用计数加1 sp1(sp2)
    SharedPtr(SharedPtr<T>&sp)
        :_ptr(sp._ptr)
        , _refcount(sp._refcount)
    {
        ++(*_refcount);
    }
    //sp1= sp2
    SharedPtr<T> &operator=(const SharedPtr<T>&sp)
    {
        if (this != &sp)
        {
            this->Release();
            this->_ptr = sp._ptr;
            this->_refcount = sp._refcount;

            ++(*_refcount);
        }
        return *this;
    }
    //当为最后一个对象时清理
    void Release()
    {
        if (--(*_refcount) == 0)
        {
            delete _ptr;
            delete _refcount;
        }
    }
    ~SharedPtr()
    {
        Release();
    }
protected:
    T*_ptr;
    int *_refcount;
};
void TestSharedPtr()
{
    SharedPtr<int> sp(new int(5));
    SharedPtr<int> sp1(sp);
    SharedPtr<int> sp2= sp1;
}

我们画出模型就很容易分析了


拷贝前的每个对象各有一个引用计数,拷贝后,sp1的应用计数是2,sp1和sp2指向同一块空间,所以sp2的引用计数也是2。
似乎引用计数很实用,很强大。但是遇到这种情况:
循环引用
#include<iostream>
using namespace std;
#include<boost/shared_ptr.hpp>
truct Node
{
    shared_ptr<Node>_prev;
    shared_ptr<Node>_next;
    ~Node()
    {
        cout<< "~Node()" <<endl;
    }
};
int main()
{

    shared_ptr<Node> cur(new Node);
    shared_ptr<Node> next(new Node);

    cout << cur.use_count() << endl; //第一次调用
    cout << next.use_count() << endl;

    cur->_next = next;
    next->_prev = cur;

    cout << cur.use_count() << endl; //第二次调用
    cout << next.use_count() << endl;
    system("pause");
    return 0;
}

调用boost库中shared_ptr我们发现当使用双向循环链表时会出现循环引用计数问题。我们画出其对象模型:

当第一次调用shared_ptr指针,节点cur的_next没有指向next只有一个对象,引用计数是1,而节点next的_prev没有指向cur.故为1,
第二次调用shared_ptr指针时, 两个对象互相使用一个shared_ptr成员变量指向对方的会造成循环引用。导致引用计数失效。

第四种:弱指针(weak_ptr)解决循环引用

#include<iostream>
using namespace std;
#include<boost/weak_ptr.hpp>
#include<boost/shared_ptr.hpp>
using namespace std;
struct Node
{
    weak_ptr<Node>_prev;
    weak_ptr<Node>_next;
    ~Node()
    {
        cout<< "~Node()" <<endl;
    }
};

int main()
{

    shared_ptr<Node> cur(new Node);
    shared_ptr<Node> next(new Node);

    cout << cur.use_count() << endl;
    cout << next.use_count() << endl;

    cur->_next = next;
    next->_prev = cur;

    cout << cur.use_count() << endl;
    cout << next.use_count() << endl;
    system("pause");
    return 0;
}
我们再来观察结果发现问题得到解决如下:


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值