1. 程式人生 > 實用技巧 >第37課 智慧指標分析(指標特徵操作符( -> 、 *)過載)

第37課 智慧指標分析(指標特徵操作符( -> 、 *)過載)

1.永恆的話題:記憶體洩漏

(1)動態申請堆空間用完後不歸還

(2)C++語言中沒有垃圾回收的機制

(3)指標無法控制所指堆空間的生命週期------------指標是變數,可以指向記憶體堆空間,但是無法控制所指堆空間的生命週期

說明記憶體洩漏:

 1 #include<iostream>
 2 #include<string>
 3 
 4 using namespace std;
 5 
 6 class Test
 7 {
 8     int i;
 9 
10 public:
11 
12     Test(int i)
13     {
14         this
->i = i; 15 } 16 17 int value() 18 { 19 return i; 20 } 21 22 ~Test() 23 { 24 } 25 }; 26 27 int main() 28 { 29 for (int i = 0; i < 5; i++) 30 { 31 Test* p = new Test(i); //P指向堆空間的物件 32 //指標是區域性變數for()迴圈之後消失,但是指標開闢的空間沒有進行釋放 記憶體洩漏
33 //長時間執行會出現Bug 34 35 cout << p->value() << endl; 36 37 } 38 39 return 0; 40 }

2.深度的思考:我們需要什麼?

(1)需要一個特殊的指標指標生命期結束時主動釋放堆空間

(2)一片堆空間最多只能由一個指標來標識------------避免記憶體多次釋放

(3)杜絕指標運算指標比較-----------因為只有一個該類物件的指標能指向堆空間,避免指標越界,野指標

3.解決方案

定義一個物件,讓物件取模擬真正意思上的指標行為

---------物件代替指標,稱該物件為智慧指標 -------智慧指標本質:是個物件

(1)過載指標特徵操作符(->*)

(2)只能通過類的成員函式過載

(3)過載函式不能使用引數

(4)只能定義一個過載函式

(5)注意:智慧指標只能用來指向堆空間中的物件或變數(不能指向棧)

實驗:(1)實現特殊的指標指標生命期結束時主動釋放堆空間

 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         cout <<"Test (int i)"<< endl;
13 
14         this->i = i;
15     }
16     int value()
17     {
18         return i;
19     }
20     ~Test()
21     {
22         cout << "~Test ()" << endl;
23     }
24 
25 };
26 
27 
28 //智慧指標  本質是物件--------首先建立智慧指標類
29 class Pointer
30 {
31     Test* mp;              //指標mp指向物件Test,用於儲存要被管理物件的指標
32 
33 public:
34 
35     //建構函式被堆空間上記憶體地址初始化
36 
37     Pointer(Test* p = NULL)   //指標被堆空間上的地址初始化---引數Test*指標
38     {
39         mp = p;
40     }
41 
42     //過載->操作符
43 
44     Test* operator -> ()    //不能使用引數,所以也就只能定義一個過載函式
45     {
46         return mp;             //返回成員指標
47     }
48 
49     //過載*操作符  
50 
51     Test& operator * ()    //不能使用引數,所以也就只能定義一個過載函式
52     { 
53         return *mp;        //*作用指標意義:返回當前指標所指變數或物件
54     }
55 
56     ~Pointer()
57     {
58         delete mp;     //智慧指標被析構時,同時刪除其所管理的Test類的物件,釋放成員指標指向的堆空間
59     }
60 };
61 
62 int main()
63 {
64     for (int i = 0; i < 5; i++)
65     {
66         //    Test* p = new Test(i);   //那麼就可以用類物件名代替指標     
67 
68         Pointer p = new Test(i);     //可以動態的將申請的空間釋放
69 
70         cout << p->value() << endl;  //01234
71           
72     }
73     return 0;
74 }

實現:一片堆空間最多只能由一個指標來標識------------避免記憶體多次釋放

    不進行指標運算和比較

    方法:過載賦值操作符和拷貝構造

  1 #include<iostream>
  2 #include<string>
  3 
  4 //智慧指標  本質是物件
  5 using namespace std;
  6 
  7 class Test
  8 {
  9     int i;
 10 public:
 11     Test(int i)
 12     {
 13         cout <<"Test (int i)"<< endl;
 14         this->i = i;
 15     }
 16     int value()
 17     {
 18         return i;
 19     }
 20     ~Test()
 21     {
 22         cout << "~Test (int i)" << endl;
 23     }
 24 
 25 };
 26 
 27 class Pointer
 28 {
 29     Test* mp;    //資料成員為指標
 30 
 31 public:
 32 
 33     Pointer(Test* p = NULL)    //建構函式被堆空間上記憶體地址初始化
 34     {
 35         mp = p;
 36     }
 37 
 38 
 39     // 實現一片堆空間最多隻能由一個指標標識   杜絕多次釋放
 40 
 41     //拷貝建構函式
 42     Pointer(const Pointer& obj)                                         //自定義深拷貝建構函式
 43     {
 44      //   delete mp;        //拷貝構造階段,mP野指標不能刪除---記憶體錯誤
 45 
 46         mp = obj.mp;      //當前物件的成員指標mp指向初始化物件的成員指標所對應的堆空間---兩個指標指向一片記憶體空間
 47                           //初始化物件把他指向的堆空間完全交給當前物件-----------即保證只有一個智慧指標指向同一片記憶體
 48 
 49         const_cast<Pointer&>(obj).mp = NULL;  //所有權的傳遞,自己置空  const_cast去除只讀屬性
 50     }
 51 
 52 
 53     //賦值操作符過載函式 
 54     Pointer& operator=(const Pointer& obj)  // 1,返回值型別一定是引用Test&為了連續賦值   2,引數是const引用型別
 55     {
 56         //進行賦值操作
 57         if (this != &obj)     //3,賦值操作符不是自賦值a=a,要避免賦值,通過This判斷,this指向當前物件的地址和引數地址不同才進行賦值操作
 58         {
 59             delete mp;
 60 
 61             mp = obj.mp;     //保證賦值操作時,只能由一個智慧指標指向同一堆空間
 62 
 63             const_cast<Pointer&>(obj).mp = NULL;
 64         }
 65         return *this;     //4,返回當前物件地址
 66     }
 67 
 68     //過載指標特徵操作符
 69     Test* operator -> ()
 70     {
 71         return mp;    //返回成員指標
 72     }
 73     Test& operator * ()
 74     {
 75         return *mp;    //返回當前指標所指變數或物件
 76     }
 77 
 78     bool isNull()     //判斷當前智慧指標是否為空
 79     {
 80         return(mp == NULL);
 81     }
 82 
 83     ~Pointer()
 84     {
 85         delete mp;
 86     }
 87 };
 88 
 89 int main()
 90 {
 91     Pointer p1 = new Test(0);  //定義智慧指標P1
 92 
 93     cout << p1->value() << endl;   //0
 94 
 95     Pointer p2 = p1;          //定義智慧指標P2,用p1進行初始化,p1管理的堆空間轉給P2
 96 
 97     //不能指標比較的指標運算
 98 //    p2++;              
 99 //    if(p1==p2)         
100 
101     cout << p1.isNull() << endl;   //1空 p1指向NULL,不再指向原來的堆空間
102 
103     cout << p2->value() << endl;   //0   p2接管P1指向的堆空間------管理92行堆物件
104 
    //p2++;不過載++,因為每次只能一個智慧指標指向堆空間,這種++操作沒意義
  

       //p2智慧指標生命期結束,會自動釋放接管的堆空間
105 return 0; 106 }

//輸出結果:

//Test(int i)

//5

//1

//5

//~Test()

存在問題: 智慧指標對應的類只能指向Test固定的類型別,不能指向其他型別,能不能指向各類他能管理的類型別------以後會學到模板實現

4.小結

掌握智慧指標的建立

  智慧指標在自己生命週期結束,自動釋放所指的記憶體空間

  要求c++避免使用原生指標,那麼以後就可以使用智慧指標

(1)指標特徵操作符( -> 和 * )可以被過載

(2)過載指標特徵符能夠使用物件代替指標

(3)智慧指標只能用於指向堆空間中的記憶體

(4)智慧指標意義在於最大程度避免記憶體問題