RAII 概念(Resource acquisition is initialization)
RAII 概念
RAII(Resource acquisition is initialization)中文即是"資源取得或分配即是在初始化".
看起來有點攏統. 個人的理解是RAII是用於object上的一個寫法.
物件建立的時候, 資源取得的是時候是經由constructor來初始. 結束的時候便透過destructor來釋放.
這樣的好處就是可以確保資源在物件的生命週期內是存在的.
結束的時候就會釋放掉資源. 閉免掉memory leak的問題或因為資源的關係導致程式運作不正常
C++程式範例
底下以[3]的程式作為範例, 如下所示. bad()會造成程式lock住. 而改為RAII的good()方式可以避免掉這問題.
std::mutex m; void bad() { m.lock(); // 直接取的 lock f(); // (1) 當有例外發生(exception), 程式鎖死 if(!everything_ok()) return; // (2) 程式直接返回, 程式鎖死 m.unlock(); // 釋放lock } void good() { // 使用class包住lock. 而這邊不需像上述使用unlock. // 因為object結束會呼叫解構子. 會做unlok std::lock_guard<std::mutex> lk(m); f(); // (1) 當例外造成, lock object也會結束 if(!everything_ok()) return; // (2) 程式返回, lock object也會結束 } // 正常結束, lock object也會結束
C結合RAII
因為之前寫C的時候, 每次都會為了malloc()跟free(), 讓程式碼不簡潔或造成編譯問題.
(1) 如程式有重複的release資源的code.
(2) 透過goto方式集中資源釋放, 但導致在goto之後的變數宣告無法初始數值
#include<stdio.h> #include<stdlib.h> #include<memory> const int CB_MEM_SIEZ = 1000000; int main() { int *piData = (int *) malloc(sizeof(int) * 10); if(true) { free(piData); ///< case 1. redundant codes return 0; } if(true) goto exit_main; // case 2. ERROR: // "jump bypasses variable initialization" or "crossed initialization" // use "int b; b = 0;" instead of "int b = 0;" to avoid error int b = 0; exit_main: free(piData); return 0; }
而C語言基本上是程序語言. 基本上靠自己是無法用到RAII的方式. 幸好C跟C++是通用.
這邊就嘗試使用C++的smart pointer來達成RAII的寫法
程式碼如下, 主要是參考[4][5][6][7]. 使用不同的方式實現RAII.
藉由這樣的方式來減少case 1&2的寫法.
#include<stdio.h> #include<stdlib.h> #include<memory> const int CB_MEM_SIEZ = 1000000; struct Deletor { void operator()(int *piData) { printf("free by operator\n"); free(piData); } }; int main() { // share ptr { /* // 1 std::shared_ptr<int> sp( \ (int*) malloc(sizeof(int) * CB_MEM_SIEZ), \ [] (int *piData) { printf("share ptr free\n"); free(piData); } \ ); */ // 2 std::shared_ptr<int> sp( \ (int*) malloc(sizeof(int) * CB_MEM_SIEZ), \ Deletor() \ ); } // unique ptr { /* // 1-1 std::unique_ptr<int, void (*)(int *)> up(\ (int*) malloc(sizeof(int) * CB_MEM_SIEZ), \ [] (int *piData) { printf("unique ptr free\n"); free(piData); }\ ); */ /* // 1-2 "decltype(free)*" as "void (void*)" std::unique_ptr<int, decltype(free)*> up(\ (int*) malloc(sizeof(int) * CB_MEM_SIEZ), \ free \ ); */ // 2 std::unique_ptr<int, struct Deletor> up(\ (int*) malloc(sizeof(int) * CB_MEM_SIEZ) ); } return 0; }
補充: Array 的方式
參閱[8]修改而來的code. 要注意一點"*","->"在smart pointer已被使用在member function or member variable上
#include<stdio.h> #include<stdlib.h> #include<memory> int main() { std::unique_ptr<int[]> arrup (new int[5]); arrup[0]=5; // printf("%d\n", *arrup); //error, operator * not defined printf("%d\n", arrup[0]); std::unique_ptr<char[], void (*)(void *)> charup ((char*)(malloc(5)), free); charup[1]='b'; printf("%c\n", charup[1]); return 0; }
Reference
- Wiki - RAII
- C++ RAII
- cppreference - raii
- Using std::unique_ptr (RAII) with malloc() and free()
- Is it possible to use a C++ smart pointers together with C's malloc?
- cplusplus - unique_ptr
- cplusplus - shared_ptr
- The Smart Pointer That Makes Your C++ Applications Safer - std::unique_ptr
I am very glad that you always put in so much effort. With my remote developers working with me earlier, the updated tech niches were impossible to address. So, I always refused to have those projects due to the lack of an operational team. One day, my ex-colleague, who is also running a game-developing business, suggested Eiliana.com, and I rushed to see the magic. I hired a few experts, and it transformed my business.
回覆刪除