C++新特性---智能指针

智能指针:
    为什么需要智能指针?
        1. malloc出来的空间,没有进行释放,存在内存泄漏的问题。 
        2. 异常安全问题。如果在malloc和free之间如果存在抛异常,那么还是有内存泄漏。这种问题就叫异常安 全。
    RAII:
        是一种利用对象生命周期来控制程序资源(如内存,文件句柄,网络连接,互斥俩个等等)的简单技术
        优点:
            1.不需要显示地释放资源
            2.采用这种方式,对象所需地资源在其生命周期内始终保持有效
 
// 使用RAII思想设计的SmartPtr类 
template<class T> 
class SmartPtr { 
public:    
    SmartPtr(T* ptr = nullptr)        
        : _ptr(ptr)    
    {}
    
    ~SmartPtr()    {        
        if(_ptr)            
            delete _ptr;    
    }
private:    
    T* _ptr; 
};
 
void MergeSort(int* a, int n) {    
    int* tmp = (int*)malloc(sizeof(int)*n);    
    // 讲tmp指针委托给了sp对象,用时老师的话说给tmp指针找了一个可怕的女朋友!天天管着你,直到你go die^^    
    SmartPtr<int> sp(tmp);    
    // _MergeSort(a, 0, n - 1, tmp);            
    // 这里假设处理了一些其他逻辑    
    vector<int> v(1000000000, 10);    
    // ... 
}
 
int main() {    
    try {        
        int a[5] = { 4, 5, 2, 3, 1 };        
        MergeSort(a, 5);    
    }    
    catch(const exception& e)    {        
        cout<<e.what()<<endl;    
    }
    return 0; 
}
    智能指针原理:
        需要具备指针地行为,可以解引用,也可以通过->去访问所指空间中地内容,因此还需要将*, ->,重载
 
template<class T> 
class SmartPtr { 
public:    
    SmartPtr(T* ptr = nullptr)        
        : _ptr(ptr)    
    {}
    ~SmartPtr()    {        
        if(_ptr)            
            delete _ptr;    
    }        
    T& operator*() {return *_ptr;}    
    T* operator->() {return _ptr;} 
private
    T* _ptr; 
};
struct Date {    
    int _year;    
    int _month;    
    int _day; 
};
int main() {    
    SmartPtr<int> sp1(new int);    
    *sp1 = 10    
    cout<<*sp1<<endl;       
 
    SmartPtr<int> sparray(new Date);    
    // 需要注意的是这里应该是sparray.operator->()->_year = 2018;    
    // 本来应该是sparray->->_year这里语法上为了可读性,省略了一个->    
    sparray->_year = 2018;    
    sparray->_month = 1;    
    sparray->_day = 1; 
}

 

总结智能指针的原理:
    1.RAII特性
    2.重载opreator* 和operator->,具有像指针一样的行为
 
C++11和boost中智能指针的关系
  1. C++98中产生了第一个智能指针auto_ptr
  2. C++boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr
  3. C++11,引入了unique_ptr和share_ptr和weak_ptr.需要注意的是unique_ptr对应boost的scoped_ptr.并且这些智能指针是实现原理是参考boost中的实现的
auto_ptr {


构造->保存指针信息
析构->释放指针空间


auto_ptr<int> ptr(ptr2);
拷贝构造->(转移管理权的形式,将ptr2赋值给_ptr,置空ptr2)


auto_ptr<int> ptr = ptr2;
赋值运算符重载->(转移管理权,判断是否自己给自己赋值,先释放自己所指向的空间,在用ptr2赋值给_ptr,置空ptr2)


}

 

unique_ptr {
//线程安全

//不支持拷贝构造
C++11:
unique_ptr(const unique+ptr<T>& ap) = delete;
unique_ptr<T>& opreator=(const unique_ptr<T>& ap) = delete;


C++98:
//只声明,不实现,声明成私有


}

 

shared_ptr {
//会存在引用计数的线程安全问题

//实现了引用计数的方式

////////////////////////////////////////////////////////////////////////////////////
//此方法存在一定问题
private:
    T* _ptr
    statice int _count;
    //使用statice会存在一定问题:
    //使用了statice后每个对象只会公用一块空间,而当需要多块空间来计数的时候就不能实现
    //sp1; sp2(sp1)-> 1和2公用一个空间   sp3; sp4(sp3);->3和4公用一块空间  
    //这样就需要两个引用计数,但是statice后一个类只能存在一个引用计数            
////////////////////////////////////////////////////////////////////////////////////



public:
    shared_ptr(T* ptr)
        :_ptr(ptr)
        ,_pcount(new int(1))
    {}
    
    shared_ptr(const shared_ptr<T>& sp)
        :_ptr(sp._ptr)
        ,_pcount(sp._pcount)
    {
        (*_pcount)++;
    }


    shared_ptr<T>& operator=(const shared_ptr<T>& sp)
    {
            if(this != &sp)
            {
                //ptr1; ptr2(ptr1)
                //ptr3; ptr3 = ptr2;
                //先判断ptr2的计数是否为0,不为0则不能释放,说明其他指针还在使用
                if(--(*_pcount) == 0)
                {
                    delete _pcount;
                    delete _ptr;
                }
                _ptr = sp._ptr;
                _pcount = sp._pcount;
                (*_pcount)++;
            }
            return *this;
    }


    ~shared_ptr()
    {
        if(--(*_pcount) == 0)
        {
            delete _ptr;
            delete _pcount;
            _pcount = nullptr;
            _ptr =  nullptr;
        }
    }    


private:
    T* _ptr;
    int* _pcount;
    //_pcount 在堆上,会存在线程安全问题
}
//shared_ptr 修改确保shared_ptr的线程安全问题

shared_ptr {

public:
    shared_ptr(T* ptr)
        :_ptr(ptr)
        ,_pcount(new int(1))
    {}
    
    shared_ptr(const shared_ptr<T>& sp)
        :_ptr(sp._ptr)
        ,_pcount(sp._pcount)
    {
        (*_pcount)++;
    }

    shared_ptr<T>& operator=(const shared_ptr<T>& sp)
    {
            if(this != &sp)
            {
                //ptr1; ptr2(ptr1)
                //ptr3; ptr3 = ptr2;
                //先判断ptr2的计数是否为0,不为0则不能释放,说明其他指针还是只用
                if(--(*_pcount) == 0)
                {
                    delete _pcount;
                    delete _ptr;
                }
                _ptr = sp._ptr;
                _pcount = sp._pcount;
                (*_pcount)++;
            }
            return *this;
    }

    ~shared_ptr()
    {
        if(--(*_pcount) == 0)
        {
            delete _ptr;
            delete _pcount;
            _pcount = nullptr;
            _ptr =  nullptr;
        }
    }    

private:
    T* _ptr;
    int* _pcount;
    //_pcount 在堆上,会存在线程安全问题
}

struct Date{
    int _year;
    int _month;
    int _day;
};
shared_ptr 的循环引用问题:
    在链表的情况:
struct ListNode{
    //ListNode* _next;
    //ListNode* _prev;
    /*
    std::shared_ptr<ListNode> _next;
    std::shared_ptr<ListNode> _prev;
    */

    std::weak_ptr<ListNode> _next;
    std::weak_ptr<ListNode> _prev;

    ~ListNode()
    {
        cout << "~ListNode()" << endl;
    }
};

int main()
{
    std::shared_ptr<ListNode> node1(new ListNode);
    std::shared_ptr<ListNode> node2(new ListNode);   
    cout << node1.use_count() << endl;
    cout << node2.use_count() << endl; 

    node1->_next = node2;
    node2->_prev = node1;

    cout << node1.use_count() << endl;
    cout << node2.use_count() << endl;

    return 0;
}
        week_ptr——>为了解决shared_ptr产生的循环引用,它并不是RAII思想
            特点:
                1.不会增加引用计数
                2.只可以接收shared_ptr赋值给它
                3.本质并不是一个智能指针

posted on 2019-08-30 11:32 The_Ocean 阅读(...) 评论(...) 编辑 收藏

导航