軟體模組的核心往往是資源的「獲取 → 使用 → 釋放」生命週期。獲取時有順序,釋放時必須嚴格遵循逆序。這不僅是良好習慣,更是避免崩潰、死鎖、資源洩漏的關鍵防線。
queue_init() 依序分配 5 項資源(標註行號對應圖 1):
queue 結構體本身pthread_mutexattr_t attr 初始化queue->messages_ 陣列sem_put_ 信號量sem_get_ 信號量queue_fini() 釋放時完全逆序:
int queue_fini(queue_t **pp_queue, int size)
{
queue_t *queue = *pp_queue;
if (!queue) return -1;
sem_destroy(&queue->sem_get_); // 1. 最後分配 → 最先釋放
sem_destroy(&queue->sem_put_); // 2.
free(queue->messages_); // 3.
pthread_mutex_destroy(&queue->mutex_); // 4.
free(queue); // 5. 最先分配 → 最後釋放
return 0;
}
queue_init() 中的 goto errorN 標籤同樣遵循逆序釋放,形成「初始化與銷毀鏡像對稱」,極大降低維護心智負擔。free(queue) 再 sem_destroy(&queue->sem_get_) 屬於使用已釋放記憶體,後果不可預測。若資源間無依賴關係(如圖中第 107、108 行兩個 sem_destroy),順序可互換。但統一執行逆序習慣可免除逐一分析依賴的麻煩,屬於「防禦性編程」最佳實踐。
C++ 中應優先使用 RAII(std::unique_ptr、std::lock_guard、自定義 Wrapper)讓編譯器自動按逆序釋放(析構函數呼叫順序即為建構逆序)。但理解底層原理,對排查智慧指標循環引用、自定義資源管理類別仍不可或缺。
鐵律:分配順序 1→2→3→4→5,釋放順序必為 5→4→3→2→1。寫初始化時同步規劃銷毀,將逆序刻入肌肉記憶。