C++中为什么要引入智能指针
- malloc 或者new出来的空间没有进行释放,存在内存泄漏等问题
- 异常安全问题,如果在malloc和free之间存在异常抛出,程序转移到异常处执行还是会存在内存泄漏问题
智能指针的使用及原理
1.RAII
RAII只一种利用对象生命周期来控制程序资源(如内存,操作句柄,网络连接,互斥量等),在对象构造时获取资源,接着控制对资源的访问在对象的生命周期内始终有效,最后在对象析构的时候释放资源,实际上就是把管理一份资源的责任托付给了一个对象。
- 不需要显示的释放资源
- 保证对象所需的资源在其生命周期内始终有效
#include <iostream>
using namespace std;
template <class T>
class SmartPtr
{
public:
SmartPtr(T* ptr = nullptr)
:_ptr(ptr)
{}
~SmartPtr()
{
if (_ptr)
{
delete _ptr;
}
}
private:
T* _ptr;
};
void MergeSort(int* a, int n)
{
int* tmp = (int*) malloc(sizeof(int*));
SmartPtr<int> sp(tmp);
//处理其他的逻辑功能
}
int main()
{
try
{
int a[5] = { 2,1,7,5,9 };
MergeSort(a, 6);
}
catch (const exception& e)
{
cout << e.what() << endl;
}
system("pause");
return 0;
}
上述这种SmartPtr还不能够称为智能指针,因为指针还要有解引用*和->的功能。我们在AutoPtr中对*和->功能进行重载。
#include <iostream>
using namespace std;
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;
};
void MergeSort(int* a, int n)
{
int* tmp = (int*) malloc(sizeof(int*));
SmartPtr<int> sp(tmp);
//处理其他的逻辑功能
}
struct Date
{
int _year;
int _month;
int _day;
};
int main()
{
SmartPtr<int> sp1(new int);
*sp1 = 10;
cout << *sp1 << endl;
SmartPtr<Date> sparray(new Date);
sparray->_year = 2000;
sparray->_month = 1;
sparray->_day = 1;
system("pause");
return 0;
}
智能指针的原理:
1.RAII特性
2.重载operator*和operator->,具有指针一样的行为
2.3 std::auto_ptr
c++98中就提出了auto_ptr智能指针
#include <iostream>
#include <memory>
using namespace std;
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> ap(new Date);
//auto_ptr的问题:当对象拷贝或者赋值后,前面的对象就悬空了
//C++98中设计的auto_ptr问题是非常明显的,所以实际很多公司明确规定了不能使用auto_ptr
ap->_year = 2018;
return 0;
}
auto_ptr的实现原理
#include <iostream>
using namespace std;
template <class T>
class Auto_Ptr
{
public:
Auto_Ptr(T* ptr = NULL)
:_ptr(ptr)
{}
~Auto_Ptr()
{
if (_ptr)
delete _ptr;
}
//一旦触发拷贝,就将ap中的资源转移到当前对象中,然后令ap与其所管理的资源断开连接
//这样就解决了一块空间被多个对象使用造成程序崩溃的问题
Auto_Ptr(Auto_Ptr<T>& ap):
_ptr(ap._ptr)
{
ap.ptr = NULL;
}
Auto_Ptr<T>& operator=(const Auto_Ptr<T>& ap)
{
//检测是否给自己赋值
if (this != &ap)
{
if(_ptr)
{
delete _ptr;
}
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
T& operator*() { return *_ptr };
T* operator->() { return _ptr };
private:
T * _ptr;
};
C++11提供了更靠谱的std::unique_ptr
unique_ptr简单粗暴的防止拷贝来解决多个对象共享同一份资源的问题
template <class T>
class UniquePtr
{
public:
UniquePtr(T* ptr = nullptr)
:_ptr(ptr)
{}
~UniquePtr()
{
if (_ptr)
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
//C++98防止拷贝的方式,只声明不定义+声明成私有
UniquePtr(UniquePtr<T> const&);
UniquePtr& operator=(UniquePtr<T> const&);
//C++11防止拷贝的方式:delete
UniquePtr(UniquePtr<T> const&) = delete;
UniquePtr& operator=(UniquePtr<T> const&) = delete;
private:
T * _ptr;
};
shared_ptr是C++11中提供的支持拷贝的智能指针
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的实现原理:通过引用计数的方式实现多个shared_ptr之间的资源共享,例如同一间教室的人共享一个点灯,但是只有最后离开的人需要关灯。
- shared_ptr在其内部,给每个资源都维护了一份计数,用来记录资源被多少个对象共享;
- 在对象被销毁时(析构函数调用时),说明自己不使用该资源了,引用计数减1;
- 如果引用计数为0,说明自己是最后一个使用该资源的对象,必须释放该资源;
- 如果不是0,说明除了自己还有其他对象在使用该资源,不能释放该资源,否则其他对象会变成野指针;
//模拟实现简单的shared_ptr
template <class T>
class Shared_Ptr
{
public:
Shared_Ptr(T* ptr = nullptr)
:_ptr(ptr),
_pRefCount(new int(1)),
_pMutex(new mutex)
{}
~Shared_Ptr()
{
Release();
}
Shared_Ptr(const SharedPtr<T>& sp)
:_ptr(sp._ptr),
_pRefCount(sp._pRefCount),
_pMutex(sp._pMutex)
{
AddRefCount();
}
shared_ptr<T>& operator=(const SharedPtr<T>& sp)
{
if (_ptr != sp._ptr)
{
//释放管理的旧资源
Release();
//共享管理新对象的资源,并增加引用计数
_ptr = sp._ptr;
_pRefCount = sp._pRefCount;
_pMutex = sp._pMutex;
AddRefCount();
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
void AddRefCount()
{
//加锁或者使用加1的原子操作
_pMutex->lock();
++(*_pRefCount);
_pMutex->unlock();
}
private:
void Release()
{
bool deleteflag = false;
//引用计数减1,如果减到1,释放资源
_pMutex->lock();
if (--(*_pRefCount) == 0)
{
delete _ptr;
delete _pRefCount;
deleteflag = true;
}
_pMutex->unlock();
if (deleteflag == true)
delete _pMutex;
}
private:
int* _pRefCount; //引用计数
T* _ptr; //指向管理资源的指针
mutex* _pMutex; //互斥锁
};