智能指针是现代C++的关键,它能帮助自动管理内存、避免内存泄漏、提升代码安全性和可维护性。在C++98中就提出过auto_ptr,但是实际应用是收到市场的广泛投诉,在项目中几乎被禁用,后面C++11引入三大核心智能指针:unique_ptr、shared_ptr和weak_ptr。

        智能指针的核心思想是RAII(Resource Acquisition Is Initialization),即资源的生命周期由对象的生命周期管理——对象销毁时自动释放资源。指针指针不是指针,但是要能像指针一样操作。

一、auto_ptr

我们简单实现一下auto_ptr:

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

从代码中不难看出auto_ptr是存在巨大问题,其就体现在拷贝构造和赋值拷贝都会导致被拷贝的智能指针置空,这一点就这违反了 C++ 的基本约定:拷贝操作应该"值语义"——拷贝后两个对象等价且独立。但 auto_ptr 的拷贝会让源对象失效,极易引发 bug。而进一步就不能用于STL容器,因为 STL 容器(如 vector)在内部会进行拷贝操作(例如扩容、排序),而 auto_ptr 的拷贝会清空原对象,导致容器中出现大量 nullptr!所以auto_ptr被抛弃了。

二、unique_ptr

这是一个简易版的unique_ptr:

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

	template<class T>
	class unique_ptr
	{
	public:
		unique_ptr()
			:_ptr(nullptr)
		{}
		unique_ptr(const unique_ptr<T>& up) = delete;
		unique_ptr(unique_ptr<T>&& up)
			:_ptr(up._ptr)
		{
			up._ptr = nullptr;
		}
		unique_ptr(T* p)
			:_ptr(p)
		{}
		~unique_ptr()
		{
			delete _ptr;
			_ptr = nullptr;
		}
		void operator=(unique_ptr<T>& up) = delete;
		unique_ptr& operator=(unique_ptr<T>&& up) noexcept
		{
			if (this != &up) { // 防止自移动(虽罕见,但安全)
				delete _ptr;       // 先释放当前资源
				_ptr = up._ptr;    // 接管新资源
				up._ptr = nullptr; // 源对象置空
			}
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		T* get()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};
}

从代码中可以看到,unique_ptr禁用了拷贝构造和拷贝赋值。

特性 说明
✅ 独占所有权 同一时间只有一个 unique_ptr 拥有该资源
✅ 自动释放 离开作用域时自动调用 delete(或自定义删除器)
✅ 零开销 编译后性能 ≈ 原始指针(无引用计数)
❌ 不可拷贝 禁止拷贝构造和拷贝赋值(防止意外共享)
✅ 可移动 支持移动语义(std::move),安全转移所有权
✅ 异常安全 即使发生异常,也能保证资源释放

三、shared_ptr和weak_ptr

  • 多个 shared_ptr 可共享同一对象
  • 内部维护引用计数
  • 当最后一个 shared_ptr 销毁时,自动释放资源
	template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr),
			_pcounter(ptr ? new size_t(1) : nullptr)
		{}
		shared_ptr(shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pcounter(sp._pcounter)
		{
			++(*_pcounter);
		}

		shared_ptr(shared_ptr<T>&& sp) noexcept
			: _ptr(sp._ptr), _pcounter(sp._pcounter)
		{
			sp._ptr = nullptr;
			sp._pcounter = nullptr;
		}

		~shared_ptr()
		{
			if (_pcounter && --(*_pcounter) == 0) {
				delete _ptr;
				delete _pcounter; 
			}
		}

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

		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			if (this != &sp)
			{
				if (sp._pcounter) ++(*sp._pcounter);
				if (_pcounter && --(*_pcounter)==0)
				{
					delete _ptr;
					delete _pcounter;
				}
				_ptr = sp._ptr;
				_pcounter = sp._pcounter;
			}
			return *this;
		}

		shared_ptr<T>& operator=(shared_ptr<T>&& sp)
		{
			if (this != &sp)
			{
				if (_pcounter && --(*_pcounter) == 0)
				{
					delete _ptr;
					delete _pcounter;
				}
				_ptr = sp._ptr;
				_pcounter = sp._pcounter;
				sp._ptr = nullptr;
				sp._pcounter = nullptr;
			}
			return *this;
		}

		size_t use_count() const { return _pcounter ? *_pcounter : 0; }
	private:
		T* _ptr;
		size_t* _pcounter;
	};

但是shared_ptr还存在一个问题,即当存在循环引用的时候就会资源泄露,代码如下:

class Node {
public:
	Node(int v = 0)
		:val(v)
		, next(nullptr)
		, pre(nullptr)
	{}
	~Node()
	{
		std::cout << "~Node:" << this << std::endl;
	}
	int val;
	pl::shared_ptr<Node> next;
	pl::shared_ptr<Node> pre;
};

int main()
{
	pl::shared_ptr<Node> sp1(new Node(1));
	pl::shared_ptr<Node> sp2(new Node(2));
	sp1->next = sp2;
	sp2->pre = sp1;
	return 0;
}

运行结果如下,发现并没有调用析构函数,即资源泄露了。

这是因为Node中还存在shared_ptr,导致引用计数器一直不能清零,导致资源泄露。

针对shared_ptr的循环引用导致的资源泄露就提出了weak_ptr。weak_ptr与shared_ptr的区别就是weak_ptr没有了引用计数器,即weak_ptr只有资源的使用权没有资源的管理权。

增添weak_ptr后,就不再使用_pcounter来记录引用数量,而是使用控制块:

	template<class T>
	struct ControlBlock {
		ControlBlock(T* p):
			ptr(p),
			shared_count(1),
			weak_count(0)
		{}

		T* ptr;
		size_t shared_count;
		size_t weak_count;
	};

控制块原理如下:ptr指向数据,shared_count用于记录shared_ptr指向该数据的数量,weak_count用于记录weak_ptr指向该数据的数量。只有shared_ptr才有删除ptr的权限,即shared_count==0时,可以delete ptr;shared_ptr和weak_ptr都有权限删除控制块,即shared_count==0&&weak_count==0时,shared_ptr和weak_ptr都有权限删除控制块。

简易代码如下:

	template<class T>
	class shared_ptr {
	public:
		shared_ptr(T* p = nullptr) :_cb(p ? new ControlBlock<T>(p) : nullptr)
		{}
		shared_ptr(const shared_ptr<T>& sp)
		{
			if (sp._cb)
			{
				sp._cb->shared_count++;
			}
			_cb = sp._cb;
		}
		~shared_ptr()
		{
			if ( _cb && _cb->shared_count == 1)
			{
				_cb->shared_count--;
				delete _cb->ptr;
				if (_cb->weak_count == 0)
				{
					delete _cb;
				}
			}
		}

		T& operator*()
		{
			return *(_cb->ptr);
		}
		T* operator->()
		{
			return _cb->ptr;
		}
		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			if (this != &sp)
			{
				if (sp)
				{
					sp._cb->shared_count++;
				}
				if (_cb && --(_cb->shared_count) == 0)
				{
					delete _cb->ptr;
					if (_cb->weak_count == 0)
					{
						delete _cb;
					}
				}
				_cb = sp._cb;
			}
			return *this;
		}

		ControlBlock<T>* get()
		{
			return _cb;
		}
	private:
		ControlBlock<T>* _cb;

	};

	template<class T>
	class weak_ptr {
	public:
		weak_ptr() :
			_cb(nullptr)
		{}
		weak_ptr(shared_ptr<T>& sp) : _cb(sp.get())
		{
			if (sp.get())
			{
				
				_cb->weak_count++;
			}
		}
		~weak_ptr() {
			if (_cb && --(_cb->weak_count) == 0) {
				if (_cb->weak_count == 0&&_cb->shared_count==0)
				{
					delete _cb;
				}
			}
		}

		shared_ptr<T> lock() const
		{
			if (_cb && _cb->shared_count > 0)
			{
				return shared_ptr<T>(_cb);
			}
			return shared_ptr<T>();
		}

		weak_ptr<T>& operator=(shared_ptr<T>& sp)
		{
			if (sp.get())
			{
				sp.get()->weak_count++;
			}
			if (_cb)
			{
				if (--(_cb->weak_count) == 0 && _cb->shared_count==0) 
				{
					delete _cb;
				}
			}
			_cb = sp.get();
			return *this;
			
		}
		weak_ptr<T>& operator=(const weak_ptr<T>& sp)
		{
			if (this != &sp)
			{
				if (sp)
				{
					sp._cb->weak_count++;
				}
				if (_cb && --(_cb->weak_count) == 0)
				{
					if (_cb->shared_count == 0)
					{
						delete _cb;
					}
				}
				_cb = sp._cb;
			}
			return *this;
		}
	private:
		ControlBlock<T>* _cb;
	};

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐