RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内 存、文件句柄、网络连接、互斥量等等)的简单技术。 在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在 对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。
这种做 法有两大好处:
- 不需要显式地释放资源。
- 采用这种方式,对象所需的资源在其生命期内始终保持有效。
先实现一个普通的智能指针
template<class T> class smart_ptr { public: smart_ptr(T* ptr) :_ptr(ptr) {} ~smart_ptr() { if (_ptr) { delete _ptr; _ptr = nullptr; } } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } private: T* _ptr; };
即使中间有异常抛出,当作用域走完生命周期,析构函数会自动调用。完成析构,就不会发生了内存泄漏问题。
unique_ptr 基本实现
template<class T> class unique_ptr { public: unique_ptr(T* ptr) :_ptr(ptr) {} ~unique_ptr() { if (_ptr) { delete _ptr; _ptr = nullptr; } } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } unique_ptr(unique_ptr& p) = delete; unique_ptr operator=(unique_ptr & p) = delete; private: T* _ptr; };
unique_ptr跟普通智能指针区别不大 只是unique_ptr是不允许用拷贝和赋值的
它能使用拷贝和赋值 使用的是一种计数的方法。
template<class T> class shared_ptr { public: shared_ptr(T* ptr) :_ptr(ptr) , pcount(new int(1)) {} ~shared_ptr() { Relses(); } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } shared_ptr(shared_ptr& p) :_ptr(p._ptr) ,pcount(p.pcount) { (*pcount)++; } void Relses() { if (--(*pcount) == 0 && _ptr) { cout << _ptr << endl; delete _ptr; delete pcount; _ptr = nullptr; pcount = nullptr; } } shared_ptr & operator=(shared_ptr & p) { if (_ptr != p._ptr)//当两个指针地址不相同才赋值 { Relses(); _ptr = p._ptr; pcount = p.pcount; *(pcount)++; } return *this; } private: T* _ptr; int* pcount; };
weak_ptr基本实现
此智能指针是用来辅助shared_ptr的 用来解决循环引用问题
template<class T> class weak_ptr { public: weak_ptr() :_ptr(nullptr) {} weak_ptr(const shared_ptr& p) :_ptr(p.get()) {} weak_ptr & operator=(const shared_ptr & p) { if (_ptr != p.get()) { _ptr = p.get(); } return *this; } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } public: T* _ptr; };
循环引用问题
struct listnode { shared_ptr_next = nullptr; shared_ptr _prev = nullptr; ~listnode() { cout << "析构" << endl; } }; shared_ptr < listnode > p1(new listnode); shared_ptr < listnode > p2(new listnode); p1->_next = p2; p2->_prev = p1;
可以看到,这么本身要析构两次的,但没有释放,造成了内存泄漏
p1指向next的空间
p2指向prev的空间
指针指针赋值
next管理着prev的值
prev也管理着next的值
这时管理next有2个 p1 和prev
这是管理prev有2个 p2和next
p1和p2释放后 next和prev还有1 所以无法释放
而shared_ptr是用计数方式。所以要释放时 管理某个地址的指针为0时,才可以释放此地址
而他们两个指针互相管理着,都在等着对方为0 造成了无法释放的问题 造成内存泄漏 这叫做循环引用问题
此时就要用到weak_ptr来辅助shared_ptr来解决
weak_ptr的作用就是帮next和prev管理。不需要next和prev亲自去管理。
当weak_ptr帮忙管理时
这时管理next的有1个 p1
这时管理prev的有1个 p2
当p1和p2析构时,next和prev就自动释放了
weak只帮忙管理,不帮忙析构。
在不用拷贝时,就用unique_ptr,要拷贝就用shared_ptr
循环引用比较少见,遇到时,要用weak_ptr辅助帮忙。
定制删除器
可以自己写一个专门用来释放这个定制类
template<class T> struct Deletea { public: void operator()(T* ptr) { cout << "delete析构" << endl; delete[] ptr; } }; template<class T> struct Free { public: void operator()(T* ptr) { cout << "free析构" << endl; free(ptr); } }; struct test { ~test() { cout << "析构"; } int a; int b; }; unique_ptr> p1(new test[10]); unique_ptr > p2((test*)malloc(sizeof(test)*10)); ,free,deletea
Original: https://www.cnblogs.com/LonelyMoNan/p/16743363.html
Author: lemon-Breeze
Title: 智能指针
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/607469/
转载文章受原作者版权保护。转载请注明原作者出处!