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 关键字的作用?