- int div()
- {
- int a, b;
- cin >> a >> b;
- if (b == 0)
- throw invalid_argument("除0错误");
- return a / b;
- }
- void Func()
- {
- // 1、如果p1这里new 抛异常会导致p1不会Delete而导致内存泄漏
- // 2、如果p2这里new 抛异常会导致p1和p2都不会delete而导致内存泄漏
- // 3、如果div调用这里又会抛异常会导致p1和p2都不会delete而导致内存泄漏
- int* p1 = new int;
- int* p2 = new int;
- cout << div() << endl;
- delete p1;
- delete p2;
- }
- int main()
- {
- try
- {
- Func();
- }
- catch (exception& e)
- {
- cout << e.what() << endl;
- }
- return 0;
- }
综上,我们需要一种可以自动回收内存空间的指针。如果我们把指针套在一种类内,类在析构时会带着指针申请的空间一起析构掉。
- class A
- {
- public:
- ~A()
- {
- cout << "~A()" << endl;
- }
- //private:
- int _a1 = 0;
- int _a2 = 0;
- };
RAII(Resource Acquisition Is Initialization)直译:在初始化的时候获取资源。即在对象构造的时候获取资源,在对象的生命周期之中,控制对资源的访问,最后在对象析构的时候释放资源。
好处:
模拟实现1
- template<class T>
- class SmartPtr {
- public:
- SmartPtr(T* ptr = nullptr)
- : _ptr(ptr)
- {}
-
- ~SmartPtr()
- {
- if (_ptr)
- {
- cout << "Delete:" << _ptr << endl;
- delete _ptr;
- }
- }
-
- private:
- T* _ptr;
- };
上述还不能将其称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可 以通过->去访问所指空间中的内容,因此:模板类中还得需要将* 、->重载下,才可让其 像指针一样去使用。
- T& operator*()
- {
- return *_ptr;
- }
-
- T* operator->()
- {
- return _ptr;
- }

智能指针的思想比较简单,但如何拷贝构造是个问题

这里程序崩溃了 原因依然是之前的老问题,对同一块空间进行了两次析构。(别试,崩溃了)
那我们要跟之前一样实现深拷贝吗?不能,因为我们要的就是浅拷贝,要的就是用新的指针来指向我这块资源。
下面我们来看一下C++是如何对这里进行处理。
auto_ptr
sp2(sp1)
C98里面的大槽点,对拷贝构造的处理沿用了右值引用的资源转移。(可右值本来就是将亡值,给了就给了。左值可不是啊,如果我还需要对sp1进行访问,那就会报访问空指针的错误)。我们将auto_ptr模拟实现一下
- namespace chy
- {
- template<class T>
- class auto_ptr
- {
- public:
- //用指针来构造的
- auto_ptr(T* ptr = nullptr)
- :_ptr(ptr)
- {}
- //C98中的auto_ptr对于拷贝的问题处理有问题,相当于右值的资源转移
- //会把ap里面的资源转移给构造出来的智能指针,而ap将不在管理这些资源(置空)
- //所以如果我们再次对与于ap进行解引用操作,将会出现访问空指针的问题
- auto_ptr(auto_ptr
& ap) - :_ptr(ap._ptr)
- {
- ap._ptr = nullptr;
- }
- auto_ptr
& operator=(const auto_ptr& ap) - {
- if (this != &ap)//如果不是自己给自己赋值
- {
- if (_ptr)//原本的资源不管了(删除)
- {
- cout << "Delete" << _ptr << endl;
- delete _ptr;
- }
- _ptr = ap._ptr;
- ap._ptr = nullptr;
- }
- return *this;
- }
- ~auto_ptr()
- {
- if (_ptr)
- {
- cout << "Delete:" << _ptr << endl;
- delete _ptr;
- }
- }
- T& operator*()
- {
- return *_ptr;
- }
- T* operator->()
- {
- return _ptr;// &(*_ptr)
- }
- private:
- T* _ptr;
- };
-
- }
拷贝是没问题的 但是不能对sp1再进行解引用操作了

遇到问题,逃避问题
不让拷贝
unique_ptr(const unique_ptr
& ap) = delete;
unique_ptr& operator=(unique_ptr & ap) = delete;
- template<class T>
- class unique_ptr
- {
- public:
- unique_ptr(T* ptr=nullptr)
- :_ptr(ptr)
- {}
- unique_ptr(const unique_ptr
& ap) = delete; - unique_ptr
& operator=(unique_ptr& ap) = delete; -
- ~unique_ptr()
- {
- if (_ptr)
- {
- cout << "Delete:" << _ptr << endl;
- delete _ptr;
- }
- }
- T& operator*()
- {
- return *_ptr;
- }
- T* operator->()
- {
- return _ptr;
- }
- private:
- T* _ptr;
- };
引用计数,每有一个指针指向我这块区域,引用计数++,当最后一个指向改位置的智能指针析构时,带着区域一起走。
我这个计数怎么设置?
- {
- //..
- private:
- static int count;
- //..
- }
- template<class T>
- int shared_ptr
::_count = 0;
这样设计可以解决多个指针指向同一块空间的问题。可如果我有两块空间呢?这两块空间的引用计数是不是就变成了同一个了?
所以我们不能纳入静态成员变量,同时我们注意到,我们每指向一块空间,就需要一个单独的计数。那我们在指向空间的时候构造计数不就好了?
而且,由于多个指针共享一个计数,理所应当的成为临界资源,在访问修改时需要加锁!
于是我们在构造函数中下笔
- { //...
- shared_ptr(T* sp = nullptr)
- : _ptr(sp)
- , _pCount(new int(1))
- ,_pmtx(new std::mutex)
- {}
- private:
- T* _ptr;
- int* _pCount;
- std::mutex* _pmtx;
- }
整体实现
- template<class T>
- class shared_ptr
- {
- public:
- shared_ptr(T* sp=nullptr)
- :_ptr(sp)
- ,_pCount(new int(1))
- ,_pmtx(new std::mutex)
- {}
-
- shared_ptr(const shared_ptr
& sp) - :_ptr(sp._ptr)
- ,_pCount(sp._pCount)
- ,_pmtx(sp._pmtx)
- {
- AddCount();
- }
- shared_ptr
& operator=(const shared_ptr& sp) - {
- if (this == &sp) return *this;
- Release();
- _ptr = sp._ptr;
- _pCount = sp._pCount;
- _pmtx = sp._pmtx;
- AddCount();
- return *this;
- }
- void AddCount()
- {
- _pmtx->lock();
- ++(*_pCount);
- _pmtx->unlock();
- }
- void Release()
- {
- _pmtx->lock();
- bool flag = false;
-
- if (--(*_pCount) == 0 && _ptr)
- {
- cout << "Delete: " << _ptr << endl;
- delete _ptr;
- delete _pCount;
- flag = true;
- }
- _pmtx->unlock();
- if (flag) delete _pmtx;
-
- }
- ~shared_ptr()
- {
- Release();
- }
-
- T& operator*()
- {
- return *_ptr;
- }
- T* operator->()
- {
- return _ptr;
- }
- //这个接口是给weak_ptr准备的 用来拿到指针
- T* get() const
- {
- return _ptr;
- }
- private:
- T* _ptr;
- int* _pCount = 0;
- std::mutex* _pmtx;
- };


函数结束时,n2先析构,然后n1析构。
_next管着右边的节点。 _prev管着左边的节点
_next析构,右边节点就delete。 _prev析构,左边节点就delete
那_next什么时候析构呢?左边的节点被delete,调用析构函数,其成员函数_next就析构了。
那左边的节点什么时候被delete呢? _prev析构的时候,左边的节点析构。
那_prev什么时候析构呢?右边的节点被delete,调用析构函数,其成员函数_prev就析构了。
那左边的节点什么时候被delete呢?........(开始套娃)
weak_ptr不是常规的智能指针,没有RAII,不支持直接管理资源。weak_ptr主要用shared_ptr构造,用来解决shared_ptr循环引用问题。
在shared_ptr中新增一个函数,用来返回指针
- template<class T>
- class shared_ptr
- {
- public:
-
- //这个接口是给weak_ptr准备的 用来拿到指针
- T* get() const
- {
- return _ptr;
- }
- private:
- T* _ptr;
- int* _pCount = 0;
- std::mutex* _pmtx;
- };
weak_ptr的简单实现
- template<class T>
- class weak_ptr
- {
- public:
- weak_ptr()
- :_ptr(nullptr)
- {}
- weak_ptr(const shared_ptr
& sp) - :_ptr(sp.get())
- {}
- weak_ptr(const weak_ptr
& wp) - :_ptr(wp._ptr)
- {}
- weak_ptr
& operator=(const shared_ptr& sp) - {
- _ptr = sp.get();
- return *this;
- }
-
- T& operator*()
- {
- return *_ptr;
- }
-
- T* operator->()
- {
- return _ptr;
- }
-
-
- private:
- T* _ptr;
- };