S SmartDocs
Series: C++ cpp 322 lines · Updated 2026-04-03

unique_ptr.cpp

C++/Part4_現代CPP/Ch16_記憶體管理與智慧指標/unique_ptr.cpp

// ============================================================
// Ch16 — unique_ptr 完整示範
// 編譯:g++ -std=c++17 -Wall -o unique_ptr unique_ptr.cpp
// ============================================================

#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <cassert>

// ── 輔助函式 ──
void section(const std::string& title) {
    std::cout << "\n========================================\n";
    std::cout << "  " << title << "\n";
    std::cout << "========================================\n";
}

// ── 示範用類別 ──
class Widget {
public:
    Widget(int id, const std::string& name)
        : id_(id), name_(name) {
        std::cout << "  Widget(" << id_ << ", " << name_ << ") 建構\n";
    }
    ~Widget() {
        std::cout << "  Widget(" << id_ << ", " << name_ << ") 解構\n";
    }
    void info() const {
        std::cout << "  Widget #" << id_ << ": " << name_ << "\n";
    }
    int id() const { return id_; }
    const std::string& name() const { return name_; }

private:
    int id_;
    std::string name_;
};

// ── 多型示範 ──
class Shape {
public:
    virtual ~Shape() = default;
    virtual double area() const = 0;
    virtual std::string type() const = 0;
};

class Circle : public Shape {
public:
    explicit Circle(double r) : radius_(r) {}
    double area() const override { return 3.14159265 * radius_ * radius_; }
    std::string type() const override { return "Circle"; }
private:
    double radius_;
};

class Rectangle : public Shape {
public:
    Rectangle(double w, double h) : width_(w), height_(h) {}
    double area() const override { return width_ * height_; }
    std::string type() const override { return "Rectangle"; }
private:
    double width_, height_;
};

// ── 函式:接受 unique_ptr 參數 ──

// 以 const 參考傳遞 — 不轉移擁有權(只讀取)
void inspect(const std::unique_ptr<Widget>& w) {
    if (w) {
        std::cout << "  [inspect] ";
        w->info();
    } else {
        std::cout << "  [inspect] 指標為空\n";
    }
}

// 以值傳遞 — 接管擁有權(呼叫端必須 std::move)
void consume(std::unique_ptr<Widget> w) {
    std::cout << "  [consume] 接管了 ";
    w->info();
    // 函式結束時 w 自動釋放
}

// 回傳 unique_ptr — 工廠模式
std::unique_ptr<Shape> createShape(const std::string& type, double a, double b = 0) {
    if (type == "circle") {
        return std::make_unique<Circle>(a);
    } else if (type == "rectangle") {
        return std::make_unique<Rectangle>(a, b);
    }
    return nullptr;
}

int main() {
    std::cout << "===== Ch16:unique_ptr 完整示範 =====\n";

    // ──────────────────────────────────────────
    // 1. 建立 unique_ptr
    // ──────────────────────────────────────────
    section("1. 建立 unique_ptr");
    {
        // 方法一:make_unique(推薦)
        auto p1 = std::make_unique<int>(42);
        std::cout << "make_unique<int>: " << *p1 << "\n";

        // 方法二:直接建構(不推薦)
        std::unique_ptr<double> p2(new double(3.14));
        std::cout << "unique_ptr(new): " << *p2 << "\n";

        // 方法三:自訂類別
        auto p3 = std::make_unique<Widget>(1, "Alpha");
        p3->info();

    }  // 離開作用域時 p1, p2, p3 自動釋放
    std::cout << "(所有 unique_ptr 已自動釋放)\n";

    // ──────────────────────────────────────────
    // 2. 存取操作:*, ->, get()
    // ──────────────────────────────────────────
    section("2. 存取操作");
    {
        auto p = std::make_unique<Widget>(2, "Beta");

        // 解參考
        std::cout << "(*p).id() = " << (*p).id() << "\n";

        // 箭頭運算子
        std::cout << "p->name() = " << p->name() << "\n";

        // get() — 取得原始指標(不轉移擁有權)
        Widget* raw = p.get();
        std::cout << "raw->id() = " << raw->id() << "\n";

        // bool 轉換 — 檢查是否為空
        if (p) {
            std::cout << "p 不為空\n";
        }
    }

    // ──────────────────────────────────────────
    // 3. 移動擁有權(std::move)
    // ──────────────────────────────────────────
    section("3. 移動擁有權");
    {
        auto p1 = std::make_unique<Widget>(3, "Gamma");
        std::cout << "移動前 p1: ";
        inspect(p1);

        // 轉移擁有權
        auto p2 = std::move(p1);
        std::cout << "移動後 p1: ";
        inspect(p1);  // p1 現在是 nullptr
        std::cout << "移動後 p2: ";
        inspect(p2);

        // 驗證 p1 為空
        assert(p1 == nullptr);
        std::cout << "p1 == nullptr ✓\n";
    }

    // ──────────────────────────────────────────
    // 4. release() 和 reset()
    // ──────────────────────────────────────────
    section("4. release() 和 reset()");
    {
        // release() — 放棄擁有權,回傳原始指標
        auto p1 = std::make_unique<Widget>(4, "Delta");
        Widget* raw = p1.release();
        std::cout << "release() 後 p1 == nullptr: "
                  << (p1 == nullptr ? "是" : "否") << "\n";
        raw->info();
        delete raw;  // 呼叫端負責刪除

        // reset() — 釋放目前管理的物件
        auto p2 = std::make_unique<Widget>(5, "Epsilon");
        std::cout << "\n呼叫 reset():\n";
        p2.reset();  // 立即釋放 Widget(5)
        std::cout << "reset() 後 p2 == nullptr: "
                  << (p2 == nullptr ? "是" : "否") << "\n";

        // reset(new T) — 釋放舊的,管理新的
        auto p3 = std::make_unique<Widget>(6, "Zeta");
        std::cout << "\n呼叫 reset(new Widget):\n";
        p3.reset(new Widget(7, "Eta"));  // Widget(6) 被釋放
        p3->info();
    }

    // ──────────────────────────────────────────
    // 5. unique_ptr 與陣列
    // ──────────────────────────────────────────
    section("5. unique_ptr 與陣列");
    {
        // unique_ptr 可以管理動態陣列
        auto arr = std::make_unique<int[]>(5);
        for (int i = 0; i < 5; i++) {
            arr[i] = (i + 1) * 10;
        }

        std::cout << "unique_ptr<int[]> 內容:";
        for (int i = 0; i < 5; i++) {
            std::cout << arr[i] << " ";
        }
        std::cout << "\n";

        // 注意:陣列版本使用 [] 存取,沒有 * 和 -> 運算子
        // 實務上建議使用 std::vector 替代動態陣列
        std::cout << "(實務上建議用 std::vector 取代動態陣列)\n";
    }

    // ──────────────────────────────────────────
    // 6. unique_ptr 在容器中
    // ──────────────────────────────────────────
    section("6. unique_ptr 在容器中");
    {
        std::vector<std::unique_ptr<Shape>> shapes;

        // push_back 需要 std::move
        auto c = std::make_unique<Circle>(5.0);
        shapes.push_back(std::move(c));

        // emplace_back 更簡潔(C++14 搭配 make_unique)
        shapes.push_back(std::make_unique<Rectangle>(3.0, 4.0));
        shapes.push_back(std::make_unique<Circle>(2.5));

        // 多型遍歷
        std::cout << "所有形狀:\n";
        for (const auto& s : shapes) {
            std::cout << "  " << s->type()
                      << " 面積 = " << s->area() << "\n";
        }
    }  // vector 銷毀時自動刪除所有形狀

    // ──────────────────────────────────────────
    // 7. 傳遞 unique_ptr 給函式
    // ──────────────────────────────────────────
    section("7. 傳遞 unique_ptr 給函式");
    {
        auto w = std::make_unique<Widget>(8, "Theta");

        // 不轉移擁有權 — 傳 const 參考
        inspect(w);
        std::cout << "inspect 後 w 仍有效: " << (w ? "是" : "否") << "\n";

        // 轉移擁有權 — 傳值(必須 move)
        consume(std::move(w));
        std::cout << "consume 後 w 為空: " << (!w ? "是" : "否") << "\n";
    }

    // ──────────────────────────────────────────
    // 8. 從函式回傳 unique_ptr(工廠模式)
    // ──────────────────────────────────────────
    section("8. 工廠模式回傳 unique_ptr");
    {
        auto shape1 = createShape("circle", 10.0);
        auto shape2 = createShape("rectangle", 6.0, 8.0);
        auto shape3 = createShape("triangle", 0);

        if (shape1) {
            std::cout << shape1->type() << " 面積 = " << shape1->area() << "\n";
        }
        if (shape2) {
            std::cout << shape2->type() << " 面積 = " << shape2->area() << "\n";
        }
        if (!shape3) {
            std::cout << "未知形狀 → 回傳 nullptr\n";
        }
    }

    // ──────────────────────────────────────────
    // 9. 自訂刪除器
    // ──────────────────────────────────────────
    section("9. 自訂刪除器");
    {
        // Lambda 刪除器
        auto deleter = [](int* p) {
            std::cout << "  自訂刪除器:釋放 *p = " << *p << "\n";
            delete p;
        };

        std::unique_ptr<int, decltype(deleter)> p(new int(999), deleter);
        std::cout << "值:" << *p << "\n";
        std::cout << "離開作用域時觸發自訂刪除器:\n";
    }

    {
        // 實務範例:FILE* 管理
        std::cout << "\n【FILE* 管理範例(概念)】\n";
        std::cout << R"(
  auto fileDeleter = [](FILE* f) {
      if (f) {
          std::cout << "關閉檔案\n";
          fclose(f);
      }
  };
  std::unique_ptr<FILE, decltype(fileDeleter)> fp(
      fopen("data.txt", "r"), fileDeleter
  );
  // 離開作用域時自動 fclose
)";
    }

    // ──────────────────────────────────────────
    // 10. unique_ptr 不可複製的驗證
    // ──────────────────────────────────────────
    section("10. unique_ptr 不可複製");
    {
        auto p = std::make_unique<int>(42);

        // 以下會編譯錯誤:
        // auto q = p;                    // 錯誤:不可複製
        // std::unique_ptr<int> r = p;    // 錯誤:不可複製

        // 正確:使用 std::move
        auto q = std::move(p);
        std::cout << "移動後 *q = " << *q << "\n";
        std::cout << "p 為空: " << (p == nullptr ? "是" : "否") << "\n";
    }

    std::cout << "\n===== unique_ptr 示範結束 =====\n";
    return 0;
}

Related articles