自学内容网 自学内容网

智能指针原理

智能指针

为什么需要智能指针?

在C++中new一个对象以后,需要手动的释放这块空间,这样会触发很多情况,导致不能被释放。

int mian()
{
    pair<string,string>* p1 = new pair<string,string>;
    func();
    delete p1;
    return 0;
}

假如这里的func()函数抛出异常,那么delete不能够执行到位,会造成内存泄漏。

RAII

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:不需要显式地释放资源。采用这种方式,对象所需的资源在其生命期内始终保持有效。

第一个版本的智能指针

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;
template <class T>
class SmartPtr
{
public:
SmartPtr(T* ptr)
:_ptr(ptr)
{

}

T& operator*()
{
return *_ptr;
}

T* operator->()
{
return _ptr;
}

private:
T* _ptr;
};


int main()
{
SmartPtr<string> p1(new string("xxxxx"));
SmartPtr<string> p2(new string("yyyyyy"));
p1 = p2;
return 0;
}

这个情况下,p1的指针指向p2的空间,造成p1的空间泄露,p2指向的空间不仅有p2指向,现在还有p1指向,会导致free两次。

auto_ptr,管理权转移

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<memory>
using namespace std;
class A
{
public: 
A(int a = 1)
:_a(a)
{
cout << "A(int a = 1)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
//private:
int _a;
};
int main()
{
auto_ptr<A> p1(new A(1));
auto_ptr<A> p2(new A(2));
//auto_ptr 会将管理权转移,拷贝时,会把拷贝对象的资源管理权转移给被拷贝的对象
//导致被拷贝对象悬空,访问就会出现问题
auto_ptr<A> p3 = p1; //这里的p1就悬空了
//崩溃
p1->_a++;
p3->_a++;
return 0;
}

unique_ptr,不允许拷贝

template<class T>
class unique_ptr
{
public:
unique_ptr(T* ptr)
:_ptr(ptr)
{

}
//防止拷贝
unique_ptr(unique_ptr<T>& ptr) = delete;
unique_ptr<T> operator=(unique_ptr<T>& ptr) = delete;
    
T& operator*()
{
return *_ptr;
}

T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};

shared_ptr,引用计数

#include <iostream>
using namespace std;

template<class T>
class shared_ptr
{
public:
    shared_ptr(T* ptr)
        : _ptr(ptr)
        , _pcount(new int(1))
    {
    }
    
    ~shared_ptr()
    {
        if (--(*_pcount) == 0)
        {
            std::cout << "delete " << _ptr << std::endl;
            delete _ptr;
            delete _pcount;
        }
    }

    shared_ptr(const shared_ptr<T>& ptr)
        : _ptr(ptr._ptr)//同一个类的实例可以互相访问私有变量
        , _pcount(ptr._pcount)
    {
        (*_pcount)++;
    }

    shared_ptr<T>& operator=(const shared_ptr<T>& ptr)
    {
        if (this == &ptr)
        {
            return *this;
        }

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

        _ptr = ptr._ptr;
        _pcount = ptr._pcount;
        ++(*_pcount);

        return *this;
    }

    T& operator*()
    {
        return *_ptr;
    }

    T* operator->()
    {
        return _ptr;
    }

    T* get()
    {
        return _ptr;
    }

private:
    T* _ptr;
    int* _pcount;
};

循环引用

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<memory>
using namespace std;
class A
{
public: 
A(int a = 1)
:_a(a)
{
cout << "A(int a = 1)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
struct Node
{
A _val;
shared_ptr<Node> prev;
shared_ptr<Node> next;
};
int main()
{
shared_ptr<Node> sp1(new Node);
shared_ptr<Node> sp2(new Node);
sp1->next = sp2;
sp2->prev = sp1;
return 0;
}
  • Node 结构体包含了两个 sh ared_ptr<Node> 成员:prevnext,它们分别指向前一个和后一个节点。

  • shared_ptr 是一种智能指针,使用引用计数来管理对象的生命周期。当一个新的 shared_ptr 指向某个对象时,该对象的引用计数增加;当一个 shared_ptr 被销毁或指向其他对象时,引用计数减少。

  • sp1sp2 分别是两个 shared_ptr<Node> 对象,指向两个不同的 Node 实例。

  • 然后,sp1->next 被设置为 sp2sp2->prev 被设置为 sp1,这就导致了 sp1sp2 互相引用。

循环引用的产生

  1. sp1 引用 sp2

    • sp1->next = sp2 执行后,sp1 中的 next 指向了 sp2,这意味着 sp2 的引用计数增加了 1。
  2. sp2 引用 sp1

    • sp2->prev = sp1 执行后,sp2 中的 prev 指向了 sp1,这意味着 sp1 的引用计数也增加了 1。

由于 sp1sp2 互相引用对方,形成了一个循环引用。

循环引用的后果

main 函数结束时,sp1sp2 都应该被销毁,然而,由于它们互相引用,两个 shared_ptr 的引用计数都不会降到 0,因此内存不会被释放。

  • sp1 持有一个指向 sp2shared_ptr,这使得 sp2 的引用计数永远不会降到 0。
  • 同样,sp2 持有一个指向 sp1shared_ptr,使得 sp1 的引用计数也永远不会降到 0。

这种情况下,即使 sp1sp2 超出了作用域并且 main 函数结束了,它们所指向的 Node 对象依然无法被销毁,导致内存泄漏。

weak_ptr,不是RAII智能指针,专门解决循环引用问题

原理:不增加引用计数,不参与资源的释放管理,可以访问资源。

template<class T>
class weak_ptr
{
public:
weak_ptr(T* ptr = nullptr)
:_ptr(ptr)
{

}

weak_ptr(const shared_ptr<T>& sp)
:_ptr(sp.get())
{

}

weak_ptr<T>& operator=(const shared_ptr<T>& sp)
{
_ptr = sp.get();
return *this;
}

T& operator*()
{
return *_ptr;
}

T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};

原文地址:https://blog.csdn.net/nongcha_ice/article/details/142705462

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!