[C++]CS 106L: Standard C++ Programming 学习笔记
前言
五一比较闲,就看了看 CS106L 这门课
懒得等今年的课,看的是 Spring 2021 的,因为没代码就只看了课件
记一点学到的新东西(其实只是凑篇blog)
std::optional
(C++17)
类似于一个普通变量,但有一个“没有值”的状态
std::variant
(C++17)
类似
std::optional
,但可选择含多种类型(同一时刻只有一种值,类似
union
)
std::any
(C++17)
类似 std::any
,可含任意类型的值
语法糖,简单例子:
1 | std::pair<int, double> v{1, 3.14}; |
std::stringstream
类似 C 的 sscanf
、sprintf
,在
std::string
上做流操作
std::vector<bool>
尽量不使用,底层实现为
bitset
,导致部分功能无法正常使用
可使用 std::deque
或 std::bitset
代替
- Lambda Capture (C++11)
捕获当前作用域中部分变量值,以供 Lambda 函数使用
1 | auto Lambda = [capture-values](arguments) |
- 特殊迭代器
std::inserter
,std::front_inserter
,std::back_inserter
std::istream_iterator
,std::ostream_iterator
1 | std::copy(vec.begin(), vec.end(), std::back_inserter(vec2));//连接两个 vector |
- Anonymous namespace
匿名空间,可以将变量作用域限制在本文件内
1 | namespace{...} |
由编译器自动生成唯一名字,类似于:
1 | namespace _UNIQUE_NAME{...} |
- 内存泄漏?(这里只是简单记录一下,可能有错,仅供了解,实际问题更加复杂)
学 OI 的时候不用管,指针随便开,现在才感觉非常麻烦,C++ 中手动申请的内存不会自动回收,需要手动处理
简单例子(重载等于号时 struct 中的数组复制):
1 | struct Info |
- 函数后加 = 运算符
1 | struct Info |
- Move Semantics
考虑一段代码:
1 | std::vector<int> Func() |
这段代码中 v
首先复制 Func()
的内容,然后
Func()
销毁,比较浪费(copy 行为)
考虑有没有一种方法直接将 v
指向 Func()
的内容?(move 行为)
- lvalue / rvalue(左值 / 右值)
- lvalue:在内存中有地址,可置于 = 运算符的左边,一般生命周期为定义域
- rvalue:在内存中无地址,只存在于 = 运算符的右边,一般生命周期为当前语句
For example:
1 | int v1 = 2; //2: rvalue |
通常引用只能指向 lvalue:
1 | void Func(int& x){/*...*/} |
对 rvalue 的引用使用 &&:
1 | void Func(int&& x){/*...*/} |
那么 rvalue 的引用有什么用?
可以用于对 rvalue 实现 move 行为,以构造函数为例:
1 | struct Info |
但是......注意到里面 o.a
是 lvalue,a = o.a
这一语句执行的依然是 copy
行为,尽管复制一个指针对效率影响不大,但是如果是复制一些特殊变量可能还会达不到目的,怎么样进行
move 行为?
std::move
(C++11)
头文件 <utility>
std::move
并不移动什么,实际上 std::move
作用是转为 rvalue 的引用,等同于
std::static_cast<T&&>(...)
于是可以将代码改为:
1 | Info(Info&& o): Size(std::move(o.Size)) |
其他例子:
1 | std::vector<int> v1{1, 2, 3}, v2; |
需要注意的是,一个对 rvalue 的引用本身是 lvalue:
1 | std::vector<int> v1{1, 2, 3}, v2; |
一种避免内存泄漏的方法,即所有的初始化由构造函数执行,销毁由析构函数执行(总会执行)
1 | std::ifstream in; |
此语句不保证 in 能够正常关闭,可改为:
1 | std::ifstream in("data.txt"); |
程序会自动调用析构函数,可以正常释放资源
C++ 提供的智能指针可以自动释放内存,避免内存泄漏
1 | int* p = new int; |
可变为:
1 | std::unique_ptr<int> p(new int); |
而 std::unique_ptr
不可复制,std::shared_ptr
可复制,std::shared_ptr
在同一对象的所有指针都销毁后才销毁对象。
std::weak_ptr
可指向
std::shared_ptr
,但不影响 std::shared_ptr
的销毁机制
如何避免使用 new
和 delete
运算符呢?可使用
std::make_unique
(C++14),std::make_shared
(C++11)
代替(并且推荐)
- Variadic templates (C++11)
可变参数模板,以求和函数为例:
1 | template<typename T> |
- Spaceship operator / Three-way comparison (C++20)
在定义 operator<=>
后,编译器自动生成
==
,!=
,<
,<=
,>
,>=
运算符
其用法类似于 strcmp
,返回 -1,0,1 表示小于 / 等于 /
大于,在 C++ 实现中为三种特殊类型
用 auto operator<=>(...) = default;
生成默认函数
- Designated Initializers / Aggregate Initialization (C++20)
类似 Python 中的指定特殊变量初始化:
1 | struct Test{int a, b, c;}; |
- Attribute specifier sequence (C++11)
[[likely]]
,[[unlikely]]
(C++20)
人为提高分支预测准确率(怀疑效果如何)
。。以及其他标志
后记
还是学到不少东西
不过因为我太摸了导致这预计 20
学时的东西摸了一星期,还没做Assignments
完结撒花~