bosthhe1 / cpushpush

0 stars 0 forks source link

智能指针 #49

Open bosthhe1 opened 1 year ago

bosthhe1 commented 1 year ago

智能指针和异常也有很大关联,智能指针的原理是RAII(利用对象的生命周期来控制进程资源),这个和guard_mutx锁很像,但是也增加其他的一些特点,如定制垃圾回收器 //一下就是造成了内存的问题,a,b在堆山开辟空间,但是没有释放

void func1()
{
    int* a = new int;
    int* b = new int;
    throw string("空间不足");
}

int main()
{
    try
    {
        func1();
    }
    catch (string& e)
    {
        cout << e << endl;
    }
    catch (...)
    {
        cout << "未捕捉异常" << endl;
    }
}
bosthhe1 commented 1 year ago

智能指针有四种模式,第一种auto_prt - 管理权转移,不建议使用 其他三种为:unique_ptr,share_prt,weak_ptr 下面是对三种不同模式进行讲解

bosthhe1 commented 1 year ago

unique_ptr是比较简单粗暴,使用的是直接将拷贝构造和赋值都删除,不使用拷贝构造和赋值,这样可以防止拷贝。

template<class T>
struct Deletefile
    {
        void operator()(T* ptr)//自己手动配置回收
        {
            delete ptr;
        }
    };
template<class T,class Deletefile = Deletefile<T>>
    class unique_ptr
    {
    private:
        unique_ptr(const unique_ptr<T>& Sef) = delete;
        unique_ptr<T> operator=(const unique_ptr<T>& Sef)= delete;
    public:
        unique_ptr(T* ptr)
            :_ptr(ptr)
        {}
        ~unique_ptr()
        {
            Deletefile()(_prt);
        }
    private:
        T* _prt;
    };
void func1()
{
    int* a = new int;
    int* b = new int;
    hxh::unique_ptr<int> a1(a);//将a的内容交给a1管理,这样出了作用域就可以直接析构,不用手动调用delete
    hxh::unique_ptr<int> b1(b);
    throw string("内存泄露");
}
bosthhe1 commented 1 year ago

share_ptr的使用场景是多个指针都会使用这个对象,这样会造成多次析构的问题,可以使用引用计数的方式防止多次释放,

template<class T>
    class shared_ptr
    {
    public:
        shared_ptr(T* num =nullptr)
            :_ptr(num)
            , _CountSum(new int(1))
            , mtx(new mutex)
        {}
        shared_ptr(shared_ptr<T>& sp)
            :_ptr(sp._ptr)
        {
            mtx = sp.mtx;
            _CountSum = sp._CountSum;
            mtx->lock();
            (*_CountSum)++;
            mtx->unlock();
        }
        T* operator->()
        {
            return _ptr;
        }
        T& operator*()
        {
            return *_ptr;
        }
        shared_ptr<T>& operator=(const shared_ptr<T>& sp)
        {
            if (sp._ptr != _ptr)
            {
                mtx->lock();
                (*_CountSum)--;
                if (*_CountSum == 0)
                {
                    delete _ptr;
                    delete _CountSum;
                    mtx->unlock();
                    delete mtx;
                }
                else
                {
                    mtx->unlock();
                }
                mtx = sp.mtx;
                _ptr = sp._ptr;
                _CountSum = sp._CountSum;
                mtx->lock();
                (*_CountSum)++;
                mtx->unlock();
            }
            return *this;
        }
        ~shared_ptr()
        {
            mtx->lock();
            (*_CountSum)--;
            if (*_CountSum == 0)
            {
                delete _ptr;//share_prt 和unqiue_ptr的回收机制不一样,因为share_ptr是多个参数需要回收,所以系统的回收机制是传参数进行回收,而不是模板
                delete _CountSum;
            }
            mtx->unlock();
        }
                private:
        T* _ptr;
        int* _CountSum;
        mutex* mtx;
    };
struct Data
{
    int year;
    int month;
    int day;
    Data()
        :year(1)
        ,month(1)
        ,day(1)
    {}
};
void func3(hxh::shared_ptr<Data> a)
{
    for (int i = 0; i < 10000; i++)
    {
        hxh::shared_ptr<Data> copy(a);
        copy->year++;
        copy->month++;
        copy->day++;
    }
}
int main()
{
    hxh::shared_ptr<Data> a(new Data());
    thread td(func3, std::ref(a));
    thread td1(func3, std::ref(a));
    td.join();
    td1.join();
    cout << a->year << " " << a->month << " " << a->day << endl;
    return 0;
}

image 可以看出shared_ptr中的锁只对shared_ptr类的内部有用,出了外部还需要自己加锁

void func3(hxh::shared_ptr<Data>& a,mutex& mtx)
{
    for (int i = 0; i < 10000; i++)
    {
        hxh::shared_ptr<Data> copy(a);
        unique_lock<mutex> lk(mtx);
        copy->year++;
        copy->month++;
        copy->day++;
    }
}
bosthhe1 commented 1 year ago

关于shared_ptr的垃圾回收的伪代码

class A
{
public:
    A()
    :a(new int)
    ,b(new int)
    {}
    ~A()
    {
        delete a;
        delete b;
    }
    int* a;
    int* b;
};
struct deletefileA
{
    void operator()(A* a)
    {
        cout << "delete" << endl;
        delete a->a;
        delete a->b;
    }
};
int main()
{
    {
        A* a(new A());
        std::shared_ptr<A> sp(a,deletefileA());//shared_ptr传入垃圾回收的匿名对象对底层进行构造一个回收机制的对象,然后实现回收
    }
    return 0;
}

image

bosthhe1 commented 1 year ago

shared_ptr会出现循环引用的问题

    struct ListNode
    {
        int val;
        hxh::shared_ptr<ListNode> next;
        hxh::shared_ptr<ListNode> prev;
        ~ListNode(){ cout << "~ListNode()" << endl; }
    };
}
int main()
{ 
    {
        hxh::shared_ptr<hxh::ListNode> sp1(new hxh::ListNode);
        hxh::shared_ptr<hxh::ListNode> sp2(new hxh::ListNode);
        sp1->next = sp2;
        sp2->prev = sp1;
    }
    return 0;
}

image 我们看到当出了作用域却没有调析构函数 image 我们看到sp1要想析构那么就需要sp2析构,sp2析构那么就要sp1析构,这样构成了一份死循环

bosthhe1 commented 1 year ago

weak_ptr可以解决循环引用的问题,原理就是引用之后不用计数++

struct ListNode
{
    int val;
    std::weak_ptr<ListNode> next;//注意这里是weak_ptr,下面依然是shared_ptr,依然可以赋值,应该weak_ptr为父类,shared_ptr为子类
    std::weak_ptr<ListNode> prev;
    ~ListNode(){ cout << "~ListNode()" << endl; }
};
int main()
{
    {
        std::shared_ptr<ListNode> sp1(new ListNode);
        std::shared_ptr<ListNode> sp2(new ListNode);
        sp1->next = sp2;//weak_ptr这里可以直接引用shared_ptr
        sp2->prev = sp1; 
    }
    return 0;
}

image