ToolkitX
知识库工具箱

现代 C++

C++11/14/17/20 新特性概览

30min·高级

01. auto 和类型推导

auto 让编译器自动推断变量类型,你懒得写 long long 类型名的时候特别好用。声明变量时如果马上初始化,用 auto 编译器自己猜。auto 不会推断引用和 const,需要自己加 auto& 或 const auto&。遍历容器时 for (const auto& x : vec) 是标准写法。
cpp
#include <vector>
#include <map>

int main() {
    auto i = 42;          // int
    auto d = 3.14;        // double
    auto s = "hello";     // const char*
    
    std::vector<int> v = {1, 2, 3};
    auto it = v.begin();  // std::vector<int>::iterator
    
    std::map<std::string, int> m;
    for (const auto& [k, v] : m) {  // C++17 结构化绑定
        // k 是键, v 是值
    }
    
    // auto 不推导引用
    int x = 10;
    auto y = x;   // int, 不是 int&
    auto& z = x;  // int&
    
    return 0;
}
auto 让模板类型的迭代器和 lambda 声明变得简洁无比,但基本类型用 auto 反而降低可读性。

02. lambda 匿名函数

Lambda 就是现场写的匿名函数,三个部分:捕获列表([])、参数列表、函数体。捕获列表决定外面的变量怎么传进来:= 值捕获只读,& 引用捕获可改,[x] 只捕 x,[this] 捕当前对象成员。mutable 让值捕获的变量也能修改。最常用于 STL 算法和回调。
cpp
#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    int threshold = 5;
    
    // 基本 lambda
    auto add = [](int a, int b) { return a + b; };
    std::cout << add(3, 5) << "\n";
    
    // 捕获外部变量
    std::vector<int> v = {1, 2, 3, 4, 5, 6};
    int count = std::count_if(v.begin(), v.end(),
        [threshold](int x) { return x > threshold; }
    );
    
    // 引用捕获 - 可以修改外部变量
    int sum = 0;
    std::for_each(v.begin(), v.end(),
        [&sum](int x) { sum += x; }
    );
    
    // 泛型 lambda (C++14)
    auto generic = [](auto a, auto b) { return a + b; };
    
    return 0;
}
lambda 本质上是一个匿名函数对象(仿函数),[] 里什么都不写就等同于普通函数。

03. 智能指针

C++11 引入了三种智能指针,告别手动 new/delete。unique_ptr 独占所有权,只能移动不能复制,离开作用域自动 delete。shared_ptr 共享所有权,引用计数归零时释放,用 make_shared 创建最高效。weak_ptr 用来打破 shared_ptr 循环引用,不增加引用计数。
cpp
#include <memory>
#include <iostream>

class Resource {
public:
    Resource() { std::cout << "构造\n"; }
    ~Resource() { std::cout << "析构\n"; }
    void use() { std::cout << "使用资源\n"; }
};

int main() {
    // unique_ptr - 独占
    auto u = std::make_unique<Resource>();
    u->use();
    // std::unique_ptr<Resource> u2 = u; // 错误!不能复制
    auto u2 = std::move(u); // 转移所有权
    
    // shared_ptr - 共享
    auto s1 = std::make_shared<Resource>();
    auto s2 = s1; // s2 也指向同一个对象,引用计数=2
    std::cout << "引用计数: " << s1.use_count() << "\n"; // 2
    
    // weak_ptr - 不增加引用计数
    std::weak_ptr<Resource> w = s1;
    if (auto sp = w.lock()) { // 尝试获取 shared_ptr
        sp->use();
    }
    
    return 0; // 离开作用域自动释放所有资源
}
shared_ptr 有循环引用问题(A 指 B,B 指 A),会导致内存泄漏。用 weak_ptr 打破循环。

04. 移动语义和右值引用

移动语义是 C++11 最大的创新之一,解决了不必要的深拷贝。std::move 把左值转为右值,触发移动构造函数(偷资源而不是拷贝)。&& 是右值引用,移动构造的参数就是 T&&。对于持有堆资源的类(如 vector、string),移动比拷贝快得多因为只是转移指针。
cpp
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v1 = {1, 2, 3, 4, 5};
    
    // 拷贝:v1 不变
    auto v2 = v1;
    
    // 移动:v1 被掏空(变成空 vector)
    auto v3 = std::move(v1);
    std::cout << "v1 size: " << v1.size() << "\n"; // 0
    std::cout << "v3 size: " << v3.size() << "\n"; // 5
    
    // 移动构造函数示例
    class Buffer {
        int* data;
    public:
        Buffer(size_t size) : data(new int[size]) {}
        Buffer(Buffer&& other) noexcept : data(other.data) {
            other.data = nullptr; // 掏空
        }
        ~Buffer() { delete[] data; }
    };
    
    return 0;
}
用 std::move 时记住:移走之后原对象处于有效但未定义的状态,不要再使用它。

05. constexpr 编译期计算

constexpr 让函数和变量在编译期就能计算出结果,运行时不花时间。constexpr 函数的计算结果可以在编译时当作常量使用(如数组大小、模板参数等)。C++14 允许 constexpr 函数里有循环和分支,C++17 可以用 constexpr if 编译期选择执行路径。
cpp
#include <iostream>

// 编译期阶乘
constexpr int factorial(int n) {
    int result = 1;
    for (int i = 1; i <= n; i++) result *= i;
    return result;
}

int main() {
    constexpr int fact5 = factorial(5); // 编译时算好 = 120
    int arr[factorial(4)];               // 数组大小 24,编译时确定
    
    // constexpr if (C++17)
    template<typename T>
    auto get_value(T t) {
        if constexpr (std::is_integral_v<T>) {
            return t * 2;
        } else {
            return t;
        }
    }
    
    std::cout << fact5 << "\n";
    return 0;
}
能用 constexpr 的函数尽量声明为 constexpr,编译期计算完就是免费的运行时性能。

知识测验

1/4正确 0

auto 关键字的作用?