【c++】智能指针

在这里插入图片描述


📄前言

在C++编程中,因为各种栈溢出、堆内存泄露、野指针等问题,C++程序员对内存的管理可谓是家常便饭,但即使如此,我们也会犯错,然后被那一大堆难以跟踪的错误打个猝不及防。于是乎,C++11引入了智能指针,其能够自动管理指针的生命周期,不再需要我们手动释放,简化了内存管理的复杂性。

🌺智能指针

概念

智能指针是C++为了实现RAII(Resource Acquisition Is Initialization)模式——利用对象生命周期来控制程序资源的技术而设计的。

智能指针比起传统的指针拥有更多的功能,智能指针将传统指针和各种自动化管理函数封装在一个类当中,从而实现RAII。

智能指针的类型

c++11标准库提供了三种智能指针,它们为:"unique_ptr "、“shared_ptr”、“weak_ptr”,在 C++98 也有着智能指针 auto_ptr(已废弃),但因为其存在极其明显的设计缺陷,所以在C++11中已经被废弃 。

  • auto_ptr :是C++标准库引入的第一个指针,实现了 RAII 的基础功能,但其设计不能允许同时被多个 auto_ptr 引用,而是把对指针所有权直接转移到另一个指针,导致指针被悬空。一个为了安全而制作的类,变成了另一个危险,所以它在C++11被废弃。

  • unique_ptr :是用于代替 auto_pt r的指针,其原理就是独占指针、防止被拷贝,利用类的生命周期来销毁指针。

  • shared_ptr:比起 unique_ptr,shared_ptr 可以被多个 shared_ptr 对象引用,通过引用计数来管理指针,只有对象所有的实例都被销毁时才将指针销毁。shared_ptr 的引用计数机制是线程安全的,但指向的资源不是。

  • weak_ptr:是一种没有管理权的智能指针,可用于”观察“对象,而不对它的生命周期进行管理。除此之外,它最重要的用途就是解决 shared_ptr 的循环访问问题(下面的代码将会展示)。

智能指针的使用

  • auto_ptr 的使用:
void func()
{
    auto_ptr<int> ptr1(new int);
    auto_ptr<int> ptr2(ptr1);   //指针被转移到了ptr2

    *ptr2 = 10;     
    cout << *ptr2 << endl;
    cout << *ptr1 << endl;  // ptr1悬空,成为空指针
    //出现异常。
}
  • unique_ptr 的使用:
void func()
{
    int* arr = new int;
    unique_ptr<int> ptr(arr);
    *ptr = INT_MAX; //像指针一样访问数据。
    
    // unique_ptr<int> ptr2(ptr); unique_ptr 禁止指针被拷贝
    //离开作用域自动销毁
}
  • shared_ptr 的使用:
struct Arrary
{
    int n;
    shared_ptr<int> arr;
    Arrary() { cout << "Arrary Create" << endl; }
    ~Arrary() { cout << "Arrary delete" << endl; }
};

void func()
{
    shared_ptr<Arrary> ptr;
    {   
        shared_ptr<Arrary> vec(new Arrary);
        shared_ptr<Arrary> vec2 = make_shared<Arrary>();
        vec->arr = make_shared<int>(50);
        ptr = vec;
    }
    cout << "-------------------" << endl;
}   //函数结束时,所有的指针被销毁

// 结果
// Arrary Create
// Arrary Create
// Arrary delete
// -------------------
// Arrary delete
  • shared_ptr 的循环引用 :
// shared_ptr 的循环引用
class B;

class A 
{
public:
    A() { cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }

    shared_ptr<B> ptr;
};

class B
{
public:
    B() { cout << "B()" << endl; }
    ~B() { cout << "~B()" << endl; }

    shared_ptr<A> ptr;
};

void function()
{
    {
    	//make_shared 能提高创建内存的效率
        auto a = make_shared<A>();	// A的计数++
        auto b = make_shared<B>();	// B的计算++
        a->ptr = b;		// A++
        b->ptr = a;		// B++ 
        //此时 A B 指针的引用都为2; 
    }
    // a b 实例被销毁, A-- , B--; 
    // 但a 与 b 类内的ptr还指向着他们,等待着对方销毁。 
    // 他们的引用计数永远都不会降到零,导致内存泄漏 
    cout << "-----------------" << endl; 
}

// 结果
// A()
// B()
// -----------------
	
//	  cout: 1					  	   cout: 1
//   +-----------+    shared_ptr     +-----------+ 
//   |     A     | ----------------> |     B     | 
//   |-----------|                   |-----------|
//   |    ptr    |                   |    ptr    | 
//   +-----------+                   +-----------+
  • weak_ptr 的使用:
// weak_ptr 是用于打破循环引用而制作出来的产物
// weak_ptr 不会增加shared_ptr的引用计数

class B;

class A 
{
public:
    A() { cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }

    weak_ptr<B> ptr;	//  更换为weak_ptr
};

class B
{
public:
    B() { cout << "B()" << endl; }
    ~B() { cout << "~B()" << endl; }

    weak_ptr<A> ptr;
};

void function()
{
    {
        auto a = make_shared<A>();
        auto b = make_shared<B>();
        a->ptr = b;
        b->ptr = a;
    }
    cout << "-----------------" << endl;
}
//结果
//  A()
//  B()
// ~B()
// ~A()
// -----------------

🌻智能指针的简易实现

auto_ptr 的实现

auto _ptr 尽管已经被废弃,并且许多公司也禁止使用它,但自己动手来实现一下它,也能帮助你了解 unique_ptr 相对于它的改进。

template <class T>
class auto_ptr
{
private:
    T* _ptr;
public:
    explicit auto_ptr(T* ptr = nullptr)
        :_ptr(ptr) 
    {}	//使用explicit防止其被隐式转换

    ~auto_ptr() 
    {
        if(_ptr)
            delete _ptr; 
    }

    auto_ptr(auto_ptr<T>& sp)
        :_ptr(sp._ptr)
        {
            sp._ptr = nullptr;
        }

    auto_ptr<T>& operator=(auto_ptr<T>& sp)
    {
        if(this == &sp) return *this;

        if(_ptr)
            delete _ptr;

        _ptr = sp._ptr;
        sp._ptr = nullptr;

        return *this;
    }
		// 像指针一样使用
    T& operator*() const { return *_ptr; }

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

unique_ptr 的实现

unique_ptr 是对auto_ptr的改进,所以和auto_ptr有不少相识之处,unique_ptr 禁止自己管理的资源(指针)被转移。

template<class T>
class unique_ptr
{
public:
    explicit unique_ptr(T* ptr = nullptr) 	
        :_ptr(ptr)
    {}
    
    ~unique_ptr() 
    {
        if(_ptr)
            delete _ptr;
    }

    unique_ptr(const unique_ptr<T>& ) = delete;	//直接删除拷贝函数与赋值函数,防止资源被转移
    unique_ptr<T>& operator=(const unique_ptr<T>&) = delete;

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

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

shared_ptr 的使用

shared_ptr 使用计数机制来管理指针的生命周期,当计数为0时销毁指针,并且保证了计数功能的线程安全。

    template <class T>
    class shared_ptr
    {
    public:
        explicit shared_ptr(T* p = nullptr)
            :_ptr(p), _count(new int(1)), _mtx(new std::mutex)
        {
            std::cout << "shared_ptr(T*), count=" << *_count << std::endl;
        }
        
        shared_ptr(const shared_ptr& sp)
            :_ptr(sp._ptr), _count(sp._count), _mtx(sp._mtx)
        {
            AddRef();
            std::cout << "shared_ptr(shared_ptr&), count=" << *_count << std::endl;
        }

        void AddRef()
        {
            std::lock_guard<std::mutex> lock(*_mtx);
            ++(*_count);
        }

        shared_ptr<T>& operator=(const shared_ptr<T>& sp)
        {
            if(_ptr != sp._ptr)
            {
                release();
                _ptr = sp._ptr;
                _count = sp._count;
                _mtx = sp._mtx;
                AddRef();
            }
            std::cout << "operator=()" << std::endl;

            return *this;
        }

        ~shared_ptr()
        {
            release();
        }

        T* get() const 
        {
            return _ptr;
        }

        int count() const { return *_count; }

        void  release()
        {
            bool flag = false;

            {	//使用{}来限定作用域
                std::lock_guard<std::mutex> lock(*_mtx);	//锁住线程,并利用RAII机制自动销毁锁	
                if(--(*_count) == 0 && _ptr)
                {
                    delete _ptr;
                    delete _count;
                    flag = true;
                }
            }	//自动销毁锁

            std::cout << "relese()" << std::endl;

            if(flag) delete _mtx;	//释放锁的资源
        }

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

        T* operator->() const { return _ptr; }
    private:
        int* _count;	//计数指针
        std::mutex* _mtx;	//线程锁
        T* _ptr;	// 指针
    };

📓总结

智能指针是为了实现RAII而设计的产物,智能指针包括 “unique_ptr”、 “shared_ptr”、 “weak_ptr”,它们提供了一种安全、高效的方式来管理内存,减少了程序出错的概率,也许你在内存中看不到自动化管理的重要性,但总有一天,你会接触到多线程的管理,此时RAII便会烙入你的心中。

📜博客主页:主页
📫我的专栏:C++
📱我的github:github

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值