栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++智能指针

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++智能指针

智能指针简介

由于使用malloc、new在堆上申请空间后,容易忘记释放对应空间,C++提出了智能指针管理申请的内存,避免内存泄露。
RAII(Resource Acquisition Is Initialization),是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。
借此,我们实际上把管理一份资源的责任托管给了一个对象。这样就不需要显式地释放资源;对象所需的资源在其生命期内始终保持有效。
智能指针就是基于RAII的思想避免内存泄露,本质就是将指针封装为类模板,并通过重载运算符使其具有普通指针的功能。

auto_ptr

C++98引入了auto_ptr,采用管理权转移的思想,也就是原对象拷贝给新对象的时候,原对象就会被设置为nullptr,此时就只有新对象指向一块资源空间。
因为原来的对象被设置为nullptr,再次使用原对象会导致程序崩溃,使用并不方便。

    auto_ptr ptr1(new int(1));
    auto_ptr ptr2(ptr1);  //ptr1被置为nullptr
unique_ptr

既然auto_ptr使用不便,C++11又提出了新的智能指针。
首先是unique_ptr,它采用独占式的思想(和auto_ptr思想有点类似),某个时刻只能有一个unique_ptr指向一个动态分配的资源对象,
也就是这个资源不会被多个unique_ptr对象同时占有,它所管理的资源只能在unique_ptr对象之间进行移动。
它禁用了拷贝构造和赋值,提供move函数用于在unique_ptr对象之间进行移动。
一旦资源脱离unique_ptr(即unique_ptr被重新分配了资源)会被释放,unique_ptr析构时也会释放资源。

    int* a = new int(5);
    int* b = new int(8);
    unique_ptr ptr1(a);

    cout<<*a< 
shared_ptr 

独占式指针并不能满足所有使用场景,多数时候我们都需要多个指针指向同一资源,为了实现共享式的智能指针,C++11提出了shared_ptr。
shared_ptr采用了引用计数的思想,用一个计数指针来计数,当有多个shared_ptr指向同一资源时,他们的计数指针是共享的,释放一个shared_ptr,计数指针减一,
当最后一个shared_ptr被释放时,计数归零,释放资源。

实现shared_ptr
template
class SharedPtr
{
public:
    SharedPtr():ptr(nullptr), ptrCount(nullptr), mt(nullptr)
    {
    }
    SharedPtr(T* _ptr):ptr(_ptr)
    {
        ptrCount = new int(1);
        mt = new mutex;
    }
    //拷贝构造
    SharedPtr(SharedPtr& sPtr):ptr(sPtr.ptr), ptrCount(sPtr.ptrCount), mt(sPtr.mt)
    {
        addCount();
    }
    void addCount()
    {
        mt->lock();
        (*ptrCount)++;
        mt->unlock();
    }
    void release()
    {
        cout<<"release"<lock();
        if(--(*ptrCount) == 0)
        {
            cout<<"delete"<unlock();
        if(delete_flag)
        {
            delete mt;
            mt = nullptr;
        }
    }
    ~SharedPtr()
    {
        release();
    }
    //重载赋值, 1.指向同一地址 2.指向不同地址
    SharedPtr& operator=(const SharedPtr& sPtr)
    {
        if(sPtr.ptr != ptr)
        {
            release();  //先释放原来的
            ptr = sPtr.ptr;
            ptrCount = sPtr.ptrCount;
            mt = sPtr.mt;
            addCount();
        }
        return *this;
    }
    T& operator*()
    {
        return *ptr;
    }
    T* operator->()
    {
        return ptr;
    }
    int getCount()
    {
        return *ptrCount;
    }
private:
    T* ptr;
    int* ptrCount;
    mutex* mt;
};
shared_ptr的循环引用问题

首先看一个可以正常释放的shared_ptr

struct ListNode
{
    shared_ptr next;
};

int main()
{
    shared_ptr node1(new ListNode, [](ListNode* n){
        cout<<"delete node1"< node2(new ListNode, [](ListNode* n){
        cout<<"delete node2"< node3(nullptr, [](ListNode* n){
        cout<<"delete node3"<next = node2;
    //node2->next = node1;
    cout<<"node1的引用计数"< 

如果我们在链表中使用shared_ptr,很容易出现循环引用的问题,如果我们将node1的next指向node2,node2的next指向node1,
那么它们的应用计数都会是2,由于node1和它的next生命周期一样,node2的next不被释放,node1的计数无法减为1,node1无法释放,其next也无法释放,
这导致node2的计数也无法减为1,node2无法释放,其next也无法释放,从而进入了死锁。

int main()
{
    shared_ptr node1(new ListNode, [](ListNode* n){
        cout<<"delete node1"< node2(new ListNode, [](ListNode* n){
        cout<<"delete node2"< node3(nullptr, [](ListNode* n){
        cout<<"delete node3"<next = node2;
    node2->next = node1;
    cout<<"node1的引用计数"< 

要解决这个问题需要借助weak_ptr

weak_ptr

由于shared_ptr存在循环引用问题,C++11提供了weak_ptr服务shared_ptr。
weak_ptr不会增加引用计数,不影响对象的生命周期,C++11标准虽然将 weak_ptr 定位为智能指针的一种,但该类型指针通常不单独使用(没有实际用处),只能和 shared_ptr 类型指针搭配使用。甚至于,我们可以将 weak_ptr 类型指针视为 shared_ptr 指针的一种辅助工具,借助 weak_ptr 类型指针, 我们可以获取 shared_ptr 指针的一些状态信息,比如有多少指向相同的 shared_ptr 指针、shared_ptr 指针指向的堆内存是否已经被释放等等。
weak_ptr 模板类中没有重载 * 和 -> 运算符,这也就意味着,weak_ptr 类型指针只能访问所指的堆内存,而无法修改它。
由于weak_ptr指向的对象可能被释放,所以提供了expired()函数来判断所指对象是否已经被销毁。
将前面循环引用问题结构体内改为weak_ptr即可解决。

struct ListNode
{
    weak_ptr next;
};
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/955231.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号