C++进阶_智能指针

C++进阶_智能指针

1.为什么用智能指针
(1)内存泄漏问题:malloc出来的空间,没有经过free,会导致内存泄漏
(2)异常安全问题:在malloc和free之间存在抛异常,那么还会导致内存泄漏

#include<iostream>
#include<cstdlib>
#include<vector>

using namespace std;

int main()
{
	try
	{
		int* tmp = (int*)malloc(sizeof(int) * 10);
		vector<int> v(10, 5);
		v.at(10) = 20;//将下标为10的位置的数置为20
		cout << "free" << endl;
		free(tmp);
	}
	catch(const exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

2.智能指针的使用及其原理
RALL
RALL是一种利用对象生命周期来控制程序资源(内存、文件句柄、网络连接、互斥量等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构时释放资源。实际上把管理一份资源的责任托管给了一个对象。
好处:
(1)不需要显式的释放资源
(2)对象所需要的资源在其生命周期内始终保持有效

#include<iostream>
#include<cstdlib>
#include<vector>

using namespace std;

template<class T>
class SmartPtr
{
private:
	T* _ptr;
public:
	SmartPtr(T* ptr = nullptr) :_ptr(ptr)
	{
		cout << "SmartPtr(T)->" << _ptr << endl;
	}

	~SmartPtr()
	{
		if (_ptr)
		{
			cout << "~SmartPtr()->" << _ptr << endl;
			delete _ptr;
		}
	}
};

int main()
{
	try
	{
		int* tmp = (int*)malloc(sizeof(int) * 10);
		SmartPtr<int> sp(tmp);
		vector<int> v(10, 5);
		v.at(10) = 20;
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

智能指针的原理

  • RALL特性
  • 重载operator* 和opeator->,具有像指针一样的行为
template<class T>
class SmartPtr
{
private:
	T* _ptr;
public:
	SmartPtr(T* ptr=nullptr):_ptr(ptr)
	{}

	~SmartPtr()
	{
		if (_ptr)
		{
			delete _ptr;
		}
	}

	T& operator*()//重载*是为了取值,所以用引用
	{
		return *_ptr;
	}

	T* operator->()//重载->是为了取地址,所以用指针
	{
		return _ptr;
	}
};

struct Date
{
	int _y;
	int _m;
	int _d;
};

int main()
{
	int* tmp = new int();
	SmartPtr<int> sp(tmp);
	*sp = 10;
	cout << "*sp:" << *sp << endl;
	cout << "*tmp:" << *tmp << endl;

	Date* date = new Date();
	SmartPtr<Date> sd(date);
	sd->_y = 2020;
	sd->_m = 5;
	sd->_d = 7;
	cout << date->_y << " " << date->_m << " " << date->_d << endl;
	return 0;
}

std::auto_ptr
头文件:#inlcude
问题:auto_ptr对象拷贝赋值后,前面的对象悬空(为了解决一块空间被多个对象使用所造成的程序崩溃)

#include<iostream>
#include<memory>

using namespace std;

class Date
{
public:
	int _y;
	int _m;
	int _d;
public:
	Date()
	{
		cout << " Date()" << endl;
	}

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

int main()
{
	Date* date = new Date();
	auto_ptr<Date> ap(date);
	ap->_y = 2020;//auto_ptr对象ap拷贝或者赋值后,悬空
	auto_ptr<Date> ad(ap);//ap为空
	//ad->_y = 2019;
	return 0;
}

auto_ptr实现原理:管理权转移

#include<iostream>

using namespace std;

namespace mystd
{
	template<class T>
	class auto_ptr
	{
	private:
		T* _ptr;
	public:
		auto_ptr(T* ptr=nullptr):_ptr(ptr)
		{}

		~auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}

		auto_ptr(auto_ptr<T>& ap):_ptr(ap._ptr)
		{
			ap._ptr = nullptr;
		}

		auto_ptr<T>& operator=(auto_ptr<T>& ap)
		{
			if (this != &ap)
			{
				if (_ptr)
				{
					delete _ptr;
				}
				_ptr = ap._ptr;
				ap._ptr = nullptr;
			}
			return *this;
		}

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

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

	};
}

class Date
{
public:
	int _y;
};

int main()
{
	mystd::auto_ptr<Date> myap(new Date());
	myap->_y = 2020;
	cout << myap->_y << endl;
	mystd::auto_ptr<Date> cp(myap);
	//cp->_y = 2020;
	return 0;
}

std::unique_ptr
std::unique_ptr:防拷贝,防赋值,避免了auto_ptr问题的出现

#include<iostream>
#include<memory>

using namespace std;

int main()
{
	int* tmp = new int();
	unique_ptr<int> uq(tmp);
	*uq = 2;
	cout << *uq << " " << *tmp << endl;
	//unique_ptr<int> cp(uq);
	return 0;
}

unique_ptr实现原理:防拷贝防赋值

#include<iostream>

using namespace std;

namespace mystd
{
	template<class T>
	class unique_ptr
	{
	private:
		T* _ptr;
	private:
		unique_ptr(unique_ptr<T> const&);//拷贝
		unique_ptr<T>& operator=(unique_ptr<T> const&);//赋值
	public:
		unique_ptr(T* ptr=nullptr):_ptr(ptr)
		{}

		~unique_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}
		

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

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

	};
}

int main()
{
	int* tmp = new int();
	mystd::unique_ptr<int> uq(tmp);
	*uq = 3;
	cout << *tmp << endl;
	//mystd::unique_ptr<int> cp(uq);//防拷贝(私有成员,无法访问)
	//mystd::unique_ptr<int> cp = uq;//防赋值(私有成员,无法访问)
	return 0;
}

std::shared_ptr
std::shared_ptr:支持拷贝

#include<iostream>
#include<memory>

using namespace std;

int main()
{
	int* tmp = new int();
	shared_ptr<int> sp(tmp);
	shared_ptr<int> cp(sp);
	*sp = 10;
	cout << *tmp << " " << *sp << endl;
	*cp = 20;
	cout << *tmp << " " << *sp << endl;//拷贝
	return 0;
}

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

  • shared_ptr在内部为每个资源都维护着一份计数,用来记录该资源被几个对象共享
  • 在对象被销毁时(调用析构函数),即不再使用该资源,对象的引用计数-1
  • 当引用计数为0时,说明自己是最后一个使用该资源的对象,必须释放该资源
  • 如果不是0,则说明该资源被其他对象所使用,不能释放该资源,否则其他对象会成为野指针
    shared_ptr的循环引用
#include<iostream>
#include<memory>

using namespace std;

class Node
{
public:
	int _data;
	shared_ptr<Node> _prev;//前驱
	shared_ptr<Node> _next;//后继

public:
	Node()
	{
		cout << "Node()" << endl;
	}

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

int main()
{
	shared_ptr<Node> node1(new Node());
	shared_ptr<Node> node2(new Node());
	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;

	return 0;
}

在这里插入图片描述
说明:
(1)node1与node2两个智能指针对象指向两个结点,引用计数分别为1
(2)node1的next指向node2,node2的prev指向node1,引用计数分别变为2
(3)node1和node2分别管理两个结点,node1的next管理着node2,所以必须得等到node2释放,node1才可以释放
(4)node2的prev管理着node1,所以必须得等到node1释放,node2才可以释放
(5)最终谁也不能释放,就成了循环引用
解决方案:将结点中的_prev和_next改成weak_ptr即可。原理是node1->_next = node2;和node2->_prev = node1时weak_ptr的_next和_prev不会增加node1和node2的引用计数。

#include<iostream>
#include<memory>

using namespace std;

class Node
{
public:
	int _data;
	weak_ptr<Node> _prev;//前驱
	weak_ptr<Node> _next;//后继

public:
	Node()
	{
		cout << "Node()" << endl;
	}

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

int main()
{
	shared_ptr<Node> node1(new Node());
	shared_ptr<Node> node2(new Node());
	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;

	return 0;
}

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值