
观众老爷们大家好 我是邪修KING
本文属于系列C++ 进阶篇 ,欢迎来到C++进阶篇博客
C++重点语法运用!
一、C++11简介:为什么需要C++11?
C++11 是 C++ 的第⼆个主要版本,并且是从 C++98 起的最重要更新。它引⼊了⼤量更改,标准化了既
有实践,并改进了对 C++ 程序员可⽤的抽象。在它最终由 ISO 在 2011 年 8 ⽉ 12 ⽇采纳前,⼈们曾使
⽤名称“C++0x”,因为它曾被期待在 2010 年之前发布。C++03 与 C++11 期间花了 8 年时间,故⽽这
是迄今为⽌最⻓的版本间隔。从那时起,C++ 有规律地每 3 年更新⼀次。

稍作了解即可
C++11 是 C++ 发展史上第二个里程碑式的标准,也是自 C++98 发布以来最重要的一次版本更新。从 C++03 到 C++11 整整间隔了 8 年,这次更新不仅标准化了大量业界实践,还引入了移动语义、Lambda 表达式、可变参数模板、智能指针等一系列革命性特性,彻底改变了 C++ 的编程范式。从 C++11 开始,C++ 正式进入了「现代 C++」时代。
二、统一列表初始化 {}
其实一般情况下在学习的过程中我们小比特都有在用,这里性能优化有很大的方便性,减少了不必要的临时构造所产生的垃圾内存
1.1 C++98 的初始化痛点
C++98 中,{} 初始化只能用于数组和普通结构体,自定义类、容器都不能直接用列表初始化,初始化方式非常混乱。想要初始化一个带多个元素的 vector,只能先创建空对象,再一个个 push_back,代码繁琐且可读性差。
1// ===== C++98 支持的初始化 ===== 2int arr1[] = {1, 2, 3, 4, 5}; // 数组可以 3int arr2[5] = {0}; // 数组可以 4struct Point { int x; int y; }; 5Point p = {1, 2}; // 结构体可以 6 7// ===== C++98 不支持的初始化 ===== 8// vector<int> v = {1,2,3}; // 编译报错:容器不能直接列表初始化 9// Date d = {2025, 1, 1}; // 编译报错:自定义类不能直接列表初始化 10
1.2 C++11 统一列表初始化
C++11 扩大了 {} 的适用范围,所有内置类型、自定义类型、容器都可以用花括号进行初始化,并且可以省略赋值号 =,真正实现了「一套语法初始化所有对象」。
1#include <iostream> 2#include <vector> 3#include <string> 4using namespace std; 5 6class Date { 7public: 8 Date(int year = 1, int month = 1, int day = 1) 9 : _year(year), _month(month), _day(day) 10 { 11 cout << "Date(int, int, int) 构造函数" << endl; 12 } 13private: 14 int _year; 15 int _month; 16 int _day; 17}; 18 19int main() { 20 // 1. 内置类型初始化 21 int x1 = {2}; // 传统带=写法 22 int x2{2}; // C++11 新增:省略=的写法 23 24 // 2. 自定义类型初始化 25 // 底层原理:先用{2025,1,1}构造临时对象,再拷贝构造,编译器会优化为直接构造 26 Date d1 = {2025, 1, 1}; 27 Date d2{2024, 7, 25}; // 省略=,更简洁 28 29 // 3. 容器初始化 + 容器插入,便利性大幅提升 30 vector<int> v1 = {1, 2, 3, 4, 5}; // 直接批量初始化容器 31 vector<Date> v2; 32 v2.push_back({2025, 1, 1}); // 不用写匿名对象,直接传构造参数 33 34 return 0; 35} 36
相比 C++98 的优势:
统一了所有类型的初始化语法,不用再记忆不同类型的初始化规则
容器插入多参数对象时代码更简洁,减少冗余的匿名对象写法
1.3 底层支撑:std::initializer_list
容器之所以能支持 {1,2,3,4,5} 这种批量初始化,本质是因为 C++11 为所有 STL 容器新增了 std::initializer_list 版本的构造函数和赋值函数。
std::initializer_list 是一个轻量级的类,底层对应一段连续的常量数组,内部用两个指针指向数组首尾。它可以自动接收 {…} 形式的初始化列表,支持迭代器遍历。
1#include <initializer_list> 2#include <iostream> 3using namespace std; 4 5// 模拟 vector 支持 initializer_list 构造与赋值 6template<class T> 7class my_vector { 8public: 9 typedef T* iterator; 10 11 // initializer_list 构造函数:接收{}列表,逐个插入 12 my_vector(initializer_list<T> il) { 13 for (auto e : il) { 14 push_back(e); 15 } 16 } 17 18 // initializer_list 版本的赋值重载 19 my_vector& operator=(initializer_list<T> il) { 20 clear(); 21 for (auto e : il) { 22 push_back(e); 23 } 24 return *this; 25 } 26 27 void push_back(const T& val) { /* 尾插实现省略 */ } 28 void clear() { /* 清空实现省略 */ } 29 30private: 31 iterator _start = nullptr; 32 iterator _finish = nullptr; 33 iterator _endofstorage = nullptr; 34}; 35 36int main() { 37 my_vector<int> v = {1,2,3,4,5}; // 自动调用 initializer_list 构造 38 v = {10,20,30,40,50}; // 自动调用 initializer_list 赋值 39 40 // 实际STL用法:map也支持initializer_list初始化 41 map<string, string> dict = { 42 {"sort", "排序"}, 43 {"string", "字符串"}, 44 {"insert", "插入"} 45 }; 46 return 0; 47} 48
使用场景:批量初始化容器、批量赋值、容器插入多元素时,用 initializer_list 语法最简洁直观。
二、右值引用与移动语义:从根源减少拷贝开销
右值引用是 C++11 最核心的性能优化特性,它彻底解决了 C++98 中深拷贝带来的不必要性能损耗,是现代 C++ 效率提升的关键。
2.1 先搞懂:左值和右值
区分左值和右值的核心标准:能不能取地址。
左值(lvalue):有持久存储状态、可以取地址的对象。比如普通变量、指针解引用、数组元素等。左值可以出现在赋值号左边或右边。
右值(rvalue):临时的、无法取地址的对象。比如字面常量、表达式结果、匿名临时对象等。右值只能出现在赋值号右边。
1int main() { 2 // ===== 左值:可以取地址 ===== 3 int a = 10; 4 int* p = new int(0); 5 string s("hello"); 6 &a; &p; &s; // 都能取地址,都是左值 7 8 // ===== 右值:不能取地址 ===== 9 10; // 字面常量 10 a + 1; // 表达式计算结果 11 string("hi");// 匿名临时对象 12 // &10; &(a+1); &string("hi"); // 编译报错:右值无法取地址 13 14 return 0; 15} 16
2.2 左值引用 vs 右值引用
C++98 只有左值引用(Type&),C++11 新增右值引用(Type&&),二者本质都是给对象取别名,底层都是用指针实现。
| 引用类型 | 可引用左值 | 可引用右值 | 说明 |
|---|---|---|---|
| 左值引用 Type& | Yes | No | 只能绑定左值 |
| const 左值引用 const Type& | Yes | Yes | 通用引用,但不能修改对象 |
| 右值引用 Type&& | (move 后才可以) | Yes | 只能绑定右值 |
1int main() { 2 int a = 10; 3 4 // 左值引用:只能引用左值 5 int& r1 = a; 6 // int& r2 = 10; // 报错:左值引用不能直接绑定右值 7 8 // const 左值引用:左值右值都能绑定,但不能修改对象 9 const int& cr1 = a; 10 const int& cr2 = 10; 11 12 // 右值引用:只能绑定右值 13 int&& rr1 = 10; 14 int&& rr2 = a + 1; 15 // int&& rr3 = a; // 报错:右值引用不能直接绑定左值 16 17 // std::move:将左值强制转为右值,让右值引用可以绑定 18 int&& rr4 = move(a); 19 20 // ⚠️ 重要细节:右值引用变量本身是左值(可以取地址) 21 &rr1; // 合法,rr1是变量,是左值 22 // int&& rr5 = rr1; // 报错:rr1是左值,不能绑定右值引用 23 int&& rr5 = move(rr1); // 必须再move一次才能继续绑定右值引用 24 25 return 0; 26} 27
2.3 移动构造与移动赋值:窃取资源代替深拷贝
C++98 中,类的拷贝构造和拷贝赋值都是深拷贝,对于 string、vector 这种管理堆资源的类,拷贝时需要重新申请内存、拷贝数据,代价非常大。
C++11 新增移动构造和移动赋值:参数是右值引用,本质是「窃取」临时对象 / 将亡对象的资源,不用重新申请内存、拷贝数据,性能提升显著。
下面以自定义 string 为例,完整对比拷贝与移动的实现:
1#include <iostream> 2#include <string.h> 3#include <algorithm> 4using namespace std; 5 6namespace bit { 7 class string { 8 public: 9 // 普通构造函数 10 string(const char* str = "") 11 : _size(strlen(str)) 12 , _capacity(_size) 13 { 14 _str = new char[_capacity + 1]; 15 strcpy(_str, str); 16 cout << "string(const char*) -- 普通构造" << endl; 17 } 18 19 // ===== C++98 就有:拷贝构造(深拷贝,代价大)===== 20 string(const string& s) 21 : _size(s._size) 22 , _capacity(s._capacity) 23 { 24 _str = new char[_capacity + 1]; 25 strcpy(_str, s._str); 26 cout << "string(const string&) -- 拷贝构造【深拷贝,O(n)开销】" << endl; 27 } 28 29 // ===== C++11 新增:移动构造(交换资源,代价极小)===== 30 // 参数是右值引用,接收临时对象/将亡对象 31 string(string&& s) 32 : _str(nullptr) 33 , _size(0) 34 , _capacity(0) 35 { 36 // 直接交换资源,把s的内存偷过来,不用深拷贝 37 swap(s); 38 cout << "string(string&&) -- 移动构造【交换指针,O(1)开销】" << endl; 39 } 40 41 // ===== C++98 就有:拷贝赋值(深拷贝)===== 42 string& operator=(const string& s) { 43 if (this != &s) { 44 char* tmp = new char[s._capacity + 1]; 45 strcpy(tmp, s._str); 46 delete[] _str; 47 _str = tmp; 48 _size = s._size; 49 _capacity = s._capacity; 50 } 51 cout << "string& operator=(const string&) -- 拷贝赋值" << endl; 52 return *this; 53 } 54 55 // ===== C++11 新增:移动赋值===== 56 string& operator=(string&& s) { 57 if (this != &s) { 58 swap(s); // 交换资源 59 } 60 cout << "string& operator=(string&&) -- 移动赋值" << endl; 61 return *this; 62 } 63 64 // 交换成员 65 void swap(string& s) { 66 std::swap(_str, s._str); 67 std::swap(_size, s._size); 68 std::swap(_capacity, s._capacity); 69 } 70 71 ~string() { 72 delete[] _str; 73 _str = nullptr; 74 } 75 76 const char* c_str() const { return _str; } 77 private: 78 char* _str = nullptr; 79 size_t _size = 0; 80 size_t _capacity = 0; 81 }; 82} 83
场景 1:用临时对象初始化
1int main() { 2 bit::string s1("hello"); 3 4 // 用左值s1初始化s2 → 调用拷贝构造(深拷贝,完整复制数据) 5 bit::string s2 = s1; 6 7 // 用匿名临时对象初始化s3 → 调用移动构造(交换指针,零深拷贝) 8 bit::string s3 = bit::string("world"); 9 10 // 用move后的左值初始化s4 → 调用移动构造(s1的资源被转移,变成空串) 11 bit::string s4 = move(s1); 12 13 return 0; 14} 15
场景 2:函数传值返回(最常用的性能优化场景)
C++98 中,函数返回局部对象时,会发生两次深拷贝(局部对象→临时对象→接收对象),即使编译器优化也至少有一次深拷贝。
有了移动语义后,返回局部对象会自动调用移动构造,几乎零开销,再也不用靠输出型参数规避拷贝了。
1bit::string addStrings(bit::string num1, bit::string num2) { 2 bit::string str; 3 // ... 省略字符串相加逻辑 ... 4 return str; // str是局部对象,出作用域就销毁,属于将亡值 5} 6 7int main() { 8 // C++98:至少1次深拷贝,大对象性能损耗严重 9 // C++11:自动调用移动构造,只交换指针,性能大幅提升 10 bit::string ret = addStrings("111", "222"); 11 return 0; 12} 13
相比 C++98 的优势:
对于管理堆内存的类(string、vector、map 等),移动语义将拷贝的 O (n) 开销降为 O (1)
函数返回大对象时,不再需要用输出型参数规避拷贝,代码更优雅自然
2.4 引用折叠与万能引用
模板中的 T&& 不总是右值引用,它会根据实参的类型发生引用折叠:
传入左值 → T 推导为 int& → int& && 折叠为 int&(左值引用)
传入右值 → T 推导为 int → int&& 就是右值引用
这种既能接收左值又能接收右值的模板参数,叫做万能引用(Universal Reference)。
1// 万能引用模板:传左值实例化为左值引用,传右值实例化为右值引用 2template<class T> 3void Func(T&& t) { 4 // ... 5} 6 7int main() { 8 int a = 10; 9 Func(a); // 左值 → T=int& → t是int&类型 10 Func(10); // 右值 → T=int → t是int&&类型 11 return 0; 12} 13
2.5 完美转发:保持参数的值属性
万能引用有个问题:右值引用变量本身是左值,把它传给下一层函数时,会丢失右值属性,永远匹配左值引用版本。
完美转发 std::forward 可以保持参数原本的左 / 右值属性:左值传进去还是左值,右值传进去还是右值。
1#include <iostream> 2#include <utility> 3using namespace std; 4 5void Fun(int& x) { cout << "左值引用重载" << endl; } 6void Fun(const int& x) { cout << "const 左值引用重载" << endl; } 7void Fun(int&& x) { cout << "右值引用重载" << endl; } 8 9template<class T> 10void Function(T&& t) { 11 // Fun(t); // 永远调用左值版本,因为t是变量,表达式属性是左值 12 Fun(forward<T>(t)); // 完美转发:保持t原本的左右值属性 13} 14 15int main() { 16 17 int a = 10; 18 Function(a); // 实参是左值 → 调用左值版本 19 Function(10); // 实参是右值 → 调用右值版本 20 Function(move(a)); // 实参是右值 → 调用右值版本 21 return 0; 22} 23
使用场景:写模板函数、封装接口时,需要把参数原封不动地转发给下层函数,就用完美转发。
三、可变参数模板:让参数数量和类型都能泛化
C++98 的模板只能固定参数个数,C++11 新增可变参数模板,支持任意个数、任意类型的参数,是 STL emplace 系列接口的底层支撑。
3.1 基本语法
用 … 表示参数包:
class… Args:模板参数包,表示 0 个或多个类型
Args… args:函数参数包,表示 0 个或多个参数对象
1#include <iostream> 2using namespace std; 3 4// sizeof... 计算参数包中参数的个数 5template<class ...Args> 6void Print(Args... args) { 7 cout << "参数包个数:" << sizeof...(args) << endl; 8} 9 10int main() { 11 Print(); // 0个参数 12 Print(1); // 1个参数 13 Print(1, string("hi")); // 2个参数 14 Print(1, "hello", 2.2); // 3个参数 15 return 0; 16} 17
3.2 参数包的展开:递归方式
可变参数包不能直接遍历,通常用递归方式逐层展开:先取出第一个参数处理,剩下的参数包继续递归,直到参数包为空匹配终止函数。
1#include <iostream> 2#include <string> 3using namespace std; 4 5// 递归终止函数:参数包为空时调用 6void ShowList() { 7 cout << endl; 8} 9 10// 递归展开函数:取出第一个参数val,剩下的args继续递归 11template<class T, class ...Args> 12void ShowList(T val, Args... args) { 13 cout << val << " "; 14 ShowList(args...); // 剩下的参数包继续传递 15} 16 17template<class ...Args> 18void Print(Args... args) { 19 ShowList(args...); 20} 21 22int main() { 23 Print(1, "hello", 2.2, 'a'); // 输出:1 hello 2.2 a 24 return 0; 25} 26
编译时编译器会自动推导出对应参数个数的函数,本质还是生成多个重载函数。
3.3 实际应用:emplace 系列接口
C++11 为所有 STL 容器新增了 emplace_back、emplace 接口,底层就是可变参数模板 + 完美转发。
相比 push_back,emplace 可以直接把构造参数传给元素的构造函数,在容器节点上直接构造对象,减少一次拷贝 / 移动开销。
1#include <list> 2int main() { 3 list<bit::string> lt; 4 bit::string s1("hello"); 5 6 // 1. 传左值 → 和push_back效果一致,调用拷贝构造 7 lt.emplace_back(s1); 8 // 2. 传右值 → 和push_back效果一致,调用移动构造 9 lt.emplace_back(move(s1)); 10 // 3. 直接传构造参数 → 在节点上直接构造,连移动都省了! 11 // 这是push_back做不到的,也是emplace最大的优势 12 lt.emplace_back("world"); 13 14 list<pair<bit::string, int>> lt2; 15 // 直接传pair的构造参数,不用先构造pair对象再插入 16 lt2.emplace_back("苹果", 5); 17 18 return 0; 19} 20
优势:
减少了一次临时对象的构造 + 析构,性能更优
日常开发推荐优先用 emplace 替代 push/insert
四、类的新功能:更灵活的默认函数控制
4.1 默认生成的移动构造和移动赋值
C++98 类有 4 个默认成员函数(构造、析构、拷贝构造、拷贝赋值),C++11 新增 2 个:移动构造、移动赋值。
编译器自动生成默认移动构造 / 赋值的条件:
你没有自己实现移动构造、移动赋值
你也没有自己实现析构函数、拷贝构造、拷贝赋值
默认生成的移动构造:内置类型逐字节拷贝,自定义类型调用它的移动构造(没有就调用拷贝构造)
4.2 default 与 delete
= default:显式要求编译器生成默认版本的函数
= delete:禁止编译器生成该函数,比 C++98 设为 private 更简洁直观
1class Person { 2public: 3 Person(const char* name = "", int age = 0) 4 : _name(name), _age(age) 5 {} 6 7 // 自己写了拷贝构造,编译器就不会自动生成移动构造 8 Person(const Person& p) 9 : _name(p._name), _age(p._age) 10 {} 11 12 // 显式要求编译器生成默认版本的移动构造 13 Person(Person&& p) = default; 14 15 // 禁止拷贝赋值(C++11 写法,比C++98设private更清晰) 16 Person& operator=(const Person& p) = delete; 17 18private: 19 bit::string _name; 20 int _age; 21}; 22
4.3 成员变量缺省值
C++11 允许在类内声明成员变量时直接给默认值,如果初始化列表没有显式初始化该成员,就会使用这个缺省值。
1class A { 2private: 3 // 声明时直接给缺省值 4 int _a = 0; 5 int* _p = nullptr; 6 bit::string _s = "default"; 7}; 8
五、STL 的变化:新容器与新接口
5.1 新增容器
| 容器 | 说明 |
|---|---|
| unordered_map / unordered_set | 哈希表实现,平均 O (1) 查找,无序,性能优于 map/set |
| array | 固定大小的静态数组,比原生数组更安全,支持迭代器 |
| forward_list | 单链表,比 list 更省内存,但只能单向遍历 |
| 其中最常用的是 unordered_map/unordered_set,在不需要有序的场景下,性能比 map/set 高很多。 |
5.2 新增通用接口
所有容器都新增了右值引用版本的 push/insert、移动构造、移动赋值
所有容器都新增了 initializer_list 版本的构造和赋值
新增 emplace / emplace_back 系列接口
支持范围 for 遍历(底层封装迭代器,语法更简洁)
六、Lambda 表达式:写匿名函数的利器
6.1 为什么需要 Lambda
C++98 中,如果想给算法传一个比较规则,要么用函数指针,要么写仿函数类。仿函数需要单独定义类,代码分散、非常繁琐。
C++11 的 Lambda 表达式可以在函数内部定义匿名函数对象,写起来灵活轻便,是 STL 算法的最佳搭档。
6.2 语法格式+例子
[捕捉列表]:捕获外层作用域的变量供 Lambda 内部使用,不能为空
(参数列表):和普通函数参数一样,没有参数可以省略 ()
mutable:取消值捕捉的 const 属性,让值捕捉的变量可以修改
-> 返回值类型:追踪返回类型,一般可以省略,编译器自动推导
{函数体}:实现逻辑,不能为空
1[捕捉列表] (参数列表) mutable -> 返回值类型 { 函数体 } 2int main() { 3 int a = 0, b = 1, c = 2, d = 3; 4 5 // 1. 值捕捉a,引用捕捉b 6 auto func1 = [a, &b]() { 7 // a++; // 报错:值捕捉的变量默认是const的,不能修改 8 b++; // 引用捕捉可以修改,直接影响外面的b 9 return a + b; 10 }; 11 cout << func1() << endl; 12 13 // 2. 加mutable:值捕捉的变量也能改(改的是内部拷贝,不影响外面) 14 auto func2 = [=]() mutable { 15 a++; b++; c++; 16 return a + b + c; 17 }; 18 cout << func2() << endl; 19 20 // 3. 隐式引用捕捉:所有用到的变量都引用捕捉 21 auto func3 = [&]() { 22 a++; b++; c++; d++; // 都能改,都影响外部变量 23 }; 24 func3(); 25 26 return 0; 27} 28
6.4 典型应用:sort 自定义排序
C++98 要为每种排序规则写一个仿函数类,C++11 直接写 Lambda 即可,代码集中、可读性强。
1#include <vector> 2#include <algorithm> 3#include <string> 4using namespace std; 5 6struct Goods { 7 string _name; 8 double _price; 9 int _evaluate; 10 Goods(const char* name, double price, int evaluate) 11 : _name(name), _price(price), _evaluate(evaluate) 12 {} 13}; 14 15int main() { 16 vector<Goods> v = { 17 {"苹果", 2.1, 5}, 18 {"香蕉", 3.0, 4}, 19 {"橙子", 2.2, 3}, 20 {"菠萝", 1.5, 4} 21 }; 22 23 // 按价格升序 24 sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) { 25 return g1._price < g2._price; 26 }); 27 28 // 按评价降序 29 sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) { 30 return g1._evaluate > g2._evaluate; 31 }); 32 33 return 0; 34} 35
6.5 Lambda 底层原理
Lambda 本质就是仿函数:编译器会为每个 Lambda 生成一个唯一的匿名类,重载了 operator()。捕捉列表的变量就是这个类的成员变量,构造 Lambda 对象时把捕捉的变量初始化进去。
所以 Lambda 对象的大小、行为和仿函数完全一致,只是语法更简洁。
七、包装器 function 与 bind:统一可调用对象
7.1 std::function:统一类型
C++ 中可调用对象有很多种:普通函数、仿函数、Lambda、成员函数…… 它们类型各不相同,无法统一管理。
std::function 是一个包装器,可以把所有签名相同的可调用对象包装成同一个类型,方便统一存储、传递和管理。
头文件:
1#include <functional> 2#include <iostream> 3#include <string> 4using namespace std; 5 6// 普通函数 7int add(int a, int b) { return a + b; } 8 9// 仿函数 10struct Add { 11 int operator()(int a, int b) { return a + b; } 12}; 13 14class Calc { 15public: 16 static int plus_static(int a, int b) { return a + b; } // 静态成员函数 17 int plus_normal(int a, int b) { return a + b; } // 普通成员函数 18}; 19 20int main() { 21 // 包装普通函数 22 function<int(int, int)> f1 = add; 23 // 包装仿函数 24 function<int(int, int)> f2 = Add(); 25 // 包装Lambda 26 function<int(int, int)> f3 = [](int a, int b) { return a + b; }; 27 // 包装静态成员函数 28 function<int(int, int)> f4 = &Calc::plus_static; 29 // 包装普通成员函数(需要绑定对象) 30 Calc c; 31 function<int(int, int)> f5 = bind(&Calc::plus_normal, &c, placeholders::_1, placeholders::_2); 32 33 cout << f1(1, 2) << endl; 34 cout << f2(1, 2) << endl; 35 cout << f3(1, 2) << endl; 36 37 return 0; 38} 39
经典应用:命令映射表
用 map + function 实现字符串到函数的映射,替代冗长的 switch-case,扩展性极强,符合开闭原则。
1// LeetCode 150. 逆波兰表达式求值 2#include <vector> 3#include <stack> 4#include <map> 5#include <functional> 6#include <string> 7using namespace std; 8 9class Solution { 10public: 11 int evalRPN(vector<string>& tokens) { 12 stack<int> st; 13 // 用map映射操作符和对应的运算函数 14 map<string, function<int(int, int)>> opMap = { 15 {"+", [](int x, int y) { return x + y; }}, 16 {"-", [](int x, int y) { return x - y; }}, 17 {"*", [](int x, int y) { return x * y; }}, 18 {"/", [](int x, int y) { return x / y; }} 19 }; 20 21 for (auto& s : tokens) { 22 if (opMap.count(s)) { 23 int right = st.top(); st.pop(); 24 int left = st.top(); st.pop(); 25 st.push(opMap[s](left, right)); 26 } else { 27 st.push(stoi(s)); 28 } 29 } 30 return st.top(); 31 } 32}; 33
优势:新增运算只需要在 map 里加一行映射,不用修改核心逻辑,扩展性远优于 switch。
7.2 std::bind:调整参数
std::bind 是函数适配器,可以调整可调用对象的参数个数、参数顺序,还能绑死固定参数。
占位符 placeholders::_1、_2 表示新函数的第 1、2 个参数。
1#include <functional> 2#include <iostream> 3using namespace std; 4using namespace placeholders; 5 6int Sub(int a, int b) { 7 return (a - b) * 10; 8} 9 10int main() { 11 // 1. 原封不动包装 12 auto sub1 = bind(Sub, _1, _2); 13 cout << sub1(10, 5) << endl; // 输出50 14 15 // 2. 调换参数顺序 16 auto sub2 = bind(Sub, _2, _1); 17 cout << sub2(10, 5) << endl; // 输出-50 18 19 // 3. 绑死第一个参数,只留一个参数 20 auto sub3 = bind(Sub, 100, _1); 21 cout << sub3(5) << endl; // 输出950 22 23 return 0; 24} 25
使用场景:函数参数太多、需要固定部分参数简化调用时,用 bind 非常方便。
八、其他重要特性
8.1 智能指针
C++11 新增了 unique_ptr、shared_ptr、weak_ptr 三大智能指针,利用 RAII 机制自动管理堆内存,从根源解决内存泄漏问题。这部分内容较多,通常单独成章深入讲解。
8.2 constexpr 常量表达式
让函数、变量在编译期就能计算出结果,将运行时开销转移到编译期,提升程序运行性能。
8.3 范围 for
底层封装迭代器,让容器遍历更简洁直观
1vector<int> v = {1,2,3,4,5}; 2for (auto e : v) { // 自动遍历所有元素 3 cout << e << " "; 4} 5
九、总结
C++11 不是简单的语法糖堆砌,而是一次全方位的革命性升级:
性能层面:移动语义从根源减少了深拷贝开销,让 C++ 程序性能再上一个台阶
开发效率层面:列表初始化、Lambda、可变参数模板、范围 for 等特性,大幅简化了代码,提升了开发效率
安全层面:智能指针解决了内存泄漏难题,=delete 等语法让类的设计更安全
泛型能力层面:可变参数模板、完美转发让泛型编程更灵活、更强大
掌握了 C++11,才算真正踏入现代 C++ 的大门。后续的 C++14/17/20 都是在这个基础上的迭代和完善。
