【C/C++ 14】C++11智能指针
目录
一、智能指针概述
二、auto_ptr
三、unique_ptr
五、weak_ptr
六、定制删除器
一、智能指针概述
C++在进行异常处理的时候,若在new和delete之间或在lock和unlock之间就抛出异常了,这样会导致内存泄漏或死锁问题。
为了解决上述问题,于是就引入了智能指针(RAII)的概念。
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。
#include <iostream>
#include <map>
using namespace std;
// 使用RAII实现的SmartPtr
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr = nullptr)
: _ptr(ptr)
{}
~SmartPtr()
{
if (_ptr)
delete _ptr;
}
// 重载 * 和 ->
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
// 重载指针的 + ++ -- 功能,自增自减区分前置和后置
T* operator+(int i)
{
T* cur = _ptr;
while (i--)
{
cur++;
}
return cur;
}
T* operator++()
{
++_ptr;
return _ptr;
}
T* operator++(int)
{
T* cur = _ptr;
++_ptr;
return cur;
}
T* operator--()
{
--_ptr;
return _ptr;
}
T* operator--(int)
{
T* cur = _ptr;
--_ptr;
return cur;
}
private:
T* _ptr;
};
double Div(int a, int b)
{
if (b == 0)
throw invalid_argument("除0错误");
return (double)a / (double)b;
}
void Func()
{
SmartPtr<int> sp1(new int);
SmartPtr<int> sp2(new int[10]);
*sp1 = 5;
cout << "*sp1 = " << *sp1 << endl;
for (int i = 0; i < 10; ++i)
{
*(sp2 + i) = i;
printf("sp2[%d] = %d\n", i, *(sp2 + i));
}
cout << Div(*sp1, *sp2) << endl;
}
int main()
{
try
{
Func();
}
catch (const exception& e)
{
cout << e.what() << endl;
}
return 0;
}
二、auto_ptr
auto_ptr是C++98提出的智能指针,核心思想是转移管理,但是因其可能引发指针悬空的问题,故很多企业都禁止使用auto_ptr。
#include <iostream>
using namespace std;
namespace my
{
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;
};
}
// 结论:auto_ptr是一个失败设计,很多公司明确要求不能使用auto_ptr
int main()
{
my::auto_ptr<int> sp1(new int);
my::auto_ptr<int> sp2(sp1); // 管理权转移
// sp1悬空
*sp2 = 10;
cout << *sp2 << endl;
cout << *sp1 << endl; // 对空指针解引用
return 0;
}
三、unique_ptr
unique_ptr的实现原理:单例模式,简单粗暴的防拷贝。
// C++11中开始提供更靠谱的智能指针:unique_ptr / shared_ptr / weak_ptr
#include <iostream>
#include <mutex>
namespace my
{
template<class T>
class unique_ptr
{
public:
unique_ptr(T* ptr = nullptr)
: _ptr(ptr)
{}
~unique_ptr()
{
if (_ptr)
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;
private:
T* _ptr;
};
}
四、shared_ptr
shared_ptr的实现原理:通过引用计数的方式实现多个shared_ptr对象之间的共享资源。
- shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享
- 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一
- 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源
- 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了
#include <iostream>
#include <mutex>
namespace my
{
template<class T>
class shared_ptr
{
public:
shared_ptr(T* ptr = nullptr)
: _ptr(ptr), _pRefCount(new int(1)), _pmtx(new std::mutex)
{}
shared_ptr(const my::shared_ptr<T>& sp)
: _ptr(sp._ptr), _pRefCount(sp._pRefCount), _pmtx(sp._pmtx)
{
AddRef();
}
my::shared_ptr<T>& operator=(const my::shared_ptr<T>& sp)
{
if (_ptr != sp._ptr)
{
Release();
_ptr = sp._ptr;
_pRefCount = sp._pRefCount;
_pmtx = sp._pmtx;
AddRef();
}
return *this;
}
int use_count()
{
return *_pRefCount;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* get()const
{
return _ptr;
}
~shared_ptr()
{
Release();
}
private:
void AddRef()
{
_pmtx->lock();
*_pRefCount += 1;
_pmtx->unlock();
}
void Release()
{
_pmtx->lock();
bool flag = false;
if (--(*_pRefCount) == 0 && _ptr)
{
delete _ptr;
delete _pRefCount;
flag = true;
}
_pmtx->unlock();
if (flag == true)
delete _pmtx;
}
private:
T* _ptr;
int* _pRefCount;
std::mutex* _pmtx;
};
}
五、weak_ptr
weak_ptr称为弱指针,是一种配合shared_ptr而引入的一种智能指针,为了解决shared_ptr引用成环问题而存在。
weak_ptr指向一个由shared_ptr管理的对象而不影响所指对象的生命周期(不改变所指对象的引用计数)。
weak_ptr不提供 operator* 和 operator-> 的重载,不可直接通过weak_ptr使用对象。
weak_ptr可以指向资源,但不参与资源的管理。
#include <iostream>
#include <mutex>
namespace my
{
template<class T>
class weak_ptr
{
public:
weak_ptr(T* ptr = nullptr)
: _ptr(ptr)
{}
weak_ptr(const my::shared_ptr<T>& sp)
: _ptr(sp.get())
{}
weak_ptr<T>& operator=(const shared_ptr<T>& sp)
{
_ptr = sp.get();
return *this;
}
private:
T* _ptr;
};
}
六、定制删除器
#define _CRT_SECURE_NO_WARNINGS 1
// 上述简单实现的 unique_ptr / shared_ptr / weak_ptr 是存在缺陷的
// 一个最大的缺陷就是释放资源只能是默认的 delete 处理
// 所以我们需要定制删除器,可以通过仿函数或者lambda实现
#include <iostream>
// 定制删除器
template<class T>
struct DeleteArray
{
void operator()(const T* ptr)
{
delete[] ptr;
}
};
int main()
{
std::shared_ptr<int> sp1(new int[10], DeleteArray<int>());
std::shared_ptr<std::string> sp2(new std::string[10], DeleteArray<std::string>());
std::shared_ptr<FILE> sp3(fopen("Test.cpp", "w"), [](FILE* ptr) {fclose(ptr); });
}