目录
一、内存泄漏
1.1 什么是内存泄漏,内存泄漏的危害
1.2 内存泄漏分类
1.3 如何检测内存泄漏
1.4 如何避免内存泄漏
二、智能指针的使用及原理
2.1 RAII
2.2 智能指针的原理
2.3 智能指针的发展历程
2.4 智能指针的模拟及实现
3.1 线程安全问题
3.2 循环引用问题
C++智能指针~
什么是内存泄漏: 内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,
出现内存泄漏会导致响应越来越慢,最终卡死。
堆内存指的是程序执行中依据须要分配通过 malloc / calloc / realloc / new 等从堆中分配的一块内存,用完后必须通过调用相应的 free 或者 delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生 Heap Leak 。
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
RAII ( Resource Acquisition Is Initialization )是一种 利用对象生命周期来控制程序资源 (如内存、文件句柄、网络连接、互斥量等等)的简单技术。在对象构造时获取资源 ,接着控制对资源的访问使之在对象的生命周期内始终保持有效, 最后在 对象析构的时候释放资源 。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
- 不需要显式地释放资源;
- 采用这种方式,对象所需的资源在其生命期内始终保持有效;
auto_ptr
std::auto_ptr 文档
// C++98
// auto_ptr;
namespace rxy
{
template
class auto_ptr
{
public:
// 构造函数
auto_ptr(T* ptr = nullptr)
:_ptr(ptr)
{}
// 拷贝构造函数
auto_ptr(auto_ptr& ptr)
:_ptr(ptr._ptr)
{
ptr._ptr = nullptr;
}
// 赋值
auto_ptr& operator=(auto_ptr& ptr)
{
if (this != &ptr)
{
if (_ptr)
delete _ptr;
_ptr = ptr._ptr;
ptr._ptr = nullptr;
}
return *this;
}
// 析构函数
~auto_ptr()
{
if (_ptr)
delete _ptr;
}
// 像指针一样去使用
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
}
总结:
unique_ptr
unique_ptr 文档
// unique_ptr
namespace rxy
{
template
class unique_ptr
{
public:
unique_ptr(T* ptr)
:_ptr(ptr)
{}
unique_ptr(unique_ptr& ptr) = delete;
unique_ptr& operator=(unique_ptr& ptr) = delete;
~unique_ptr()
{
if (_ptr) delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
retrun _ptr;
}
private:
T* _ptr;
};
}
shared_ptr
// shared_ptr
// 引用计数支持多个拷贝管理同一个资源,最后一个析构对象释放资源
#include
#include
using namespace std;
namespace rxy
{
template
class shared_ptr
{
public:
shared_ptr(T* ptr = nullptr)
:_ptr(ptr)
,_count(new int(1))
,_mtx(new mutex)
{}
shared_ptr(const shared_ptr& ptr)
:_ptr(ptr._ptr)
, _count(ptr._count)
, _mtx(ptr._mtx)
{
AddCount();
}
void AddCount()
{
_mtx->lock();
(*_count)++;
_mtx->unlock();
}
shared_ptr& operator=(const shared_ptr& ptr)
{
if (_ptr != ptr._ptr)
{
if (_ptr) Release();
_ptr = ptr._ptr;
_count = ptr._count;
_mtx = ptr._mtx;
AddCount();
}
return *this;
}
void Release()
{
bool flag = false;
_mtx->lock();
if (_ptr && --(*_count) == 0)
{
delete _ptr;
delete _count;
flag = true;
}
_mtx->unlock();
if (flag == true)
delete _mtx;
}
~shared_ptr()
{
if (_ptr) Release();
}
int GetCount()
{
return *_count;
}
T* Get() const
{
return _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
int* _count;
mutex* _mtx;
};
}
weak_ptr
// 简化版的weak_ptr
template
class weak_ptr
{
public:
weak_ptr()
:_ptr(nullptr)
{}
weak_ptr(const shared_ptr& ptr)
:_ptr(ptr.Get())
{}
weak_ptr& operator=(shared_ptr& ptr)
{
// 调用shared_ptr的
_ptr = ptr.Get();
}
T* operator->()
{
return _ptr;
}
T& operator*()
{
retrun* _ptr;
}
private:
T* _ptr;
};
shared_ptr 的线程安全分为两方面:1. 智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时 ++ 或 -- ,这个操作不是原子的,引用计数原来是 1 , ++ 了两次,可能还是 2. 这样引用计数就错乱了。会导致资源未释放或者程序崩溃的问题。所以只能指针中引用计数++ 、 -- 是需要加锁的,也就是说引用计数的操作是线程安全的。2. 智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题。
#include
using namespace std;
struct ListNode
{
int _data;
shared_ptr _prev;
shared_ptr _next;
};
int main()
{
shared_ptr node1(new ListNode);
shared_ptr node2(new ListNode);
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;
}
// 解决方案:
// 在引用计数的场景下,把节点中的_prev和_next改成weak_ptr就可以了
// 原理就是:
// node1->_next = node2;
// 和node2->_prev = node1;时
// weak_ptr的_next和_prev不会增加node1和node2的引用计数。
struct ListNode
{
int _data;
weak_ptr _prev;
weak_ptr _next;
~ListNode() { cout << "~ListNode()" << endl; }
};
int main()
{
shared_ptr node1(new ListNode);
shared_ptr node2(new ListNode);
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;
}
// 仿函数的删除器
template
struct FreeFunc
{
void operator()(T* ptr)
{
cout << "free:" << ptr << endl;
free(ptr);
}
};
template
struct DeleteArrayFunc
{
void operator()(T* ptr)
{
cout << "delete[]" << ptr << endl;
delete[] ptr;
}
};
int main()
{
FreeFunc freeFunc;
std::shared_ptr sp1((int*)malloc(4), freeFunc);
DeleteArrayFunc deleteArrayFunc;
std::shared_ptr sp2((int*)malloc(4), deleteArrayFunc);
std::shared_ptr sp4(new A[10], [](A* p) {delete[] p; });
std::shared_ptr sp5(fopen("test.txt", "w"), [](FILE* p){fclose(p); });
return 0;
}
坚持打卡