
C++11 的定位
C++11(ISO/IEC 14882:2011)是”现代 C++”的起点。它带来的不是零散的新语法,而是三套改变了日常写法的能力:类型推导(auto/decltype)、所有权模型(智能指针、移动语义)、函数式风格(lambda、range-for)。C++14/17 都是它的增量完善,理解 C++11 是理解后续所有版本的地基。

类型推导:auto 与 decltype
auto 按”值”推导,会丢弃引用与顶层 const——要引用就显式写 auto&,要只读就写 const auto&。decltype 保留完整类型(含引用)。
#include <vector>
auto i = 42; // int
auto d = 3.14; // double
auto s = "hello"; // const char*
std::vector<int> vec;
auto it = vec.begin(); // std::vector<int>::iterator
int x = 10;
decltype(x) y = 20; // y 也是 int
auto 最常见的用途是迭代器和 lambda 变量——这类类型写出来又长又没信息量。函数返回类型则建议显式写,接口处可读性优先。
nullptr 与强类型枚举
void f(int);
void f(void*);
f(NULL); // 问题:NULL 是 0,两个重载都能匹配 → 编译错误或误选 int 版
f(nullptr); // 明确选中 void* 版本
enum class Color { Red, Green, Blue };
Color c = Color::Red; // 必须带作用域
int n = static_cast<int>(c); // 不隐式转 int,显式转换
nullptr 有独立类型 std::nullptr_t,可与任何指针比较,不会被重载决议误匹配到整数版本。枚举用 enum class 是默认选择:作用域限定、不隐式转 int,避免旧 enum 的全局命名污染。
智能指针:所有权显式化
#include <memory>
// unique_ptr:独占所有权,不可拷贝、可移动
std::unique_ptr<int> p1(new int(10));
auto p2 = std::move(p1); // 所有权转移,p1 变为空
// shared_ptr:引用计数共享
std::shared_ptr<int> sp(new int(20));
auto sp2 = sp; // 计数 +1,最后一个释放时 delete
// weak_ptr:弱引用,不增加计数,防止循环引用
std::weak_ptr<int> wp = sp;
if (auto locked = wp.lock()) { // 提权成功才安全使用
*locked = 30;
}
裸 new/delete 只应出现在极少数底层代码里。接口之间统一约定:要么传引用,要么传智能指针,绝不裸指针进出——否则所有权语义混乱,悬空指针只能靠排查。
右值引用与移动语义
移动构造/移动赋值”偷走”源对象的资源(指针、句柄)而不是深拷贝,这是 vector 扩容、字符串拼接能保持高效的根本原因。
void process(int& x) { /* 左值版本 */ }
void process(int&& x) { /* 右值版本 */ }
process(a); // 左值 → 调 int& 版本
process(20); // 右值 → 调 int&& 版本
class MyString {
char* data;
public:
// 移动构造:偷走源对象的资源,并把源对象置空
MyString(MyString&& other) noexcept : data(other.data) {
other.data = nullptr; // 源对象析构时不会双重释放
}
};
两个易错点:std::move 本身不移动任何东西,它只是把左值 cast 成右值引用,让重载决议选中移动版本;移动构造必须标 noexcept,否则 vector 扩容时移动会悄悄退化成拷贝(标准库要求强异常保证时只敢用拷贝),性能问题只能靠 profile 发现。
Lambda 表达式
int factor = 10;
auto f = [factor](int x) { return x * factor; }; // 按值捕获
auto g = [&](int x) { return x + factor; }; // 按引用捕获全部
std::sort(v.begin(), v.end(),
[](int a, int b) { return a > b; }); // 匿名比较器
lambda 替代了函数对象(functor)的绝大部分用途:就地定义、就近使用、能捕获局部变量。mutable 允许修改按值捕获的副本;C++14 起参数可用 auto(泛型 lambda)。
constexpr 与编译期计算
constexpr int square(int x) { return x * x; }
int arr[square(5)]; // 编译期算出 25,可作为数组长度
注意 C++11 的 constexpr 函数体只能有一条 return,实用价值有限;循环、分支等完整能力在 C++14 才放开。
标准库扩充
std::thread/std::async:语言级线程与异步任务,配合 mutex / lock_guard;<random>:mt19937 + 分布对象,替代劣质的 rand();<chrono>:类型安全的时间点与时长;std::tuple/std::array:定长多元组与定长数组;unordered_map/unordered_set:哈希容器进标准库;<regex>:正则表达式。
其他重要特性
- override / final:override 让编译器检查”确实在重写基类虚函数”,final 禁止继续重写;
- 统一初始化:
{}初始化所有场景;类内成员可直接初始化; - 委托构造、=default / =delete:显式控制特殊成员函数;
- 可变参数模板:类型安全的变参,替代 C 的 va_list;
- using 别名:替代 typedef,且支持模板别名;
- static_assert:编译期断言;noexcept:异常规格;原始字符串:
R"(...)"免转义。
实战体会
auto 要用但别滥用:迭代器、lambda、复杂模板类型上它省一半打字,局部变量全用 auto 会让代码失去自文档能力。
移动语义是最容易被低估的特性:一个正确实现移动构造的类,在大容器场景下性能差一个数量级;但忘了加 noexcept,vector 扩容时移动会退化成拷贝,这个坑只能靠性能分析发现。
智能指针不要和裸指针混着传。所有跨模块接口统一传引用或智能指针,悬空指针问题基本绝迹。



