1、为什么需要智能指针?
智能指针是一种预防型的内存泄漏的解决方案。智能指针在C++没 有垃圾回收器环境下,可以很好的解决异常安全等带来的内存泄漏问题。
2、什么是智能指针?
简单的来说就是设计一个类,让它能够具有指针的行为(重载 * 和 -> ),同时能够自主管理资源的申请与释放(体现在构造函数与析构函数);
以下为一个简单的只能指针的代码实现:
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;
};
3、智能指针的分类
auto_ptr
>> 实现原理:由于上述简易智能指针存在浅拷贝问题,所以auto_ptr通过管理权的转移使多个多个对象虽然能够共用一份资源,但是只有一个对象能够对资源具有释放的权利。
>>缺点:当对象拷贝或者赋值后,前面的对象就悬空了
>>模拟实现:
template<class T>
class Auto_ptr {
public:
Auto_ptr(T* ptr = nullptr)
: _ptr(ptr)
{}
// 如果当前对象调用了拷贝构造函数就将Ap中_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) {
// 释放当前对象的资源
if (_ptr) {
delete _ptr;
}
// 转移资源管理权
_ptr = Ap._ptr;
Ap._ptr = nullptr;
}
return *this;
}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
~Auto_ptr() {
if (_ptr){
delete _ptr;
}
}
private:
T* _ptr;
};
unique_ptr
>> 实现原理:实现一份资源只能被一个对象占用(在auto_ptr的基础上禁用了拷贝构造函数与赋值运算符重载函数
>> 缺点:无法实现资源的共享
>>模拟实现:
template<class T>
class Unique_ptr{
public:
Unique_ptr(T* ptr)
: _ptr(ptr)
{}
// 重载*
T& operator*() {
return *_ptr;
}
// 重载->
T* operator->() {
return _ptr;
}
~Unique_ptr() {
if (_ptr){
delete _ptr;
}
}
// C++98中实现方法为:将拷贝构造函数和赋值运算符重载函数的声明私有化,也可以protected化
private:
Unique_ptr(Unique_ptr<T>& Up);
Unique_ptr& operator=(Unique_ptr<T>& Up);
// C++11中直接将拷贝构造函数和赋值运算符重载函数使用delete关键字删除
Unique_ptr(Unique_ptr<T>& Up) = delete;
Unique_ptr& operator=(Unique_ptr<T>& Up) = delete;
private:
T* _ptr;
};
shared_ptr
>> 实现原理:通过引用计数实现多个对象共享一份资源,当有一个对象需要销毁时,资源计数就减1,若资源计数为了0就必须要释放资源
>> 缺点:
①不能实现对所有类型资源的释放;这个可以使用定制删除器解决,使用删除器时类似于使用仿函数
②线程安全问题:主要体现在两个方面:1)由于shared_ptr管理的资源是存放在堆上,当多个线程同时去访问同一份资源时可能会导致线程安全问题;2)由于引用计数是多个线程共享的,所以引用计数的++和--操作就是非原子性,可能会被操作多次,会导致资源问释放或者程序崩溃等线程安全问题;解决方法就是对引用计数的++和--操作加锁
③循环引用问题:主要体现在双向循环链表中,到两个节点连起来之后,data1的Sp1._ptr资源释放时候usecount-1,此时usecount为1,data1还不能被销毁,因为data2中的_pre还在使用data1的资源;data2的Sp2._ptr使用完毕之后usecount-1,此时也为1,data2也不能销毁,因为data1的pNext还在使用;这样就导致data1想销毁就得等data2的_pre离开,data2想要销毁就得等data1的pNext离开,双方僵持下来就导致了循环引用
>>模拟实现:已解决了线程安全问题
template<class T>
class Shared_ptr {
public:
Shared_ptr(T* ptr = nullptr)
: _ptr(ptr)
, _pRefCount(new int(1))
, _pMutex(new mutex)
{}
Shared_ptr(Shared_ptr<T>& Sp)
: _ptr(Sp._ptr)
, _pRefCount(Sp._pRefCount)
, _pMutex(Sp._pMutex)
{
AddRefCount();
}
Shared_ptr<T>& operator(const Shared_ptr<T>& Sp) {
if (_ptr != Sp._ptr) {
// 释放旧资源
Realse();
// 共享新资源
_ptr = Sp._ptr;
_pRefCount = Sp._pRefCount;
_pMutex = Sp._pMutex;
// 资源计数+1
AddRefCount();
}
return *this;
}
~Shared_ptr() {
Realse();
}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
// 需要对++操作进行加锁
void AddRefCount() {
_pMutex->lock();
++(*_pRefCount);
_pMutex->unlock();
}
private:
void Realse() {
_pMutex->lock();
// 当资源计数-1后为0释放资源
if (--(*_pRefCount) == 0) {
delete _ptr;
delete _pRefCount;
}
_pMutex->unlock();
if (_pMutex) {
delete _pMutex;
}
}
private:
T* _ptr;
int* _pRefCount;
mutex* _pMutex;
};
weak_ptr:weak_ptr主要就是用来与shared_ptr搭配使用解决循环引用的问题
>>实现原理,将data1和data2中的_pNext和_pre类型改为weak_ptr;当data1->_pNext = data2和data2->_pre = data1时不会增加data1和data2的资源计数
4、C++11中的智能指针与boost库中的智能指针的区别
boost库中的智能指针是C++11智能指针的原版,以下为智能指针的发展史
① C++ 98 中产生了第一个智能指针auto_ptr.
② C++ boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr.
③C++ TR1,引入了shared_ptr等。不过注意的是TR1并不是标准版。
④C++ 11,引入了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr对应boost的 scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的。