智能指针

目录

RAII

auto_ptr

unique_ptr

shared_ptr

shared_ptr的循环引用

weak_ptr

删除器


智能指针的出现主要是针对程序的资源泄露问题而产生的。

RAII

RAII(Resource Acquisition Is Initialization)是种利用对象生命周期来控制程序资源的简单技术。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处: 1、不需要显式地释放资源。 2、采用这种方式,对象所需的资源在其生命期内始终保持有效。

智能指针还应具有指针的行为,因此还需要重载*和->

template
class SmartPtr{
public:
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}
	~SmartPtr(){
		cout << "delete:" << _ptr << endl;
		delete _ptr;
	}
	T& operator*(){
		return *_ptr;
	}
	T* operator->(){
		return _ptr;
	}
private:
	T* _ptr;
};

总结只能指针的原理:1、具有RAII特性2、重载operation*和operation->,具有指针一样的行为

auto_ptr

C++98版本的库中就提供了auto_ptr的智能指针。auto_ptr的实现原理:管理权转移的思想。下边是对auto_ptr的模拟

template
	class auto_ptr {
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}
		auto_ptr( auto_ptr& ap):_ptr(ap._ptr) {
			ap._ptr = nullptr;
		}
		auto_ptr& operator=(auto_ptr& ap) {
			if (this != &ap) {
				if (_ptr) {
					delete _ptr;
				}
				_ptr = ap._ptr;
				ap._ptr = nullptr;
			}
			return *this;
		}
		~auto_ptr() {
			cout << "delete:" << _ptr << endl;
			delete _ptr;
		}
		T& operator*() {
			return *_ptr;
		}
		T* operator->() {
			return _ptr;
		}
	private:
		T* _ptr;
	};

 上边的ap1中的资源经过拷贝构造后,ap1的资源转移到了ap2去管理了。

unique_ptr

unique_ptr的实现原理:简单粗暴的防拷贝。下边是对unique_ptr的模拟

template
	class unique_ptr {
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}
		//防拷贝 C++11   
		unique_ptr(unique_ptr& ap) = delete;
		unique_ptr& operator=(unique_ptr& ap) = delete;

		~unique_ptr() {
			cout << "delete:" << _ptr << endl;
			delete _ptr;
		}
		T& operator*() {
			return *_ptr;
		}
		T* operator->() {
			return _ptr;
		}
	private:
		T* _ptr;
	};

将拷贝构造和赋值重载进行了禁用。

shared_ptr

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

template
	struct Delete {
		void operator()(T* ptr) {
			cout << "delete" << endl;
			delete ptr;
		}
	};
	template>
	class shared_ptr{
	public:
		shared_ptr(T* ptr = nullptr)
			: _ptr(ptr)
			, _pCount(new int(1))
		{}

		void Release(){
			if (--(*_pCount) == 0){
				//cout << "Delete:" << _ptr << endl;
				//delete _ptr;

				D()(_ptr);
				delete _pCount;
			}
		}

		~shared_ptr(){
			Release();
		}

		// sp1(sp2)
		shared_ptr(const shared_ptr& sp)
			: _ptr(sp._ptr)
			, _pCount(sp._pCount){
			(*_pCount)++;
		}

		// sp1 = sp5
		// sp1 = sp1
		shared_ptr& operator=(const shared_ptr& sp){
			//if (this == &sp)
			if (_ptr == sp._ptr){
				return *this;
			}

			// 减减被赋值对象的计数,如果是最后一个对象,要释放资源
			Release();

			// 共管新资源,++计数
			_ptr = sp._ptr;
			_pCount = sp._pCount;

			(*_pCount)++;

			return *this;
		}

		T& operator*(){
			return *_ptr;
		}
		T* operator->(){
			return _ptr;
		}
		int use_count() {
			return *_pCount;
		}
		T* get() const{
			return _ptr;
		}
	private:
		T* _ptr;
		int* _pCount;// 引用计数
	};

智能指针_第1张图片

 我们可以观察到sp1的资源由2个对象(sp1、sp2)共享。sp3的资源仅由自己管理。sp4的资源由2个对象(sp4、sp5)共享。

shared_ptr的循环引用

struct Node{
	int _val;
	hzp::shared_ptr _next;
	hzp::shared_ptr _prev;

	~Node(){
		cout << "~Node" << endl;
	}
};

void test_weak_ptr(){
	std::shared_ptr n1(new Node);
	std::shared_ptr n2(new Node);

	n1->_next = n2;
	n2->_prev = n1;
}

上边的代码逻辑可以参考下图

智能指针_第2张图片

待函数结束时,n2先析构,n1再析构,引用计数各自减至1。但是函数结束了,按理应该销毁,引用计数应该为0。但是左边中的_next指向的右边,右边的_prev指向的左边。二者谁也不让谁,就导致了循环引用的问题。 

weak_ptr

shared_ptr的循环引用问题的解决引出了weak_ptr。weak_ptr和特点是不增加引用计数,是一个辅助型的智能指针。

_next和_prev是weak_ptr的时候,其不参与资源释放管理,可以访问和修改资源,但是不增加计数,就不存在循环引用问题了。

下边是weak_ptr的模拟

// 辅助型智能指针,使命配合解决shared_ptr循环引用问题
	template
	class weak_ptr{
	public:
		weak_ptr()
			:_ptr(nullptr)
		{}

		weak_ptr(const shared_ptr& sp)
			:_ptr(sp.get())
		{}

		weak_ptr(const weak_ptr& wp)
			:_ptr(wp._ptr)
		{}

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

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

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

删除器

首先我们需要知道new和malloc的关系,delete和free的关系。比如new int[5],其中new的过程中包括malloc+5次构造函数,delete[]的过程包括free+5次析构函数。

但是new的过程中,编译器能通过代码中的[ ]得知该使用几次构造函数,而对应的delete[]是如何知道使用几次析构函数的呢?

下边可以参考vs平台下的机制。

智能指针_第3张图片

delete[]的ptr的前4个字节中存放了应该使用几次析构函数的次数。也就是在delete[]的时候,ptr并不是delete的起始位置,而是(char*)ptr-4的位置。 如果只是delete的话,起始位置就是ptr。

智能指针_第4张图片

void test_deleter(){
	// 仿函数对象
	std::shared_ptr n1(new Node[5], DeleteArray());
	std::shared_ptr n2(new Node);

	std::shared_ptr n3(new int[5], DeleteArray());

	std::shared_ptr n4((int*)malloc(sizeof(12)), Free());
}

你可能感兴趣的:(C++,c++,开发语言)