Files

6.0 KiB
Raw Permalink Blame History

slug, title, description, toc, authors, tags, categories, series, date, lastmod, featuredVideo, featuredImage, draft, enableComment
slug title description toc authors tags categories series date lastmod featuredVideo featuredImage draft enableComment
[C++ 筆記] 用 Modern C++ 的方式寫程式吧 [C++ 筆記] 用 Modern C++ 的方式寫程式吧 true
awin
c++
Programming
C++ 筆記
2026-01-04T00:00:00 2026-01-04T00:00:00 false true

Modern C++ 已經改變很多了

歷史包袱的重量

C++ 一直在相容性上不遺餘力,舊的語法能跑,新的語法與關鍵字又一直疊加,最後呈現出來的就是複雜。作為一個長久以來的使用者,看著其他程式語言的崛起與更新,雖然在理性上很能夠理解,但是情感上還是會想掙扎一下,讓我們看看 Modern C++ 改變的那一面,拋棄到舊的習慣,讓我們可以在不失去效能的情況下,用「比較輕鬆」的方式來寫 C++。

Modern C++ 的改變

1. 記憶體管理

告別手動記憶體管理(new/delete

// 舊式 C/C++ 方式
BigBuffer* buffer = new BigBuffer(size);
// ... 使用 buffer
delete buffer;  // 容易忘記,造成記憶體洩漏

// Modern C++ 方式
auto buffer = std::make_unique<BigBuffer>(size);
// 自動管理記憶體,無需手動 delete

使用 Smart Pointer

  • std::unique_ptr:獨佔所有權
  • std::shared_ptr:共享所有權
  • std::weak_ptr:弱引用,避免循環依賴

2. 用容器取代原生陣列

// 危險的 C 陣列
int arr[100];  // 無邊界檢查,容易越界

// 安全的 Modern C++ 容器
std::array<int, 100> arr;    // 編譯時大小固定
std::vector<int> vec;        // 動態大小

3. 自動型別推導

C++ 11 開始支援 auto 關鍵字

// 舊的方式,冗長的型別宣告
std::vector<std::string>::iterator it = vec.begin();

// 簡潔的 auto
auto it = vec.begin();

// 結構化綁定C++17
auto [key, value] = map_pair;

4. 現代迴圈語法

// 傳統 C 風格迴圈
for (int i = 0; i < arr.size(); ++i) {
    process(arr[i]);
}

// Modern C++ 範圍迴圈
for (const auto& elem : arr) {
    process(elem);
}

5. Lambda 表達式

// 傳統函式物件
struct Comparator {
    bool operator()(int a, int b) const {
        return a < b;
    }
};

// Modern C++ Lambda
auto compare = [](int a, int b) { return a < b; };

Modern C++ 的其他好東西

使用 std::optional 明確表達「可能為空」的情況

std::optional<int> findValue(const std::map<int, int>& mapping, int key) {
    auto it = mapping.find(key);
    if (it != mapping.end()) {
        return it->second;  // 找到了,返回值
    }
    return std::nullopt;    // 沒找到,返回空值
}

// 使用前必須檢查
auto value = findValue(numbers, 42);
if (value.has_value()) {
    std::cout << "Found value" << value.value() << std::endl;
}

像 Python 可以 return None 來表示空值,std::nullopt 也提供了一個表達空值的方式。 傳統的方式只能像是:

bool findResult(const std::vector<int>& mapping, int target, int& result) {
    for (const auto& value : mapping) {
        if (value == target) {
            result = value;  // 找到了,設置結果
            return true;     // 返回成功
        }
    }
    return false;            // 沒找到,返回失敗
}

constexpr 可以在編譯時計算

constexpr int factorial(int n) {
    return (n <= 1) ? 1 : n * factorial(n - 1);
}

// 在編譯時就能計算出結果
constexpr int fact5 = factorial(5);  // 編譯時計算為 120

using 簡化程式

using稱為型別別名Type Aliastypedef 的功能基本上是一樣的:它們都是為現有的型別建立一個「別名」,而不是建立新的型別。

  • using using [新名字] = [舊型別];
  • typedef typedef [舊型別] [新名字]; // 比較不直覺

using 有一個 typedef 做不到的是模板別名Template Alias

例如

template <typename T> using Handler = std::function<void(const T&)>;
Handler<int> intHandler;

using 來簡化 std::function 的例子。例如說我們的參數是一個 callback function

void setOnProgress(std::function<void(double, size_t)> callback) {
    progressCb = callback;
}

// 用 using 簡化
using ProgressCallback = std::function<void(double percentage, size_t bytesSent)>;

void setOnProgress(ProgressCallback callback) {
	progressCb = callback;
}

表達力

Modern C++ 讓程式碼更接近自然語言:

auto result = numbers
    | std::views::filter([](int x) { return x % 2 == 0; }) // 挑偶數
    | std::views::transform([](int x) { return x * x; })   // 轉平方
    | std::ranges::to<std::vector>();                      // 直接收進 vector

C++23 加入了 std::ranges::to 更重要的是不用再寫一堆 Iterator。

效能

Modern C++ 的效能提升,精確來說是實踐了 「零成本抽象Zero-overhead abstraction

  • constexpr:這真的很好用。能把計算直接塞進編譯階段,執行時完全不用花 CPU 時間。到了 C++20 更有 consteval 強制編譯期計算。
  • 移動語意 (Move Semantics):這是 C++ 的續命符。它解決了以前回傳大型物件時,為了怕拷貝太慢得傳指標或寫得扭扭捏捏的問題。現在直接 return 沒負擔。

試試看吧,漸進式升級

一開始先試著用 std::arraystd::vector 來取代傳統的 array。 然後用 smart pointer 來取代 new/delete。 過程中用 auto 來少打一些字。 用 using 來簡化落落長的型別。 用 Lambda 來簡化 callback不用在遠遠的地方定義 function

試試看,當冗長的 code 變得清爽,思路也比較不容易打結。

最後

雖然 C++ 還是有很多可以改進的地方,例如 package 的使用跟 Python 相比就麻煩很多。 但還是有在改變,試試看吧。