概述
C++11 是 C++ 历史上最重要的一次版本升级,引入了 auto、智能指针、移动语义、Lambda 等现代 C++ 的核心能力。本文按主题梳理最常用的新特性。
一、自动类型推导
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 与 x 同类型 (int)
二、智能指针
#include <memory>
// unique_ptr:独占所有权,不可复制可移动
std::unique_ptr<int> p1(new int(10));
auto p2 = std::move(p1);
// shared_ptr:共享所有权,引用计数
std::shared_ptr<int> sp(new int(20));
auto sp2 = sp; // 计数 +1
// weak_ptr:弱引用,不增加计数,防循环引用
std::weak_ptr<int> wp = sp;
if (auto locked = wp.lock()) { /* 安全使用 */ }
三、右值引用与移动语义
void process(int& x) { /* 左值版本 */ }
void process(int&& x) { /* 右值版本 */ }
process(a); // 左值
process(20); // 右值
class MyString {
char* data;
public:
// 移动构造:偷资源而不是拷贝
MyString(MyString&& other) noexcept : data(other.data) {
other.data = nullptr;
}
};
std::vector<std::string> v1, v2;
v2.push_back(std::move(v1[0])); // 移动而非复制
四、Lambda 表达式
auto add = [](int a, int b) { return a + b; };
int x = 10;
auto print_x = [x]() { std::cout << x; }; // 值捕获
auto counter = [count = 0]() mutable { return ++count; };
五、基于范围的 for 循环
std::vector<int> vec = {1, 2, 3, 4};
for (int x : vec) { } // 值拷贝(只读小对象用)
for (int& x : vec) { x *= 2; } // 引用修改
for (const int& x : vec) { } // const 引用(大对象推荐)
六、其他重要特性
nullptr 与强类型枚举
foo(nullptr); // 明确空指针,替代有歧义的 NULL
enum class Color { Red, Green }; // 作用域枚举,不能隐式转 int
Color c = Color::Red;
constexpr
constexpr int square(int x) { return x * x; }
int arr[square(5)]; // 编译期计算
override / final
class Base {
virtual void foo() const;
virtual void bar() final; // 禁止派生类重写
};
class Derived : public Base {
void foo() const override; // 明确重写,编译器帮忙检查签名
};
初始化列表与类内初始化
std::vector<int> v{1, 2, 3}; // 统一初始化
class A { int x = 10; std::string s{"hi"}; };
变长模板与折叠表达式
template<typename... Args>
void print(Args... args) {
(std::cout << ... << args) << 'n'; // C++17 折叠表达式
}
std::thread / std::function / std::array / std::tuple
#include <thread> #include <functional> #include <array> #include <tuple>
std::mutex mtx;
void task() { std::lock_guard<std::mutex> lock(mtx); /* 临界区 */ }
std::thread t1(task); t1.join();
std::function<int(int,int)> f = [](int a, int b){ return a + b; };
std::array<int, 5> arr = {1,2,3,4,5};
auto t = std::make_tuple(1, 2.5, "hello");
int first = std::get<0>(t);
using 别名 / static_assert / noexcept / 原始字符串
using IntVector = std::vector<int>; // 替代 typedef,支持模板
static_assert(sizeof(int) == 4, "int must be 4 bytes"); // 编译期断言
void foo() noexcept {} // 承诺不抛异常
const char* path = R"(C:Program FilesMyApp)"; // 原始字符串,无需转义
实战体会
auto 要用但别滥用。写迭代器、复杂模板类型时 auto 能省一大半打字,但局部变量全用 auto 会让代码失去自文档能力。我的习惯是:类型能从右侧一眼看出来的地方才用(比如 auto it = v.begin()),函数返回类型则尽量显式写,接口处可读性优先。
智能指针不要和裸指针混着传。早期图省事,接口参数直接传 raw pointer 给内部的 shared_ptr,结果所有权语义混乱,排查悬空指针花了不少时间。后来统一约定:所有跨模块接口要么传引用,要么传智能指针,绝不裸指针进出。
移动语义是最容易被低估的特性。一个正确实现了移动构造的类,在大容器场景下性能能差一个数量级;但如果你忘了给移动构造加 noexcept,vector 扩容时移动会被悄悄退化成拷贝,这个坑靠性能分析才能发现。


