赞
踩
首先我们来分析一下下面这段代码
#include<iostream> using namespace std; int div() { int a, b; cin >> a >> b; if (b == 0) throw invalid_argument("除0错误"); return a / b; } void Func() { 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; }
通过分析我们可以知道会有下面三种情况的异常
1、p1这里new 抛异常,由于不存在释放的问题,不需要处理。
2、p2这里new 抛异常,p1成功申请空间,只需抛出异常,然后释放p1。
3、div调用这里又会抛异常,p1、p2都成功申请空间,只需抛出异常,然后释放p1、p2。
然后对代码进行改进,改进代码如下:
#include<iostream> using namespace std; int div() { int a, b; cin >> a >> b; if (b == 0) throw invalid_argument("除零错误"); return a/b; } void Func() { int* p1 = new int; int* p2 = nullptr; try { p2 = new int; } catch (...) { delete p1; throw; } try { cout << div() << endl; } catch (...) { delete p1; delete p2; throw; } delete p1; delete p2; } int main() { try { Func(); } catch (exception& e) { cout << e.what() << endl; } return 0; }
可以看到代码可读性不高,并且很繁琐,所以我们有没有什么方法改进呢?
**什么是内存泄漏:**内存泄漏是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等,出现内存泄漏会导致响应越来越慢,最终卡死
总结: 内存泄漏非常常见,解决方案分为两种:1、事前预防型。如智能指针等。2、事后查错型。如泄漏检测工具
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存问题、文件句柄、网络连接、互斥量等)的简单技术
在对象构造时获取资源, 接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。
这种做法有两大好处:
#include<iostream> using namespace std; template<class T> class SmartPtr { public: SmartPtr(T* ptr) :_ptr(ptr) {} ~SmartPtr() { cout << "delete:" << &_ptr << endl; delete _ptr; } private: T* _ptr; }; int div() { int a, b; cin >> a >> b; if (b == 0) throw invalid_argument("除零错误"); return a/b; } void Func() { SmartPtr<int> sp1(new int); SmartPtr<int> sp2(new int); cout << div() << endl; } int main() { try { Func(); } catch (exception& e) { cout << e.what() << endl; } return 0; }
运行结果:
上述的SmartPtr还不能将其称为智能指针,因为他还不具有指针的行为。指针可以解引用,也可以通过->去访问所指空间中的内容,因此:AutoPtr模板类中还得需要将* 、->重载下,才可让其像指针一样去使用
#include<iostream> using namespace std; 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; }; int div() { int a, b; cin >> a >> b; if (b == 0) throw invalid_argument("除零错误"); return a/b; } void Func() { SmartPtr<int> sp1(new int); SmartPtr<int> sp2(new int); cout << div() << endl; } int main() { try { Func(); } catch (exception& e) { cout << e.what() << endl; } return 0; }
总结一下智能指针的原理:
C++98版本的库中就提供了auto_ptr的智能指针。
auto_ptr的实现原理:管理权转移的思想
#pragma once #include<iostream> using namespace std; namespace Maria { template<class T> class auto_ptr { public: auto_ptr(T* ptr) :_ptr(ptr) {} ~auto_ptr() { if (_ptr) { cout << "delete:" << &_ptr << endl; delete _ptr; } } //管理权转移 auto_ptr(auto_ptr<T>& ap) :_ptr(ap._ptr) { ap._ptr = nullptr; } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } private: T* _ptr; }; void test_auto() { auto_ptr<int> ap1(new int(1)); auto_ptr<int> ap2(ap1); } }
运行结果:
但是当我们想对被ap2拷贝的ap1进行解引用的时候,我们就发现程序崩了。
也就是说,ap1悬空了,访问不到了。
实现原理:简单粗暴的防拷贝
#pragma once #include<iostream> using namespace std; namespace Maria { template<class T> 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<T>& up) = delete;//C++11 private: T* _ptr; unique_ptr(const unique_ptr<T>& up);//C++98 }; void test_unique() { unique_ptr<int> up1(new int(1)); unique_ptr<int> up2(up1); } } T* operator->() { return _ptr; } //防拷贝 //拷贝构造和赋值是默认成员函数,不写会自动生成 //unique_ptr(const unique_ptr<T>& up) = delete;//C++11 private: T* _ptr; unique_ptr(const unique_ptr<T>& up);//C++98 }; void test_unique() { unique_ptr<int> ap1(new int(1)); unique_ptr<int> ap2(ap1); } }
运行结果:
可以看到当我们想要拷贝up1的时候,就无法调用拷贝构造了
实现原理:通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
#pragma once #include<iostream> #include<mutex> using namespace std; #include<thread> namespace Maria { template<class T> class shared_ptr { public: shared_ptr(T* ptr) :_ptr(ptr) ,_pcount(new int(1)) ,_pmtx(new mutex) {} ~shared_ptr() { Release(); } void Release() { bool deleteFlag = false; _pmtx->lock(); if (--(*_pcount) == 0) { cout << "delete:" << &_ptr << endl; delete _ptr; delete _pcount; deleteFlag = true; } _pmtx->unlock(); if (deleteFlag) { delete _pmtx; } } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } //sp2 = sp4 shared_ptr<T> operator=(const shared_ptr<T>& sp) { //没有管理同一块资源 if (_ptr!= sp._ptr) { Release(); _ptr = sp._ptr; _pcount = sp._pcount; _pmtx = sp._pmtx; AddCount(); } return *this; } void AddCount() { _pmtx->lock(); ++(*_pcount); _pmtx->unlock(); } T* get() { return _ptr; } int use_count() { return *_pcount; } shared_ptr(const shared_ptr<T>& sp) :_ptr(sp._ptr) , _pcount(sp._pcount) , _pmtx(sp._pmtx) { AddCount(); } private: T* _ptr; int* _pcount; mutex* _pmtx; }; struct Date { int _year = 0; int _month = 0; int _day = 0; }; void sharePtrFunc(Maria::shared_ptr<Date>& sp, size_t n, mutex& mtx) { cout << sp.get() << endl; for (size_t i = 0; i < n;i++) { Maria::shared_ptr<Date> copy(sp); mtx.lock(); sp->_year++; sp->_month++; sp->_day++; mtx.unlock(); } } void test_shared() { shared_ptr<int> sp1(new int(1)); shared_ptr<int> sp2(sp1); shared_ptr<int> sp3(sp2); shared_ptr<int> sp4(new int(10)); sp4 = sp1; sp1 = sp1; sp1 = sp2; } void test_shared_safe() { Maria::shared_ptr<Date> p(new Date); const size_t n = 1000000; mutex mtx; thread t1(sharePtrFunc, ref(p), n, ref(mtx)); thread t2(sharePtrFunc, ref(p), n, ref(mtx)); t1.join(); t2.join(); cout << p.use_count() << endl; cout << p->_year << endl; cout << p->_month << endl; cout << p->_day << endl; } }
运行结果:
循环引用分析
解决方案:在引用计数的场景下,把节点中的_prev和_next改成weak_ptr就可以了
原理:node1->_next = node2;和node2->_prev = node1;时weak_ptr的_next和_prev不会增加node1和node2的引用计数
struct ListNode { std::weak_ptr<ListNode> _next; std::weak_ptr<ListNode> _prev;; int val; ~ListNode() { cout << "~ListNode()" << endl; } }; void test_shared_cycle() { std::shared_ptr<ListNode> n1 ( new ListNode); std::shared_ptr<ListNode> n2 (new ListNode); n1->_next = n2; n2->_prev = n1; }
运行结果:
继续分析上面的代码,我们可以发现释放对象我们都是写死使用delete,那么对象如果不是new出来的,我们又应该怎样释放呢?shared_ptr设计了一个删除器来解决这个问题
#pragma once #include<functional> #include<iostream> #include<mutex> #include<thread> using namespace std; namespace Maria { template<class T> class shared_ptr { public: shared_ptr(T* ptr = nullptr) :_ptr(ptr) , _pcount(new int(1)) , _pmtx(new mutex) {} template <class D> shared_ptr(T* ptr ,D del) :_ptr(ptr) , _pcount(new int(1)) , _pmtx(new mutex) ,_del(del) {} shared_ptr(const shared_ptr<T>& sp) :_ptr(sp._ptr) , _pcount(sp._pcount) , _pmtx(sp._pmtx) { AddCount(); } ~shared_ptr() { Release(); } void Release() { bool deleteFlag = false; _pmtx->lock(); if (--(*_pcount) == 0) { if (_ptr) { cout << "delete:" << &_ptr << endl; //delete _ptr; _del(_ptr); } delete _pcount; deleteFlag = true; } _pmtx->unlock(); if (deleteFlag) { delete _pmtx; } } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } //sp2 = sp4 shared_ptr<T> operator=(const shared_ptr<T>& sp) { //没有管理同一块资源 if (_ptr != sp._ptr) { Release(); _ptr = sp._ptr; _pcount = sp._pcount; _pmtx = sp._pmtx; AddCount(); } return *this; } void AddCount() { _pmtx->lock(); ++(*_pcount); _pmtx->unlock(); } T* get()const { return _ptr; } int use_count() { return *_pcount; } private: T* _ptr; int* _pcount; mutex* _pmtx; function<void(T*)> _del = [](T* ptr) {/*delete ptr; cout << "delete ptr" << endl;*/ }; }; struct Date { int _year = 0; int _month = 0; int _day = 0; ~Date() {} }; void sharePtrFunc(Maria::shared_ptr<Date>& sp, size_t n, mutex& mtx) { cout << sp.get() << endl; for (size_t i = 0; i < n; i++) { Maria::shared_ptr<Date> copy(sp); mtx.lock(); sp->_year++; sp->_month++; sp->_day++; mtx.unlock(); } } void test_shared() { shared_ptr<int> sp1(new int(1)); shared_ptr<int> sp2(sp1); shared_ptr<int> sp3(sp2); shared_ptr<int> sp4(new int(10)); sp4 = sp1; sp1 = sp1; sp1 = sp2; } void test_shared_safe() { Maria::shared_ptr<Date> p(new Date); const size_t n = 1000000; mutex mtx; thread t1(sharePtrFunc, ref(p), n, ref(mtx)); thread t2(sharePtrFunc, ref(p), n, ref(mtx)); t1.join(); t2.join(); cout << p.use_count() << endl; cout << p->_year << endl; cout << p->_month << endl; cout << p->_day << endl; } template<class T> class weak_ptr { public: weak_ptr() :_ptr(nullptr) {} weak_ptr(const shared_ptr<T>& sp) :_ptr(sp.get()) {} ~weak_ptr() { cout << "~weak_ptr()" << endl; } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } private: T* _ptr; }; struct ListNode { Maria::weak_ptr<ListNode> _next; Maria::weak_ptr<ListNode> _prev;; int val; ~ListNode() { cout << "~ListNode()" << endl; } }; void test_shared_cycle() { Maria::shared_ptr<ListNode> n1 ( new ListNode); Maria::shared_ptr<ListNode> n2 (new ListNode); cout << n1.use_count() << endl; cout << n2.use_count() << endl; n1->_next = n2; n2->_prev = n1; cout << n1.use_count() << endl; cout << n2.use_count() << endl; } //定制删除器 template<class T> struct DeleteArray { void operator()(T* ptr) { cout << "void operator()(T* ptr)" << endl; delete[] ptr; } }; void test_shared_delete() { Maria::shared_ptr<Date> spa0(new Date); Maria::shared_ptr<Date> spa1(new Date[10], DeleteArray<Date>()); Maria::shared_ptr<Date> spa2(new Date[10], [](Date* ptr) { delete[] ptr; cout << "lambda delete[]" << endl; }); Maria::shared_ptr<FILE> spa3(fopen("test.cpp", "r"), [](FILE* ptr) { fclose(ptr); cout << "lambda fclose" << endl; }); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。