[C++系列]熟练掌握这3个指针,更好应对面试C++智能指针的知识题问

为什么需要智能指针?
用来解决申请出来的空间,没有进行及时释放,造成内存泄漏的问题。

1. 内存泄漏

内存泄漏不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

  • 堆内存泄漏
    malloc/realloc/new等从堆中分配的一块内存,用完后必须通过相应的free或者delete删除掉,而造成堆内存泄漏则是对其申请的内存没有进行释放。
  • 系统资源泄露
    实用系统分配的资源,套接字,文件描述符,管道等没有使用对应的函数释放掉,导致系统资源的浪费。

而智能指针则是解决内存泄漏的事前预防型方案!

2. 智能指针的使用

1. RAII

在对象构造时获取资源,在对象析构时释放资源。
RAII思想:根据对象的生命周期控制资源的生命周期
RAII一个应用: 智能指针 -->根据智能指针对象的生命周期控制动态开辟的资源的生命周期

template <class T>
class smartPtr
{
public:
	smartPtr(T* ptr)
		:_ptr(ptr)
	{}

	~smartPtr()
	{

		if (_ptr)
		{
			delete _ptr;
			cout << "~smartPtr delete" << endl;
		}

	}

	//管理权转移
	smartPtr(smartPtr<T>& sp)
		:_ptr(sp._ptr)
	{
		sp._ptr = nullptr;
	}

	smartPtr<T>& operator=(smartPtr<T>& sp)
	{
		if (this != &sp)
		{
			//释放原有资源
			if (_ptr)
				delete _ptr;
			//管理权转移
			_ptr = sp._ptr;
			sp._ptr = nullptr;
		}
		return *this;
	}

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

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

private:
	T* _ptr;
};

C++库中智能指针都定义在memory这个头文件之中。

2. auto_ptr

实现的原理:管理权转移的思想。

void testAutoPtr()
{
	//auto_ptr: 一般禁止使用, 有解引用异常的风险
	auto_ptr<Date> ap(new Date);
	cout << ap->_year << endl;
	//拷贝时发生管理权转移
	auto_ptr<Date> copy = ap; //当对象拷贝或者赋值后,前面的对象就悬空了
	//发生管理权转移之后,智能指针不再拥有资源,故不能访问资源
	//cout << ap->_year << endl;
	cout << copy->_year << endl;
}

3. unique_ptr

设计思路:防拷贝,直接不让拷贝和赋值

template <class T>
class uniquePtr
{
	uniquePtr(T* ptr)
		:_ptr(ptr)
	{}

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

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

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

	//防拷贝
	uniquePtr(const uniquePtr<T>& up) = delete;
	uniquePtr<T>& operator=(const uniquePtr<T>& up) = delete;
private:
	T* _ptr;
};
void testUniquePtr()
{
	unique_ptr<Date> up(new Date);
	//unique_ptr: 防拷贝-->拷贝构造和赋值运算符定义为delete函数
	
	/*unique_ptr<Date> copy = up;
	unique_ptr<Date> up2(new Date);
	up2 = up;*/
}

4. shared_ptr

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

  • shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
  • 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
  • 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
  • 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。
    这是一个更加靠谱,并且支持拷贝的智能指针
void testSharedPtr()
{
	shared_ptr<Date>  sp(new Date);
	cout << sp->_year << endl;
	cout << sp.use_count() << endl;
	shared_ptr<Date> copy(sp);
	cout << sp->_year << endl;
	cout << sp.use_count() << endl;
	shared_ptr<Date> sp2(new Date);
	sp2 = sp;
	cout << sp->_year << endl;
	cout << sp.use_count() << endl;

}

在这里插入图片描述
为了解决线程安全问题,我们可以给计数自减和自增加锁:

template <class T, class deletor>
class sharedPtr
{
public:
	sharedPtr(T* ptr, deletor del)
		:_ptr(ptr)
		, _useCount(new int(1))
		, _mtx(new mutex)
		, _del(del)
	{}

	~sharedPtr()
	{
		//引用计数自减
		//if (--(*_useCount) == 0)
		if (subRef() == 0)
		{
			//delete _ptr;
			//定制删除器进行空间释放
			del(_ptr);
			delete _useCount;
			delete _mtx;
			cout << "~sharedPtr" << endl;
		}
	}

	sharedPtr(const sharedPtr<T>& sp)
		:_ptr(sp._ptr)
		, _useCount(sp._useCount)
		, _mtx(sp._mtx)
	{
		//++(*_useCount);
		addRef();
	}

	sharedPtr<T>& operator=(const sharedPtr<T>& sp)
	{
		//if (this != &sp)
		//判断管理的资源是否相同
		if (_ptr != sp._ptr)
		{
			//判断当前管理的资源是否需要释放
			//if (--(*_useCount) == 0)
			if (subRef() == 0)
			{
				delete _ptr;
				delete _useCount;
				delete _mtx;
			}
			//赋值
			_ptr = sp._ptr;
			_useCount = sp._useCount;
			_mtx = sp._mtx;
			//++(*_useCount);
			addRef();
		}
		return *this;
	}

	int getUseCount()
	{
		return *_useCount;
	}

	int addRef()
	{
		_mtx->lock();
		++(*_useCount);
		_mtx->unlock();
		return *_useCount;
	}

	int subRef()
	{
		_mtx->lock();
		--(*_useCount);
		_mtx->unlock();
		return *_useCount;
	}

private:
	T* _ptr;
	int* _useCount;
	mutex* _mtx;
	deletor _del;
};

shared_ptr所存在的循环引用的问题:
在这里插入图片描述

template <class T>
struct ListNode
{
	/*shared_ptr<ListNode<T>> _prev;
	shared_ptr<ListNode<T>> _next;*/

	weak_ptr<ListNode<T>> _prev;
	weak_ptr<ListNode<T>> _next;

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

void testSharedPtr5()
{
	shared_ptr<ListNode<int>> sp(new ListNode<int>);
	shared_ptr<ListNode<int>> sp2(new ListNode<int>);

	cout << sp.use_count() << endl;
	cout << sp2.use_count() << endl;

	sp->_next = sp2;
	sp2->_prev = sp;

	cout << sp.use_count() << endl;
	cout << sp2.use_count() << endl;

	//weak_ptr: 不能直接使用,需要用shared_ptr进行初始化, 专门解决特殊场景下shared_ptr循环引用的问题
	//weak_ptr<ListNode<int>> wp(new ListNode<int>);
}

5. 定制删除器

//定制删除器
template <class T>
struct deleteArray
{
	void operator()(T* ptr)
	{
		delete[] ptr;
		cout << "delete Array" << endl;
	}
};

template <class T>
struct freeM
{
	void operator()(T* ptr)
	{
		free(ptr);
		cout << "free" << endl;
	}
};

void testSharedPtr6()
{

	/*deleteArray<A> da;
	shared_ptr<A> sp(new A[100], da);*/

	freeM<int> fm;
	shared_ptr<int> sp((int*)malloc(sizeof(int)), fm);
}

3. 智能指针总结

在这里插入图片描述

©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页