【C++游记】C++11特性
枫の个人主页
你不能改变过去,但你可以改变未来
算法/C++/数据结构/C
Hello,这里是小枫。C语言与数据结构和算法初阶两个板块都更新完毕,我们继续来学习C++的内容呀。C++是接近底层有比较经典的语言,因此学习起来注定枯燥无味,西游记大家都看过吧~,我希望能带着大家一起跨过九九八十一难,降伏各类难题,学会C++,我会尽我所能,以通俗易懂、幽默风趣的方式带给大家形象生动的知识,也希望大家遇到困难不退缩,遇到难题不放弃,学习师徒四人的精神!!!故此得名【C++游记】
话不多说,让我们一起进入今天的学习吧~~~
目录
1. C++11 的发展历史
2. 列表初始化
2.1 C++98 传统的 {} 初始化
2.2 C++11 中的统一初始化
2.3 std::initializer_list
3. 右值引用与移动语义
3.1 左值与右值
3.2 左值引用与右值引用
3.3 移动构造与移动赋值
3.4 右值引用的应用场景
3.5 引用折叠与完美转发
4. 可变参数模板
4.1 基本语法
4.2 包扩展
4.3 emplace 系列接口
5. 新的类功能
5.1 默认移动构造与移动赋值
5.2 成员变量默认初始化
5.3 default 与 delete
5.4 final 与 override
6. STL 中的变化
7. Lambda 表达式
7.1 基本语法
7.2 捕捉列表
7.3 Lambda 的应用
8. 包装器
8.1 std::function
8.2 std::bind
9. 总结
10. 结语
1. C++11 的发展历史
C++11 是 C++ 语言的一次重大更新,于 2011 年 8 月 12 日由 ISO 正式发布。它引入了大量新特性,标准化了既有实践,并改进了 C++ 程序员可用的抽象能力。
在 C++11 之前,C++98/03 已经使用了十多年,而 C++11 与 C++03 间隔了 8 年,是迄今为止最长的版本间隔。自 C++11 开始,C++ 标准委员会决定每 3 年发布一个新版本。
版本 | 发布年份 | 主要特性 |
---|---|---|
C++98 | 1998 | STL、模板、字符串、IO 流 |
C++11 | 2011 | 移动语义、初始化列表、auto/decltype、lambda、constexpr、多线程 |
C++14 | 2014 | 泛型 lambda、返回类型推导、变量模板 |
C++17 | 2017 | 结构化绑定、if constexpr、并行算法、filesystem |
C++20 | 2020 | 协程、模块、概念、范围库 |
C++23 | 2023 | 改进的范围、std::expected、flat_map/set |
2. 列表初始化
2.1 C++98 传统的 {}
初始化
C++98 中,只有数组和简单结构体可以使用 {}
初始化:
struct Point {int _x;int _y;
};int main() {int array1[] = {1, 2, 3, 4, 5};int array2[5] = {0};Point p = {1, 2};return 0;
}
2.2 C++11 中的统一初始化
C++11 引入了统一初始化语法,几乎所有类型都可以使用 {}
初始化:
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() {// 内置类型int x1 = {2};int x2 {2}; // 可以省略 =// 自定义类型Date d1 = {2025, 1, 1};Date d2 {2024, 7, 25};// 容器初始化std::vector<Date> v;v.push_back({2025, 1, 1}); // 更简洁
}
2.3 std::initializer_list
C++11 引入了 std::initializer_list
,允许容器和自定义类型支持任意长度的 {}
初始化:
#include <initializer_list>template<class T>
class vector {
public:vector(std::initializer_list<T> il) {for (auto e : il)push_back(e);}// ...
};int main() {std::vector<int> v = {1, 2, 3, 4, 5};std::map<std::string, std::string> dict = {{"sort", "排序"}, {"string", "字符串"}};
}
3. 右值引用与移动语义
3.1 左值与右值
- 左值:有持久状态,可以取地址的表达式(变量、解引用指针等)
- 右值:临时值,不能取地址的表达式(字面量、临时对象、表达式结果等)
int main() {int a = 1; // a 是左值,1 是右值int b = a + 2; // b 是左值,a+2 是右值std::string s1 = "hello"; // s1 是左值std::string s2 = s1 + " world"; // s2 是左值,s1+" world" 是右值
}
3.2 左值引用与右值引用
int main() {int a = 1;// 左值引用只能绑定左值int& r1 = a;// 右值引用只能绑定右值int&& rr1 = 10;// const 左值引用可以绑定右值const int& cr = 10;// 右值引用可以绑定 move(左值)int&& rr2 = std::move(a);
}
3.3 移动构造与移动赋值
移动语义允许资源所有权的转移,避免不必要的拷贝:
class string {
public:// 移动构造string(string&& s) noexcept: _str(nullptr), _size(0), _capacity(0) {swap(s); // 交换资源,不进行深拷贝}// 移动赋值string& operator=(string&& s) noexcept {if (this != &s) {clear();swap(s);}return *this;}// ...
private:char* _str;size_t _size;size_t _capacity;
};
3.4 右值引用的应用场景
- 提高性能:避免大型对象拷贝
- 解决返回值问题:函数返回局部对象时自动使用移动语义
- 容器操作优化:
push_back
、insert
等支持右值引用版本
std::vector<std::string> v;
std::string s = "hello";v.push_back(s); // 拷贝构造
v.push_back(std::move(s)); // 移动构造,效率更高
3.5 引用折叠与完美转发
引用折叠规则:
- 右值引用的右值引用折叠为右值引用 (
T&& &&
→T&&
) - 其他组合均折叠为左值引用 (
T& &
,T& &&
,T&& &
→T&
)
完美转发:使用 std::forward
保持值类别
template<class T>
void Function(T&& t) {Fun(std::forward<T>(t)); // 完美转发
}
4. 可变参数模板
4.1 基本语法
template<class... Args>
void Print(Args&&... args) {std::cout << sizeof...(args) << std::endl; // 参数个数
}int main() {Print(); // 0个参数Print(1, "hello", 3.14); // 3个参数
}
4.2 包扩展
void ShowList() {std::cout << std::endl;
}template<class T, class... Args>
void ShowList(T x, Args... args) {std::cout << x << " ";ShowList(args...); // 递归展开
}template<class... Args>
void Print(Args... args) {ShowList(args...);
}
4.3 emplace 系列接口
C++11 容器新增了 emplace
系列接口,直接在容器内存中构造对象:
std::list<std::pair<std::string, int>> lt;
lt.emplace_back("apple", 1); // 直接构造pair,无需临时对象
5. 新的类功能
5.1 默认移动构造与移动赋值
当类没有自定义以下函数时,编译器会自动生成移动构造和移动赋值:
- 析构函数
- 拷贝构造函数
- 拷贝赋值运算符
5.2 成员变量默认初始化
class Person {
private:std::string _name = "unknown";int _age = 0;
};
5.3 default
与 delete
class Person {
public:Person() = default; // 使用默认构造Person(const Person&) = delete; // 禁止拷贝
};
5.4 final
与 override
final
:防止类被继承或虚函数被重写override
:明确表示重写基类虚函数
6. STL 中的变化
- 新容器:
std::unordered_map
、std::unordered_set
、std::forward_list
等 - 新接口:移动构造、移动赋值、
emplace
系列、initializer_list
构造 - 算法扩展:更多算法支持移动语义
7. Lambda 表达式
7.1 基本语法
[capture-list](parameters) -> return_type { function-body }
auto add = [](int a, int b) { return a + b; };
std::cout << add(1, 2) << std::endl;
7.2 捕捉列表
[var]
:值捕捉[&var]
:引用捕捉[=]
:隐式值捕捉[&]
:隐式引用捕捉[this]
:捕捉当前对象
int a = 1, b = 2;
auto sum = [a, &b]() {b = 3; // 修改外部变量return a + b;
};
7.3 Lambda 的应用
std::vector<Goods> v = {{"apple", 2.1, 5}, {"banana", 3, 4}};
std::sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._price < g2._price; // 按价格排序
});
8. 包装器
8.1 std::function
统一各种可调用对象的类型:
#include <functional>int f(int a, int b) { return a + b; }struct Functor {int operator()(int a, int b) { return a + b; }
};int main() {std::function<int(int, int)> f1 = f;std::function<int(int, int)> f2 = Functor();std::function<int(int, int)> f3 = [](int a, int b) { return a + b; };
}
8.2 std::bind
调整函数参数:
using namespace std::placeholders;int Sub(int a, int b) { return (a - b) * 10; }int main() {auto sub1 = std::bind(Sub, _1, _2); // 正常顺序auto sub2 = std::bind(Sub, _2, _1); // 交换参数auto sub3 = std::bind(Sub, 100, _1); // 固定第一个参数
}
9. 总结
C++11 引入了许多革命性的特性,极大地提升了 C++ 的表达能力和性能:
- 统一初始化:
{}
和initializer_list
- 移动语义:右值引用、移动构造、移动赋值
- 泛型编程增强:可变参数模板、完美转发
- 类功能增强:默认移动操作、成员初始化、
default
/delete
- STL 改进:新容器、新接口
- 编程便捷性:lambda 表达式、包装器
10. 结语
今日C++到这里就结束啦,如果觉得文章还不错的话,可以三连支持一下。感兴趣的宝子们欢迎持续订阅小枫,小枫在这里谢谢宝子们啦~小枫の主页还有更多生动有趣的文章,欢迎宝子们去点评鸭~C++的学习很陡,时而巨难时而巨简单,希望宝子们和小枫一起坚持下去~你们的三连就是小枫的动力,感谢支持~