[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_inserterstd::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
完结撒花~






