Serie: C++
cpp
361 líneas
· Actualizado 2026-04-03
raii.cpp
C++/Part2_物件導向/Ch09_建構子與解構子/raii.cpp
// ============================================================
// Ch09 — RAII(Resource Acquisition Is Initialization)
// 資源取得即初始化 — C++ 最重要的慣用手法之一
// 編譯:g++ -std=c++17 -Wall -o raii raii.cpp
// ============================================================
#include <iostream>
#include <string>
#include <fstream>
#include <stdexcept>
#include <memory>
// ============================================================
// 1. FileGuard — 檔案資源的 RAII 包裝
// 建構子開檔,解構子關檔
// ============================================================
class FileGuard {
private:
std::ofstream file;
std::string filename;
public:
// 建構子:取得資源(開檔)
explicit FileGuard(const std::string& filename)
: file(filename), filename(filename) {
if (!file.is_open()) {
throw std::runtime_error("無法開啟檔案: " + filename);
}
std::cout << " [FileGuard] 開啟檔案: " << filename << std::endl;
}
// 解構子:釋放資源(關檔)
~FileGuard() {
if (file.is_open()) {
file.close();
std::cout << " [FileGuard] 關閉檔案: " << filename << std::endl;
}
}
// 禁止拷貝(檔案 handle 不應被複製)
FileGuard(const FileGuard&) = delete;
FileGuard& operator=(const FileGuard&) = delete;
// 寫入資料
void write(const std::string& content) {
file << content;
std::cout << " [FileGuard] 寫入: " << content;
}
void writeLine(const std::string& content) {
file << content << "\n";
std::cout << " [FileGuard] 寫入行: " << content << std::endl;
}
};
// ============================================================
// 2. MemoryBlock — 記憶體資源的 RAII 包裝
// 模擬簡單的動態記憶體管理
// ============================================================
class MemoryBlock {
private:
int* data;
int size;
std::string name;
public:
// 建構子:取得資源(配置記憶體)
MemoryBlock(const std::string& name, int size)
: data(new int[size]), size(size), name(name) {
std::fill(data, data + size, 0);
std::cout << " [MemoryBlock] " << name
<< " 配置 " << size << " 個 int ("
<< size * sizeof(int) << " bytes)" << std::endl;
}
// 解構子:釋放資源
~MemoryBlock() {
std::cout << " [MemoryBlock] " << name
<< " 釋放記憶體" << std::endl;
delete[] data;
}
// 禁止拷貝
MemoryBlock(const MemoryBlock&) = delete;
MemoryBlock& operator=(const MemoryBlock&) = delete;
int& operator[](int index) {
if (index < 0 || index >= size) {
throw std::out_of_range("MemoryBlock: 索引超出範圍");
}
return data[index];
}
int operator[](int index) const {
if (index < 0 || index >= size) {
throw std::out_of_range("MemoryBlock: 索引超出範圍");
}
return data[index];
}
int getSize() const { return size; }
void print() const {
std::cout << " [" << name << "] = [";
for (int i = 0; i < size; ++i) {
if (i > 0) std::cout << ", ";
std::cout << data[i];
}
std::cout << "]" << std::endl;
}
};
// ============================================================
// 3. LockGuard — 模擬鎖的 RAII 包裝
// (簡化版,展示概念)
// ============================================================
class SimpleMutex {
private:
bool locked = false;
std::string name;
public:
explicit SimpleMutex(const std::string& name) : name(name) {}
void lock() {
locked = true;
std::cout << " [Mutex:" << name << "] 🔒 已上鎖" << std::endl;
}
void unlock() {
locked = false;
std::cout << " [Mutex:" << name << "] 🔓 已解鎖" << std::endl;
}
bool isLocked() const { return locked; }
};
// RAII 鎖管理器 — 類似 std::lock_guard
class LockGuard {
private:
SimpleMutex& mutex;
public:
// 建構子:取得鎖
explicit LockGuard(SimpleMutex& m) : mutex(m) {
mutex.lock();
}
// 解構子:釋放鎖
~LockGuard() {
mutex.unlock();
}
// 禁止拷貝與賦值
LockGuard(const LockGuard&) = delete;
LockGuard& operator=(const LockGuard&) = delete;
};
// ============================================================
// 4. SimpleUniquePtr — 簡易智慧指標
// 展示 unique_ptr 的核心 RAII 概念
// ============================================================
template <typename T>
class SimpleUniquePtr {
private:
T* ptr;
public:
// 建構子:取得擁有權
explicit SimpleUniquePtr(T* p = nullptr) : ptr(p) {
if (ptr) {
std::cout << " [SimpleUniquePtr] 取得資源擁有權" << std::endl;
}
}
// 解構子:釋放資源
~SimpleUniquePtr() {
if (ptr) {
std::cout << " [SimpleUniquePtr] 自動釋放資源" << std::endl;
delete ptr;
}
}
// 禁止拷貝
SimpleUniquePtr(const SimpleUniquePtr&) = delete;
SimpleUniquePtr& operator=(const SimpleUniquePtr&) = delete;
// 允許移動
SimpleUniquePtr(SimpleUniquePtr&& other) noexcept : ptr(other.ptr) {
other.ptr = nullptr;
}
SimpleUniquePtr& operator=(SimpleUniquePtr&& other) noexcept {
if (this != &other) {
delete ptr;
ptr = other.ptr;
other.ptr = nullptr;
}
return *this;
}
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
T* get() const { return ptr; }
explicit operator bool() const { return ptr != nullptr; }
};
// ============================================================
// 示範:RAII 如何防止例外導致的資源洩漏
// ============================================================
void riskyOperation(bool shouldThrow) {
std::cout << "\n 進入 riskyOperation (shouldThrow="
<< (shouldThrow ? "true" : "false") << ")" << std::endl;
MemoryBlock block("risk_block", 5);
block[0] = 42;
block[1] = 99;
block.print();
if (shouldThrow) {
std::cout << " 💥 即將拋出例外!" << std::endl;
throw std::runtime_error("模擬錯誤");
// MemoryBlock 的解構子仍然會被呼叫 — RAII 保證!
}
std::cout << " 正常完成" << std::endl;
// MemoryBlock 離開作用域,解構子自動釋放
}
// 對比:不使用 RAII 的危險寫法(僅說明,不實際執行)
void unsafeExample() {
std::cout << "\n ❌ 不使用 RAII 的危險寫法(僅為說明):" << std::endl;
std::cout << R"(
int* data = new int[100];
// ... 如果這裡拋出例外 ...
// data 永遠不會被 delete → 記憶體洩漏!
delete[] data;
)" << std::endl;
std::cout << " ✓ 使用 RAII 的安全寫法:" << std::endl;
std::cout << R"(
MemoryBlock block("safe", 100);
// ... 即使這裡拋出例外 ...
// block 的解構子仍會自動呼叫 → 不會洩漏!
)" << std::endl;
}
// ============================================================
// main
// ============================================================
int main() {
std::cout << "========================================" << std::endl;
std::cout << " Ch09 RAII — 資源取得即初始化" << std::endl;
std::cout << "========================================\n" << std::endl;
// ------ 1. FileGuard ------
std::cout << "【1】FileGuard — 檔案 RAII\n" << std::endl;
{
FileGuard file("/tmp/raii_demo.txt");
file.writeLine("Hello RAII!");
file.writeLine("這是自動管理的檔案。");
file.writeLine("離開作用域時會自動關閉。");
std::cout << " 即將離開區塊..." << std::endl;
}
std::cout << " 區塊已結束\n" << std::endl;
// ------ 2. MemoryBlock ------
std::cout << "【2】MemoryBlock — 記憶體 RAII\n" << std::endl;
{
MemoryBlock mb("data", 5);
mb[0] = 10;
mb[1] = 20;
mb[2] = 30;
mb.print();
std::cout << " 即將離開區塊..." << std::endl;
}
std::cout << " 記憶體已自動釋放\n" << std::endl;
// ------ 3. LockGuard ------
std::cout << "【3】LockGuard — 鎖的 RAII\n" << std::endl;
SimpleMutex mutex("資料鎖");
{
LockGuard guard(mutex);
std::cout << " (在臨界區中執行工作...)" << std::endl;
std::cout << " 即將離開區塊..." << std::endl;
}
std::cout << " 鎖已自動釋放\n" << std::endl;
// ------ 4. SimpleUniquePtr ------
std::cout << "【4】SimpleUniquePtr — 智慧指標 RAII\n" << std::endl;
{
SimpleUniquePtr<int> p1(new int(42));
std::cout << " *p1 = " << *p1 << std::endl;
SimpleUniquePtr<std::string> p2(new std::string("Hello RAII!"));
std::cout << " *p2 = " << *p2 << std::endl;
std::cout << " p2->length() = " << p2->length() << std::endl;
// SimpleUniquePtr<int> p3 = p1; // ✗ 編譯錯誤!禁止拷貝
SimpleUniquePtr<int> p3(std::move(p1)); // ✓ 允許移動
std::cout << " 移動後 p1 是否有效: " << (p1 ? "是" : "否") << std::endl;
std::cout << " *p3 = " << *p3 << std::endl;
std::cout << " 即將離開區塊..." << std::endl;
}
std::cout << " 資源已自動釋放\n" << std::endl;
// ------ 5. 例外安全示範 ------
std::cout << "【5】RAII 在例外發生時的保護\n" << std::endl;
// 正常情況
try {
riskyOperation(false);
} catch (const std::exception& e) {
std::cout << " 捕獲例外: " << e.what() << std::endl;
}
std::cout << std::endl;
// 例外情況 — RAII 確保資源仍會被釋放
try {
riskyOperation(true);
} catch (const std::exception& e) {
std::cout << " 捕獲例外: " << e.what() << std::endl;
std::cout << " ☝ 注意:MemoryBlock 的解構子仍然被呼叫了!" << std::endl;
}
// ------ 6. 概念比較 ------
std::cout << std::endl;
std::cout << "【6】RAII vs 手動管理 — 概念比較" << std::endl;
unsafeExample();
// ------ 總結 ------
std::cout << "【總結】RAII 的核心原則\n" << std::endl;
std::cout << " 1. 建構子取得資源(開檔、配置記憶體、上鎖)" << std::endl;
std::cout << " 2. 解構子釋放資源(關檔、釋放記憶體、解鎖)" << std::endl;
std::cout << " 3. 利用 C++ 自動解構機制保證不洩漏" << std::endl;
std::cout << " 4. 即使發生例外,解構子仍會被呼叫" << std::endl;
std::cout << " 5. 標準函式庫大量使用此模式:" << std::endl;
std::cout << " - std::unique_ptr / std::shared_ptr" << std::endl;
std::cout << " - std::lock_guard / std::unique_lock" << std::endl;
std::cout << " - std::fstream" << std::endl;
std::cout << " - std::vector / std::string" << std::endl;
std::cout << "\n========================================" << std::endl;
std::cout << " 範例結束" << std::endl;
std::cout << "========================================" << std::endl;
return 0;
}
Artículos relacionados
C++
c
Actualizado 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/deviceAlpha.h).
Leer artículo →
C++
c
Actualizado 2026-07-21
finalproject.c
finalproject.c — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.c).
Leer artículo →
C++
cpp
Actualizado 2026-07-21
finalproject.cpp
finalproject.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.cpp).
Leer artículo →
C++
c
Actualizado 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/deviceAlpha.h).
Leer artículo →
C++
c
Actualizado 2026-07-21
lab8.c
lab8.c — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.c).
Leer artículo →
C++
cpp
Actualizado 2026-07-21
lab8.cpp
lab8.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.cpp).
Leer artículo →