手写智能指针unique_ptr、shared_ptr

shared_ptr

shared_ptr主要是维护裸指针和计数器,除了基本的构造函数和析构函数外,还要重载:赋值、拷贝、解引用。

#include<iostream>

template <typename T>
class shared_ptr{

private:
	T* ptr;
	size_t* cnt;
	/*
	将引用计数减1,如果减后变为0
	说明没有其他实例引用该对象,于是释放占用的内存。
	*/
	void realse(){
		if(cnt && --(*cnt) == 0){
			delete ptr;
			delete cnt;
		}
	}
	
public:
	// 初始化
	explicit shared_ptr(T* _ptr = nullptr):ptr(_ptr),
		cnt(_ptr ? new size_t(1) : nullptr){
		
	}
	// 拷贝操作
	// 传入other,令this等于other并修改引用计数
	shared_ptr(const shared_ptr& other):ptr(other.ptr),cnt(other.cnt){
		if(cnt){
			++(*cnt);
		}
	}
	//赋值操作
	//shared_ptr& 表示返回的是一个引用,除了减少开销外
	//还可以支持a = b = c这样的操作
	shared_ptr& operator=(const shared_ptr& other){
		/*
		在赋值运算符重载函数中,通常是比较对象的地址是否相同
		常见的做法是将 other 的地址与 this 进行比较,即 this != &other
		如果写的是 *this != other,要重载 == 运算符
		*/
		if( this != &other){
			realse();
			ptr = other.ptr;
			cnt = other.cnt;
			if(cnt){
				++(*cnt);
			}
		}
		//这里this前加*号是this是指向当前实例的指针
		//解引用后才能返回实例(引用)
		return *this;
	}
	//析构函数
	~shared_ptr(){
		realse();
	}
	// 有时可能需要对指针进行算数运算,或其它需求
	// 总之get()用来支持返回原指针
	T* get() const {
		return ptr;
	}
	// 常规的解引用
	T* operator->() const {
		return *ptr;
	}
	T& operator*() const {
		return *ptr;
	}
	size_t getCnt() const {
		return cnt ? *cnt : 0;
	}
};
class Myclass{
public:
	Myclass(){
		
	}
	void doSomething(){
		
	}
};
int main(){
	shared_ptr<Myclass> ptrA(new Myclass());
	std::cout<< ptrA.getCnt() << '\n';
	shared_ptr<Myclass> ptrB;
	ptrB = ptrA;
	std::cout<< ptrA.getCnt() << '\n';
	std::cout<< ptrB.getCnt() << '\n';
}

unique_ptr

因为是unique,为了保证独占语义,要把拷贝赋值和拷贝构造ban了,对应代码里这两句:

unique_ptr(const unique_ptr &) = delete;
unique_ptr &operator=(const unique_ptr &) = delete;

其它同理,用右值引用实现移动构造和移动赋值。

#include <iostream>

template<typename T>
class unique_ptr{
	
private:
	T* ptr;
	
public:
	explicit unique_ptr(T* _ptr = nullptr):ptr(_ptr){
		
	}
	// 移动构造函数:
	// 使用右值引用实现移动赋值:
	// 先把p.ptr 赋值给新的ptr
	// 然后将p.ptr 置空
	unique_ptr(unique_ptr &&p) noexcept : ptr(p.ptr){
		p.ptr = nullptr;
	}
	//将拷贝构造运算ban掉,不允许拷贝,符合独占的定义
	//其中(const class &)是拷贝构造的常用写法
	unique_ptr(const unique_ptr &) = delete;
	//同理将拷贝赋值运算ban掉
	unique_ptr &operator=(const unique_ptr &) = delete;
	//析构函数
	~unique_ptr(){
		if(ptr) 
			delete ptr;
	}
	//移动赋值运算符
	inline unique_ptr<T> &operator=(unique_ptr &&p) noexcept{
		std::swap(*this,p);
		//p是一个右值引用,右值引用是临时的
		//出了这个作用域就会被释放掉,满足"移动赋值"的定义
		return *this;
	}
	// 有时可能需要对指针进行算数运算,或其它需求
	// 总之get()用来支持返回裸指针
	T* get() const {
		return ptr;
	}
	//inline表示内联函数,可以展开函数体直接嵌入
	//但编译器不一定采用,只是个建议
	//这里重载了解引用运算符(*),且返回的是引用(&)
	inline T &operator*() const{
		return *get();
	}
	//重载*运算符,返回原指针
	inline T *operator->() const{
		return get();
	}
	//将ptr的值修改为q
	inline void reset(T *q = nullptr)noexcept{
		if(q != ptr){
			if(ptr) delete ptr;
			ptr = q;
		}
	}
	//释放资源并返回裸指针
	inline T *release(T *q = nullptr) noexcept{
		T *res = ptr;
		ptr = nullptr;
		return res;
	}
};
posted @   liyishui  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示