智能指针(RAII技术,auto_ptr,unique_ptr,shared_ptr)

智能指针

1、智能指针解决的问题

1、内存泄漏问题,开辟出来的空间没有进行释放,存在内存泄漏
2、异常安全问题,若是malloc和free之间若是存在抛出异常,会出现内存泄漏

2、智能指针的使用及原理

2.1、RAII技术

RAII(Resource ACquisition Is Initialization)是利用对象生命周期来控制程序资源(内存,文件句柄,网络连接,互斥量)的技术

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内保持有效,直至该对象被析构,此时释放资源

2.2、RAII技术的优点

1、不需要显示释放资源
2、对象所需的资源在其生命周期内始终有效

2.3、使用RAII技术设计SmartPtr

#include <iostream>
#include <vector>

using namespace std;

//使用RAII技术设计SmartPtr类
template<class T>
class SmartPtr
{
public:
	SmartPtr(T* ptr = nullptr)
		:m_ptr(ptr)
	{

	}

	~SmartPtr()
	{
		if (m_ptr)
		{
			delete m_ptr;
		}
	}
private:
	T* m_ptr;
};

void dealMergeSort(int* a, int start, int end, int* tmp)
{
	if (start >= end)
	{
		return;
	}
	int mid = (start + end) / 2;
	dealMergeSort(a, start, mid, tmp);
	dealMergeSort(a, mid + 1, end, tmp);

	int p = start;
	int q = mid + 1;
	int index = start;

	while (p <= mid && q <= end)
	{
		if (a[p] < a[q])
		{
			tmp[index] = a[p];
			p++;
		}
		else
		{
			tmp[index] = a[q];
			q++;
		}
		index++;
	}
	for (; p <= mid; p++)
	{
		tmp[index] = a[p];
		index++;
	}
	for (; q <= end; q++)
	{
		tmp[index] = a[q];
		index++;
	}
	for (int i = start; i < end; i++)
	{
		a[i] = tmp[i];
	}
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	SmartPtr<int> sp(tmp);
	dealMergeSort(a, 0, n - 1, tmp);

	vector<int> v(100000000, 10);
}

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

2.4、智能指针原理

1、使用了RAII技术
2、重载了*和->运算符

#include <iostream>

using namespace std;

//智能指针需要具备指针的行为,如解引用,箭头访问
//智能指针使用了RAII技术

template<class T>
class SmartPtr
{
public:
	SmartPtr(T* ptr = nullptr)
		:m_ptr(ptr)
	{

	}

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

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

	T* operator -> ()
	{
		return m_ptr;
	}
private:
	T* m_ptr;
};

struct Date
{
	int m_year;
	int m_month;
	int m_day;
};

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

	SmartPtr<Date> sparray(new Date);
	sparray->m_year = 2020;
	sparray->m_month = 4;
	sparray->m_day = 27;
	system("pause");
	return 0;
}

3、auto_ptr

3.1、auto_ptr的使用

#include <iostream>
#include <memory>

using namespace std;

class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
	}

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

public:
	int m_year;
	int m_month;
	int m_day;
};

int main()
{
	auto_ptr<Date> ap(new Date);
	auto_ptr<Date> copy(ap);

	//当对象拷贝或者赋值后,前面的对象就悬空了

	ap->m_year = 2020;
	ap->m_month = 4;
	ap->m_day = 28;
	system("pause");
	return 0;
}

3.2、auto_ptr的模拟实现
管理权转移

#include <iostream>

using namespace std;

template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr = nullptr)
		:m_ptr(ptr)
	{

	}

	~AutoPtr()
	{
		if (m_ptr)
		{
			delete m_ptr;
		}
	}

	AutoPtr<T>& operator = (AutoPtr<T>& ap)
	{
		if (*this != ap)
		{
			if (m_ptr)
			{
			//释放当前对象的资源
				delete m_ptr;
			}
			//将ap对象的资源转移到当前对象
			m_ptr = ap.m_ptr;
			ap.m_ptr = nullptr;
		}
		return *this;
	}

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

	T* operator -> ()
	{
		return m_ptr;
	}
private:
	T* m_ptr;
};

struct Date
{
	int m_year;
	int m_month;
	int m_day;
};

int main()
{
	AutoPtr<Date> ap(new Date);
	AutoPtr<Date> copy(ap);

	//ap对象访问资源时就会出现问题
	ap->m_year = 2020;
	ap->m_month = 4;
	ap->m_day = 28;
	system("pause");
	return 0;
}

4、unique_ptr

4.1、unique_ptr的使用

#include <iostream>
#include <memory>
using namespace std;

struct Date
{
	int m_year;
	int m_month;
	int m_day;
};

int main()
{
	unique_ptr<Date> up(new Date);
	//C++11防止拷贝和赋值
	//unique_ptr<Date> copy(up);
	up->m_year = 2020;
	up->m_month = 4;
	up->m_day = 28;
	system("pause");
	return 0;
}

4.2、unique_ptr的模拟实现
防拷贝

#include <iostream>

using namespace std;

template<class T>
class UniquePtr
{
public:
	UniquePtr(T* ptr = nullptr)
		:m_ptr(ptr)
	{

	}

	~UniquePtr()
	{
		if (m_ptr)
		{
			delete m_ptr;
		}
	}

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

	T* operator -> ()
	{
		return m_ptr;
	}
private:
	//C++98防拷贝和赋值(拷贝和赋值私有化,只声明)
	UniquePtr(UniquePtr<T> const &);
	UniquePtr& operator = (UniquePtr<T> const &);
	//C++11防拷贝和赋值用delete
	UniquePtr(UniquePtr<T> const &) = delete;
	UniquePtr& operator = (UniquePtr<T> const &) = delete;
private:
	T* m_ptr;
};

struct Date
{
	int m_year;
	int m_month;
	int m_day;
};

int main()
{
	UniquePtr<Date> up(new Date);
	//UniquePtr<Date> copy(up);

	up->m_year = 2020;
	up->m_month = 4;
	up->m_day = 28;
	system("pause");
	return 0;
}

5、shared_ptr

5.1、shared_ptr的使用

#include <iostream>
#include <memory>

using namespace std;

struct Date
{
	int m_year;
	int m_month;
	int m_day;
};

int main()
{
	//shared_ptr通过引用计数支持智能指针对象的拷贝
	shared_ptr<Date> sp(new Date);
	shared_ptr<Date> copy(sp);

	cout << "ref count:" << sp.use_count() << endl;
	cout << "ref count:" << copy.use_count() << endl;
	system("pause");
	return 0;
}

5.2、shared_ptr的模拟实现
通过引用计数支持智能指针对象的拷贝
shared_ptr的原理

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

1、shared_ptr的内部,给每个资源都维护着一份计数,用来记录该资源被几个对象共享
2、在对象被销毁的时候,对象的引用计数减一
3、若是引用计数是0,则证明该对象是最后一个使用该资源的对象,用完必须释放资源
4、若不是0,则不能释放该资源否则会变成野指针

#include <iostream>
#include <mutex>

using namespace std;

template <class T>
class SharedPtr
{
public:
	SharedPtr(T* ptr = nullptr)
		:m_ptr(ptr)
		, m_pRefCount(new int(1))
		, m_pMutex(new mutex)
	{

	}

	~SharedPtr()
	{
		Release();
	}

	SharedPtr(const SharedPtr<T>& sp)
		:m_pRefCount(sp.m_pRefCount)
		, m_ptr(sp.m_ptr)
		, m_pMutex(sp.m_pMutex)
	{
		AddRefCount();
	}

	SharedPtr& operator = (const SharedPtr<T>& sp)
	{
		if (this != &sp)
		{
			Release();

			m_pRefCount = sp.m_pRefCount;
			m_ptr = sp.m_ptr;
			m_pMutex = sp.m_pMutex;

			AddRefCount();
		}
		return *this;
	}

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

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

	int UseCount()
	{
		return *m_pRefCount;
	}

	T* Get()
	{
		return m_ptr;
	}

	void AddRefCount()
	{
		m_pMutex->lock();
		(*m_pRefCount)++;
		m_pMutex->unlock();
	}
private:
	void Release()
	{
		bool deleteflag = false;
		m_pMutex->lock();
		if ((*m_pRefCount)-- == 0)
		{
			delete m_ptr;
			delete m_pRefCount;
			deleteflag = true;
		}
		m_pMutex->unlock();
		if (deleteflag == true)
		{
			delete m_pMutex;
		}
	}
private:
	int* m_pRefCount;
	T* m_ptr;
	mutex* m_pMutex;
};

int main()
{
	SharedPtr<int> sp1(new int(10));
	SharedPtr<int> sp2(sp1);
	*sp2 = 20;

	cout << sp1.UseCount() << endl;
	cout << sp2.UseCount() << endl;

	SharedPtr<int> sp3(new int(10));
	sp2 = sp3;

	cout << sp1.UseCount() << endl;
	cout << sp2.UseCount() << endl;
	cout << sp3.UseCount() << endl;

	sp1 = sp3;

	cout << sp1.UseCount() << endl;
	cout << sp2.UseCount() << endl;
	cout << sp3.UseCount() << endl;

	system("pause");
	return 0;
}

5.3、shared_ptr的异常安全

1、智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时++或–,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2,这样会导致资源未释放或者程序崩溃,–需要加锁
2、智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题

#include <iostream>
#include <memory>
#include <thread>

using namespace std;

struct Date
{
	int m_year;
	int m_month;
	int m_day;
};

void SharedPtrFunc(shared_ptr<Date>& sp, size_t n)
{
	cout << sp.get() << endl;
	for (size_t i = 0; i < n; i++)
	{
		shared_ptr<Date> copy(sp);

		copy->m_year++;
		copy->m_month++;
		copy->m_day++;
	}
}

int main()
{
	shared_ptr<Date> p(new Date);
	cout << p.get() << endl;

	const size_t n = 100;
	thread t1(SharedPtrFunc, p, n);
	thread t2(SharedPtrFunc, p, n);

	t1.join();
	t2.join();

	cout << p->m_year << endl;
	cout << p->m_month << endl;
	cout << p->m_day << endl;
	system("pause");
	return 0;
}

5.4、shared_ptr的循环引用

循环引用的分析
1、node1和node2两个智能指针对象指向两个节点,引用计数变成1,不需要手动delete
2、node1的m_pnext指向node2,node2的m_prev指向node1,引用计数变成2
3、node1和node2析构,引用计数减1,但是m_next还是指向下一个节点,但是m_prev还是指向上一个节点
4、m_next是node1的成员,m_prev是node2的成员,只有node1和node2析构,m_next和m_prev才会析构,这就构成了循环引用,谁都不会释放

#include <iostream>
#include <memory>

using namespace std;

class ListNode
{
public:
	int m_data;
	shared_ptr<ListNode> m_prev;
	shared_ptr<ListNode> m_next;
public:
	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};

int main()
{
	shared_ptr<ListNode> node1(new ListNode);
	shared_ptr<ListNode> node2(new ListNode);

	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;

	node1->m_next = node2;
	node2->m_prev = node1;

	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;
	system("pause");
	return 0;
}

5.5、shared_ptr的循环引用的解决

将shared_ptr改成weak_ptr
node1->m_next = node2 node2->m_prev = node1 此时的weak_ptr的m_next和m_prev不会增加node1和node2的引用计数

#include <iostream>
#include <memory>

using namespace std;

class ListNode
{
public:
	int m_data;
	weak_ptr<ListNode> m_prev;
	weak_ptr<ListNode> m_next;
public:
	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};

int main()
{
	shared_ptr<ListNode> node1(new ListNode);
	shared_ptr<ListNode> node2(new ListNode);

	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;

	node1->m_next = node2;
	node2->m_prev = node1;

	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;
	system("pause");
	return 0;
}

5.6、shared_ptr管理非new对象
shared_ptr设计了删除器来解决非new对象

#include <iostream>
#include <memory>

using namespace std;

template<class T>
class FreeFunc
{
public:
	void operator () (T* ptr)
	{
		cout << "Free:" << ptr << endl;
		free(ptr);
	}
};

template<class T>
class DeleteArrayFunc
{
public:
	void operator () (T* ptr)
	{
		cout << "delete[]" << ptr << endl;
		delete[] ptr;
	}
};

int main()
{
	FreeFunc<int> freeFunc;
	shared_ptr<int> sp1((int*)malloc(sizeof(int)), freeFunc);

	DeleteArrayFunc<int> deleteArrayFunc;
	shared_ptr<int> sp2((int*)malloc(sizeof(int)), deleteArrayFunc);
	system("pause");
	return 0;
}

6、RAII技术设计守卫锁

用RAII技术可以设计守卫锁,防止异常安全导致的死锁

#include <iostream>
#include <mutex>
#include <thread>

using namespace std;

template<class T>
class LockGuard
{
public:
	LockGuard(mutex& mtx)
		:m_mutex(mtx)
	{
		m_mutex.lock();
	}

	~LockGuard()
	{
		m_mutex.unlock();
	}

	LockGuard(const LockGuard<mutex>&) = delete;
private:
	mutex& m_mutex;
};

mutex mtx;
static int n = 0;

void Func()
{
	for (size_t i = 0; i < 100000; i++)
	{
		LockGuard<mutex> lock(mtx);
		n++;
	}
}

int main()
{
	int begin = clock();
	thread t1(Func);
	thread t2(Func);
	t1.join();
	t2.join();
	int end = clock();
	cout << n << endl;
	cout << "cost time:" << end - begin << endl;
	system("pause");
	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值