在cpp中为了避免内存泄漏采取了一系列手段,例如智能指针,智能指针采用RAII思想来管理内存(Resource Acquisition Is Initialization是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。)在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
1.不需要显式地释放资源。
2.采用这种方式,对象所需的资源在其生命期内始终保持有效。
在cpp中有三种智能指针供我们使用分别是unique_ptr,shared_ptr,weak_ptr。
1.unique_ptr:
unique_ptr不允许复制和拷贝(可以移动构造和移动赋值)本质上就是一个unique_ptr管理一个内存空间(如果是多个指针指向同一份内存要用后面的shared_ptr)。
所以在unique_ptr的特性下要实现它的基本功能就十分简单了:
template<typename T>
class unique_ptr
{
public:
typedef unique_ptr<T> self;
unique_ptr(T* ptr):_ptr(ptr){}
~unique_ptr()
{
cout << "~unique_ptr()" << endl;
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
unique_ptr(const self& ptr) = delete;
self& operator=(const self& ptr) = delete;
private:
T* _ptr;
};
2.shared_ptr
有多个指针指向同一分内存时就要考虑使用shared_ptr了,shared_ptr的本质是采用引用计数的方式来记录有多少个指针指向这份空间,当发生拷贝或赋值时引用计数就++,shared_ptr析构时引用计数就--,只用当引用计数为0时才会释放空间。具体实现如下:
template<typename T>
class share_ptr
{
public:
typedef share_ptr<T> self;
share_ptr(T* ptr = nullptr) :_ptr(ptr) ,_count(new int(1)){}
~share_ptr()
{
relese();
}
share_ptr(const self& ptr)
{
_count = ptr._count;
_ptr = ptr._ptr;
++(*_count);
}
int use_count()
{
return *_pcount;
}
T* get() const
{
return _ptr;
}
void relese()
{
if (--(*_count) == 0)
{
delete _count;
delete _ptr;
cout << "~share_ptr()" << endl;
}
}
self& operator=(const self& ptr)
{
//自己给自己赋值时不做改变
//当自身指向内容发生改变时先将原来内容的引用计数--再把新指向内容的引用计数++
if (ptr._ptr != _ptr)
{
relese();
_ptr = ptr._ptr;
_count = ptr._count;
++(*_count);
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
int* _count;//采用指针方式使其所有指向同一块空间的shared_ptr访问同一个计数
T* _ptr;
};
实际上shared_ptr有一个坑就是循环引用问题,例如双链表:
struct ListNode
{
//当前节点需要释放就要引用计数为0
//这就要先释放左右两边节点
//但是我本身又指向了左右节点
//要释放左右节点就要先释放我本身
//所以陷入死循环
typename _val;
shared_ptr<ListNode> _next;
shared_ptr<ListNode> _prev;
};
所以cpp引入了weak_ptr来解决该问题。
3.weak_ptr
它的构造和析构不会引起引用记数的增加或减少同时weak_ptr 没有重载*和->
template<class T>
class weak_ptr
{
public:
typedef weak_ptr<T> self;
weak_ptr()
:_ptr(nullptr)
{}
weak_ptr(const shared_ptr<T>& sp)
{
_ptr = sp.get();
}
self& operator=(const shared_ptr<T>& sp)
{
_ptr = sp.get();
return *this;
}
private:
T* _ptr;
};
由于它的构造和析构不会引起引用记数的增加或减少所以完美解决了循环引用的问题。
感谢观看~