1. 程式人生 > >C++實現智慧指標(一)

C++實現智慧指標(一)

參考自《C5-C++ Primer》和實驗樓相關實驗。

一. 智慧指標概念

  • 智慧指標的引入:

C++中,通過new/delete這對運算子進行動態記憶體的管理。動態記憶體使用很容易出問題,因為確保在正確時間釋放記憶體是很困難的。有時忘記釋放記憶體,產生記憶體洩漏;有時在指標還引用記憶體時就釋放了它,產生引用非法記憶體的指標。因此,為了更容易和安全地使用動態記憶體,C++11提供了智慧指標(smart pointer)型別來管理動態物件。

  • 智慧指標的使用:
所謂的智慧指標,首先它是一個指標,所以能指向記憶體資源;然後它區別於一般的指標,它是智慧的,而所謂的智慧是指它:1.能夠自動的幫你釋放記憶體,2.還能夠知道什麼時候釋放記憶體才是安全的。

標準提供兩種智慧指標:shared_ptr允許多個指標指向同一個物件;unique_ptr獨佔所指物件;weak_ptr指向shared_ptr所管理的物件。本實驗主要實現shared_ptr的功能。

  • 智慧指標的實現原理:
C++中智慧指標的實現主要依賴於兩個技術概念:
1. 解構函式,物件被銷燬時會被呼叫的一個函式,對於基於棧的物件而言,如果物件離開其作用域則物件會被自動銷燬,而此時解構函式也自動會被呼叫。

2. 引用計數技術,維護一個計數器用於追蹤資源(如記憶體)的被引用數,當資源被引用時,計數器值加1,當資源被解引用時,計算器值減1。

智慧指標的大致實現原理就是在解構函式中,檢查所引用物件的引用計數,如果引用計數為0,則真正釋放該物件記憶體。

二. 實現版本v1

  • 定義智慧指標類
  • 建立建構函式:具備預設建構函式;指定型別建構函式
  • 定義為模板類
  • 解構函式:釋放記憶體
實現智慧指標:標頭檔案 smartpointer.h
/* 
* file name : smartpointer.h
* desp : 智慧指標版本v1
*/
#ifndef __SMARTPOINTER_H__
#define __SMARTPOINTER_H__

template <typename T>  // 將智慧指標類定義成模板類
class SmartPointer {
public:
    // 預設建構函式
    SmartPointer():mPointer(NULL) {std::cout <<"create unknown smart pointer."<< std::endl;}    
    // 接收不同指標型別的建構函式
    SmartPointer(T *p):mPointer(p) {std::cout <<"create smart pointer at "<<static_cast<const void*>(p)<<std::endl;}     
    // 解構函式
    ~SmartPointer(){
        std::cout << "release smart pointer at "<<static_cast<const void*>(mPointer)<<std::endl;
        // 實現記憶體資源自動銷燬機制
        if (mPointer) delete mPointer;
    }
private:
    T *mPointer; // 指向智慧指標實際對應的記憶體資源,根據引數自動推導規則,定義內部資源指標型別
};
#endif // __SMARTPOINTER_H__

測試智慧指標:測試檔案 sptestcase1.cpp

/* 
* file name : sptestcase1.cpp
* desp : 智慧指標測試程式碼 case1 測試智慧指標的建立與銷燬
*/

#include <iostream>
#include "smartpointer.h"
class SomeClass{
public:
    SomeClass(){std::cout << "SomeClass default constructor !"<<std::endl;}
};

void testcase1()
{
    // 建立一個不知所指的智慧指標
    SmartPointer<char> spunknown;
    // 建立空智慧指標
    SmartPointer<char> spnull = NULL;
    // 建立指向具體類的智慧指標
    SmartPointer<SomeClass> spclass = new SomeClass;
    // 建立字串的智慧指標
    SmartPointer<const char> spstr = "Hello world!";  
}

int main(void)
{
    testcase1();
    return 0;
}

編譯執行:

$ g++ -o sptestcase1 sptestcase1.cpp
$ ./sptestcase1

執行結果分析:


  • V1版本不足

簡單實現智慧指標類,智慧指標釋放指向“hello world”指標時出錯。我們的智慧指標不能指向無法被delete釋放的記憶體資源。

三. 知識點查漏補缺

  • new的不同用法
(1)int *p=new int;//無初始化
(2)int *p=new int(5);//初始化為5
(3)建立類  Test *test=new Test();  delete test;//加括號呼叫沒有引數的建構函式,不加括號呼叫預設建構函式或唯一的建構函式
(4)int *p=new int[10];//開闢容量為10的陣列  delete []p;
  • 類模板
//類的定義
template<class T>
class Test
{
private:
    T n;
    const T i;
public:
    Test():i(0) {}
    Test(T k);
    ~Test(){}

    void print();
    T operator+(T x);
};
//類的使用
類名<實際的型別> eg.Test<int> test;//宣告一個物件 
  • staic_cast<const void *>(p)

//地址?轉換型別? 

我們這裡定義的是模版類,因為不知道這個指標的具體型別。使用使用了const void *。

 const void * p 這是定義了一個指標p,p可以指向任意型別的值,但它指向的值必須是常量。在這種情況下,我們不能修改被指向的物件,但可以使指標指向其他物件。