C++智能指针
讲解智能指针之前,我们先看一个样例
样例引入
来看看这段代码
double dev(double input1, double input2)
{
int* arr = new int[10];
if (input1 == 0 || input2 == 0)
{
string s = "不能除以零";
throw s;
}
else
{
delete[] arr;
arr = nullptr;
return input1 / input2;
}
}
int main()
{
double x, y;
try
{
cin >> x >> y;
double ret = dev(x, y);
cout << ret << endl;
}
catch (const string& ret)
{
cout << ret << endl;
}
return 0;
}
当我们这个代码的除数为0的时候,我们就会抛异常,后面的delete[] arr就不会执行,显然这样就造成了内存泄漏,没有正确释放资源

那么应该怎么做?我们可以试试在throw s之前的时候就把资源给释放了

如果有多个内存资源的申请,我们就要释放多次,这样也麻烦,但是new本身当内存申请失败的时候也会抛异常,这时候我们就还需要一层逻辑

这样就非常麻烦,基于这个场景c++设计了应该东西叫智能指针。
RAII和智能指针的设计思路
C++智能指针的原理
讲使用之前我们先来讲讲原理,首先我们看看智能指针的构造这里我们主要模拟实现一个简单的share_ptr的场景,首先我们要知道share_ptr有一个关键就是引用计数,这个是什么意思我们期望我们有多个不同的指针对象来管理同一块资源,我们就需要引用计数,那么引用计数该怎么实现呢,我们可以用一个int* pcount的指针来代表计数多个shared_ptr指向资源时就++引⽤计 数,shared_ptr对象析构时就--引⽤计数,引⽤计数减到0时代表当前析构的shared_ptr是最后⼀ 个管理资源的对象,则析构资源,为什么不能用static int pcount来呢,那是因为我们期望的是多个指针对象管理同一块资源而不是由这个share_ptr类创建的所有指针对象来管理同一块资源,所以要用int* pcount。因为static是这个类创建的所有对象都可以使用这个静态变量。


share_ptr代码实现
下面这个是没有删除器的版本
template<class T>
class share_ptr
{
public:
explicit share_ptr(T* input_ptr = nullptr)
:_ptr(input_ptr)
,_count(new int(1))
{}
share_ptr(const share_ptr<T>& input_ptr)
:_ptr(input_ptr._ptr)
,_count(input_ptr._count)
{
(*_count)++;
}
~share_ptr()
{
release();
}
void release()
{
if (--(*_count) == 0)
{
delete _count;
delete _ptr;
_ptr = nullptr;
_count = nullptr;
}
}
//p2 = p1;
share_ptr<T>& operator=(const share_ptr<T>& input_ptr)
{
if (input_ptr._ptr != _ptr)
{
release();
_ptr = input_ptr._ptr;
_count = input_ptr._count;
(*_count)++;
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return this->_ptr;
}
T* get() const
{
return _ptr;
}
int use_count() const
{
return *_count;
}
private:
T* _ptr;
int* _count;
};
这个代码我们就实现了一个简单的可以供给单线程使用的智能指针,只能满足最基本功能是share_ptr 来挑几个重点的代码讲解
为什么赋值重载不用this != input_ptr来判断
我们的本意是看这两个智能指针所管理的对象一不一样,而不是看这两个指针类是不是一样的
智能指针的使用
auto_ptr
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr)
:_ptr(ptr)
{}
auto_ptr(auto_ptr<T>& sp)
:_ptr(sp._ptr)
{
// 管理权转移
sp._ptr = nullptr;
}
auto_ptr<T>& operator=(auto_ptr<T>& ap)
{
// 检测是否为⾃⼰给⾃⼰赋值
if (this != &ap)
{
// 释放当前对象中资源
if (_ptr)
delete _ptr;
// 转移ap中资源到当前对象中
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
~auto_ptr()
{
if (_ptr)
{
cout << "delete:" << _ptr << endl;
delete _ptr;
}
}
// 像指针⼀样使⽤
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
unique_ptr
是C++11设计出来的智能指针,他的名字翻译出来是唯⼀指针,他的特点的不⽀持拷 ⻉,只⽀持移动。如果不需要拷⻉的场景就⾮常建议使⽤他。我们来看看他的使用

为什么unique_pte不支持拷贝只支持移动?
因为unique_ptr 的核心思想是:同一时间只能有一个 unique_ptr 指向并管理某块资源。如果允许拷贝,就会导致多个 unique_ptr 指向同一块内存,这直接违背了“唯一”的初衷.
在转移过程中,源 unique_ptr 会主动放弃所有权(内部指针被置为 nullptr),而目标标 unique_ptr 接管资源。
那么move是可以的因为,move是c++的移动语义,移动不是拷贝,我的目的很明确,要求转移资源而不是拷贝资源,这样不会违背唯一性的初衷。

unique_ptr的模拟实现
template<class T>
class unique_ptr
{
public:
explicit unique_ptr(T* ptr)
:_ptr(ptr)
{}
~unique_ptr()
{
if (_ptr)
{
cout << "delete:" << _ptr << endl;
delete _ptr;
}
}
// 像指针⼀样使⽤
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
unique_ptr(const unique_ptr<T>& sp) = delete;
//这里明确表示了禁止拷贝构造和赋值重载
unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;
unique_ptr(unique_ptr<T>&& sp)
:_ptr(sp._ptr)
{
sp._ptr = nullptr;
}
unique_ptr<T>& operator=(unique_ptr<T>&& sp)
{
delete _ptr;
_ptr = sp._ptr;
sp._ptr = nullptr;
}
private:
T* _ptr;
};
share_ptr
定制删除器
weak_ptr
share_ptr的循环引用问题
我们来看下面的代码
struct ListNode
{
int _data;
std::shared_ptr<ListNode> _next;
std::shared_ptr<ListNode> _prev;
ListNode()
:_data(0)
,_next(nullptr)
,_prev(nullptr)
{}
~ListNode()
{
std::cout << "~ListNode()" << std::endl;
}
};
void function()
{
std::shared_ptr<ListNode> d1(new ListNode);
std::shared_ptr<ListNode> d2(new ListNode);
d1->_next = d2;
d2->_prev = d1;
}
上面这个代码是一个典型的循环引用
内存泄漏
更多推荐
所有评论(0)