使用智能指针解决可能导致的内存泄漏的问题

て烟熏妆下的殇ゞ 提交于 2019-12-08 11:12:30

一个程序中可能会因为各种情况导致内存泄漏的问题,程序泄漏可能不会一下子被写程序的人发现,因为它可能是一点点的被泄漏,直到内存被耗尽之后才会知道内存泄漏这个问题,但是我们不应该写出这样的程序来坑我们自己。因此在写程序的时候应该尽量避免这类问题的产生

执行流跳转是什么??在一个程序顺序往下执行的时候,在遇到return , break, goto,continue等语句的时候,就会引发执行流跳转。假如说我们在程序开头部分new了一个变量或其他,那么与它配套使用的delete就应该出现在它的下一句,但是可能在他们两中间要有一些条件执行,这些条件中的某些语句可能引发程序结束或退出,那么后面的delete语句则不会被执行,也就是说我们前面申请的空间没有还给系统,这样就会导致内存的泄漏。

如何解决这个问题?RAII(资源分配即初始化)就可以解决。RAII是一种解决问题的思想,用一个类来封装资源的分配和释放,不是具体的某种实现的类或者函数,基于它的思想提出的智能指针是解决问题的根本。
智能指针在快速发展的互联网发展过程中也分为如下几类:
AutoPtr:自动指针管理权转移

template <class T>
class AutoPtr
{
public:
    AutoPtr(T* ptr) :_ptr(ptr)
    {}
    ~AutoPtr()
    {
        if (_ptr)
        {
            cout << "haha" << endl;
            delete _ptr;
        }
    }
    //拷贝构造函数
    AutoPtr(AutoPtr<T>& ap) :_ptr(ap._ptr)
    {
        ap._ptr = NULL;
    }
    //赋值运算符重载
    AutoPtr<T>& operator= (AutoPtr<T>& ap)
    {
        if (this != &ap)
        {
            if (_ptr)
                delete[] _ptr;
            _ptr = ap._ptr;
            ap._ptr = NULL;
        }
        return *this;
    }
    //运算符*的重载
    T& operator*()
    {
        return *_ptr;
    }
    //运算符->的重载
    T* operator->()
    {
        return _ptr;
    }
private:
    T* _ptr;
};

struct auto
{
    int a1;
    int a2;
};

void Func()
{
    int *p1 = new int(2);
    *p1 = 10;
    AutoPtr<int> ap1(new int(2));
    *ap1 = 10;//运算符*的重载
    AutoPtr<auto> ap2(new auto);
    ap2->a1 = 10;
    ap2->a2 = 20;//->的重载  当指向结构体成员的时候会用到->
}
void testAutoPtr()
{
    Func();
}

通过程序运行可以看到结果是输出了两次haha,可以看到func函数中没有写任何delete,但是它却释放了开辟的空间。这就是AutoPtr的作用。

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;
    }


    //为什么设为私有的??就是为了防拷贝,所以只声明不实现
private:
    //拷贝构造函数
    ScopedPtr(ScopedPtr<T>& sp);
    //赋值运算符的重载
    ScopedPtr<T>& operator=(ScopedPtr<T>& sp);
private:
    T* _ptr;
};

void testScopPtr()
{
    ScopedPtr<int> sp(new int(2));
    //当想要拷贝的时候会让程序崩掉,这样就不会出现一个空间多次释放的情况
    ScopedPtr<int> sp1(sp);
}

SharedPtr:引用计数指针 由于会出现引用循环的问题,所以 配合weakptr使用
循环引用:在引用计数问题上,会出现循环引问题,为什么?假如结构体设为一个链表,那么在释放_prev,_next的时候就会出现问题,什么问题??_prev指向cur,_next指向next,出了作用域之后会cur和next都不指向其空间了,但是空间还有指向它的指针,所以就不会被释放,被释放的空间互相依赖,所以陷入了一个循环
看图可能会更加明白

这里写图片描述

template <class T>
class WeakedPtr;

template<class T>
class SharePtr
{
public:
    friend class WeakedPtr<T>;
    SharePtr(T* ptr =  NULL) :_ptr(ptr), _refCount(new int(1))
    {}
    ~SharePtr()
    {
        if (--(*_refCount) == 0)
        {

            delete _ptr;
            delete _refCount;
        }
    }
    //s1(s2)
    SharePtr(const SharePtr<T>& sp) :_ptr(sp._ptr), _refCount(sp._refCount)
    {
        (*_refCount)++;
    }
    //sp1 = sp2
    SharePtr<T>& operator=(SharePtr<T>& sp)
    {
            if (_ptr != sp._ptr)
            {
                if (--(*_refCount) == 0)
                {
                    delete _ptr;
                    delete _refCount;
                }
                _ptr = sp._ptr;
                _refCount = sp._refCount;
                (*_refCount)++;
            }
        return *this;
    }
    //为了像指针一样才进行*\->的重载
    //->的重载
    T* operator->()
    {
        return _ptr;
    }
    //*的重载
    T& operator*()
    {
        return *_ptr;
    }
    //查看引用计数的多少
    int UseCount()
    {
        return *_refCount;
    }
private:
    T* _ptr;
    int* _refCount;//一块空间有一个指针
};

//解决了循环引用问题
template <class T>
class WeakedPtr
{
public:
    WeakedPtr() :_ptr(NULL)
    {}
    WeakedPtr(const SharePtr<T>& sp)
        :_ptr(sp._ptr)
    {}

    WeakedPtr<T>& operator=(const SharePtr<T>&sp)
    {
        _ptr = sp._ptr;
        return *this;
    }
    T& operator* ()
    {
        return *_ptr;
    }
    T* operator->()
    {
        return _ptr;
    }
private:
    T* _ptr;
};


struct  ListNode
{
    int _data;
    WeakedPtr<ListNode> _next;
    WeakedPtr<ListNode> _prev;

    ~ListNode()
    {
        cout << "~ListNode" << endl;
    }
};
void testSharePtr()
{
    //SharePtr<int> sp1(new int(1));
    //SharePtr<int> sp2(sp1);

    SharePtr<ListNode> cur(new ListNode);
    SharePtr<ListNode> next(new ListNode);

    cout << cur.UseCount() << endl;
    cout << next.UseCount() << endl;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!