本章主要讲解学习C++中智能指针的概念及使用
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
{
throw "Division by zero condition!";
}
return (double)a / (double)b;
}
void Func()
{
//如果发生除0错误抛出异常在外部进行捕获,那么下面的array没有得到释放
int* array = new int[10];
int len, time;
cin >> len >> time;
cout << Division(len, time) << endl;
cout << "delete []" << array << endl;
delete[] array;
}
int main()
{
try
{
Func();
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
return 0;
}
如果在malloc和free之间如果存在抛异常,那么还是有内存泄漏
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
{
throw "Division by zero condition!";
}
return (double)a / (double)b;
}
void Func()
{
// 这里捕获异常后并不处理异常,异常还是交给外面处理
// 这里捕获了再重新抛出去
int* array = new int[10];
try {
int len, time;
cin >> len >> time;
cout << Division(len, time) << endl;
}
catch (...)
{
cout << "delete []" << array << endl;
delete[] array;
throw;
}
// ...
cout << "delete []" << array << endl;
delete[] array;
}
int main()
{
try
{
Func();
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
return 0;
}
注:这种方式比较麻烦,不实用,由此引入了智能指针
内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费
长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死
堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定
内存泄漏非常常见,解决方案分为两种: 1、事前预防型。如智能指针等 2、事后查错型。如泄漏检测工具
// 使用RAII思想设计的SmartPtr类
template<class T>
class SmartPtr {
public:
SmartPtr(T* ptr = nullptr)
: _ptr(ptr)
{}
~SmartPtr()
{
if (_ptr)
delete _ptr;
}
private:
T* _ptr;
};
template<class Mutex>
class LockGuard
{
public:
LockGuard(Mutex& mtx)
:_mutex(mtx)
{
_mutex.lock();
}
~LockGuard()
{
_mutex.unlock();
}
LockGuard(const LockGuard<Mutex>&) = delete;
private:
// 注意这里必须使用引用,否则锁的就不是一个互斥量对象
Mutex& _mutex;
};
上述的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; }
private:
T* _ptr;
};
class Date
{
public:
Date() { cout << "Date()" << endl; }
~Date() { cout << "~Date()" << endl; }
int _year;
int _month;
int _day;
};
int main()
{
auto_ptr<Date> ap(new Date);
auto_ptr<Date> copy(ap);
// auto_ptr的问题:当对象拷贝或者赋值后,前面的对象就悬空了
// C++98中设计的auto_ptr问题是非常明显的,所以实际中很多公司明确规定了不能使用auto_ptr
ap->_year = 2018;
return 0;
}
//auto_ptr:管理权转移-存在问题
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr)
:_ptr(ptr)
{}
auto_ptr(auto_ptr<T>& ap)
:_ptr(ap._ptr)// 转移资源
{
ap._ptr = nullptr;
}
auto_ptr<T>& operator=(auto_ptr<T>& ap)
{
if (this != &ap)//防止自我赋值
{
cout << "~auto_ptr:" << _ptr << endl;
delete _ptr;
_ptr = ap._ptr;
ap._ptr = nullptr;
}
return *this;
}
~auto_ptr()
{
if (_ptr)
{
cout << "~auto_ptr:" << _ptr << endl;
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return &_ptr;
}
private:
T* _ptr;
};
int main()
{
unique_ptr<Date> up(new Date);
// unique_ptr的设计思路非常的粗暴-防拷贝,也就是不让拷贝和赋值。
unique_ptr<Date> copy(up);//error
return 0;
}
//unique_ptr:不存在拷贝和赋值-没有管理权的转移
template<class T>
class unique_ptr
{
public:
unique_ptr(T* ptr)
:_ptr(ptr)
{}
unique_ptr(unique_ptr<T>& ap) = delete;
unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;
~unique_ptr()
{
if (_ptr)
{
cout << "~unique_ptr:" << _ptr << endl;
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return &_ptr;
}
private:
T* _ptr;
};
注:C++98防拷贝的方式:只声明不实现+声明成私有;C++11防拷贝的方式修饰函数为delete
int main()
{
// shared_ptr通过引用计数支持智能指针对象的拷贝
shared_ptr<Date> sp(new Date);
shared_ptr<Date> copy(sp);
cout << "ref count:" << sp.use_count() << endl;
cout << "ref count:" << copy.use_count() << endl;
return 0;
}
//shared_ptr:多个智能指针管理一个内存资源,最后一个智能指针释放内存
template<class T>
class shared_ptr
{
public:
explicit shared_ptr(T* ptr)
:_ptr(ptr)
, _pCount(new int(1))
, _mtx(new mutex)
{}
void add_pCount()
{
// 加锁或者使用加1的原子操作
_mtx->lock();
++(*_pCount);
_mtx->unlock();
}
void release_pCount()
{
// 引用计数减1,如果减到0,则释放资源
bool flg = false;
_mtx->lock();
--(*_pCount);
if ((*_pCount) == 0 && _ptr)
{
cout << "~shared_ptr:" << _ptr << endl;
D del;
delete _ptr;
delete _pCount;
flg = true;
}
_mtx->unlock();
if (flg)
delete _mtx;
}
shared_ptr(shared_ptr<T>& sp)
:_ptr(sp._ptr)
, _pCount(sp._pCount)
, _mtx(sp._mtx)
{
add_pCount();
}
//shared_ptr<T>& operator=(shared_ptr<T>& sp)
//{
// if (_ptr != sp._ptr)//管理资源地址相同则不用处理
// {
// release_pCount();
// _ptr = sp._ptr;
// _pCount = sp._pCount;
// _mtx = sp._mtx;
// add_pCount();
// }
// return *this;
//}
void swap(shared_ptr<T>& sp)
{
std::swap(_ptr, sp._ptr);
std::swap(_pCount, sp._pCount);
std::swap(_mtx, sp._mtx);
}
//现代式写法
shared_ptr<T>& operator=(shared_ptr<T> sp)
{
swap(sp);
return *this;
}
~shared_ptr()
{
release_pCount();
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return &_ptr;
}
T* get()
{
return _ptr;
}
size_t use_count()
{
return *_pCount;
}
private:
T* _ptr;//管理的内存资源
int* _pCount;//计数
mutex* _mtx;//多线程互斥
//堆上开辟-多个智能指针共享计数和互斥锁
};
// 1.演示引用计数线程安全问题,就把AddRefCount和SubRefCount中的锁去掉
// 2.演示可能不出现线程安全问题,因为线程安全问题是偶现性问题,main函数的n改大一些概率就变大了,就容易出现了。
// 3.下面代码我们使用SharedPtr演示,是为了方便演示引用计数的线程安全问题,将代码中的SharedPtr换成shared_ptr进行测试,可以验证库的shared_ptr,发现结论是一样的
void SharePtrFunc(SharedPtr<Date>& sp, size_t n)
{
cout << sp.Get() << endl;
for (size_t i = 0; i < n; ++i)
{
// 这里智能指针拷贝会++计数,智能指针析构会--计数,这里是线程安全的。
SharedPtr<Date> copy(sp);
// 这里智能指针访问管理的资源,不是线程安全的。所以我们看看这些值两个线程++了2n次,但是最终看到的结果,并一定是加了2n
copy->_year++;
copy->_month++;
copy->_day++;
}
}
int main()
{
SharedPtr<Date> p(new Date);
cout << p.Get() << endl;
const size_t n = 100;
thread t1(SharePtrFunc, p, n);
thread t2(SharePtrFunc, p, n);
t1.join();
t2.join();
cout << p->_year << endl;
cout << p->_month << endl;
cout << p->_day << endl;
return 0;
}
一般来说shared_ptr可以满足资源管理的大部分情况,但是也有些情况是shared_ptr不能处理的,这时候就需要使用weak_ptr
struct ListNode
{
int _data;
shared_ptr<ListNode> _prev;
shared_ptr<ListNode> _next;
~ListNode() { cout << "~ListNode()" << endl; }
};
int main()
{
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> 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的引用计数,即weak_ptr不会参与空间资源的管理,只是作为一个解决循环引用的工具
struct ListNode
{
int _data;
weak_ptr<ListNode> _prev;
weak_ptr<ListNode> _next;
~ListNode(){ cout << "~ListNode()" << endl; }
};
int main()
{
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> 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<class T>
class weak_ptr
{
public:
weak_ptr()
:_ptr(nullptr)
{}
weak_ptr(const weak_ptr<T>& wp)
:_ptr(wp._ptr)
,_pCount(wp._pCount)
{}
weak_ptr(const shared_ptr<T>& sp)
:_ptr(sp._ptr)
, _pCount(sp._pCount)
{}
weak_ptr<T>& operator=(const weak_ptr<T>& wp)
{
_ptr = wp._ptr;
_pCount = wp._pCount;
}
weak_ptr<T>& operator=(const shared_ptr<T>& sp)
{
_ptr = sp._ptr;
_pCount = sp._pCount;
}
private:
T* _ptr;
int* _pCount;
};
// 仿函数的删除器
template<class T>
struct FreeFunc {
void operator()(T* ptr)
{
cout << "free:" << ptr << endl;
free(ptr);
}
};
template<class T>
struct DeleteArrayFunc {
void operator()(T* ptr)
{
cout << "delete[]" << ptr << endl;
delete[] ptr;
}
};
int main()
{
FreeFunc<int> freeFunc;
shared_ptr<int> sp1((int*)malloc(4), freeFunc);
DeleteArrayFunc<int> deleteArrayFunc;
shared_ptr<int> sp2((int*)malloc(4), deleteArrayFunc);
return 0;
}
template<class T>
class DelRef
{
public:
void operator()(T*& ptr)
{
if (ptr)
{
delete ptr;
ptr = nullptr;
}
}
};
template<class T>
class Free
{
public:
void operator()(T*& ptr)
{
if (ptr)
{
free(ptr);
ptr = nullptr;
}
}
};
class FClose
{
public:
void operator()(FILE*& pf)
{
if (pf)
{
fclose(pf);
pf = nullptr;
}
}
};
//shared_ptr:多个智能指针管理一个内存资源,最后一个智能指针释放内存
template<class T,class D=DefaultDel<T>>
class shared_ptr
{
friend class weak_ptr<T>;
public:
explicit shared_ptr(T* ptr)
:_ptr(ptr)
, _pCount(new int(1))
, _mtx(new mutex)
{}
void add_pCount()
{
_mtx->lock();
++(*_pCount);
_mtx->unlock();
}
void release_pCount()
{
bool flg = false;
_mtx->lock();
--(*_pCount);
if ((*_pCount) == 0 && _ptr)
{
cout << "~shared_ptr:" << _ptr << endl;
D del;
del(_ptr);
delete _pCount;
flg = true;
}
_mtx->unlock();
if (flg)
delete _mtx;
}
shared_ptr(shared_ptr<T,D>& sp)
:_ptr(sp._ptr)
, _pCount(sp._pCount)
, _mtx(sp._mtx)
{
add_pCount();
}
//shared_ptr<T>& operator=(shared_ptr<T>& sp)
//{
// if (_ptr != sp._ptr)//管理资源地址相同则不用处理
// {
// release_pCount();
// _ptr = sp._ptr;
// _pCount = sp._pCount;
// _mtx = sp._mtx;
// add_pCount();
// }
// return *this;
//}
void swap(shared_ptr<T,D>& sp)
{
std::swap(_ptr, sp._ptr);
std::swap(_pCount, sp._pCount);
std::swap(_mtx, sp._mtx);
}
//现代式写法
shared_ptr<T>& operator=(shared_ptr<T,D> sp)
{
swap(sp);
return *this;
}
~shared_ptr()
{
release_pCount();
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return &_ptr;
}
T* get()
{
return _ptr;
}
size_t use_count()
{
return *_pCount;
}
private:
T* _ptr;//管理的内存资源
int* _pCount;//计数
mutex* _mtx;//多线程互斥
//堆上开辟-多个智能指针共享计数和互斥锁
};
注:这里的模拟并不一定就是C++中真真的底层实现