关于auto_ptr/unique_ptr/shared_ptr

问题引出

因为C++ 中没有垃圾回收机制, 在用new完对象后很容易忘记delete这个对象,从而造成内存泄漏。而利用在函数栈退出时栈上的对象会被释放这个特征, 由此可以联想到用一个其他的对象将new出的资源进行管理, 只要函数栈跳出, 那么这个对象的析构函数顺带将其管理的内存释放掉, 从而组织内存泄漏

RAll 思想

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

};
 

但是RAII 不能称为智能指针因为还不具备指针的操作

利用RAII的思想 然后再重载 * -> 使之具有指针的操作那么就是所谓的可以管理资源的智能指针了;

但是 : 有个新的问题 智能指针是一个类的封装, 实例对象的时候会有拷贝构造函数, 那么就有可能为多个对象同时管理一个资源的情况, 这样再智能指针对象析构的时候, 每个对象都会对目标区域析构一次,从而造成多次释放导致程序崩溃.
注意: 注意if判空是对自己指针值判空,目标区域释放没有释放不清楚;

auto_ptr

auto_ptr : 1) 通过移交管理权的思想, 将资源转移到一个新的auto_ptr 的对象上, 同时将原对象的指针置为nullptr;
2) 在发生赋值的时候将原管理的对象进行释放, 同时获得新的资源

实现原理如下:

#pragma once

template <class T>
class Auto_ptr
{
private:
	T* _ptr;

public:
	Auto_ptr(T* ptr = nullptr)
		:_ptr(ptr)
	{}

	~Auto_ptr()  
	{
		if (_ptr)
		{
			delete _ptr;
			_ptr = nullptr;
		}
	}

	Auto_ptr(Auto_ptr& ptr)
		:_ptr(ptr._ptr)
	{
		ptr._ptr = nullptr;
	}

	Auto_ptr & operator = (Auto_ptr& ptr) // 释放旧资源获得新资源
	{
		if (this != &ptr)
		{
			if (_ptr)
			{
				delete _ptr;
			}
			_ptr = ptr._ptr;
			ptr._ptr = nullptr;
		}
		return *this;
	}
	
	T & operator * ()
	{
		return *_ptr;
	}

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

测试代码:

#include <iostream>
#include "auto_ptr.h"
#include <string>
using namespace std;

string* test()
{
	string* a = new string("hello world");
	
	cout << *a<< endl;
	
	Auto_ptr<string> p(a);

	cout << *p << endl;

	Auto_ptr<string> p2(p);

//	cout << *p << endl;    // 此句代码崩溃 因为p中的指针以及被置为空
	p = p2;
//	cout << *p2 << endl;  // 此句代码崩溃因为p2的资源已经交给了p;
	cout << *p << endl;
	return a;
}

void test1() 
{
	int a = 1;
	int b = 2;
	a = a + b;
}

int main()
{
	string* a =test();
	test1();
    std::cout << *a; // 在使用智能指针后 *a 无法打印 因为在栈退出的时候会释放Auto_ptr 的对象
	                 // 在析构的时候顺带把 堆上的内存也析构调了.
}

注意: 现在再从实现原理层来分析会发现,这里拷贝后把p2对象的指针赋空了,导致p2对象悬空 // 通过p2对象访问资源时就会出现问题。
在这里插入图片描述
在main函数中“hello world” 没有打印出来, 因为资源已经被auto_ptr 的对象释放

unique_ptr

通过将拷贝构造函数和 = 的重载全部设置为私有c++98, 或者直接将其删除c++11
简单实现如下:

template <class T>
class Unique_ptr
{
private:
	T* _str;

public:
	Unique_ptr(T* str = nullptr)
		:_str(str)
	{}

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

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

	Unique_ptr & operator = (Unique_ptr<T> & ptr) = delete;
	Unique_ptr(Unique_ptr<T>& ptr) = delete;
};

使用C++11自带的unique_ptr 可以看到这个函数已经被删除
在这里插入图片描述
方式十分暴力, 直接删除了,但是有时候有需要进行拷贝, 或者赋值 有没有更加优雅的办法呢?

shared_ptr

shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源

  1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
  2. 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
  3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
  4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了

template <class T>
class Shared_ptr {
private:
	T* _ptr;
	int* _count;
	mutex _pMutex;


	void add_count()
	{
			_pMutex->lock();
			*_count++;
			_pMutex->unlock();
	}

	void Release()
	{
		bool detection = false;
		_pMutex->lock();
		_count--;
		if (_count == 0)
		{
			delete _ptr;
			delete _count;
			detection = true;
		}
		_pMutex->unlock();
		
		if (detection == true)
			delete _pMutex;
	}

public:
	Shared_ptr(T* ptr = nullptr)
		:_ptr(ptr)
		, _count(new int(1))
		, _pMutex(new mutex)

	{}

	~Shared_ptr()
	{
		Release();
	}

	Shared_ptr(Shared_ptr<T>& ptr)
	{
		_ptr = ptr._ptr;
		_count = ptr._count;
		_pMutex = ptr._pMutex;
		add_count();
	}
	
	Shared_ptr & operator = (Shared_ptr<T>& ptr)
	{
		if (this != &ptr)
		{
			Release();
			_ptr = ptr;
			_count = ptr._count;
			_pMutex = ptr._pMutex;
			add_count();
		}
		return *this;
	}

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

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

亮点:通过在堆上申请内存, 然后在拷贝构造函数和 = 的重载中将同一份资源的数量联系在一起, 只有当内部的计数为0的时候才进行释放, 有了拷贝和赋值整个指针用起来更加的灵活

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值