经过异常的学习,我知道抛异常如果不注意是有可能造成资源泄漏的问题的,那么如何避免这种问题同时还能很好地抛异常呢?
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,是通过构造函数传进去的