【cpp进阶】c++11的新特性(概述版)

关注我,学习c++不迷路:

个人主页:爱装代码的小瓶子

专栏如下:

  1. c++学习
  2. Linux学习

后续会更新更多有趣的小知识,关注我带你遨游知识世界

期待你的关注。


文章目录

    • [第一章: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的推导算法

  1. 将表达式转换为右值引用(如果适用)
  2. 忽略顶层const和引用
  3. 根据初始化表达式推导类型

性能影响

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大痛点:

  1. 类型冗长 → auto、decltype
  2. 性能浪费 → 移动语义
  3. 没有线程 → std::thread
  4. 内存泄漏 → 智能指针
  5. 函数对象繁琐 → Lambda
  6. 模板限制 → 可变参数模板
  7. 初始化混乱 → 统一初始化
  8. 空指针问题 → nullptr
  9. 枚举不安全 → enum class
  10. 编译期计算困难 → constexpr

C++11的成功指标

  • 采用率:几乎所有现代C++项目都使用C++11+
  • 性能:在保持C性能的同时,获得了Rust/Python的生产力
  • 生态:催生了现代C++生态系统(Boost、Qt、Unreal等)
  • 社区:C++从"垂死"变为"最活跃"的语言之一

C++11的哲学

"让C++既快又容易,既强大又安全,既现代又兼容。"

这就是C++11的故事------一个关于重生、现代化、性能与安全并重的传奇。它不仅改变了C++,也改变了我们编写高性能软件的方式。从2011年至今,C++11的特性仍然是现代C++的基石,每一代新标准都在它的肩膀上继续前行。

相关推荐
_OP_CHEN8 小时前
【从零开始的Qt开发指南】(十一)Qt常用控件之多元素控件与容器类控件深度解析
开发语言·qt·前端开发·多元素控件·gui开发·qt常用控件·容器类控件
Robot侠8 小时前
视觉语言导航从入门到精通(二)
开发语言·人工智能·python·llm·vln
SmoothSailingT8 小时前
C#——Lazy<T>懒加载机制
开发语言·单例模式·c#·懒加载
程序喵大人8 小时前
SQLITE问题整理
开发语言·数据库·c++·sqlite
Neolnfra8 小时前
文件包含漏洞终极指南
开发语言·安全·web安全·网络安全·系统安全·php·可信计算技术
简单点好不好8 小时前
2025--简单点--python之状态模式
开发语言·python·状态模式
1+2单片机电子设计8 小时前
基于 STM32 的网络授权时钟系统设计与实现
开发语言·stm32·单片机·嵌入式硬件·php·51单片机
Tjohn98 小时前
Java环境配置(JDK8环境变量配置)补充
java·开发语言
天赐学c语言8 小时前
12.17 - 合并两个有序数组 && include<> 和 include““ 的区别
c++·算法·leecode