【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++的基石,每一代新标准都在它的肩膀上继续前行。

相关推荐
wjs20249 分钟前
DOM CDATA
开发语言
Tingjct11 分钟前
【初阶数据结构-二叉树】
c语言·开发语言·数据结构·算法
猷咪37 分钟前
C++基础
开发语言·c++
IT·小灰灰39 分钟前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧40 分钟前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q41 分钟前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳041 分钟前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾41 分钟前
php 对接deepseek
android·开发语言·php
CSDN_RTKLIB44 分钟前
WideCharToMultiByte与T2A
c++
2601_949868361 小时前
Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现
java·开发语言·flutter