通俗易懂学习C++智能指针

智能指针是干什么的?

智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏

什么是内存泄漏?

内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而
造成了内存的浪费。

内存泄漏有什么危害?

长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死

智能指针的原理

对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源

我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:

  • 不需要显式地释放资源。
  • 采用这种方式,对象所需的资源在其生命期内始终保持有效。
智能指针的原理:
  1. RAII特性(是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术)
  2. 重载operator*和opertaor->,具有像指针一样的行为。

auto_ptr指针

auto_ptr智能指针的缺点:当对象拷贝或者赋值后,前面的对象就悬空了

auto_ptr智能指针的模拟实现
template<class T>
class Auto_ptr{
public:
	Auto_ptr(T* ptr=nullptr):_ptr(ptr){}
	~Auto_ptr(){
		if (_ptr)
		delete _ptr;
	}
	Auto_ptr(Auto_ptr<T> &s)//拷贝构造会释放s对象的指针,造成s指针与管理对象断开联系
		:_ptr (s._ptr){
		s._ptr = NULL;
	}
	Auto_ptr<T>& operator=(Auto_ptr<T>& s){
		if (this != &s._ptr){
			if (_ptr)
				delete _ptr;
			_ptr = s._ptr;
			s._ptr = NULL;
		}
		return *this;
	}
	T& operator*(){
		return *_ptr;
	}
	T* operator->(){
		return _ptr;
	}
private:
	T* _ptr;
};

unique_ptr指针

unique_ptr的实现原理:简单粗暴的防拷贝,下面简化模拟实现了一份UniquePtr来了解它的原理

template<class T>
class Unique_ptr{
public:
	Unique_ptr(T* ptr=nullptr):_ptr(ptr){}
	~Unique_ptr(){
		if (_ptr)
		delete _ptr;
	}
	T& operator*(){
		return *_ptr;
	}
	T* operator->(){
		return _ptr;
	}
private:
	Unique_ptr(Unique_ptr<T> &s)
		:_ptr(s._ptr){
		s._ptr = NULL;
	}
	Unique_ptr<T>& operator=(Unique_ptr<T>& s){
		if (this != &s._ptr){
			if (_ptr)
				delete _ptr;
			_ptr = s._ptr;
			s._ptr = NULL;
		}
		return *this;
	}
private:
	T* _ptr;
};

shared_ptr指针

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

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

template<class T>
class Shared_ptr{
public:
	Shared_ptr(T* ptr) 
		:_ptr(ptr),_pMutex(new mutex),_pRefrCount(new int(1))
	{}
	~Shared_ptr(){
		Realse();
	}
	Shared_ptr(const Shared_ptr<T>& sp)
		:_ptr(sp._ptr), _pRefrCount(sp._pRefCount), _pMutex(sp._pMutex)
	{
		AddRefCount();
	}
	Shared_ptr<T>* operator=(const Shared_ptr<T>& sp){
		if (this != sp){
			Realse();
			_ptr = sp._ptr;
			_pRefrCount = sp._pRefrCount;
			_pMutex = sp._pMutex;
			AddRefCount();
		}
		return this;
	}
	T& operator*(){ return *_ptr; }
	T* operator->(){ return _ptr; }

	int UseCount(){ return *_pRefrCount; }
	T* Get(){ return _ptr; }
	void AddRefCount(){
		_pMutex.lock();
		++(*_pRefrCount);
		_pMutex.unlock();
	}
private:
	void Realse(){
		bool flag = false;
		_pMutex.lock();
		if (--(*_pRefrCount) == 0){
			delete _pRefCount;
			delete _ptr;
			flag = true;
		}
		_pMutex.unlock();
		if (flag == true);
		delete _pMutex;
	}
private:
	T* _ptr;
	int* _pRefrCount;
	mutex _pMutex;
};

weak_ptr指针

weak_ptr是配合shared_ptr而引入的一种智能指针来协助shared_ptr工作,它可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起引用记数的增加或减少。没有重载*和->但可以使用lock获得一个可用的shared_ptr对象。

实例:

#include<iostream>
#include<memory>
using namespace std;
struct ListNode
{
	int _data;
	weak_ptr<ListNode> _prev;
	weak_ptr<ListNode> _next;
	~ListNode(){ cout << "~ListNode()" << endl; }
};
int main()
{
	shared_ptr<ListNode> node1(new ListNode);
	shared_ptr<ListNode> node2(new ListNode);
	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;
	node1->_next = node2;
	node2->_prev = node1;
	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;
	system("pause");
	return 0;
}

在这里插入图片描述
总结:

  • 智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏
  • auto_ptr智能指针:当对象拷贝或者赋值后,前面的对象就悬空了。
  • unique_ptr智能指针:防止智能指针拷贝和复制。
  • shared_ptr智能指针:通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
  • weak_ptr智能指针:可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起引用记数的增加或减少。
  • 对shared_ptr进行初始化时不能将一个普通指针直接赋值给智能指针,因为一个是指针,一个是类。可以通过make_shared函数或者通过构造函数传入普通指针。并可以通过get函数获得普通指针。

猜你喜欢

转载自blog.csdn.net/weixin_44826356/article/details/107540457