C++primer 5th 类似shared_ptr和unique_ptr的简易模板的实现

这篇博客介绍了如何根据C++ Primer 5th的课后习题,实现类似shared_ptr和unique_ptr的智能指针模板类。这类模板能自动管理动态内存,避免悬挂指针、内存泄漏等问题。但文章指出,即使使用这些自定义的智能指针,仍可能在循环引用的情况下导致内存泄漏,此时需借助weak_ptr来解决。同时,文章提到了C++11智能指针的常见使用误区。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++ primer 5th 课后习题 --实现自己版本的shared_ptr和unique_ptr
行为像指针的类都可以像这样进行类比定义,可以自动管理动态内存,无需自己delete
不用再担心悬挂指针、内存泄漏或对同一内存区域两次delete之类的错误
但是注意:无论是shared_ptr还是我们自己实现的类模板my_shared_ptr都存在循环引用时(比如用于双向链表时),依然导致内存泄漏的问题。解决办法是弱引用指针weak_ptr配合shared_ptr使用。

其他关于智能指针使用的注意事项可见C++11智能指针的常见误区 Top10

//16.28 编写自己版本的shared_ptr和unique_ptr
//======================shared_ptr=============================
template <typename T>
class my_shared_ptr {
	using DelFuncPtr = void(*)(T*);	//删除器的函数指针
public:
	//构造函数
	my_shared_ptr(T* _ptr=nullptr,DelFuncPtr _del=nullptr)
				:ptr(_ptr),del(_del),count_ptr(new size_t(ptr!=nullptr)){ }//指向了元素则将引用计数置为1 否则置为0

	//析构函数
	~my_shared_ptr() {
		if (!ptr) return;
		if (--*count_ptr==0) {
			del ? del(ptr) : delete ptr;//是否传入了自定义删除器?是 调用删除器删除ptr : 否 直接删除ptr
			delete count_ptr;
			cout << "the left hand side has been released!" << endl;
		}
		ptr = nullptr;
		del = nullptr;
		count_ptr = nullptr;
	}

	//拷贝构造函数
	my_shared_ptr(const my_shared_ptr& msp) :ptr(msp.ptr),del(msp.del),count_ptr(msp.count_ptr){
		++*count_ptr;//拷贝构造后引用计数+1
	}
	//拷贝赋值运算符
	//my_shared_ptr& operator=(const my_shared_ptr& rhs) {
	//	++*rhs.count_ptr;//先递增右侧运算对象的引用计数 即可保证自赋值的安全
	//	if (--*count_ptr==0) {//递减本对象的引用计数 如果为0释放资源
	//		del ? del(ptr) : delete ptr;
	//		delete count_ptr;
	//	}
	//	//更新指针
	//	ptr = rhs.ptr;
	//	count_ptr = rhs.count_ptr;
	//	del = rhs.del;
	//	return *this;
	//}

	//可以编写自定义的swap函数来优化分配了资源的类代码
	//注:使用拷贝和交换技术的赋值运算符自动就是异常安全的 且能正确处理自赋值
	void swap(my_shared_ptr& rhs) noexcept{
		using std::swap;//虽然编译器可以根据参数类型调用标准库的swap 但此声明是个好习惯
		swap(ptr,rhs.ptr);
		swap(count_ptr,rhs.count_ptr);
		swap(del,rhs.del);
	}
	
	//对于智能指针来说 拷贝赋值和移动赋值其实是一个概念
	my_shared_ptr& operator=(my_shared_ptr rhs) {//不传引用而传值
		//因为=拷贝赋值函数是以传值方式传递给=运算符 因此拷贝了右侧的副本 调用了拷贝构造函数 右侧引用计数+1
		//交换操作中又是引用传递 因此swap后 rhs的副本和左侧状态进行了交换
		//当退出=运算符函数作用域后 rhs临时副本调用析构函数 递减原本左侧的引用计数 如果为0则释放资源
		swap(rhs);
		return *this;
	}

	//移动构造函数 直接接管资源
	my_shared_ptr(my_shared_ptr&& msp)noexcept :my_shared_ptr() { swap(msp); }//先委托默认构造函数 后交换状态


	//其他成员函数
	void reset(T* _ptr=nullptr,DelFuncPtr _del=nullptr) {
		//使用拷贝和交换技术
		my_shared_ptr temp(_ptr,_del);//构造一个临时对象
		swap(temp);
	}

	T* get() const noexcept{
		return ptr;//我们必须保证get()到的指针不会被delete! 特别是不能用get()的指针初始化另外一个智能指针或为其赋值
	}

	T& operator*()const noexcept {
		return *ptr;
	}

	T* operator->()const noexcept {
		return ptr;
	}

	size_t use_count() const noexcept{
		return *count_ptr;
	}
	
	explicit operator bool() const noexcept {
		//不允许发生隐式类型转换 注:if() while()等条件判断自动发生类型转换
		return (ptr!=nullptr);
	}
private:
	T* ptr = nullptr;	//指向元素
	DelFuncPtr del= nullptr;	//指向可调用对象作为删除器
	size_t* count_ptr = nullptr;	//指向引用计数
};









//======================unique_ptr=============================
//my_unique_ptr默认使用的删除器
class Deleter {
public:
	//成员模板:
	template <typename T>
	void operator()(T* p)const {
		delete p;
		cout << "deleted p by default Delter" << endl;
	}
};
template <typename T,typename D=Deleter>
class my_unique_ptr {
public:
	my_unique_ptr(T* _ptr, const D& _del = D()) :ptr(_ptr), del(_del) { cout << "constrctor" << endl; }
	~my_unique_ptr() { del(ptr); }

	//unique_ptr由于独占对象 因此不允许拷贝 定义其拷贝构造函数和拷贝赋值运算符为删除的
	my_unique_ptr(const my_unique_ptr&) = delete;
	my_unique_ptr& operator=(const my_unique_ptr&) = delete;

	//移动构造函数和移动赋值运算符可用
	my_unique_ptr(my_unique_ptr&& moved)noexcept :ptr(moved.ptr),del(std::move(moved.del)){
		//将被移动对象置为可析构状态
		moved.ptr = nullptr;
	}

	my_unique_ptr& operator=(my_unique_ptr&& moved) {
		if (this!=&moved) {//直接检查自赋值
			del(ptr);//释放旧资源
			ptr = moved.ptr;
			del = std::move(moved.del);
			moved.ptr = nullptr;//置为可析构状态
		}
		return *this;
	}

	//其他成员函数
	void reset(T* p=nullptr) noexcept {
		del(ptr);//释放旧资源
		ptr = p;
	}
	
	//注:正如std::unique_ptr一样 release()返回的指针需要自己管理内存资源 函数内部并没有释放掉原有资源
	T* release() {//放弃对指针的控制权返回指针 并将ptr置为空
		T* ret = ptr;
		ptr = nullptr;
		return ret;
	}
	
	void swap(my_unique_ptr& rhs) {
		using std::swap;
		swap(ptr,rhs.ptr);
		swap(del,rhs.del);
	}

	T* get()  const noexcept{
		return ptr;
	}

	D& get_deleter() noexcept {
		return del;
	}
	const D& get_deleter() const noexcept { 
		return del;
	}

	explicit  operator bool() const noexcept {
		return (ptr!=nullptr);
	}

	T& operator*() const noexcept {
		return *ptr;
	}
	T* operator->()const noexcept {
		return ptr;
	}
	T& operator[](size_t n)const noexcept {
		return ptr[n];
	}
private:
	T* ptr=nullptr;//指向元素
	D del;//删除器
};





int main() {
	//test for shared_ptr
	cout << "==================test for shared_ptr====================" << endl;
	my_shared_ptr<int> sp1(new int(5));
	my_shared_ptr<int> sp2(new int(10));
	my_shared_ptr<int> sp3(sp1);
	//sp3=nullptr 发生了nullptr->my_shared_ptr的隐式转换
	DebugDelete dd;
	cout << "sp1-use_count: " << sp1.use_count() << " sp1-adress: " << sp1.get() << " sp1-deref: " << *sp1 << endl;
	cout << "sp2-use_count: " << sp2.use_count() << " sp2-adress: " << sp2.get() << " sp2-deref: " << *sp2 << endl;
	cout << "sp3-use_count: " << sp3.use_count() << " sp3-adress: " << sp3.get() << " sp3-deref: " << *sp3 << endl;

	cout << "after swap sp1 = sp2 : " << endl;
	sp1 = sp2;
	cout << "sp1-use_count: " << sp1.use_count() << " sp1-adress: " << sp1.get() << " sp1-deref: " << *sp1 << endl;
	cout << "sp2-use_count: " << sp2.use_count() << " sp2-adress: " << sp2.get() << " sp2-deref: " << *sp2 << endl; 
	cout << "sp3-use_count: " << sp3.use_count() << " sp3-adress: " << sp3.get() << " sp3-deref: " << *sp3 << endl;

	cout << "after reset sp2.reset(new int(x)): " << endl;
	sp2.reset(new int(15));
	sp2.reset(new int(20));
	cout << "sp1-use_count: " << sp1.use_count() << " sp1-adress: " << sp1.get() << " sp1-deref: " << *sp1 << endl;
	cout << "sp2-use_count: " << sp2.use_count() << " sp2-adress: " << sp2.get() << " sp2-deref: " << *sp2 << endl;
	cout << "sp3-use_count: " << sp3.use_count() << " sp3-adress: " << sp3.get() << " sp3-deref: " << *sp3 << endl;

	cout << "after bound a deleter: " << endl;
	sp3.reset(new int(25), [](int* p) { delete p; cout << "release by lambda"<<endl; });
	cout << "sp1-use_count: " << sp1.use_count() << " sp1-adress: " << sp1.get() << " sp1-deref: " << *sp1 << endl;
	cout << "sp2-use_count: " << sp2.use_count() << " sp2-adress: " << sp2.get() << " sp2-deref: " << *sp2 << endl;
	cout << "sp3-use_count: " << sp3.use_count() << " sp3-adress: " << sp3.get() << " sp3-deref: " << *sp3 << endl;


	cout << "==================test for unique_ptr====================" << endl;
	DebugDelete d;
	my_unique_ptr<int> up1(new int(1));
	cout << "sp1-adress: " << up1.get() << " sp1-deref: " << *up1 << endl;
	if (up1) {
		cout << "explicit bool(): up1 is not empty pointer" << endl;
	}
	my_unique_ptr<int,DebugDelete> up2(new int(2),d);
	cout << "up2-adress: " << up2.get() << " up2-deref: " << *up2 << endl;

	cout << endl<<"test for move constructor: " << endl;
	my_unique_ptr<int> up3(std::move(up1));
	cout << "up3-adress: " << up3.get() << " up3-deref: " << *up3 << endl;
	my_unique_ptr<int,DebugDelete> up4(std::move(up2));
	cout << "up4-adress: " << up4.get() << " up4-deref: " << *up4 << endl;

	cout<< endl << "test for reset: " << endl;
	up1.reset(new int(3));
	if (up1) {
		cout << "explicit bool(): up1 is not empty pointer" << endl;
		cout << "sp1-adress: " << up1.get() << " sp1-deref: " << *up1 << endl;
	}
	up2.reset();
	if (!up2) {
		cout << "explicit bool(): up2 is  empty pointer" << endl;
	}

	cout << endl << "test for release:" << endl;
	auto receive = up1.release();
	if (!up1) {
		cout << "after up1.release() up1 points to nullptr" << endl;
		cout << "and the receive pointer is: " << *receive << endl;
	}

	cout << endl << "test for swap:" << endl;
	up3.swap(up1);
	if (!up3) {
		cout << "after swap up3 points to nullptr " << endl;
		cout << "and the up1 now points to riginal value that up3 pointed to: " << *up1 << endl;
	}

	cout << endl << "test for get_deleter: " << endl;
	up1.get_deleter().operator()(new int(2));
	up2.get_deleter().operator()(new int());
	cout << endl<< "===============out of area, call the destructor==============" << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值