之前定义指针申请内存空间使用后要进行delete进行资源释放,但是如果在资源释放之前就抛出异常,则不会进行正常的资源释放,造成资源泄露的问题。长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。
例如:
void MemoryLeaks()
{
// 1.内存申请
int* p1 = new int[10];
throw;
delete[] p1;
}
因此通过定义智能指针,利用对象的生命周期来控制程序资源,在生命周期结束时自动释放资源。
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。这种做法有两大好处:
//智能指针:
//1.实现RALL思想
//2.实现指针功能:*,->
template <class T>
class AutoPtr
{
public:
//RALL
//构造函数获取资源的管理权
AutoPtr(T* ptr)
:_ptr(ptr)
{}
//析构中释放资源
~AutoPtr()
{
if (_ptr)
delete _ptr;
}
T* operator->()
{
return _ptr;
}
T& operator*()
{
return *_ptr;
}
//管理权转移
AutoPtr(AutoPtr<T>& ap)
:_ptr(ap.ptr)
{
ap._ptr = nullptr;
}
AutoPtr<T>& operator=(AutoPtr<T>& ap)
{
if (this != &ap)
{
if (_ptr)
delete _ptr;
//管理权转移
_ptr = ap._ptr;
ap._ptr = nullptr;
}
return *this;
}
private:
T* _ptr;
};
class A
{
public:
int _a = 10;
};
void test()
{
错误写法:该写法容易造成资源二次释放
//int* ptr = new int;
//SmartPtr sp(ptr);
//SmartPtr sp2(ptr);
//正确写法:
AutoPtr<int> sp1(new int);
//SmartPtr sp2((int*)malloc(sizeof(int)));
AutoPtr<A> sp2(new A);
//智能指针,编译器调用 析构自动释放内存,不存在内存泄露的问题
*sp1 = 10;
sp2->_a = 100;
(*sp2)._a = 1000;
//普通指针,手动释放内存,存在内存泄漏的问题
int* p = new int;
A* pa = new A;
*p = 1;
pa->_a = 5;
(*pa)._a = 55;
delete p;
delete pa;
}
C++98版本的库中就提供了auto_ptr的智能指针。
auto_ptr的实现原理:管理权转移的思想。
拷贝时直接将新指针指向被拷贝对象资源,被拷贝对象指针悬空。
auto_ptr<int> sp1(new int);
auto_ptr<int> sp2(sp1); // 管理权转移
//sp1悬空
unique_ptr的实现原理:简单粗暴的防拷贝。
//unique_ptr:防拷贝
unique_ptr<int> up(new int);
//unique_ptr的拷贝构造为删除函数
//unique_ptr up2(up);
unique_ptr<int> up3(new int);
//unique_ptr赋值运算符为删除函数
//up3 = up;
C++11中开始提供更靠谱的并且支持拷贝的shared_ptr.
shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
循环引用分析:
struct ListNode
{
shared_ptr<ListNode> _next;
shared_ptr<ListNode> _prev;
int _data;
~ListNode()
{
cout << "~ListNode" << endl;
}
};
void test1()
{
shared_ptr<ListNode> n1(new ListNode);
shared_ptr<ListNode> n2(new ListNode);
cout << n1.use_count() << endl;
cout << n2.use_count() << endl;
n1->_next = n2;
n2->_prev = n1;
cout << n1.use_count() << endl;
cout << n2.use_count() << endl;
}
使用weak_ptr可以在拷贝时不进行计数+1操作,因此可以正常调用析构,进行资源释放。
struct ListNode
{
weak_ptr<ListNode> _next;
weak_ptr<ListNode> _prev;
int _data;
~ListNode()
{
cout << "~ListNode" << endl;
}
};
void test1()
{
shared_ptr<ListNode> n1(new ListNode);
shared_ptr<ListNode> n2(new ListNode);
cout << n1.use_count() << endl;
cout << n2.use_count() << endl;
n1->_next = n2;
n2->_prev = n1;
cout << n1.use_count() << endl;
cout << n2.use_count() << endl;
}