标签搜索

目 录CONTENT

文章目录

C++中各种智能指针的实现及弊端(一).md

小小城
2021-08-22 / 0 评论 / 0 点赞 / 5 阅读 / 2,452 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-05-02,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

C++中各种智能指针的实现及弊端(一)

@[toc]

一、为什么需要智能指针?

首先看一段代码:

#include <vector>
void _MergeSort(int* a, int left, int right, int* tmp)
{
	if (left >= right) 
		return;

	int mid = left + ((right - left) >> 1);
	// [left, mid]
	// [mid+1, right]
	_MergeSort(a, left, mid, tmp);
	_MergeSort(a, mid + 1, right, tmp);
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int index = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
			tmp[index++] = a[begin1++];
		else
			tmp[index++] = a[begin2++];
	}
	while (begin1 <= end1)
		tmp[index++] = a[begin1++];
	while (begin2 <= end2)
		tmp[index++] = a[begin2++];
	memcpy(a + left, tmp + left, sizeof(int)*(right - left + 1));
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	_MergeSort(a, 0, n - 1, tmp);
	// 这里假设处理了一些其他逻辑
	vector<int> v(1000000000, 10);
	// ...
	// free(tmp);
}

int main()
{
	int a[5] = { 4, 5, 2, 3, 1 };
	MergeSort(a, 5);
	return 0;
}

从上面的代码很容易看出一个问题,就是:

  1. malloc出来的空间,没有进行释放,存在内存泄漏的问题。
  2. 异常安全问题。如果在malloc和free之间如果存在抛异常,那么还是有内存泄漏。这种问题就叫异常安全。

实际编写C/C++代码的过程中这类问题还有很多,比如new出来的空间,malloc出来的空间,文件流指针,及网络编程中等等,很容易出现申请的空间没有释放和文件描述符没有关闭等问题,就会造成资源泄漏的问题,并且这类问题不一定会立即显示出来,后期排错很困难,所以就引出了智能指针。

二、智能指针的使用及原理:

  •  1.RAII:
  • RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
  • 原理:在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。

借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:

  1. 不需要显式地释放资源。
  2. 采用这种方式,对象所需的资源在其生命期内始终保持有效。
// 使用RAII思想设计的SmartPtr类
template<class T>
class SmartPtr 
{
public:
	SmartPtr(T* ptr = nullptr)
	: _ptr(ptr)
	{}
	~SmartPtr()
	{
		if(_ptr)
			delete _ptr;
	}
private:
	T* _ptr;
};

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	// 讲tmp指针委托给了sp对象,用时老师的话说给tmp指针找了一个可怕的女朋友!天天管着你,直到你go die^^
	SmartPtr<int> sp(tmp);
	// _MergeSort(a, 0, n - 1, tmp);
	// 这里假设处理了一些其他逻辑
	vector<int> v(1000000000, 10);
	// ...
}

int main()
{
	try 
	{
		int a[5] = { 4, 5, 2, 3, 1 };
		MergeSort(a, 5);
	}
	catch(const exception& e)
	{
	cout<<e.what()<<endl;
	}
	return 0;
}

上面的代码还不能称为智能指针,因为他还没有指针的行为,所以还要对解引用和->进行运算符重载

template<class T>
class SmartPtr 
{
public:
	SmartPtr(T* ptr = nullptr)
	: _ptr(ptr)
	{}
	~SmartPtr()
	{
		if(_ptr)
			delete _ptr;
	}
	T& operator*() {return *_ptr;}
	T* operator->() {return _ptr;}
private:
	T* _ptr;
};

struct Date
{
	int _year;
	int _month;
	int _day;
};

int main()
{
	SmartPtr<int> sp1(new int);
	*sp1 = 10
	cout<<*sp1<<endl;
	SmartPtr<int> sparray(new Date);
	// 需要注意的是这里应该是sparray.operator->()->_year = 2018;
	// 本来应该是sparray->->_year这里语法上为了可读性,省略了一个->
	sparray->_year = 2018;
	sparray->_month = 1;
	sparray->_day = 1;
}

到这里基于RAII的智能指针已经实现完成;但是从上面的代码很容易发现智能指针的缺陷:浅拷贝问题*

注意⚠️不能采用深拷贝的方式解决(资源是外部用户申请,类管理资源作用,例如String)

如果不清楚深浅拷贝的请参考:
深浅拷贝问题

0

评论区