[C.C++] C++11-17 新特性详解

370 0
Honkers 2026-4-22 22:09:28 来自手机 | 显示全部楼层 |阅读模式

C++11-17 新特性详解

基于作者10年工作经验整理的编程笔记,涵盖C++11/14/17核心语言特性与实战技巧。

简介

C++11 到 C++17 是 C++ 语言的一次重大升级,引入了 lambda 表达式、auto 类型推导、移动语义、智能指针、thread_local 存储类型等重要语言特性。本文聚焦于这些核心语言特性,结合实际开发经验,帮助开发者深入理解和正确使用现代 C++ 的关键能力。

一、Lambda 表达式

1.1 基本语法

  1. [capture](parameters) -> return_type { body }
复制代码
  1. // 基本用法
  2. auto add = [](int a, int b) -> int {
  3. return a + b;
  4. };
  5. cout << add(3, 5) << endl; // 输出 8
复制代码

1.2 捕获列表

  1. int x = 10, y = 20;
  2. // 值捕获
  3. auto f1 = [x, y]() { return x + y; };
  4. // 引用捕获
  5. auto f2 = [&x, &y]() { x++; y++; };
  6. // 全部值捕获
  7. auto f3 = [=]() { return x + y; };
  8. // 全部引用捕获
  9. auto f4 = [&]() { x++; y++; };
  10. // 混合捕获
  11. auto f5 = [&x, y]() { x += y; };
复制代码

1.3 Lambda 与函数指针

没有捕获变量的 lambda 可以转换为函数指针:

  1. typedef int(*FuncPtr)(int, int);
  2. FuncPtr ptr = [](int a, int b) -> int { return a + b; };
复制代码

Lambda 表达式非常适合用作回调函数参数,简洁且耦合度低。

二、auto 与 decltype

2.1 auto 自动类型推导

  1. auto a = 10; // int
  2. auto b = 3.14; // double
  3. auto c = "hello"; // const char*
  4. auto it = vec.begin(); // vector<int>::iterator
  5. // auto 只能在函数或复合语句中使用
复制代码

2.2 decltype

decltype 获取表达式的类型:

  1. int x = 10;
  2. decltype(x) y = 20; // y 的类型为 int
复制代码

三、左值引用、右值引用与完美转发

3.1 基本概念

  • 左值:有名字,能取地址
  • 右值:无名字,不能取地址
  1. int a = 10; // a 是左值,10 是右值
  2. int &lr = a; // 左值引用
  3. int &&rr = 10; // 右值引用
复制代码

3.2 万能引用(转发引用)

  1. template <typename T>
  2. void func(T&& arg); // 万能引用:可以接受左值和右值
复制代码

3.3 std::move 与 std::forward

  1. // std::move:将左值转换为右值引用(无运行时开销,只是类型转换)
  2. std::string str = "hello";
  3. std::string other = std::move(str); // str 变为空
  4. // std::forward:完美转发,保持参数的原始值类别
  5. template <typename T>
  6. void wrapper(T&& arg) {
  7. target(std::forward<T>(arg)); // 保持 arg 的左值/右值属性
  8. }
复制代码

重点:std::move 和 std::forward 不会产生任何字节的可执行代码,它们只是类型转换。

3.4 移动语义的编译器优化

  1. A getA() {
  2. A a;
  3. return a; // 理论上触发移动构造函数
  4. }
  5. A b = getA();
  6. // VS:触发移动构造函数
  7. // GCC:可能进行 RVO/NRVO 优化,直接在 b 的位置构造 a,跳过移动
复制代码

四、智能指针实战

4.1 shared_ptr 的 reset 误区

  1. shared_ptr<int> pInt = make_shared<int>(45);
  2. // reset 不是直接释放内存,而是减少引用计数
  3. // 当引用计数减为 0 时才真正释放
  4. pInt.reset(); // 引用计数从 1 变为 0,释放内存
  5. // 注意作用域
  6. {
  7. shared_ptr<int> pInt = make_shared<int>(45);
  8. } // pInt 在此处释放
  9. // *pInt = 8; // 错误!pInt 已经被释放
复制代码

4.2 shared_ptr 循环引用

  1. class TestA {
  2. public:
  3. void func1(shared_ptr<TestB> ptrB) { m_ptrB = ptrB; }
  4. private:
  5. shared_ptr<TestB> m_ptrB; // 改为 weak_ptr 解决
  6. };
  7. class TestB {
  8. public:
  9. void func1(shared_ptr<TestA> ptrA) { m_ptrA = ptrA; }
  10. private:
  11. shared_ptr<TestA> m_ptrA; // 改为 weak_ptr 解决
  12. };
  13. // 循环引用导致两者都无法释放
  14. // 解决方法:将其中一个改为 weak_ptr
复制代码

4.3 智能指针与异常安全

如果一个函数中有频繁的错误判断和返回,每次返回前都需要释放资源,则可将资源放入智能指针,因为智能指针是异常安全的。

五、std::function 与 std::bind

5.1 std::function

通用的函数包装器,可以包装函数指针、lambda、函数对象等:

  1. std::function<int(int, int)> func;
  2. func = [](int a, int b) { return a + b; };
  3. func = std::plus<int>(); // 函数对象
  4. // 注意:std::function 不能直接赋值 nullptr
  5. // std::function<int(int,int)> f = nullptr; // 某些编译器可能报错
  6. // std::function<int(int,int)> f; // 默认为空,调用会抛出异常
复制代码

5.2 std::bind

  1. int func(int a, int& b, int c);
  2. // bind 默认是值传递
  3. auto bound = std::bind(func, a, std::ref(b), c);
  4. // 使用 std::ref 进行引用传递
复制代码

5.3 std::ref 与 std::move

std::ref 用于在需要引用传递的地方包装参数,std::move 用于将左值转换为右值引用。

六、emplace 与优先队列

6.1 emplace

C++11 STL 新增 emplace 成员函数,参数直接传递构造参数,减少对象复制:

  1. vector<pair<string, int>> vec;
  2. vec.push_back(make_pair("hello", 1)); // 需要构造临时对象
  3. vec.emplace_back("hello", 1); // 直接在容器中原地构造
复制代码

6.2 priority_queue

自动排序的队列,新增元素自动排序:

  1. // 默认大顶堆
  2. priority_queue<int> pq;
  3. pq.push(3);
  4. pq.push(1);
  5. pq.push(5);
  6. // top() 返回 5
  7. // 自定义比较(小顶堆)
  8. auto cmp = [](int a, int b) { return a > b; };
  9. priority_queue<int, vector<int>, decltype(cmp)> pq(cmp);
复制代码

七、thread_local

thread_local 是 C++11 引入的存储类型,变量在每个线程中拥有独立的实例:

  1. thread_local int count = 0;
  2. // 每个线程都有自己的 count 副本
  3. // 线程开始时构造,线程结束时析构
  4. // 可以与 static/extern 联合使用
  5. thread_local static int var; // 线程局部 + 静态存储
复制代码

C++ 中四种存储周期:

  • automatic:栈上,函数结束释放
  • static:静态存储区,程序结束释放
  • dynamic:堆上,手动释放
  • thread:线程存储,线程结束释放

八、位运算技巧

8.1 基本运算

运算用途
n & 1判断奇偶
a ^= b; b ^= a; a ^= b;不用临时变量交换两个数
~n + 1求相反数
x << n乘以 2^n
x >> n除以 2^n
n & (n-1) == 0判断是否为 2 的幂
`n= (1 << k)`
n &= ~(1 << k)将第 k 位设为 0
x ^= (1 << n)切换第 n 位
(x >> n) & 1获取第 n 位的值

8.2 统计二进制中 1 的个数(汉明重量)

  1. int count = 0;
  2. while (n) {
  3. n &= (n - 1); // 清除最低位的 1
  4. count++;
  5. }
复制代码

九、实用技巧

9.1 整型溢出

  1. uint32_t vara = 12340;
  2. uint64_t varb = 1024 * 1024 * vara; // 溢出!字面值运算时已溢出
  3. uint64_t varb = 1024 * 1024 * (uint64_t)vara; // 正确
复制代码

9.2 snprintf 返回值

  1. char buf[32];
  2. int len = snprintf(buf, sizeof(buf), "hello %s", name);
  3. // len 是实际写入的字符数(不含 '\0')
  4. // 如果 len >= sizeof(buf),说明被截断
复制代码

9.3 pragma 指令

  1. #pragma once // 防止头文件重复包含
  2. #pragma pack(n) // 设置结构体对齐
  3. #pragma comment(lib, "mylib") // 指定链接库
  4. #pragma warning(push) // 保存警告状态
  5. #pragma warning(disable: 4996) // 禁用指定警告
  6. #pragma warning(pop) // 恢复警告状态
复制代码

9.4 资源释放原则

遵循栈的出栈入栈规则

  • 被依赖的资源先初始化(入栈)
  • 释放时先释放有依赖的模块(出栈)
  • 否则依赖的资源先被释放,其他模块将无法正常运行

总结

C++11 到 C++17 引入的语言特性极大地提升了 C++ 的表达能力和安全性。从 lambda 表达式到移动语义,从 auto 类型推导到智能指针,这些特性让 C++ 代码更简洁、更高效、更安全。

关键实战要点:

  • 熟练使用 lambda 表达式和 std::function,简化回调和函数对象
  • 理解移动语义和完美转发,避免不必要的拷贝开销
  • 使用智能指针和 RAII 模式管理资源,杜绝内存泄漏
  • 掌握 emplace 等新接口,优化容器操作性能
  • 了解 thread_local 等线程相关语言特性

原始笔记来源: hhjt/c++11learn.cpp

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2026 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行