目录
经过异常的学习,我知道抛异常如果不注意是有可能造成资源泄漏的问题的,那么如何避免这种问题同时还能很好地抛异常呢?
RAII是一种利用对象生命周期来控制程序资源的技术 (其实也是一种指导思想):
template<class T>
class smartPtr
{
public:
smartPtr(T* ptr):_ptr(ptr){}
~smartPtr()
{
cout << "delete[]" <<_ptr<< endl;
delete[] _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
这就是一个简单的RAII指导写出来的智能指针(auto_ptr)的一小部分
在对象构造的时候获取资源,在对象析构的时候释放资源
采用这种方法,对象所需要的资源在其生命周期内始终保持有效
auto_ptr里面比较难解决的是它的赋值拷贝问题 ,如果采用类自己生成的赋值构造函数是不行的,默认的构造函数对于内置类型采用值拷贝,自定义类型调用该类型的拷贝构造。
值拷贝的话,就会面临重复delete的情况,针对这种情况,auto_ptr采用资源转移的方式,新对象来管理这份资源,原来的对象管理资源的指针置空
unique_ptr:
这个智能指针针对赋值拷贝问题的解决办法会比较暴力一些,它是直接让赋值拷贝函数不生成
unique_ptr(const unique_ptr<T>& ptr) = delete;
unique_ptr<T>& operator=(const unique_ptr<T>& ptr) = delete;
shared_ptr:
shared_ptr:就是要求需要能够进行赋值拷贝,那该怎么处理资源清理的问题呢
template<class T>
class shared_ptr
{
public:
shared_ptr(T* ptr) :_ptr(ptr), _pCount(new int(1)) {}
~shared_ptr()
{
release();
}
void release()
{
if (--(*_pCount) == 0)
{
delete _ptr;
_ptr = nullptr;
delete _pCount;
_pCount = nullptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* Get()
{
return _ptr;
}
shared_ptr(const shared_ptr<T>& sp) :_ptr(sp._ptr), _pCount(sp._pCount)
{
(*_pCount)++;
}
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{
if (_ptr != sp._ptr)
{
release();
_ptr = sp._ptr;
_pCount = sp._pCount;
(*_pCount)++;
}
return *this;
}
private:
T* _ptr;
int* _pCount;
};
shared_ptr里面需要注意,我们使用的引用计数的加减问题,比如:当我们再进行赋值的时候,一个指针被赋值,那他就不再指向之前的资源了,所以需要再赋值之前对原来资源的引用计数进行减减
shared_ptr还存在一个循环引用的问题 :
struct listnode {
std::shared_ptr<listnode> a;
std::shared_ptr<listnode> b;
};
int main()
{
std::shared_ptr<listnode> sp1(new listnode);
std::shared_ptr<listnode> sp2(new listnode);
sp1->a = sp2;
sp2->b = sp1;
}
大家看现在的这个代码就已经构成了循环引用的问题了
sp1 和 sp2 释放后,a和b都是自定义成员变量,析构需要调用自己的析构函数,a想要调用自己的析构函数就需要b释放,b释放需要a调用自己的析构函数释放,这样就构成循环引用的问题了
weak_ptr:
template<class T>
class weak_ptr
{
public:
weak_ptr():_ptr(nullptr) {}
~weak_ptr()
{
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* Get()
{
return _ptr;
}
weak_ptr(const shared_ptr<T>& sp) :_ptr(sp.Get())
{
}
weak_ptr<T>& operator=(const shared_ptr<T>& sp)
{
if (_ptr != sp.Get())
{
_ptr = sp.Get();
}
return *this;
}
private:
T* _ptr;
};
struct listnode {
zcj::weak_ptr<listnode> a;
zcj::weak_ptr<listnode> b;
};
int main()
{
zcj::shared_ptr<listnode> sp1(new listnode);
zcj::shared_ptr<listnode> sp2(new listnode);
cout<<sp1.use_count();
cout << sp2.use_count();
sp1->a = sp2;
sp2->b = sp1;
cout << sp1.use_count();
cout << sp2.use_count();
}
weak_ptr:起到指向资源,但是不参与资源清理的工作,依然也是可以通过weak_ptr访问和修改资源的。
这只是我们自己设置的结构体,那不同的资源有不同的释放的方法,可是要注意一点哦我们的智能指针默认的释放方式是delete。
可以看到我们是可以通过自定义仿函数的方式,这样应对不同的资源,由于我们自己知道自己申请的资源的释放方式是什么所以,所以可以自定义 。
不同的是shared_ptr,是通过构造函数传进去的