シリーズ: C++
cpp
322 行
· 更新日 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;
}
関連記事
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).
記事を読む →