
关注我,学习c++不迷路:
专栏如下:
后续会更新更多有趣的小知识,关注我带你遨游知识世界

期待你的关注。

文章目录
-
- [第一章:C++11前夜 - 一个被时代抛弃的巨人](#第一章:C++11前夜 - 一个被时代抛弃的巨人)
-
- [1.1 C++的"黑暗时代"(1998-2009)](#1.1 C++的"黑暗时代"(1998-2009))
-
- [1.1.1 C++03的原罪](#1.1.1 C++03的原罪)
- [1.1.2 具体被"骂"的痛点(代码级)](#1.1.2 具体被"骂"的痛点(代码级))
- [1.2 社区的呼声](#1.2 社区的呼声)
- [第二章:C++11的诞生 - 一场长达十年的革命](#第二章:C++11的诞生 - 一场长达十年的革命)
-
- [2.1 标准化委员会的艰难历程](#2.1 标准化委员会的艰难历程)
-
- [2.1.1 关键人物](#2.1.1 关键人物)
- [2.1.2 设计哲学](#2.1.2 设计哲学)
- [2.2 特性详解:从语法到实现](#2.2 特性详解:从语法到实现)
-
- [2.2.1 自动类型推导:auto的革命](#2.2.1 自动类型推导:auto的革命)
- [2.2.2 decltype:类型反射](#2.2.2 decltype:类型反射)
- [2.2.3 右值引用和移动语义:性能革命的核心](#2.2.3 右值引用和移动语义:性能革命的核心)
- [2.2.4 Lambda表达式:函数式编程进入C++](#2.2.4 Lambda表达式:函数式编程进入C++)
- [2.2.5 智能指针:内存管理的终极方案](#2.2.5 智能指针:内存管理的终极方案)
- [2.2.6 线程库:并发编程标准化](#2.2.6 线程库:并发编程标准化)
- [2.2.7 可变参数模板:元编程的飞跃](#2.2.7 可变参数模板:元编程的飞跃)
- [2.2.8 constexpr:编译期计算](#2.2.8 constexpr:编译期计算)
- [2.2.9 nullptr:空指针的救星](#2.2.9 nullptr:空指针的救星)
- [2.2.10 统一初始化](#2.2.10 统一初始化)
- [2.2.11 强类型枚举](#2.2.11 强类型枚举)
- [2.2.12 override和final关键字](#2.2.12 override和final关键字)
- [2.2.13 委托构造函数](#2.2.13 委托构造函数)
- [2.2.14 继承构造](#2.2.14 继承构造)
- [2.2.15 静态断言](#2.2.15 静态断言)
- [2.2.16 原始字符串字面量](#2.2.16 原始字符串字面量)
- [2.2.17 用户定义字面量](#2.2.17 用户定义字面量)
- [2.2.18 新的容器和算法](#2.2.18 新的容器和算法)
- [2.2.19 正则表达式](#2.2.19 正则表达式)
- [2.2.20 时间库](#2.2.20 时间库)
- [2.2.21 tuple](#2.2.21 tuple)
- [2.2.22 type_traits](#2.2.22 type_traits)
- [2.2.23 异常处理改进](#2.2.23 异常处理改进)
- [2.2.24 内存对齐](#2.2.24 内存对齐)
- [2.2.25 新的文件系统(C++17才正式引入,但C++11有实验性支持)](#2.2.25 新的文件系统(C++17才正式引入,但C++11有实验性支持))
- 第三章:C++11的设计哲学和影响
-
- [3.1 零开销抽象原则的实现](#3.1 零开销抽象原则的实现)
- [3.2 类型安全的提升](#3.2 类型安全的提升)
- [3.3 并发编程的标准化](#3.3 并发编程的标准化)
- [3.4 对现代编程范式的影响](#3.4 对现代编程范式的影响)
- 第四章:C++11的采用和争议
-
- [4.1 编译器支持时间表](#4.1 编译器支持时间表)
- [4.2 争议和批评](#4.2 争议和批评)
- [4.3 成功案例](#4.3 成功案例)
- 第五章:C++11的遗产
-
- [5.1 对后续标准的影响](#5.1 对后续标准的影响)
- [5.2 编程风格的转变](#5.2 编程风格的转变)
- [5.3 性能提升的量化](#5.3 性能提升的量化)
- [第六章:总结 - C++11的历史地位](#第六章:总结 - C++11的历史地位)
第一章:C++11前夜 - 一个被时代抛弃的巨人
1.1 C++的"黑暗时代"(1998-2009)
1.1.1 C++03的原罪
1998年,C++标准(ISO/IEC 14882:1998)发布,这是C++的第一个国际标准。然而,这个标准在2003年仅做了微小修订(C++03),之后长达8年没有重大更新。
在这期间,世界发生了巨变:
- 2004年:Google发布Gmail,开启Web 2.0时代
- 2007年:iPhone发布,移动互联网时代来临
- 2008年:Android发布
- 2009年:Node.js发布,事件驱动编程流行
而C++呢?程序员们还在用着1998年的语法,忍受着各种不便。
1.1.2 具体被"骂"的痛点(代码级)
痛点1:迭代器地狱
cpp
// 在C++03中,写一个简单的泛型函数有多痛苦?
template<typename Container>
void print_all(const Container& c) {
// 必须写这么长!
for (typename Container::const_iterator it = c.begin();
it != c.end(); ++it) {
std::cout << *it << " ";
}
}
// 如果容器是const的,还要写const_iterator版本
template<typename Container>
void print_all_mutable(Container& c) {
for (typename Container::iterator it = c.begin();
it != c.end(); ++it) {
std::cout << *it << " ";
}
}
// 使用时
std::vector<int> vec = {1, 2, 3};
print_all(vec); // OK
const std::vector<int> cvec = {1, 2, 3};
print_all(cvec); // OK
print_all_mutable(vec); // OK
print_all_mutable(cvec); // 错误!需要const版本
痛点2:对象创建的性能灾难
cpp
// C++03中的临时对象问题
std::vector<std::string> getNames() {
std::vector<std::string> result;
result.push_back("Alice");
result.push_back("Bob");
result.push_back("Charlie");
return result; // 这里会发生什么?
}
// 使用时
std::vector<std::string> names = getNames();
// 实际发生:
// 1. getNames()内部创建vector
// 2. push_back三次,可能多次扩容
// 3. return时,可能拷贝整个vector到names
// 4. 如果编译器不够聪明,可能还有额外拷贝
// 为了优化,程序员不得不写:
std::vector<std::string> names;
names.reserve(3); // 手动预分配
names.push_back("Alice");
names.push_back("Bob");
names.push_back("Charlie");
// 还是无法避免返回时的拷贝
痛点3:没有线程,没有未来
cpp
// C++03中,多线程编程是噩梦
#ifdef _WIN32
#include <windows.h>
DWORD WINAPI ThreadFunc(LPVOID lpParam) {
// Windows线程
return 0;
}
#else
#include <pthread.h>
void* ThreadFunc(void* arg) {
// POSIX线程
return NULL;
}
#endif
// 没有标准互斥锁
// 没有标准条件变量
// 没有标准future/promise
// 每个平台都要重写一遍
痛点4:智能指针的尴尬
cpp
// C++03只有auto_ptr,设计有缺陷
std::auto_ptr<int> p1(new int(5));
std::auto_ptr<int> p2 = p1; // p1现在是空指针!
// 这种隐式所有权转移违反直觉
// 没有shared_ptr,程序员只能:
// 1. 手动delete(容易忘记)
// 2. 使用boost::shared_ptr(不是标准库)
// 3. 自己实现引用计数(容易出错)
痛点5:模板元编程的极限
cpp
// C++03中,可变参数模板不存在
// 想写一个printf-like函数?
template<typename T1>
void printf(const char* fmt, T1 arg1) {
// 解析fmt,打印arg1...
}
template<typename T1, typename T2>
void printf(const char* fmt, T1 arg1, T2 arg2) {
// 解析fmt,打印arg1, arg2...
}
// 需要为每个参数数量写一个版本!
// 想获取类型信息?
#define MAKE_REF(x) typename std::add_reference<x>::type
// 宏!丑陋!容易出错!
痛点6:初始化不一致
cpp
// C++03中,初始化方式五花八门
int a(5); // 构造函数
int b = 5; // 拷贝初始化
int c = {5}; // 列表初始化(C++03有限支持)
int d[] = {1, 2, 3}; // 数组初始化
struct Point { int x, y; };
Point p1(10, 20); // 构造函数
Point p2 = {10, 20}; // C++03不支持!
1.2 社区的呼声
2005年,C++标准委员会在东京会议上,Herb Sutter(委员会主席)宣布:"C++0x"将在2009年发布。社区充满期待,但随后是漫长的等待。
2007年,Boost库发布,提供了大量实验性特性:
- Boost.Thread(线程)
- Boost.SmartPtr(智能指针)
- Boost.Lambda(Lambda表达式)
- Boost.Function(函数对象)
这些库证明了C++可以现代化,但也暴露了标准库的落后。
第二章:C++11的诞生 - 一场长达十年的革命
2.1 标准化委员会的艰难历程
2.1.1 关键人物
- Bjarne Stroustrup:C++之父,坚持"零开销抽象"原则
- Herb Sutter:委员会主席,推动现代化
- Francis Glassborow:推动安全性和易用性
- Doug Gregor:负责编译期编程特性
2.1.2 设计哲学
C++11的核心原则:
原则1:零开销抽象
"你不用为你不使用的特性付费,你使用的特性几乎和手写C一样快"
原则2:向后兼容
"98年的代码应该能在11年编译器上运行"
原则3:类型安全
"让错误在编译期暴露,而不是运行期"
原则4:表达力
"让程序员能用更少的代码做更多的事"
2.2 特性详解:从语法到实现
2.2.1 自动类型推导:auto的革命
设计背景 :
C++的类型系统强大但繁琐。随着模板和STL的普及,类型名变得越来越长。
实现细节:
cpp
// auto的推导规则(简化版)
auto x = 5; // int
auto y = 5.0; // double
auto z = x + y; // double
// 引用和const
int& r = x;
auto a = r; // int(引用被忽略)
auto& b = r; // int&(保留引用)
const auto c = x; // const int
// 数组和函数
int arr[5];
auto d = arr; // int*(退化为指针)
auto& e = arr; // int(&)[5](保留数组引用)
// 函数返回值
template<typename T1, typename T2>
auto add(T1 a, T2 b) -> decltype(a + b) {
return a + b;
}
为什么需要尾随返回类型?
cpp
// 这样不行,因为decltype(a+b)在参数列表之后才定义
template<typename T1, typename T2>
decltype(a + b) add(T1 a, T2 b) { // 错误:a和b未定义
return a + b;
}
// 必须用尾随返回类型
template<typename T1, typename T2>
auto add(T1 a, T2 b) -> decltype(a + b) {
return a + b;
}
auto的推导算法:
- 将表达式转换为右值引用(如果适用)
- 忽略顶层const和引用
- 根据初始化表达式推导类型
性能影响:
cpp
// auto不会产生任何运行时开销
auto x = 5; // 等价于 int x = 5;
// 在模板中,auto可以避免不必要的类型转换
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin(); // std::vector<int>::iterator
// 如果写成其他类型,可能发生隐式转换,影响性能
2.2.2 decltype:类型反射
设计背景 :
模板元编程需要获取表达式类型,但C++03只能通过复杂技巧。
实现细节:
cpp
// decltype的基本用法
int x = 5;
decltype(x) y = 10; // int
decltype((x)) z = x; // int&(注意括号!)
// 函数返回类型推导
template<typename T1, typename T2>
auto multiply(T1 a, T2 b) -> decltype(a * b) {
return a * b;
}
// 在泛型代码中的威力
template<typename Container>
void process(Container& c) {
// 获取元素类型
typedef typename Container::value_type T;
// 获取迭代器类型
typedef typename Container::iterator It;
// 获取引用类型
typedef typename Container::reference Ref;
// C++11之前,这些都需要手动写
// 现在可以用decltype自动推导
auto it = c.begin();
decltype(*it) value = *it; // 获取元素的引用类型
}
decltype的推导规则:
cpp
// 规则1:简单表达式
int x;
decltype(x) // int
decltype(x + 1) // int
// 规则2:左值表达式
decltype((x)) // int&(括号产生左值)
// 规则3:函数调用
int f();
decltype(f()) // int
// 规则4:成员访问
struct S { int m; };
S s;
decltype(s.m) // int
decltype((s.m)) // int&
// 规则5:解引用
int* p;
decltype(*p) // int&(解引用是左值)
// 规则6:数组
int arr[5];
decltype(arr) // int[5]
decltype(arr[0]) // int
2.2.3 右值引用和移动语义:性能革命的核心
设计背景 :
C++03中,临时对象的拷贝是性能杀手。例如:
cpp
std::vector<int> createVector() {
std::vector<int> v;
v.push_back(1);
v.push_back(2);
return v; // 拷贝!
}
std::vector<int> result = createVector(); // 又是拷贝!
即使编译器做RVO(返回值优化),也不是标准保证的。
右值引用的诞生:
cpp
// C++11引入右值引用&&
int x = 5;
int& r1 = x; // 左值引用
int&& r2 = 10; // 右值引用(绑定到临时对象)
// 函数重载区分左值和右值
void func(int& x) { std::cout << "左值引用\n"; }
void func(int&& x) { std::cout << "右值引用\n"; }
int a = 5;
func(a); // 左值引用
func(10); // 右值引用
func(a + 5); // 右值引用
移动构造函数和移动赋值:
cpp
class MyString {
char* data;
size_t len;
public:
// 普通构造函数
MyString(const char* str) {
len = strlen(str);
data = new char[len + 1];
strcpy(data, str);
}
// 拷贝构造函数(深拷贝)
MyString(const MyString& other) {
len = other.len;
data = new char[len + 1];
strcpy(data, other.data);
std::cout << "拷贝构造\n";
}
// 移动构造函数(转移所有权)
MyString(MyString&& other) noexcept
: data(other.data), len(other.len) {
other.data = nullptr; // 关键:置空源对象
other.len = 0;
std::cout << "移动构造\n";
}
// 拷贝赋值
MyString& operator=(const MyString& other) {
if (this != &other) {
delete[] data;
len = other.len;
data = new char[len + 1];
strcpy(data, other.data);
}
return *this;
}
// 移动赋值
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data; // 释放自己的资源
data = other.data; // 接管资源
len = other.len;
other.data = nullptr;
other.len = 0;
}
return *this;
}
~MyString() { delete[] data; }
};
// 使用场景
MyString create() {
MyString s("hello");
return s; // 移动构造!
}
MyString str = create(); // 移动构造!
std::move的实现:
cpp
// std::move的本质
template<typename T>
typename std::remove_reference<T>::type&&
move(T&& t) {
return static_cast<typename std::remove_reference<T>::type&&>(t);
}
// 使用
MyString s1("hello");
MyString s2 = std::move(s1); // 强制移动
// s1现在是空的!
完美转发:
cpp
// 完美转发解决的问题
void inner(int& x) { std::cout << "左值\n"; }
void inner(int&& x) { std::cout << "右值\n"; }
template<typename T>
void outer(T&& x) {
inner(x); // 总是调用左值版本!
}
outer(5); // 期望右值,实际左值
int a = 5;
outer(a); // 期望左值,实际左值
// 解决方案:完美转发
template<typename T>
void outer(T&& x) {
inner(std::forward<T>(x)); // 保持值类别
}
// std::forward的实现
template<typename T>
T&& forward(typename std::remove_reference<T>::type& t) {
return static_cast<T&&>(t);
}
引用折叠规则:
cpp
// T&&的推导规则
template<typename T>
void f(T&& x); // 万能引用
int a = 5;
f(a); // T = int&,推导为int& && -> int&
f(5); // T = int,推导为int&&
移动语义在STL中的应用:
cpp
// C++11的vector
std::vector<std::string> vec;
vec.push_back("hello"); // 移动构造string,避免拷贝
// 重新分配时
std::vector<std::string> v1 = {"a", "b", "c"};
std::vector<std::string> v2 = std::move(v1); // 移动整个vector
// v1现在是空的,v2拥有所有元素
2.2.4 Lambda表达式:函数式编程进入C++
设计背景 :
C++03中,使用STL算法需要写函数对象,代码冗长:
cpp
// C++03
struct PrintFunctor {
void operator()(int x) const {
std::cout << x << std::endl;
}
};
std::for_each(vec.begin(), vec.end(), PrintFunctor());
// 或者用boost::lambda
std::for_each(vec.begin(), vec.end(), std::cout << boost::lambda::_1 << "\n");
Lambda的语法:
cpp
// 完整语法
[capture](parameters) mutable -> return-type { body }
// 简化版
[capture](parameters) { body }
捕获机制详解:
cpp
int a = 1, b = 2, c = 3;
// 值捕获(拷贝)
auto f1 = [a, b]() { return a + b; }; // 捕获a和b的副本
// 引用捕获
auto f2 = [&a, &b]() { a = 10; b = 20; }; // 修改外部变量
// 隐式捕获
auto f3 = [=]() { return a + b + c; }; // 按值捕获所有
auto f4 = [&]() { a = 10; b = 20; c = 30; }; // 按引用捕获所有
// 混合捕获
auto f5 = [=, &a]() {
return a + b + c; // a是引用,b和c是值
};
// 捕获this(成员函数)
class MyClass {
int x;
public:
void foo() {
auto f = [this]() { return x; }; // 捕获this指针
}
};
// 初始化捕获(C++14)
auto f6 = [x = 5, y = a + b]() { return x + y; };
Lambda的实现原理:
cpp
// Lambda实际上是编译器生成的类
auto lambda = [a, &b](int x) { return a + b + x; };
// 编译器大致生成:
class LambdaType {
private:
int a; // 值捕获
int& b; // 引用捕获
public:
LambdaType(int a_, int& b_) : a(a_), b(b_) {}
int operator()(int x) const {
return a + b + x;
}
};
// 使用时
int a = 1, b = 2;
LambdaType lambda(a, b);
lambda(3); // 调用operator()
Lambda与STL的完美结合:
cpp
std::vector<int> vec = {5, 2, 8, 1, 9};
// 排序
std::sort(vec.begin(), vec.end(), [](int a, int b) {
return a > b; // 降序
});
// 查找
auto it = std::find_if(vec.begin(), vec.end(), [](int x) {
return x % 2 == 0; // 找第一个偶数
});
// 计数
int count = std::count_if(vec.begin(), vec.end(), [](int x) {
return x > 5;
});
// 转换
std::vector<int> squares;
std::transform(vec.begin(), vec.end(), std::back_inserter(squares),
[](int x) { return x * x; });
// 删除
vec.erase(std::remove_if(vec.begin(), vec.end(), [](int x) {
return x < 3;
}), vec.end());
Lambda的高级用法:
cpp
// 递归Lambda(需要std::function)
std::function<int(int)> factorial = [&](int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
};
// 捕获复数
std::complex<double> c(1.0, 2.0);
auto f = [c](double x) { return x * c; };
// 在多线程中
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i) {
threads.emplace_back([i]() { // 按值捕获循环变量
std::cout << "Thread " << i << "\n";
});
}
2.2.5 智能指针:内存管理的终极方案
设计背景 :
C++03的auto_ptr有致命缺陷:
cpp
std::auto_ptr<int> p1(new int(5));
std::auto_ptr<int> p2 = p1; // p1现在是空指针!
*p1 = 10; // 未定义行为!
unique_ptr:独占所有权
cpp
// 基本使用
std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = std::make_unique<int>(5); // 更安全
// 不可复制
// auto p3 = p1; // 错误!
// 可以移动
auto p3 = std::move(p1); // p1变为空,p3拥有资源
// 自定义删除器
auto file_deleter = [](FILE* f) { if (f) fclose(f); };
std::unique_ptr<FILE, decltype(file_deleter)> f(fopen("test.txt", "r"), file_deleter);
// 数组支持
std::unique_ptr<int[]> arr(new int[10]);
arr[0] = 5; // 正确
shared_ptr:共享所有权
cpp
// 引用计数
std::shared_ptr<int> p1 = std::make_shared<int>(5);
{
std::shared_ptr<int> p2 = p1; // 引用计数=2
std::shared_ptr<int> p3 = p1; // 引用计数=3
} // p2, p3销毁,引用计数=1
// 离开作用域,p1销毁,引用计数=0,释放内存
// make_shared的优势
std::shared_ptr<int> p = std::make_shared<int>(5);
// 一次内存分配:同时分配int和控制块
std::shared_ptr<int> p2(new int(5)); // 两次分配
weak_ptr:打破循环引用
cpp
// 循环引用问题
struct Node {
std::shared_ptr<Node> next;
std::shared_ptr<Node> prev;
};
auto n1 = std::make_shared<Node>();
auto n2 = std::make_shared<Node>();
n1->next = n2;
n2->prev = n1; // 循环引用,内存泄漏!
// 解决方案
struct Node {
std::weak_ptr<Node> next; // 使用weak_ptr
std::weak_ptr<Node> prev;
};
// 使用weak_ptr
std::weak_ptr<int> wp = p1;
if (auto sp = wp.lock()) { // 尝试提升为shared_ptr
// sp在作用域内有效
std::cout << *sp << std::endl;
} // sp销毁,不影响引用计数
enable_shared_from_this:
cpp
class SharedObject : public std::enable_shared_from_this<SharedObject> {
public:
std::shared_ptr<SharedObject> getShared() {
return shared_from_this(); // 安全获取shared_ptr
}
};
// 错误用法
SharedObject obj;
auto sp = obj.getShared(); // 未定义行为!obj不在shared_ptr管理中
// 正确用法
auto sp = std::make_shared<SharedObject>();
auto sp2 = sp->getShared(); // 正确
2.2.6 线程库:并发编程标准化
设计背景 :
C++03没有标准线程,程序员依赖平台API:
cpp
// Windows
HANDLE hThread = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL);
// Linux
pthread_t thread;
pthread_create(&thread, NULL, ThreadFunc, NULL);
std::thread:
cpp
// 基本使用
void task() {
std::cout << "Hello from thread\n";
}
std::thread t(task);
t.join(); // 等待线程结束
// 带参数
void add(int a, int b) {
std::cout << a + b << "\n";
}
std::thread t(add, 3, 4);
t.join();
// Lambda
std::thread t([](int x) {
std::cout << x * 2 << "\n";
}, 5);
t.join();
// 移动语义
std::thread t1(task);
std::thread t2 = std::move(t1); // t1不再代表线程
互斥锁和锁守卫:
cpp
std::mutex mtx;
int counter = 0;
void increment() {
for (int i = 0; i < 100000; ++i) {
std::lock_guard<std::mutex> lock(mtx); // 自动加锁解锁
++counter;
}
// lock_guard在作用域结束时自动解锁
}
// unique_lock更灵活
std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 不立即加锁
// ... 做一些不加锁的操作
lock.lock(); // 手动加锁
// ... 临界区
lock.unlock(); // 手动解锁
条件变量:
cpp
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
std::vector<int> data;
void producer() {
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lock(mtx);
data.push_back(42);
ready = true;
}
cv.notify_one(); // 通知等待的线程
}
void consumer() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; }); // 等待条件满足
std::cout << "Got: " << data[0] << "\n";
}
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
future和promise:
cpp
// 异步获取结果
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread t([&prom]() {
std::this_thread::sleep_for(std::chrono::seconds(1));
prom.set_value(42); // 设置结果
});
std::cout << "Result: " << fut.get() << "\n"; // 阻塞等待结果
t.join();
// async函数
std::future<int> fut = std::async(std::launch::async, []() {
return 42;
});
std::cout << fut.get() << "\n";
原子操作:
cpp
std::atomic<int> counter{0};
void worker() {
for (int i = 0; i < 100000; ++i) {
++counter; // 原子递增,无锁
}
}
std::thread t1(worker);
std::thread t2(worker);
t1.join();
t2.join();
std::cout << counter << "\n"; // 200000
2.2.7 可变参数模板:元编程的飞跃
设计背景 :
C++03中,模板参数数量固定,限制了泛型编程。
基本语法:
cpp
// 可变参数模板
template<typename... Args>
class Tuple {
// Args是一个模板参数包
};
template<typename... Args>
void print(Args... args) {
// args是一个函数参数包
}
参数包展开:
cpp
// 递归展开(C++11风格)
// 终止条件
template<typename T>
void print(T t) {
std::cout << t;
}
// 递归步骤
template<typename T, typename... Args>
void print(T t, Args... args) {
std::cout << t << " ";
print(args...); // 递归调用
}
// 使用
print(1, "hello", 3.14); // 1 hello 3.14
完美转发:
cpp
template<typename... Args>
void forward(Args&&... args) {
some_function(std::forward<Args>(args)...);
}
// 实际应用:make_shared
template<typename T, typename... Args>
std::shared_ptr<T> make_shared(Args&&... args) {
return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
}
参数包长度:
cpp
template<typename... Args>
struct count {
static const size_t value = sizeof...(Args);
};
// 使用
static_assert(count<int, double, char>::value == 3, "");
类型安全的printf:
cpp
// 简化版
template<typename T>
void printf(const char* fmt, T arg) {
while (*fmt) {
if (*fmt == '%' && *(fmt+1) == 's') {
std::cout << arg;
fmt += 2;
} else {
std::cout << *fmt++;
}
}
}
template<typename T, typename... Args>
void printf(const char* fmt, T arg, Args... args) {
while (*fmt) {
if (*fmt == '%' && *(fmt+1) == 's') {
std::cout << arg;
printf(fmt + 2, args...);
return;
} else {
std::cout << *fmt++;
}
}
}
2.2.8 constexpr:编译期计算
设计背景 :
C++03中,常量表达式必须在编译期确定,但无法自定义函数。
基本用法:
cpp
// 编译期计算
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
int arr[factorial(5)]; // 编译期计算数组大小
constexpr int result = factorial(10); // 编译期计算
// 编译期条件
constexpr int get_size(bool b) {
return b ? 10 : 20;
}
int arr2[get_size(true)]; // 大小为10
限制:
cpp
// C++11的constexpr函数限制:
// 1. 函数体只能有一个return语句
// 2. 不能有局部变量
// 3. 不能有循环
// 4. 不能有if-else(C++14放宽)
// C++11允许:
constexpr int fib(int n) {
return n <= 1 ? n : fib(n-1) + fib(n-2);
}
// C++11不允许:
constexpr int complex_calc(int n) {
int sum = 0; // 错误:不能有局部变量
for (int i = 0; i < n; ++i) { // 错误:不能有循环
sum += i;
}
return sum;
}
constexpr对象:
cpp
constexpr int x = 5; // 编译期常量
constexpr int y = x + 3; // 编译期计算
struct Point {
constexpr Point(int x, int y) : x(x), y(y) {}
int x, y;
};
constexpr Point p(10, 20); // 编译期构造
2.2.9 nullptr:空指针的救星
设计背景 :
C++03中,NULL通常定义为0或((void*)0),导致重载歧义:
cpp
void func(int) { std::cout << "int\n"; }
void func(void*) { std::cout << "pointer\n"; }
func(NULL); // 调用func(int)!
nullptr的实现:
cpp
// nullptr的类型
const class {
public:
template<typename T>
operator T*() const { return nullptr; }
template<typename T, typename U>
operator T U::*() const { return nullptr; }
// 防止误用
void* operator&() const = delete;
} nullptr = {};
使用场景:
cpp
void func(int) {}
void func(char*) {}
func(nullptr); // 错误:没有匹配
func(0); // 调用func(int)
// 正确用法
void func(int*) {}
func(nullptr); // 正确
// 模板中
template<typename T>
void foo(T* ptr) {}
foo(nullptr); // T推导为void,foo<void>(nullptr)
2.2.10 统一初始化
设计背景 :
C++03有多种初始化方式,不统一且容易出错。
列表初始化:
cpp
// C++11统一初始化
int a{5}; // 直接初始化
int b = {5}; // 拷贝初始化
int c{5.5}; // 错误:窄化转换
// 对象
struct Point { int x, y; };
Point p1{10, 20}; // OK
Point p2 = {10, 20}; // OK
// 容器
std::vector<int> vec = {1, 2, 3, 4};
std::map<std::string, int> ages = {{"Alice", 25}, {"Bob", 30}};
// 防止窄化
int x = 5.5; // C++03: OK,丢失精度
int y{5.5}; // C++11: 错误!
初始化列表:
cpp
class MyVector {
public:
MyVector(std::initializer_list<int> list) {
for (int x : list) {
push_back(x);
}
}
};
MyVector v = {1, 2, 3, 4, 5};
2.2.11 强类型枚举
设计背景 :
C++03的枚举是弱类型的:
cpp
enum Color { Red, Green, Blue };
enum Fruit { Apple, Banana, Cherry };
Color c = Red;
int x = c; // 隐式转换为int
c = 5; // 错误,但某些编译器允许
c = Apple; // 错误,但可以编译!
enum class:
cpp
enum class Color { Red, Green, Blue };
enum class Fruit { Apple, Banana, Cherry };
Color c = Color::Red;
// int x = c; // 错误:不能隐式转换
// c = 5; // 错误
// c = Fruit::Apple; // 错误:类型不同
// 必须显式转换
int x = static_cast<int>(c);
指定底层类型:
cpp
enum class Status : uint8_t {
Success,
Failure,
Timeout
}; // 只占1字节
2.2.12 override和final关键字
设计背景 :
C++03中,重写虚函数容易出错:
cpp
class Base {
public:
virtual void foo(int x) {}
};
class Derived : public Base {
public:
virtual void foo(double x) {} // 拼写错误!不是重写
};
override:
cpp
class Base {
public:
virtual void foo(int x) {}
};
class Derived : public Base {
public:
void foo(int x) override {} // 明确表示重写
// void foo(double x) override {} // 编译错误!
};
final:
cpp
class Base final { // 不能被继承
};
class Derived : public Base { // 错误
};
class Base2 {
public:
virtual void foo() final {} // 不能被重写
};
class Derived2 : public Base2 {
// void foo() override {} // 错误!
};
2.2.13 委托构造函数
设计背景 :
C++03中,多个构造函数有重复代码:
cpp
class Foo {
public:
Foo() {
init();
value = 0;
}
Foo(int x) {
init();
value = x;
}
Foo(int x, int y) {
init();
value = x + y;
}
private:
void init() { /* 初始化 */ }
int value;
};
委托构造:
cpp
class Foo {
public:
Foo() : Foo(0) {} // 委托给Foo(int)
Foo(int x) : value(x) {
init();
}
Foo(int x, int y) : Foo(x + y) {} // 委托给Foo(int)
private:
void init() { /* 初始化 */ }
int value;
};
2.2.14 继承构造
设计背景 :
派生类需要重新声明基类的构造函数。
using声明构造函数:
cpp
class Base {
public:
Base(int) {}
Base(int, double) {}
};
class Derived : public Base {
public:
using Base::Base; // 继承Base的所有构造函数
};
Derived d1(5); // OK
Derived d2(5, 3.14); // OK
2.2.15 静态断言
设计背景 :
C++03中,编译期检查需要复杂的模板技巧。
static_assert:
cpp
template<typename T>
void process(T x) {
static_assert(sizeof(T) >= 4, "T must be at least 4 bytes");
static_assert(std::is_integral<T>::value, "T must be integral");
// ...
}
process(5); // OK
process(1.0); // 错误:T must be integral
2.2.16 原始字符串字面量
设计背景 :
处理正则表达式和JSON时,转义字符很麻烦。
R前缀:
cpp
// C++03
std::string path = "C:\\Users\\Alice\\Documents";
std::string regex = "\\d+\\.\\d+";
// C++11
std::string path = R"(C:\Users\Alice\Documents)";
std::string regex = R"(\d+\.\d+)";
// 包含括号
std::string text = R"==(包含"(和)"的文本)==";
2.2.17 用户定义字面量
设计背景 :
让自定义类型也能使用字面量语法。
语法:
cpp
// 后缀定义
constexpr long double operator"" _deg(long double deg) {
return deg * 3.1415926535 / 180;
}
constexpr long double operator"" _rad(long double rad) {
return rad;
}
// 使用
auto angle = 90.0_deg; // 转换为弧度
auto pi = 3.1415926535_rad;
// 字符串字面量
std::string operator"" _s(const char* str, size_t len) {
return std::string(str, len);
}
auto text = "hello"_s; // std::string
2.2.18 新的容器和算法
array:
cpp
// 固定大小数组,栈上分配
std::array<int, 5> arr = {1, 2, 3, 4, 5};
arr.size(); // 5
arr[0] = 10;
// 与C数组对比
int carr[5]; // 没有size(),容易越界
forward_list:
cpp
// 单向链表,更省内存
std::forward_list<int> fl = {1, 2, 3};
fl.push_front(0); // 只能在头部插入
unordered_map/unordered_set:
cpp
// 哈希表,O(1)查找
std::unordered_map<std::string, int> ages;
ages["Alice"] = 25;
ages["Bob"] = 30;
// 自定义哈希
struct Point { int x, y; };
struct PointHash {
size_t operator()(const Point& p) const {
return std::hash<int>()(p.x) ^ std::hash<int>()(p.y);
}
};
std::unordered_map<Point, int, PointHash> pointMap;
新算法:
cpp
std::vector<int> vec = {1, 2, 3, 4, 5};
// C++11新增
std::all_of(vec.begin(), vec.end(), [](int x) { return x > 0; }); // true
std::any_of(vec.begin(), vec.end(), [](int x) { return x == 3; }); // true
std::none_of(vec.begin(), vec.end(), [](int x) { return x < 0; }); // true
// 移除元素
vec.erase(std::remove_if(vec.begin(), vec.end(), [](int x) {
return x % 2 == 0;
}), vec.end());
// 填充
std::fill_n(std::back_inserter(vec), 5, 0); // 添加5个0
2.2.19 正则表达式
设计背景 :
C++03没有标准正则表达式库。
基本用法:
cpp
#include <regex>
std::string text = "My phone: 123-456-7890";
std::regex pattern(R"(\d{3}-\d{3}-\d{4})");
// 查找
std::smatch match;
if (std::regex_search(text, match, pattern)) {
std::cout << "Found: " << match[0] << "\n";
}
// 替换
std::string result = std::regex_replace(text, pattern, "XXX-XXX-XXXX");
// 迭代所有匹配
auto words_begin = std::sregex_iterator(text.begin(), text.end(), pattern);
auto words_end = std::sregex_iterator();
for (auto it = words_begin; it != words_end; ++it) {
std::cout << it->str() << "\n";
}
2.2.20 时间库
chrono:
cpp
#include <chrono>
#include <thread>
// 时间点
auto start = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(std::chrono::seconds(1));
auto end = std::chrono::high_resolution_clock::now();
// 时长
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << duration.count() << "ms\n";
// 自定义时长
using namespace std::chrono_literals;
auto d1 = 1s; // 1秒
auto d2 = 500ms; // 500毫秒
auto d3 = 1.5s; // 1.5秒
2.2.21 tuple
设计背景 :
C++03没有标准元组类型。
基本用法:
cpp
std::tuple<int, std::string, double> t(1, "hello", 3.14);
// 获取元素
int id = std::get<0>(t);
std::string name = std::get<1>(t);
double value = std::get<2>(t);
// 解包(C++17)
auto [id2, name2, value2] = t;
// tie
int id3;
std::string name3;
double value3;
std::tie(id3, name3, value3) = t;
2.2.22 type_traits
设计背景 :
C++03中,类型检查需要复杂模板技巧。
常用type_traits:
cpp
#include <type_traits>
// 基本检查
static_assert(std::is_integral<int>::value, "");
static_assert(!std::is_integral<double>::value, "");
// 转换
static_assert(std::is_same<int, std::remove_const<const int>::type>::value, "");
static_assert(std::is_same<int*, std::add_pointer<int>::type>::value, "");
// 条件
template<typename T>
void foo(T x) {
if constexpr (std::is_same<T, int>::value) {
// 只有T是int时才编译
}
}
2.2.23 异常处理改进
noexcept:
cpp
void func1() noexcept {} // 不抛出异常
void func2() {} // 可能抛出异常
void func3() noexcept(false) {} // 可能抛出异常
// 移动构造函数通常noexcept
class MyClass {
public:
MyClass(MyClass&& other) noexcept {
// 移动资源
}
};
std::exception_ptr:
cpp
std::exception_ptr eptr;
try {
throw std::runtime_error("error");
} catch (...) {
eptr = std::current_exception();
}
try {
if (eptr) {
std::rethrow_exception(eptr);
}
} catch (const std::exception& e) {
std::cout << e.what() << "\n";
}
2.2.24 内存对齐
aligned_storage和aligned_alloc:
cpp
// 对齐内存分配
void* ptr = std::aligned_alloc(64, 1024); // 64字节对齐
// 对齐类型
struct alignas(64) CacheLine {
int data[16];
};
CacheLine line; // 自动对齐到64字节边界
2.2.25 新的文件系统(C++17才正式引入,但C++11有实验性支持)
filesystem:
cpp
#include <filesystem>
namespace fs = std::filesystem;
// 遍历目录
for (const auto& entry : fs::directory_iterator("/path")) {
std::cout << entry.path() << "\n";
}
// 文件操作
fs::create_directory("test");
fs::copy_file("source.txt", "dest.txt");
fs::remove("test");
第三章:C++11的设计哲学和影响
3.1 零开销抽象原则的实现
C++11所有特性都遵循"不为你不使用的特性付费":
cpp
// auto:编译期类型推导,无运行时开销
auto x = 5; // 等价于 int x = 5;
// Lambda:编译器生成函数对象,无虚函数开销
auto f = [](int x) { return x * 2; };
// 等价于编译器生成的类,直接内联
// 移动语义:编译期决定调用哪个函数
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = std::move(v1); // 移动构造,O(1)
// 如果写成std::vector<int> v2 = v1; // 拷贝构造,O(n)
// constexpr:编译期计算
constexpr int x = factorial(10); // 编译期计算,无运行时开销
3.2 类型安全的提升
cpp
// nullptr vs NULL
void func(int) {}
void func(void*) {}
func(NULL); // 调用func(int),可能错误
func(nullptr); // 编译错误,类型安全
// 强类型枚举
enum class Color { Red, Green, Blue };
Color c = Color::Red;
// c = 5; // 错误
// int x = c; // 错误
// static_assert
template<typename T>
void process(T x) {
static_assert(sizeof(T) >= 4, "T too small");
}
3.3 并发编程的标准化
C++11让多线程编程第一次成为语言标准的一部分:
cpp
// 跨平台线程
std::thread t([]() {
std::cout << "Hello from thread\n";
});
t.join();
// 原子操作
std::atomic<int> counter{0};
++counter; // 无锁,线程安全
// 条件变量
std::condition_variable cv;
cv.notify_one(); // 跨平台通知
3.4 对现代编程范式的影响
函数式编程:
cpp
// Lambda + 算法 = 函数式风格
std::vector<int> vec = {1, 2, 3, 4, 5};
auto result = vec
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; });
RAII的强化:
cpp
// 智能指针让RAII无处不在
{
auto file = std::make_unique<FILE, decltype(&fclose)>(fopen("test.txt", "r"), &fclose);
// 自动关闭
}
元编程的普及:
cpp
// 可变参数模板让元编程更强大
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // C++17折叠表达式
}
第四章:C++11的采用和争议
4.1 编译器支持时间表
- 2011年:标准发布
- 2012-2013年:GCC 4.8+、Clang 3.3+、MSVC 2013+ 基本支持
- 2014-2015年:主流编译器完全支持
4.2 争议和批评
争议1:复杂性增加
批评者认为C++11让语言更复杂,学习曲线更陡峭。
争议2:移动语义的复杂性
cpp
// 移动语义的陷阱
class MyClass {
int* data;
public:
MyClass(MyClass&& other) : data(other.data) {
other.data = nullptr;
}
void foo() {
MyClass obj = std::move(*this); // 危险!
// obj.data = nullptr,*this.data也变成nullptr
}
};
争议3:auto的滥用
cpp
// 过度使用auto降低可读性
auto result = some_function(); // 什么类型?不清楚
// 明确类型更好
std::vector<std::pair<int, std::string>> result = some_function();
4.3 成功案例
案例1:游戏引擎
cpp
// UE4大量使用C++11
auto task = [](Enemy* enemy) {
enemy->update();
enemy->render();
};
std::vector<std::thread> workers;
for (auto* enemy : enemies) {
workers.emplace_back(task, enemy);
}
案例2:高性能计算
cpp
// 移动语义减少拷贝
std::vector<Matrix> matrices = load_matrices();
// C++03: 多次拷贝
// C++11: 移动语义,零拷贝
案例3:网络服务器
cpp
// Lambda + 线程 = 高并发
server.on_request([](Request req, Response& res) {
auto data = process(req);
res.set_body(data);
});
第五章:C++11的遗产
5.1 对后续标准的影响
C++11奠定了现代C++的基础:
- C++14:完善C++11(泛型Lambda、变量模板)
- C++17:增加重要特性(结构化绑定、if constexpr、并行算法)
- C++20:模块、协程、概念(更大胆的现代化)
- C++23:继续完善
5.2 编程风格的转变
从C风格到现代C++:
cpp
// C风格
int* arr = new int[10];
// ... 使用
delete[] arr;
// 现代C++
std::vector<int> arr(10);
// 自动管理内存
从面向对象到多范式:
cpp
// 现代C++融合多种范式
// 1. 面向对象
class MyClass { /* ... */ };
// 2. 函数式
auto f = [](int x) { return x * 2; };
// 3. 泛型
template<typename T> void process(T x) { /* ... */ }
// 4. 过程式
void legacy_function() { /* ... */ }
5.3 性能提升的量化
移动语义的收益:
cpp
// 测试:创建1000个vector,每个10000个元素
// C++03:约500ms(大量拷贝)
// C++11:约50ms(移动语义)
// 提升:10倍
Lambda的收益:
cpp
// 排序1000万个整数
// C++03:需要写函数对象,代码量增加5倍
// C++11:Lambda,代码简洁,编译器更容易内联
// 性能:相同或更好
第六章:总结 - C++11的历史地位
C++11不是简单的版本更新,而是一次语言重生。它解决了C++03的10大痛点:
- ✅ 类型冗长 → auto、decltype
- ✅ 性能浪费 → 移动语义
- ✅ 没有线程 → std::thread
- ✅ 内存泄漏 → 智能指针
- ✅ 函数对象繁琐 → Lambda
- ✅ 模板限制 → 可变参数模板
- ✅ 初始化混乱 → 统一初始化
- ✅ 空指针问题 → nullptr
- ✅ 枚举不安全 → enum class
- ✅ 编译期计算困难 → constexpr
C++11的成功指标:
- 采用率:几乎所有现代C++项目都使用C++11+
- 性能:在保持C性能的同时,获得了Rust/Python的生产力
- 生态:催生了现代C++生态系统(Boost、Qt、Unreal等)
- 社区:C++从"垂死"变为"最活跃"的语言之一
C++11的哲学:
"让C++既快又容易,既强大又安全,既现代又兼容。"
这就是C++11的故事------一个关于重生、现代化、性能与安全并重的传奇。它不仅改变了C++,也改变了我们编写高性能软件的方式。从2011年至今,C++11的特性仍然是现代C++的基石,每一代新标准都在它的肩膀上继续前行。