1. 初識智能指針ios
在c++語言中沒有垃圾回收機制,內存泄漏這個問題就不得不讓程序員本身來解決,稍有不慎就會給軟件帶來Bug,幸運的是咱們可使用智能指針去解決內存泄漏的問題。this
(1)動態申請堆空間,用完後不歸還;spa
(2)C++ 語言中沒有垃圾回收的機制;
1)Java、C# 語言中都引入了垃圾回收機制,按期檢測內存,若發現沒有使用,則回收;
2)垃圾回收機制能夠很好的避免內存泄漏;
3)C++ 中的動態內存申請和歸還徹底依賴開發者,稍有不慎就會出錯;
(3)指針沒法控制所指堆空間的生命週期;(根本緣由)
1)經過指針能夠指向動態內存空間,可是卻不可以控制動態內存空間的生命週期;
2)也就是指針和動態內存空間沒有必然聯繫,即便指針變量銷燬了,動態內存空間還能夠存在;
補充:屢次釋放多個指針指向的同一塊堆空間也會使軟件出現Bug;
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 class Test 7 { 8 int i; 9 public: 10 Test(int i) 11 { 12 this->i = i; 13 } 14 int value() 15 { 16 return i; 17 } 18 ~Test() 19 { 20 } 21 }; 22 23 int main() 24 { 25 for(int i=0; i<5; i++) 26 { 27 // 指針p指向所申請的堆空間,可是並無手動歸還這塊內存;當進行下一次循環時,指針p又指向了一塊新的堆空間,這樣前一次的堆空間就永遠沒法歸還了, 28 // 同時,指針p是一個局部變量,for循環結束後指針P就銷燬了,這就意味着這片空間永遠沒法歸還了; 29 Test* p = new Test(i); 30 31 cout << p->value() << endl; 32 33 // delete p; // 正確作法:每次用完以後記得歸還所申請的堆空間,不然就會形成內存泄漏 34 } 35 36 return 0; 37 }
(1)須要一個特殊的指針,即智能指針對象(普通類對象,經過重載指針操做符就可使對象指向堆空間),經過類的構造函數完成;
(2)指針生命週期結束時主動釋放堆空間,能夠經過類的析構函數完成;
(3)一片堆空間最多隻能由一個指針標識,爲的是避免屢次釋放內存,經過拷貝構造函數和賦值操做符完成;
(4)杜絕指針運算和指針比較;
1) 杜絕指針運算能夠避免指針越界和野指針;
2)上面的第三個需求知足了,指針比較就沒有意義了;
3)不重載類的運算符(算術運算符、關係運算符、++、--),當進行指針(類對象)運算與比較時,程序會編譯失敗。
(5)重載指針特徵操做符(-> 和 *);
1)經過重載指針操做符使得類對象具有了指針的行爲;
2)建立一個類對象,讓這個對象經過操做符重載模擬真正的指針行爲;
注:只能經過類的成員函數重載指針操做符,且該重載函數不能使用參數;
經過類的成員函數重載指針特徵操做符,從而使得類對象能夠模擬指針的行爲,這個類對象稱爲智能指針。
使用智能指針的注意事項:只能指向堆空間的對象或變量,不容許指向棧對象。
智能指針的表現形象:使用類對象來取代指針。
1 #include <iostream> 2 3 using namespace std; 4 5 class Test 6 { 7 int i; 8 public: 9 Test(int i) 10 { 11 cout << "Test(int i)::" << i << endl; 12 this->i = i; 13 } 14 int value() 15 { 16 return i; 17 } 18 ~Test() 19 { 20 cout << "~Test()::" << i << endl; 21 } 22 }; 23 24 class Pointer 25 { 26 private: 27 Test *mp; 28 public: 29 Pointer(Test *p = NULL) 30 { 31 mp = p; 32 } 33 Pointer(const Pointer& obj) 34 { 35 mp = obj.mp; 36 const_cast<Pointer&>(obj).mp = NULL; 37 } 38 Pointer& operator=(const Pointer& obj) 39 { 40 if(this != &obj) 41 { 42 if(mp != NULL) 43 { 44 delete mp; 45 } 46 mp = obj.mp; 47 const_cast<Pointer&>(obj).mp = NULL; 48 } 49 50 return *this; 51 } 52 Test* operator->() 53 { 54 return mp; 55 } 56 Test& operator*() 57 { 58 return *mp; 59 } 60 bool isNull() 61 { 62 return (mp == NULL); 63 } 64 ~Pointer() 65 { 66 delete mp; 67 } 68 69 }; 70 71 int main(int argc, char const *argv[]) 72 { 73 cout << "-------1-------------" << endl; 74 Pointer pt1 = new Test(10); // Test(int i)::10 75 cout << pt1->value() << endl; // 10 76 cout << (*pt1).value() << endl; // 10 77 78 cout << "-------2-------------" << endl; 79 Pointer pt2 = new Test(5); // Test(int i)::5 80 cout << pt2->value() << endl; // 5 81 82 cout << "-------3-------------" << endl; 83 Pointer pt3 = pt2; // 將指針pt2的使用權交給指針pt3 84 cout << pt2.isNull() << endl; // 1 85 cout << pt3->value() << endl; // 5 86 87 cout << "-------4-------------" << endl; 88 pt3 = pt1; // 將指針pt1的使用權交給指針pt3 // ~Test()::5 89 cout << pt1.isNull() << endl; // 1 90 91 cout << "-------5-------------" << endl; 92 Pointer pt4; 93 pt4 = pt3; // 將指針pt3的使用權交給指針pt4 // ~Test()::10 94 cout << pt3.isNull() << endl; // 1 95 96 return 0; 97 } 98 99 /** 100 * 智能指針的需求: 101 * 指針的生命週期結束時,主動的釋放堆空間 102 * 一片堆空間最多隻能由一個指針標識 103 * 杜絕指針運算和指針比較 104 * 105 * 使用智能指針的注意事項:只能指向堆空間的對象或變量,不容許指向棧變量 106 * 智能指針的表現形象:使用類對象來取代指針 107 * 108 */
注:這個案列只實現了一個類的內存回收,關於任意類的內存回收,會在後續的模板技術中介紹。
知識回顧
因爲智能指針相關的類重載了指針操做符 ,因此其對象能夠像原生的指針同樣使用,本質上智能指針對象就是類對象。可是,此時的智能指針對象有很大的侷限性,不能靈活的指向任意的類對象。爲了解決這個問題,智能指針類模板就出現了。
(1)現代 C++ 開發庫中最重要的類模板之一;(如 STL 標準庫、Qt )
(2)是C++開發中自動內存管理的主要手段;
(3)可以在很大程度上避開內存相關的問題。
(1) auto_ptr
1)生命週期結束時,銷燬指向的內存空間;(避免只借不還的現象出現)
2)不能指向堆數組,只能指向堆對象(變量);(若須要使用堆數組,咱們能夠本身實現內存回收機制)
3)一片堆空間只屬於一個智能指針對象;或者,多個智能指針對象不能指向同一片堆空間;(避免屢次釋放同一個指針;)
(2)shared_ptr
帶有引用計數機制,支持多個指針對象指向同一片內存;
(3)weak_ptr
配合 shared_ptr 而引入的一種智能指針;
(4)unique_ptr
一個指針對象指向一片內存空間,不能拷貝構造和賦值(auto_ptr 的進化版,沒有使用權的轉移);
1 #include <iostream> 2 #include <string> 3 #include <memory> // 智能指針類模板的頭文件 4 5 using namespace std; 6 7 class Test 8 { 9 string m_name; 10 public: 11 Test(const char* name) 12 { 13 cout << "construct @" << name << endl; 14 15 m_name = name; 16 } 17 18 void print() 19 { 20 cout << "member @" << m_name << endl; 21 } 22 23 ~Test() 24 { 25 cout << "destruct @" << m_name << endl; 26 } 27 }; 28 29 int main() 30 { 31 auto_ptr<Test> pt(new Test("smartPoint")); 32 33 cout << "pt = " << pt.get() << endl; // pt.get() 返回指針所指向數組的首地址 34 35 pt->print(); 36 37 cout << endl; 38 39 auto_ptr<Test> pt1(pt); // pt 轉移了對堆空間的控制權,指向 NULL; 40 41 cout << "pt = " << pt.get() << endl; 42 cout << "pt1 = " << pt1.get() << endl; 43 44 pt1->print(); 45 46 return 0; 47 } 48 /** 49 * 運行結果: 50 * construct @smartPoint 51 * pt = 0x1329c20 52 * member @smartPoint 53 * 54 * pt = 0 55 * pt1 = 0x1329c20 56 * member @smartPoint 57 * destruct @smartPoint 58 * /
(1)QPointer
1)當其指向的對象被銷燬(釋放)時,它會被自動置空;
可使用多個 QPointer 智能指針指向同一個對象,當這個對象被銷燬的時候,全部的智能指針對象都變爲空,這能夠避免屢次釋放和野指針的問題。
2)析構時不會自動銷燬所指向的對象;(!!!)
也就是當 QPointer 對象生命週期完結的時候,不會自動銷燬堆空間中的對象,須要手動銷燬;
(2)QSharedPointer(和 STL中shared_ptr 類似)
1)引用計數型智能指針(引用計數的對象是堆空間申請的對象);
2)能夠被自由地拷貝和賦值;
3)當引用計數爲 0 時,才刪除指向的對象;(這個智能指針對象生命週期結束後,引用計數減一)
(3)其它的智能指針(QweakPointer;QScopedPointer;QScopedArrayPointer;QSharedDataPointer;QExplicitlySharedDataPointer;)
爲何QT要從新開發本身的內存管理機制,而不直接使用已有的STL中智能指針?
這個和它的架構開發思想相關,由於 Qt 有本身的內存管理思想,可是這些思想並無在 STL 中實現,爲了將這種內存管理思想貫徹到 Qt 中的方方面面,因此Qt 纔開發本身的智能指針類模板。
1 #include <QPointer> 2 #include <QSharedPointer> 3 #include <QDebug> 4 5 class Test : public QObject // Qt 開發中都要將類繼承自 QObject 6 { 7 QString m_name; 8 public: 9 Test(const char* name) 10 { 11 qDebug() << "construct @" << name; 12 13 m_name = name; 14 } 15 16 void print() 17 { 18 qDebug() << "member @" << m_name; 19 } 20 21 ~Test() 22 { 23 qDebug() << "destruct @" << m_name ; 24 } 25 }; 26 27 int main() 28 { 29 QPointer<Test> pt(new Test("smartPoint")); 30 QPointer<Test> pt1(pt); 31 QPointer<Test> pt2(pt); 32 33 pt->print(); 34 pt1->print(); 35 pt2->print(); 36 37 delete pt; // 手工刪除,這裏只用刪除一次就可,上述三個指針都指向NULL; 38 39 qDebug() << "pt = " << pt; 40 qDebug() << "pt1 = " << pt1; 41 qDebug() << "pt2 = " << pt2; 42 43 qDebug() << "QPointer 與 QSharedPointer 的區別" << endl; 44 45 QSharedPointer<Test> spt(new Test("smartPoint")); // 引用計數是相對於 Test("smartPoint") 對象而言; 46 QSharedPointer<Test> spt1(spt); 47 QSharedPointer<Test> spt2(spt); 48 49 spt->print(); 50 spt1->print(); 51 spt2->print(); 52 53 return 0; 54 } 55 56 /** 57 * 運行結果: 58 * construct @ smartPoint 59 * member @ "smartPoint" 60 * member @ "smartPoint" 61 * member @ "smartPoint" 62 * destruct @ "smartPoint" 63 * pt = QObject(0x0) 64 * pt1 = QObject(0x0) 65 * pt2 = QObject(0x0) 66 * 67 * QPointer 與 QSharedPointer 的區別 68 * 69 * construct @ smartPoint 70 * member @ "smartPoint" 71 * member @ "smartPoint" 72 * member @ "smartPoint" 73 * destruct @ "smartPoint" 74 * /
參照 auto_ptr 的設計,一樣會在拷貝構造函數和賦值操做符中發生堆空間控制權的轉移。
1 // smartPointer.hpp 智能指針模板類 2 #ifndef SMARTPOINTER_H 3 #define SMARTPOINTER_H 4 5 template 6 <typename T> 7 class SmartPointer 8 { 9 private: 10 T *mp; 11 public: 12 SmartPointer(T *p = 0); 13 SmartPointer(const SmartPointer<T>& obj); 14 SmartPointer<T>& operator=(const SmartPointer<T>& obj); 15 T* operator->(); 16 T& operator*(); 17 bool isNull(); 18 T* get(); 19 ~SmartPointer(); 20 21 }; 22 23 template 24 <typename T> 25 SmartPointer<T>::SmartPointer(T *p) 26 { 27 mp = p; 28 } 29 30 template 31 <typename T> 32 SmartPointer<T>::SmartPointer(const SmartPointer<T>& obj) 33 { 34 mp = obj.mp; 35 const_cast<SmartPointer&>(obj).mp = 0; 36 } 37 38 template 39 <typename T> 40 SmartPointer<T>& SmartPointer<T>::operator=(const SmartPointer<T>& obj) 41 { 42 if(this != &obj) 43 { 44 if(mp != 0) 45 { 46 delete mp; 47 } 48 mp = obj.mp; 49 const_cast<SmartPointer<T>&>(obj).mp = 0; 50 } 51 52 return *this; 53 } 54 55 template 56 <typename T> 57 T* SmartPointer<T>::operator->() 58 { 59 return mp; 60 } 61 62 template 63 <typename T> 64 T& SmartPointer<T>::operator*() 65 { 66 return *mp; 67 } 68 69 template 70 <typename T> 71 bool SmartPointer<T>::isNull() 72 { 73 return (mp == 0); 74 } 75 76 template 77 <typename T> 78 T* SmartPointer<T>::get() 79 { 80 return mp; 81 } 82 83 template 84 <typename T> 85 SmartPointer<T>::~SmartPointer() 86 { 87 delete mp; 88 } 89 90 #endif 91 92 // main.cpp 測試文件 93 94 #include <iostream> 95 #include "test.hpp" 96 97 using namespace std; 98 99 class Test 100 { 101 string m_name; 102 public: 103 Test(const char* name) 104 { 105 cout << "construct @" << name << endl; 106 107 m_name = name; 108 } 109 110 void print() 111 { 112 cout << "member @" << m_name << endl; 113 } 114 115 ~Test() 116 { 117 cout << "destruct @" << m_name << endl; 118 } 119 }; 120 121 class Demo 122 { 123 string m_name; 124 public: 125 Demo(const char* name) 126 { 127 cout << "construct @" << name << endl; 128 129 m_name = name; 130 } 131 132 void print() 133 { 134 cout << "member @" << m_name << endl; 135 } 136 137 ~Demo() 138 { 139 cout << "destruct @" << m_name << endl; 140 } 141 }; 142 143 144 145 int main(int argc, char const *argv[]) 146 { 147 SmartPointer<Test> pt(new Test("SmartPointer Template <Test>")); 148 149 cout << "pt = " << pt.get() << endl; 150 151 pt->print(); 152 153 cout << endl; 154 155 SmartPointer<Test> pt1(pt); 156 157 cout << "pt = " << pt.get() << endl; 158 cout << "pt1 = " << pt1.get() << endl; 159 160 pt1->print(); 161 162 //--------------------------------------------------------------- 163 cout << "--------------------------------------------" << endl; 164 165 SmartPointer<Demo> spt(new Demo("SmartPointer Template <Demo>")); 166 167 cout << "spt = " << spt.get() << endl; 168 169 spt->print(); 170 171 cout << endl; 172 173 SmartPointer<Demo> spt1(spt); 174 175 cout << "spt = " << spt.get() << endl; 176 cout << "spt1 = " << spt1.get() << endl; 177 178 spt1->print(); 179 180 return 0; 181 } 182 /** 183 * 運行結果: 184 * construct @SmartPointer Template <Test> 185 * pt = 0x17bcc20 186 * member @SmartPointer Template <Test> 187 * 188 * pt = 0 189 * pt1 = 0x17bcc20 190 * member @SmartPointer Template <Test> 191 * ----------------------------------------- 192 * construct @SmartPointer Template <Demo> 193 * spt = 0x17bd090 194 * member @SmartPointer Template <Demo> 195 * 196 * spt = 0 197 * spt1 = 0x17bd090 198 * member @SmartPointer Template <Demo> 199 * destruct @SmartPointer Template <Demo> 200 * destruct @SmartPointer Template <Test> 201 */
智能指針可以儘量的避開內存相關的問題,主要表如今:
(1)內存泄漏
(2)屢次釋放