欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

智能指针

程序员文章站 2022-06-02 13:56:09
...

智能指针是行为类似于指针的类对象,利用对象的析构函数,在对象过期时让其析构函数删除指向的堆内存。 智能指针主要有两类:
- 不带引用计数的智能指针
C++库的auto_ptr
Boost库的scrope_ptr
- 带引用计数的智能指针
shared_ptr
auto_ptr是C++98提供的方案,现在C++11已经摒弃。
一、auto_ptr

 template<typename T> 
class Auto_ptr 
{ public: 
    Auto_ptr(T* ptr = NULL):_ptr(ptr) 
    { 
        ptr = NULL; 
    } 
    Auto_ptr(Auto_ptr<T>& rhs) 
    { 
        _ptr = rhs._ptr; 
        rhs._ptr = NULL; 
    } 
    Auto_ptr<T>& operator=(Auto_ptr<T>& rhs) 
    {
        if(this != &rhs) 
        { 
            delete _ptr;
            _ptr = rhs._ptr; 
            rhs._ptr = NULL; 
        } 
        return *this; 
    }
    ~Auto_ptr() 
    { 
        delete _ptr;
        _ptr = NULL;
    } 
    T& operator*() 
    { 
        return *_ptr; 
    } 
    T* operator->() 
    { 
        return _ptr; 
    } 
private: T* _ptr; 
}; 
class Test 
{ 
public: 
    Test(int a = 10):ma(a){} 
    void Show() 
    { 
        cout<<ma<<endl; 
    } 
private: int ma; 
}; 
void Test1(int a) 
{ 
    int* p = new int(); 
    if (a <= 0) 
    { 
        throw exception("a < 0");
    }//如果说发生异常,delete p不会被调用,会造成内存泄漏 delete p; 
} 
void Test2() 
{ 
    Test* p = new Test(10);
    p->Show(); 
    delete p; 
    //如果没有delete p;则Test类的析构函数无法被调用。在Test2函数中,p是一个局部指针变量,函数结束p会被释放掉,但是p所指向的堆内存无法被释放。 
} 
int main() 
{ 
    Test* p = new Test(10);
    p->Show(); 
    delete []p;
    //如果忘记写delete就会造成内存泄漏 
    Test* p1 = new Test(20); 
    Auto_ptr<Test> ap1(p1); 
    ap1->Show();
    Auto_ptr<Test> ap2(ap1);
    //程序会出错 ap1->Show(); return 0; 
}

基于上述的情况,为了解决忘记delete或由于其他原因而造成的内存泄漏问题,使用了智能指针。将一个指针托管于一个对象,利用对象的特点:系统调用构造函数和析构函数,也就是说在Auto_ptr的析构函数释放,生存周期到时,自动会释放掉托管的指针指向的堆内存。

缺点: 在上述代码中,到ap2时程序会崩溃,这也就是auto_ptr的缺点:一个对象只能被一个智能指针所指向,调用ap2(ap1)时,就会把ap1置NULL,在ap1->Show()就会崩溃。

另外,auto_ptr不能管理对象数组。一次分配多个对象我们必须要用delete[]去释放,不然会出错。

最终的问题其实就是delete和delete[]的区别问题。 delete用于释放单个对象,获取的是对象的地址,并且调用free,而对象数组的分配有所不同,除了分配所需要大小的堆内存空间外,这段堆内存的开头还分配了4个字节的空间,用于存放分配对象的个数,在释放的时候必须把最开头的四个字节包含进去,delete[]在释放的时候,将得到的地址减4,这样取得了最开始的地址,这样的释放才是正确的释放,而delete的释放并没有减4的操作,所以无法正确的释放。 可以看到auto_ptr的析构函数中只有delete的释放,所以对象数组必然出错。

不要讲auto_ptr储存在容器中,因为赋值和拷贝构造后原指针无法使用。

智能指针
上图来自https://my.oschina.net/hevakelcj/blog/465978
二、scoped_ptr
scoped_ptr 的实现原理是防止对象间的拷贝和赋值,即将拷贝构造和赋值运算符重载放入保护或私有的访问限定符内,只声明不定义防止他人在类外拷贝。简单粗暴地解决了auto_ptr的缺点,提高了代码的安全性,但是导致功能不完整。

template<class T>
class ScopedPtr
{
public:
    ScopedPtr(T * p = NULL)
        :_Ptr(p)
    {
    }
    ~ScopedPtr()
    {
        if (NULL != _Ptr)
        {
            delete _Ptr;
            _Ptr = NULL;
        }
    }
    T& operator*()
    {
        return *_Ptr;
    }
    T* operator->()
    {
        return _Ptr;
    }
private:
    ScopedPtr(ScopedPtr<T>& sp);//只声明不实现,并且声明为私有,防止其他人实现并调用  
    ScopedPtr<T>& operator=(ScopedPtr<T>& sp);
private:
    T* _Ptr;
};

三、shared_ptr & weak_ptr
shared_ptr的实现原理是通过引用计数(int *count)来实现,拷贝或赋值时将引用计数加1,析构时只有当引用计数减到0才释放空间,否则只需将引用计数减1即可。
智能指针

shared_ptr没有解决相互引用导致内存泄漏的问题,即指两个对象相互引用,造成相互制约,导致指针不能释放。使用一个弱引用智能指针(weak_ptr)来打破循环引用(weak_ptr不增加引用计数)

weak_ptr是一个弱引用,只引用,不计数。如果一块内存被shared_ptr和weak_ptr同时引用,当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前需要检查weak_ptr是否为空指针。

weak_ptr是一种不控制所指向对象生存期的智能指针,它指向一个shared_ptr管理的对象。将一个weak_ptr绑定到shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使有weak_ptr指向对象,对象还是会被释放。当我们创建一个weak_ptr时,要用一个shared_ptr来初始化它。

class RefManage
{
public:
    static RefManage& getIntance()
    {
        return rm;
    }
private:
    RefManage() :length(0){}
    RefManage(const RefManage&rhs) :length(0){}
public:
    void addRef(void* ptr)
    {
        cout << "add:" << endl;
        if (ptr != NULL)
        {
            int index = find(ptr);
            if (index < 0)//不存在 插入到新的结点上
            {
                Node tmp(ptr);
                tmp.refcount++;
                node[length++] = tmp;
                cout << "   addr:  " << ptr << "  refcount:  " << tmp.refcount << endl;
            }
            else//存在 引用计数加1
            {
                node[index].refcount++;
                cout << "   addr:  " << ptr << "  refcount:  " << node[index].refcount<< endl;
            }
        }
    }
    void delRef(void* ptr)
    {
        cout << "delRef" << endl;
        if (ptr != NULL)
        {
            int index = find(ptr);
            if (index < 0)
            {
                throw exception("addr not exsit!");
            }
            else
            {
                if (node[index].refcount != 0)
                {
                    node[index].refcount--;
                }
                cout << "   addr:  " << ptr << "  refcount:  " << node[index].refcount << endl;
            }
        }
    }
    int getRef(void *ptr)
    {
        if (ptr == NULL)
        {
            return 0;
        }
        int index = find(ptr);
        if (index < 0)//addr不存在返回-1
        {
            return -1;
        }
        return node[index].refcount;//存在返回引用计数
    }
private:
    int find(void* ptr)
    {
        for (int i = 0; i < length; ++i)
        {
            if (node[i].refaddr == ptr)
            {
                return i;
            }
        }
        return -1;
    }
    class Node
    {
    public:
        Node(void* p = NULL) :refaddr(p), refcount(0){}
        void* refaddr;
        int refcount;
    };
    Node node[10];
    int length;//记录当前数组的有效长度
    static RefManage rm;
};  
RefManage RefManage::rm;

template<typename T>
class Shared_ptr
{
public:
    Shared_ptr(T* p = NULL)
    {
        _ptr = p;
        AddRef();
    }
    Shared_ptr(const Shared_ptr<T>& rhs)
    {
        _ptr = rhs._ptr;
        AddRef();
    }
    Shared_ptr<T>& operator=(const Shared_ptr<T>& rhs)
    {
        if (this != &rhs)
        {
            DelRef();
            if (0 == GetRef())
            {
                delete _ptr;
            }
            _ptr = rhs._ptr;
            AddRef();
        }
        return *this;
    }
    T* GetPtr()const
    {
        return _ptr;
    }
    ~Shared_ptr()
    {
        DelRef();
        if (0 == GetRef())
        {
            delete _ptr;
        }
    }
    T& operator*()
    {
        return *_ptr;
    }
    T* operator->()
    {
        return _ptr;
    }
private:
    void AddRef()
    {
        rm.addRef(_ptr);
    }
    void DelRef()
    {
        rm.delRef(_ptr);
    }
    int GetRef()
    {
        return rm.getRef(_ptr);
    }
    T* _ptr;
    static RefManage &rm;
};
template<typename T>
RefManage& Shared_ptr<T>::rm = RefManage::getIntance();

template<typename T>
class Weak_ptr
{
public:
    Weak_ptr(T* p = NULL)
    {
        _ptr = p;
    }
    Weak_ptr(const Shared_ptr<T>& rhs)
    {
        _ptr = rhs.GetPtr();
    }
    Weak_ptr<T>& operator=(const Shared_ptr<T>& rhs)
    {
        _ptr = rhs.GetPtr();
        return *this;
    }
    ~Weak_ptr()
    {
        _ptr = NULL;
    }

private:
    T* _ptr;
};