系列: C++
cpp
266 行
· 更新於 2026-04-03
dynamic_memory.cpp
C++/Part4_現代CPP/Ch16_記憶體管理與智慧指標/dynamic_memory.cpp
// ============================================================
// Ch16 — 動態記憶體管理(Dynamic Memory Management)
// 編譯:g++ -std=c++17 -Wall -o dynamic_memory dynamic_memory.cpp
// ============================================================
#include <iostream>
#include <string>
// ── 輔助函式:分隔線 ──
void section(const std::string& title) {
std::cout << "\n========================================\n";
std::cout << " " << title << "\n";
std::cout << "========================================\n";
}
// ── 用來示範動態記憶體的類別 ──
class Resource {
public:
Resource(int id) : id_(id) {
std::cout << " Resource(" << id_ << ") 建構\n";
}
~Resource() {
std::cout << " Resource(" << id_ << ") 解構\n";
}
void use() const {
std::cout << " 使用 Resource(" << id_ << ")\n";
}
private:
int id_;
};
int main() {
std::cout << "===== Ch16:動態記憶體管理 =====\n";
// ──────────────────────────────────────────
// 1. Stack vs Heap 基礎
// ──────────────────────────────────────────
section("1. Stack vs Heap 基礎");
{
// Stack 配置 — 離開作用域自動釋放
int stackVar = 42;
std::cout << "Stack 變數:" << stackVar << "\n";
std::cout << "Stack 變數位址:" << &stackVar << "\n";
// Heap 配置 — 需要手動 delete
int* heapVar = new int(100);
std::cout << "Heap 變數:" << *heapVar << "\n";
std::cout << "Heap 變數位址:" << heapVar << "\n";
delete heapVar; // 手動釋放
}
std::cout << "(Stack 變數已自動釋放)\n";
// ──────────────────────────────────────────
// 2. new / delete 單一物件
// ──────────────────────────────────────────
section("2. new / delete 單一物件");
{
// 配置基本型別
int* pi = new int(42);
double* pd = new double(3.14);
std::cout << "int: " << *pi << ", double: " << *pd << "\n";
delete pi;
delete pd;
// 配置自訂類別
std::cout << "\n配置 Resource 物件:\n";
Resource* r = new Resource(1);
r->use();
delete r; // 觸發解構子
}
// ──────────────────────────────────────────
// 3. new[] / delete[] 陣列
// ──────────────────────────────────────────
section("3. new[] / delete[] 陣列");
{
const int SIZE = 5;
int* arr = new int[SIZE];
// 初始化
for (int i = 0; i < SIZE; i++) {
arr[i] = (i + 1) * 10;
}
// 存取
std::cout << "動態陣列內容:";
for (int i = 0; i < SIZE; i++) {
std::cout << arr[i] << " ";
}
std::cout << "\n";
delete[] arr; // 注意:必須用 delete[]
// 物件陣列
std::cout << "\n配置 Resource 陣列(3 個):\n";
Resource* resources = new Resource[3]{{10}, {20}, {30}};
for (int i = 0; i < 3; i++) {
resources[i].use();
}
std::cout << "釋放 Resource 陣列:\n";
delete[] resources;
}
// ──────────────────────────────────────────
// 4. 記憶體洩漏示範(Memory Leak)
// ──────────────────────────────────────────
section("4. 記憶體洩漏示範");
{
std::cout << "【問題程式碼(概念示範,此處有手動修正)】\n";
std::cout << R"(
void leakyFunction() {
int* data = new int[1000];
// 做一些事情...
// 忘記 delete[] data; ← 記憶體洩漏!
} // data 指標被銷毀,但 heap 上的記憶體未釋放
)";
// 實際示範(為了不真的洩漏,我們還是釋放它)
int* data = new int[100];
for (int i = 0; i < 100; i++) data[i] = i;
std::cout << "已配置 100 個 int(正確釋放)\n";
delete[] data;
// 函式中提前 return 造成的洩漏
std::cout << "\n【提前 return 造成洩漏的模式】\n";
std::cout << R"(
bool process() {
int* buffer = new int[1024];
if (someCondition) {
return false; // ← buffer 洩漏!
}
delete[] buffer;
return true;
}
)";
}
// ──────────────────────────────────────────
// 5. 懸空指標示範(Dangling Pointer)
// ──────────────────────────────────────────
section("5. 懸空指標示範");
{
std::cout << "【概念示範】\n";
int* p = new int(42);
std::cout << "刪除前:*p = " << *p << "\n";
delete p;
// 此時 p 仍然存著舊位址,但記憶體已釋放
// std::cout << *p; // 未定義行為!不要這樣做
std::cout << "delete 後 p 仍然持有位址:" << p << "\n";
std::cout << "但存取 *p 是未定義行為!\n";
// 正確做法:delete 後設為 nullptr
p = nullptr;
std::cout << "設為 nullptr 後 p = " << p << "\n\n";
// 另一種懸空指標:指向已離開作用域的區域變數
std::cout << "【另一種懸空情況:回傳區域變數的位址】\n";
std::cout << R"(
int* dangerous() {
int local = 42;
return &local; // ← 回傳區域變數位址 → 懸空指標!
}
)";
}
// ──────────────────────────────────────────
// 6. 重複刪除示範(Double Delete)
// ──────────────────────────────────────────
section("6. 重複刪除示範");
{
std::cout << "【概念示範 — 不實際執行以避免崩潰】\n";
std::cout << R"(
int* p = new int(42);
int* q = p; // q 和 p 指向同一塊記憶體
delete p; // 第一次刪除 — 正確
delete q; // 第二次刪除同一塊記憶體 — 未定義行為!
)";
std::cout << "\n防範方式:\n";
std::cout << " 1. delete 後立即設為 nullptr(delete nullptr 是安全的)\n";
std::cout << " 2. 明確誰「擁有」指標(誰負責 delete)\n";
std::cout << " 3. 使用智慧指標(最佳解法)\n";
// 安全的做法
int* safe = new int(42);
delete safe;
safe = nullptr;
delete safe; // delete nullptr 是安全的,不會有任何副作用
std::cout << "\ndelete nullptr 是安全的 ✓\n";
}
// ──────────────────────────────────────────
// 7. 例外安全問題
// ──────────────────────────────────────────
section("7. 例外造成的洩漏問題");
{
std::cout << "【問題程式碼】\n";
std::cout << R"(
void unsafeFunction() {
Resource* r1 = new Resource(1);
Resource* r2 = new Resource(2); // 如果這裡拋出例外...
// ...做一些事情,可能拋出例外...
delete r2; // 這些永遠不會執行
delete r1;
}
)";
std::cout << "\n解法:使用智慧指標\n";
std::cout << R"(
void safeFunction() {
auto r1 = std::make_unique<Resource>(1);
auto r2 = std::make_unique<Resource>(2);
// 即使拋出例外,資源也會自動釋放
}
)";
}
// ──────────────────────────────────────────
// 8. new / delete 配對規則總結
// ──────────────────────────────────────────
section("8. new / delete 配對規則總結");
{
std::cout << "┌──────────────────┬──────────────────┐\n";
std::cout << "│ 配置方式 │ 釋放方式 │\n";
std::cout << "├──────────────────┼──────────────────┤\n";
std::cout << "│ new T │ delete p │\n";
std::cout << "│ new T[n] │ delete[] p │\n";
std::cout << "│ new T(args) │ delete p │\n";
std::cout << "│ new T[n]{...} │ delete[] p │\n";
std::cout << "└──────────────────┴──────────────────┘\n";
std::cout << "\n混用 delete 和 delete[] 會導致未定義行為!\n";
}
// ──────────────────────────────────────────
// 9. 為什麼要用智慧指標
// ──────────────────────────────────────────
section("9. 為什麼要用智慧指標");
{
std::cout << "原始 new/delete 的問題:\n";
std::cout << " ✗ 容易忘記 delete → 記憶體洩漏\n";
std::cout << " ✗ 不確定誰「擁有」指標 → 重複刪除或遺漏\n";
std::cout << " ✗ 例外拋出時跳過 delete → 洩漏\n";
std::cout << " ✗ 提前 return 跳過 delete → 洩漏\n";
std::cout << "\n智慧指標的優點:\n";
std::cout << " ✓ 自動管理記憶體(RAII)\n";
std::cout << " ✓ 擁有權語意清晰\n";
std::cout << " ✓ 例外安全\n";
std::cout << " ✓ unique_ptr 零額外開銷\n";
// 簡單對比
std::cout << "\n--- 傳統寫法 ---\n";
{
Resource* r = new Resource(100);
r->use();
delete r;
}
std::cout << "\n--- 現代寫法(下一個範例檔會詳細示範) ---\n";
std::cout << R"(
auto r = std::make_unique<Resource>(100);
r->use();
// 離開作用域自動釋放,不需要 delete
)";
}
std::cout << "\n===== 動態記憶體管理示範結束 =====\n";
return 0;
}
相關文章
C++
c
更新於 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/deviceAlpha.h).
閱讀文章 →
C++
c
更新於 2026-07-21
finalproject.c
finalproject.c — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.c).
閱讀文章 →
C++
cpp
更新於 2026-07-21
finalproject.cpp
finalproject.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.cpp).
閱讀文章 →
C++
c
更新於 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/deviceAlpha.h).
閱讀文章 →
C++
c
更新於 2026-07-21
lab8.c
lab8.c — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.c).
閱讀文章 →
C++
cpp
更新於 2026-07-21
lab8.cpp
lab8.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.cpp).
閱讀文章 →