智能指针+守卫锁--借于RAII思想

RAII(Resource Acquisition Is Initialization)(资源获得即初始化)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量)等的简单技术–本质托管资源-管理权转移,托管权利交给你,自己置空不管
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源
托管给对象的好处:
(1)不需要显式地释放资源。
(2)采用这种方式,对象所需的资源在其生命期内始终保持有效
采用RAII思想进行了智能指针的设计但是不是意味着RAII是智能指针,智能指针是RAII思想的一种实现
构造函数的时候把指针保存起来,然后析构函数自动释放
tempalte
smartptr{
public:
smartptr(T* ptr):_ptr(null){
}
~smarrtptr(){
if(_ptr){
delete _ptr;
}
}
private:
T* _ptr;
};
但是Smartptr还不能将其称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可以通过->去访问所指空间中的内容,所以需要重载* 和->

template<class T>
class smartptr{
shareptr(T*ptr):_ptr(nullptr){}
//解引用的返回值有可能是值也有可能是指针
T& operator*(){//此处可以不用&但是最好用&
return *_ptr;//*(this->ptr)
}
T* operator->(){
return _ptr;//(this->ptr)--所以返回值为指针 ---之后就成了->
}
~smartptr(){
if(_ptr){
delete _ptr;
}
}
private:
T* _ptr;
}

但是有问题–是浅拷贝–非深拷贝
int main(){
smartptrsp1(new int );
smartptrsp2(sp1 );
}
sp2为sp1的拷贝构造—这种时候就出错了指针指向的是同一块内存,而smartptr默认的是浅拷贝同一块内存释放两次程序奔
智能指针模拟的是指针,像指针,深拷贝不合适,本质让sp1和sp2共同管理那块内存
C++98版本的库中就提供了auto_ptr的智能指针
管理权转移交给this指针
template
class AutoPtr
{
public:
AutoPtr(T* ptr = NULL)
: _ptr(ptr)
{}
管理权转移,交给this指针,ap置空,就可以完成拷贝构造了
auto_ptr(const auto_ptr&ap){
一个人管理它
:ptr(ap._ptr){
ap._ptr = NULL;
//但是有问题,给它传参后自己置空了自己的值没有了,所以严禁使用auto_ptr,会导致悬空的问题,有缺陷
}
//ap1=ap3
autoptr& operator=(AutoPtr& ap)
{
// 检测是否为自己给自己赋值
if(this != &ap)
{
// 释放当前对象中资源
if(_ptr)
delete _ptr;
// 转移ap中资源到当前对象中
_ptr = ap._ptr;
ap._ptr = NULL;
}
return this;
}
~AutoPtr()
{
if(_ptr)
delete _ptr;
}
T& operator
() {return _ptr;}
T
operator->() { return _ptr;}
prvite:
T* _ptr;
};
void test_auto_ptr(){
auto_ptrap1(new int);
auto_ptrap2(ap1);
}
//不能使用autoptr有严重的缺陷:当对象拷贝或者赋值后,前面的对象就悬空了
c++11引进uniqueptr—直接防拷贝,就是直接将拷贝和赋值给禁止了
tempalte
class uniqueptr{
uiqueptr(T* ptr(null)):_ptr(null){}
T& operator*(){
return _ptr;//(this->ptr)
}
T* operator(){
return _ptr;//(this->ptr)
}
~uiqueptr(){
if(_ptr){
delete _ptr;
}
}
private:
uniqueptr(uniqueptrconst &)=delete;
uniqueptr&operaotr=(uniqueptrconst &)=delete;
private:
T* _ptr;
}
然后c++11引进了新的智能指针shareptr
shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。

  1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。

  2. 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。

  3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;

  4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。
    #include
    #include
    template
    class SharedPtr
    {
    public:
    SharedPtr(T* ptr = nullptr)
    : _ptr(ptr)
    , _pRefCount(new int(1))
    , _pMutex(new mutex)
    {}
    ~SharedPtr() {Release();}
    SharedPtr(const SharedPtr& sp)
    : _ptr(sp._ptr)
    , _pRefCount(sp._pRefCount)
    , _pMutex(sp._pMutex)
    {
    AddRefCount();
    }
    // sp1 = sp2
    SharedPtr& operator=(const SharedPtr& sp)
    {
    //if (this != &sp)
    if (_ptr != sp._ptr)
    {
    // 释放管理的旧资源
    Release();
    // 共享管理新对象的资源,并增加引用计数
    _ptr = sp._ptr;
    _pRefCount = sp._pRefCount;
    _pMutex = sp._pMutex;
    AddRefCount();
    }
    return this;
    }
    T& operator
    () {return _ptr;}
    T
    operator->() {return _ptr;}
    int UseCount() {return _pRefCount;}
    T
    Get() { return _ptr; }
    void AddRefCount()
    {
    // 加锁或者使用加1的原子操作
    _pMutex->lock();
    ++(_pRefCount);
    _pMutex->unlock();
    }
    private:
    void Release()
    {
    bool deleteflag = false;
    _pMutex.lock();
    if (–(
    _pRefCount) == 0)
    {
    delete _ptr;
    delete _pRefCount;
    deleteflag = true;
    }
    _pMutex.unlock();
    if(deleteflag == true)
    delete _pMutex;
    }
    private:
    int* _pRefCount; // 引用计数
    T* _ptr; // 指向管理资源的指针
    mutex* _pMutex; // 互斥锁
    };
    但是shareptr还存在一个问题就是线程安全问题
    有两点
    . 智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时++或–,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2.这样引用计数就错乱了。会导致资源未释放或者程序崩溃的问题。所以只能指针中引用计数++、–是需要加锁的,也就是说引用计数的操作是线程安全的。

  5. 智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题
    shareptr循环引用问题:
    shared_ptr node1(new ListNode);
    shared_ptr 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; }
    node2 引用计数为1,node1 引用计数为1
    _next也是智能指针,node2变成2
    _prevet也是智能指针,node1变成了 2,
    node1和node2都调用析构计数减减变成1,但是谁都不会释放
    _prev依赖node1这个空间,node1又依赖_next(是该空间的成员)而它又依赖node2—>_preve,死循环就无法释放了
    node2空间释放取决于_next,而node1空间释放取决于_prev
    在这里插入图片描述
    解决方案:
    两个空间智能指针相互作用—>弱指针,不增加引用计数,我可以管理指向你但是不管你的析构不增加引用计数,接收share_Ptr赋值给它
    struct listnode{
    std::weak_ptr _next;
    std::weak_ptr _prev;
    ~listnode(){
    cout<<"~listnode"<<endl;
    }
    };
    int main()
    weak_ptr不增加引用计数
    {
    std::weak_ptr node1(new listnode);
    std::weak_ptr node2(new listnode);
    cout<<node1.use_count()<<endl;
    cout<<node2.use_count()<<endl;
    node->_next=node2;
    node->prev=node1;
    cout<<node1.use_count()<<endl;
    cout<<node2.use_count()<<endl;
    }
    引用计数不变始终是1,调用weak _ptr就可以保证用share_ptr但是引用计数不会增加
    两个空间两个空间智能指针相互作用(循环引用)—>弱指针
    智能指针管new出来的
    如果不是new出来的对象怎么解决?
    int main(){
    share_ptrsp1(new BB);–>对
    share_ptrsp2((BB*)malloc(sizetof(BB)));–>不行
    没有初始化
    share_ptrsp3((string*)malloc(sizeof(string)));—>出错
    没有初始化,string对象有指针指向资源,new出来,malloc全是随机值,也有指针但是是随机值释放不行
    二者malloc区别:
    share_ptrsp4(new BB[10]);–>不行
    –>仿函数解决—实现删除器—shareptr提供

    template
    struct FreeFunc {
    void operator()(T* ptr)
    {
    cout << “free:” << ptr << endl;
    free(ptr);
    }
    };
    template
    struct DeleteArrayFunc {
    void operator()(T* ptr)
    {
    cout << “delete[]” << ptr << endl;
    delete[] ptr;
    }
    };
    int main()
    {
    FreeFunc freeFunc;
    shared_ptr sp1((int*)malloc(4), freeFunc);
    DeleteArrayFunc deleteArrayFunc;
    shared_ptr sp2((int*)malloc(4), deleteArrayFunc);
    return 0; }
    RAII思想实现守卫锁
    tempate
    class lock_gard{
    lock_guar(Mutex mutex):_mtx(mutex)
    {
    _mtx.lock();
    }
    ~lock_guar(){
    _mtx.unlock;
    }
    lock_gard(const lock_gard<Mutex&>)=delete;
    private:
    Mutex& _mtx;//
    }必须使用引用,不然锁的是同一个锁,锁不住,锁的不是一个互斥量对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值