目录
1.RAIl(智能指针的雏形)
2.拷贝导致的问题以及智能指针发展历史
2.1拷贝的问题(资源被析构两次)
2.2auto_ptr(资源权转移,不建议使用)
2.3unique_ptr(防拷贝,在不需要拷贝的情况下使用)
2.4.2循环引用的问题(重点)
3.定制删除器
智能指针是拿来处理异常安全问题的
int div()
{int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b;
}void func()
{int* p1 = new int;cout << div() << endl; // 异常安全的问题delete p1;
}int main()
{try{func();}catch (const exception& e){cout << e.what() << endl;}return 0;
}
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源,在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。(就是把原本资源托管给一个类对象)
优势:
template
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; };int div() {int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0错误");return a / b; }void func() {SmartPtr sp1(new int);SmartPtr sp2(new int);SmartPtr sp3(new int);*sp1 = 10;cout << *sp1 << endl;(*sp1)++;(*sp1)++;cout << *sp1 << endl;cout << div() << endl; }int main() {try{func();}catch (const exception& e){cout << e.what() << endl;}return 0; } 执行结果·:
解决了如果抛异常,在堆上开的空间得不到释放,导致的内存问题;
重载operator*和opertaor->,具有像指针一样的行为,可以正常访问
// RAII,把资源托管给一个类对象
// 用起来像指针一样
template
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;
};int main()
{//SmartPtr sp1(new int);//SmartPtr sp2(sp1);SmartPtr sp1(new int);SmartPtr sp2(new int);sp2 = sp1;return 0;
}
执行结果
不能深拷贝的原因
//auto_ptr
namespace LDJ
{templateclass auto_ptr{public:auto_ptr(T* ptr):_ptr(ptr){}auto_ptr(auto_ptr& sp):_ptr(sp._ptr)//:_ptr(nullptr){// 管理权转移sp._ptr = nullptr;//swap(_ptr, sp._ptr);}~auto_ptr(){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}}// 像指针一样使用T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};
}// 结论:auto_ptr是一个失败设计
int main()
{LDJ::auto_ptr sp1(new int);LDJ::auto_ptr sp2(sp1); // 管理权转移// sp1悬空*sp2 = 10;cout << *sp1 << endl;cout << *sp2 << endl;return 0;
}
执行结果
namespace LDJ
{template>class unique_ptr{public:unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}}// 像指针一样使用T& operator*(){return *_ptr;}T* operator->(){return _ptr;}unique_ptr(const unique_ptr& sp) = delete;unique_ptr& operator=(const unique_ptr& sp) = delete;private:T* _ptr;};
}
namespace LDJ
{templateclass shared_ptr{public:shared_ptr(T* ptr = nullptr):_ptr(ptr), _pRefCount(new int(1)){}void AddRef(){++*(_pRefCount);}void Release(){if (--(*_pRefCount) == 0 && _ptr){delete _ptr;delete _pRefCount;}}shared_ptr(const shared_ptr& sp):_ptr(sp._ptr), _pRefCount(sp._pRefCount), _pmtx(sp._pmtx){AddRef();}~shared_ptr(){Release();}// 像指针一样使用T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;int* _pRefCount;}
}
2.4.1operator=需要处理一下
shared_ptr& operator=(const shared_ptr& sp){//if (this != &sp)if (this->_pRefCount != sp._pRefCount){Release();_ptr = sp._ptr;_pRefCount = sp._pRefCount;AddRef();return *this;}}
struct Node {~Node(){cout << "~Node" << endl;}int val;std::shared_ptr
_next;std::shared_ptr _prev; }; int main() {std::shared_ptr sp1(new Node);std::shared_ptr sp2(new Node);sp1->_next = sp2;sp2->_prev = sp1;return 0; } 执行结果:如果被析构了会打印两行~Node,然而并没有,说明sp1和sp2都没有被释放
循环引用的具体原因:
解决循环引用weak_ptr
struct Node
{~Node(){cout << "~Node" << endl;}int val;std::weak_ptr _next;std::weak_ptr _prev;
};
int main()
{std::shared_ptr sp1(new Node);std::shared_ptr sp2(new Node);cout << "sp1Count:" << sp1.use_count() << endl;cout << "sp2Count:" << sp2.use_count() << endl;sp1->_next = sp2;sp2->_prev = sp1;cout << "sp1Count:" << sp1.use_count() << endl;cout << "sp2Count:" << sp2.use_count() << endl;return 0;
}
执行结果:前后sp1和sp2的计数都为1;所以可以证明weak_ptr不增加引用计数且解决了循环引用的问题;
那么如果资源不是new出来的呢?比如:new[]、malloc、fopen,delete就不够用了
template
struct DeleteArray
{void operator()(const T* ptr){delete[] ptr;}
};
struct DeleteFile
{void operator()(FILE* ptr){fclose(ptr);}
};unique_ptr up1(new A);unique_ptr> up2(new A[10]);unique_ptr up3(fopen("test.txt", "w"));