Bläddra i källkod

feat: 添加 move 的例子和赋值规则

NiceTry12138 10 månader sedan
förälder
incheckning
34b5e8320f
1 ändrade filer med 79 tillägg och 3 borttagningar
  1. 79 3
      cpp/现代C++/现代C++.md

+ 79 - 3
cpp/现代C++/现代C++.md

@@ -192,7 +192,7 @@ class Point1 {
 
 既然知道这个类的第一个属性是一个指针,是否可以通过指针获取到虚函数表,然后获取虚函数表的第一个函数并执行它呢?
 
-> **tip**: 下面的例子可能在 MSVC 中运行失败,因为 MSVC 的虚函数表的第一位存储着 tpye_info 也就是类的类型信息
+> **tip**: 下面的例子可能在 MSVC 中运行失败,因为 MSVC 的虚函数表的第一位存储着 type_info 也就是类的类型信息
 
 ```cpp
 #include <iostream>
@@ -522,7 +522,7 @@ int main()
 - MSVC 存储在类型关联的虚函数表中
 - GCC/Clang 存储在独立的内存区域中
 
-也就是说,如果使用 `MSCV` 运行前面的通过虚函数表直接执行虚函数的例子是不能正常运行的,因为 `MSVC` 的虚函数表存储着额外的信息
+也就是说,如果使用 `MSVC` 运行前面的通过虚函数表直接执行虚函数的例子是不能正常运行的,因为 `MSVC` 的虚函数表存储着额外的信息
 
 
 
@@ -578,6 +578,8 @@ public:
 
 ### 左值右值
 
+#### 定义
+
 - 左值:命名对象、可取地址、可赋值
 	- 基本变量类型、数组、数组元素
 	- 字符串字面量 `"success"[0]` 和 `&"success"` 可以正常编过
@@ -640,7 +642,81 @@ int main()
 }
 ```
 
-> mscv 编不过,clang 可以
+> msvc 编不过,clang 可以
 
 可以注意以下 `process` 函数,虽然 `process(x+y)` 触发的是右值,但是在函数中,`data` 其实是个左值
 
+#### 移动
+
+- 左值:有身份,不呢个移动
+- 纯右值:没身份,可移动
+- 将亡值:有身份,可以移动
+
+当源对象是一个左值,移动左值并不安全,因为左值后续持续存在,可能被引用,虽然可以将左值强制转换为右值,但是需要自负安全
+
+当源对象是一个右值,移动很安全
+
+```cpp
+void func(TestCls&& in);
+```
+
+上述代码,形参 `in` 到底是右值还是左值?
+
+- 对函数调用者来说, `in` 是一个右值引用,要传递优质
+- 对函数内部来说,`in` 是一个左值引用(可以取地址)
+
+```cpp
+TestCls a1(1, 2);
+TestCls a2 = a1;		// 左值源 拷贝构造
+a1 = a2;				// 左值源 拷贝赋值
+a1 = std::move(a2);		// 移动赋值
+
+// a2.process();		// 危险操作 
+```
+
+> `std::move` 转为右值
+
+一般来说,一个对象被 `std::move` 之后,这个对象被认定为无效对象,后续代码中不应再次使用,所以在 `std::move` 之后仍然执行 `a2.process()` 是危险操作,可能导致不确定后果
+
+```cpp
+TestCls GetTestCls()
+{
+	TestCls a1(1, 2);
+	return a1;
+	// 编译器自动优化为 return std::move(a1);
+}
+
+TestCls a2 = GetTestCls();	// 不触发任何构造 复制初始化,但可能被优化
+TestCls a3(GetTestCls());	// 不触发任何构造 直接初始化
+a2 = GetTestCls();			// 触发移动赋值,此时 a2 已经构造完毕,无法触发优化
+```
+
+上述代码,`GetTestCls` 函数返回值是一个临时对象,在接收的时候触发的是移动构造
+
+上述代码,`TestCls a3(GetTestCls())` 不会触发任何构造,这是因为 C++ 的返回值优化,编译器优化**直接构造对象到目标内存**,消除临时对象
+
+- 返回值优化(RVO):编译器优化直接构造对象到目标内存,消除临时对象
+- 命名返回值优化(NRVO):针对命名局部变量返回时的优化(C++17 起部分场景被强制要求)
+
+针对 `TestCls a(GetTestCls())` 预期行为是构建临时对象,调用移动构造。实际是编译器直接在 `a` 对象的内存位置构造对象,完全跳过了拷贝、移动构造
+
+针对 `TestCls a2 = GetTestCls()` 从 C++17 开始强制要求进行拷贝省略,等同于直接构造,无需任何拷贝、移动操作
+
+| 构造表达式 | C++ 标准 | 是否触发拷贝、移动构造 | 原因 |
+| --- | --- | --- | --- |
+| TestCls a(GetTestCls()) | <C++17 | 无 | RVO 优化 |
+|  | ≥C++17 | 无 | 强制拷贝省略 |
+| TestCls a2 = GetTestCls() | <C++17 | 无 | 拷贝省略优化 |
+|  | ≥C++17 | 无 | 强制拷贝省略 |
+
+#### 赋值规则
+
+左值、左值引用、右值、右值引用的赋值规则
+
+|  | & | const & | && | const && |
+| --- | --- | --- | --- | --- |
+| 左值 | 可以 | 可以 |  |  |
+| const 左值 |  | 可以 |  |  |
+| 右值 |  | **可以** | 可以 | 可以 |
+| const 右值 |  | 可以 |  | 可以 |
+