▌第一次閱讀本系列的,可以先看:


▌第一次閱讀垃圾回收器系列,可以先看:


▌前文提要:

為了解決全域變數的問題,創建一個儲存用的函數 connect_address_pool :
void connect_address_pool(void ****get_address_pool, void **get_index){

    /*用於儲存記憶體地址池的空間*/
 static void **address_pool = NULL;
 static int index = 0;  //address_pool 的索引

 *get_address_pool = &address_pool;
 *get_index = &index;
}
只要利用:
/*用於取得記憶體地址池的資料*/
void ***address_pool = NULL;
int *index;

/*取得記憶體地址池的資料*/
connect_address_pool(&address_pool, &index);
就可以中任意地方操作 connect_address_pool 函數中儲存的變數的值。

這樣的操作包含了輸入輸出兩個部分,
根據 最小權限原則 以及物件導向的封裝性
connect_address_pool 是連結的方式,給出的權限太多,不符合。
我們需要獲得更好的封裝性
應該製作 getter 和 setter 這樣的轉接口。
標記的部分 register_address功能跟 setter 差不多,
所以以下實作 getter 。

▌getter 的製作:

void get_address_pool(void ***get_address_pool, int *get_index){
    
    /*用於取得記憶體地址池的資料*/
    void ***address_pool = NULL;
    int *index;

    /*取得記憶體地址池的資料*/
    connect_address_pool(&address_pool, &index);
    
    *get_address_pool = *address_pool;
    *get_index = *index;
}
注意第二項參數是 int* 而不能是 void*,因為是 void 是不完全類型。
有關知識點可參看 Days 7: 重溫指標:數據、數據類型的本質
只不過是 connect_address_pool 的弱化版...
變成單純的按址傳值,沒有了反向修改函數內的值的能力。
由連接變成閱讀。
下面修改釋放的部分 free_all_register_address

▌先回顧一下 free_all_register_address 原本的樣子是怎樣?

void free_all_register_address(void) {

    /*用於取得記憶體地址池的資料*/
 void ***address_pool = NULL;
 int *index;

 /*取得記憶體地址池的資料*/
 connect_address_pool(&address_pool, &index);
    
 /*遍歷 address_pool 的空間*/
 for (int i = 0; i < *index; i++) {
  free((*address_pool)[i]);  //釋放曾經記錄過的記憶體地址的空間
  (*address_pool)[i] = NULL;
 }

    /*釋放用於紀錄的空間*/
 free(*address_pool);
 *address_pool = NULL;
}
使用連結的方式,要宣告 ***address_pool , *index ,
使用時又要用 *address_pool*index ,
這種不方便、不好看的形式。

▌以下是修改後的樣子:

void free_all_register_address(void) {

 /*用於取得記憶體地址池的資料*/
 void **address_pool = NULL;
 int index;

 /*取得記憶體地址池的資料*/
 get_address_pool(&address_pool, &index);

 /*遍歷 address_pool 的空間*/
 for (int i = 0; i < index; i++) {
  free(address_pool[i]);  //釋放曾經記錄過的記憶體地址的空間
  address_pool[i] = NULL;
 }

 /*釋放用於紀錄的空間*/
 free(address_pool);
 address_pool = NULL;
}
樣子幾乎沒有分別,
不過 free_all_register_address 可操作的權限減少了,
只能讀取數據,不能反向修改儲存數據的值。
不需要宣告 ***address_pool , *index 這些高維度的變數,
》高維度 = 抽象 = 難以理解 = 難以維護。
也不需要用 *address_pool*index ,
這種不方便、不好看的形式。

▌進行封裝:

getter 和 setter 出現了,
不過使用者還是可以直接調用 connect_address_pool ,
即 變數還是 public 的。
封裝失敗,全卷完。
咳。沒有記錯的話
巨集是可以覆蓋函數的,所以只要,
#define connect_address_pool() printf("欸欸,不要用這個函數哦。");
注意這個巨集宣告必須放置在 被覆蓋函數 以及 任何使用到該函數 的 函數 後。
使用者就無法調用 connect_address_pool ,
這樣就可以把變數由 public -> private 了喔。

▌進一步的優化方式:

有沒有發現跟 隊列 這種資料結構很相似,
要更進一步的話,可以用結構體(struct)封裝出一個隊列
然後用 push(), pop() 這樣的函數進行操作。
代碼量可能會更少或更漂亮。
不過這一部分我就不做出來了。

▌參考資料: