【C++】 C++11 知识点梳理(上)
一、C11 发展概述版本发布年份核心新增特性C981998模板、STL、容器、算法、字符串、流C112011列表初始化、auto/decltype、Lambda、右值引用 / 移动语义、constexpr、多线程、内存模型、正则、智能指针、哈希容器、std::arrayC142014读写锁、泛型 LambdaC172017折叠表达式、constexpr if、结构化绑定、string_view、文件系统、std::any/optional/variantC202020协程、模块、Concept、Ranges 库C232023类模板参数推导、标准库模块化、打印接口、扁平容器等二、列表初始化 std::initializer_list2.1 C98 旧式{}初始化仅支持数组、结构体使用大括号初始化语法零散struct Point { int _x; int _y; }; int main() { int arr1[] {1,2,3,4,5}; int arr2[5] {0}; Point p {1, 2}; return 0; }2.2 C11 统一列表初始化C11 实现万物可用{}初始化支持内置类型、自定义类、容器且可省略 同时会做窄转换检查。核心用法#include iostream #include vector using namespace std; struct Point { int _x; int _y; }; class Date { public: Date(int year 1, int month 1, int day 1) : _year(year), _month(month), _day(day) {} private: int _year, _month, _day; }; int main() { // 1. 内置类型 int x1 {2}; int x2{2}; // 省略 // 2. 自定义结构体/类 Point p1{1, 2}; Date d1{2025, 1, 1}; const Date d2{2024, 7, 25}; // 引用绑定临时对象 // 3.需要注意的是C98⽀持单参数时类型转换也可以不⽤{} Date d3 { 2025}; Date d4 2025; // 4. 容器便捷构造/插入 vectorDate v; v.push_back({2025, 1, 1}); // 直接传初始化列表简化代码 return 0; }特性编译器会优化Date d {a,b,c}不会产生额外临时对象 拷贝构造直接构造。禁止隐式窄转换int a{3.14};编译报错int a 3.14;C98 允许截断。2.3std::initializer_list解决容器批量初始化问题STL 所有容器都增加了接收initializer_list的构造函数与赋值重载。底层原理std::initializer_list内部仅保存两个指针起始、末尾不拷贝数据开销极小。⭐底层指向栈上数组支持迭代器遍历。示例#include iostream #include vector #include map using namespace std; int main() { // 1. 容器直接批量初始化 vectorint v1{1,2,3,4,5}; vectorint v2 {10,20,30}; // 2. map 结合列表初始化 mapstring, string dict{ {name, zhangsan}, {age, 20} }; // 3. 列表赋值 v1 {100, 200, 300}; return 0; }自定义类支持initializer_list#include initializer_list #include vector class MyVector { private: vectorint data; public: // 接收初始化列表构造函数 MyVector(initializer_listint il) { for (auto val : il) data.push_back(val); } }; // 使用 MyVector mv{1,2,3,4}; std::initializer_listint mylist; mylist { 10, 20, 30 }; cout sizeof(mylist) endl; // 这⾥begin和end返回的值initializer_list对象中存的两个指针 // 这两个指针的值跟i的地址跟接近说明数组存在栈上 int i 0; cout mylist.begin() endl; cout mylist.end() endl; cout i endl; // {}列表中可以有任意多个值 // 这两个写法语义上还是有差别的第⼀个v1是直接构造 // 第⼆个v2是构造临时对象临时对象拷⻉v2优化为直接构造 vectorint v1({ 1,2,3,4,5 }); vectorint v2 { 1,2,3,4,5 }; const vectorint v3 { 1,2,3,4,5 }; // 这⾥是pair对象的{}初始化和map的initializer_list构造结合到⼀起⽤了 mapstring, string dict { {sort, 排序}, {string, 字符串}};三、右值引用 移动语义C11 性能核心3.1 左值 右值左值 (lvalue)有名字、可寻址、能出现在赋值号左边普通变量、解引用指针、引用。右值 (rvalue)无名字、临时对象、字面量、表达式结果不可寻址。int a 10; // a 左值 int b a 20; // a20 右值 string s hello; // hello 字符串字面量是右值3.2 左值引用 右值引用语法左值引用类型只能绑定左值const 左值引用可绑定右值。右值引用类型只能绑定右值想要绑定左值需要move。int main() { int a 10; int r1 a; // 左值引用绑定左值 const int r2 20; // const 左值引用绑定右值 int rr1 20; // 右值引用绑定右值 // int rr2 a; // 报错右值引用不能直接绑左值 int rr3 move(a);// std::move 将左值转为右值 // 重点右值引用变量本身是**左值** int rr4 100; // int rr5 rr4; // 报错 int rr6 move(rr4); return 0; }3.3 引用延长生命周期const 左值引用和右值引用都可以延长临时对象生命周期#include string using namespace std; int main() { string s1 test; const string r1 s1 s1; // 延长生命周期不可修改 string r2 s1 s1; // 延长生命周期可修改 r2 end; return 0; }3.4 重载匹配规则同时提供三种重载时普通左值 → 匹配Tconst左值 → 匹配const T右值 /move(左值)→ 匹配T#include iostream using namespace std; void f(int x) { cout 左值引用\n; } void f(const int x) { cout const 左值引用\n; } void f(int x) { cout 右值引用\n; } int main() { int a 1; const int ca 2; f(a); // 左值引用 f(ca); // const 左值引用 f(100); // 右值引用 f(move(a)); // 右值引用 return 0; }3.5 移动构造 移动赋值针对深拷贝类string/vector设计转移资源所有权替代深拷贝提升性能。函数原型// 移动构造 类名(类名 源) noexcept; // 移动赋值 类名 operator(类名 源) noexcept;模拟简易 string完整示例#include iostream #include cstring #include algorithm using namespace std; class String { private: char* _str nullptr; size_t _size 0; size_t _capacity 0; public: String(const char* str ) { _size strlen(str); _capacity _size; _str new char[_capacity 1]; strcpy(_str, str); } // 拷贝构造深拷贝 String(const String s) { _size s._size; _capacity s._capacity; _str new char[_capacity 1]; strcpy(_str, s._str); } // 移动构造窃取资源 String(String s) noexcept { swap(_str, s._str); swap(_size, s._size); swap(_capacity, s._capacity); } // 拷贝赋值 String operator(const String s) { if (this ! s) { delete[] _str; _size s._size; _capacity s._capacity; _str new char[_capacity 1]; strcpy(_str, s._str); } return *this; } // 移动赋值 String operator(String s) noexcept { if (this ! s) { swap(_str, s._str); swap(_size, s._size); swap(_capacity, s._capacity); } return *this; } ~String() { delete[] _str; _str nullptr; } const char* c_str() const { return _str; } }; int main() { String s1(hello); String s2 s1; // 拷贝构造 String s3 move(s1); // 移动构造 String s4(world); s4 move(s3); // 移动赋值 return 0; }3.6 移动语义两大应用场景函数传值返回局部对象局部对象作为右值触发移动构造减少拷贝。容器接口重载STLpush_back/insert都提供const T和T两个版本传入左值 → 拷贝构造传入右值 /move对象 → 移动构造int main() { std::listbit::string lt; bit::string s1(111111111111111111111); lt.push_back(s1); cout ************************* endl; lt.push_back(bit::string(22222222222222222222222222222)); cout ************************* endl; lt.push_back(3333333333333333333333333333); cout ************************* endl; lt.push_back(move(s1)); cout ************************* endl; return 0; } 运⾏结果 string(char* str) string(const string s) -- 拷⻉构造 ************************* string(char* str) string(string s) -- 移动构造 ~string() -- 析构 ************************* string(char* str) string(string s) -- 移动构造 ~string() -- 析构 ************************* string(string s) -- 移动构造 ************************* ~string() -- 析构 ~string() -- 析构 ~string() -- 析构 ~string() -- 析构 ~string() -- 析构3.7 值类别细分C11 扩展C11 把右值细分为两类纯右值 (prvalue)字面量、表达式结果、传值返回临时对象传统意义上的右值。将亡值 (xvalue)move结果、右值引用转换结果。合称泛左值 (glvalue) 左值 将亡值。3.8 引用折叠 万能引用C 不允许 “引用的引用”模板中出现时遵循引用折叠规则T →TT →TT →TT →T万能引用模板参数T传入左值 → 推导T 类型折叠后为左值引用传入右值 → 推导T 类型最终为右值引用templateclass T void f1(T x) {} // 由于引⽤折叠限定f2实例化后可以是左值引⽤也可以是右值引⽤ templateclass T void f2(T x) {} int main() { typedef int lref; typedef int rref; int n 0; lref r1 n; // r1 的类型是 int lref r2 n; // r2 的类型是 int rref r3 n; // r3 的类型是 int rref r4 1; // r4 的类型是 int // 没有折叠-实例化为void f1(int x) f1int(n); f1int(0); // 报错 // 折叠-实例化为void f1(int x) f1int(n); f1int(0); // 报错 // 折叠-实例化为void f1(int x) f1int(n); f1int(0); // 报错 // 折叠-实例化为void f1(const int x) f1const int(n); f1const int(0); // 折叠-实例化为void f1(const int x) f1const int(n); f1const int(0); // 没有折叠-实例化为void f2(int x) f2int(n); // 报错 f2int(0); // 折叠-实例化为void f2(int x) f2int(n); f2int(0); // 报错 // 折叠-实例化为void f2(int x) f2int(n); // 报错 f2int(0); return 0;3.9 完美转发std::forward作用保留参数原有左 / 右值属性配合万能引用使用。std::move一律转为右值std::forwardT原样转发左值转左值右值转右值templateclass T void wrapper(T t) { // fun(t); // t 是左值永远调用左值版本 fun(forwardT(t)); // 完美转发保留原值类别 }