std::move

当我们调用 std::move(something) 的时候,此时还并没有发生所有权的转义,我们只是给 something 上打了一个标签,告诉 C++ 现在这个 something 可以被移动.当我们执行 another = std::move(something) 这个赋值的时候,C++ 调用 another 的移动构造函数,在 another 的移动构造函数里,我们把 something 的底层指针赋值给 another,并且将 something 清空,整个移动的过程其实是在移动构造函数里完成的,而非 std::move() 里.

可以看综合示例std::ignore = std::move(ptrs[0]) 这一行.由于左边是 std::ignore,其移动构造函数不会做任何事,所以 ptrs[0] 并没有清空,所以 observer.use_count() 依然为 22

综合示例

一个很好的例子,综合运用了 shared_ptr, weak_ptr 和引用计数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include "../exercise.h"
#include <memory>

int main(int argc, char **argv) {
auto shared = std::make_shared<int>(10);
std::shared_ptr<int> ptrs[]{shared, shared, shared};

std::weak_ptr<int> observer = shared;
ASSERT(observer.use_count() == 4, "");
// observer 是 weak_ptr,不持有对数据的所有权
// 所以此时只有 shared, ptrs[0,1,2] 有所有权
// 因此是 4

ptrs[0].reset();
ASSERT(observer.use_count() == 3, "");
// 清空了 ptrs[0],还剩下 shared, ptrs[1,2] 所以为 3

ptrs[1] = nullptr;
ASSERT(observer.use_count() == 2, "");
// 清空了 ptrs[1],还剩下 shared, ptrs[2] 所以为 2

ptrs[2] = std::make_shared<int>(*shared);
ASSERT(observer.use_count() == 1, "");
// 这里,std::make_shared 开辟了一块新空间
// 只不过这块新空间所保存的数据也是 10
// std::make_shared<int>(*shared) 实际上做了一次 int 的复制
// 此时,shared 指向旧空间,ptrs[2] 指向新空间,ptrs[0,1] 依旧为空
// 故对 observer 来说,只有 shared 指向这块空间,因此 count = 1

ptrs[0] = shared;
ptrs[1] = shared;
ptrs[2] = std::move(shared);
ASSERT(observer.use_count() == 3, "");
// 此时,ptrs[0,1] 重新指向旧空间
// 而 ptrs[2] = std::move(shared) 把 shared 置空,ptrs[2] 也重新指向旧空间
// observer 一直观察的是旧空间,因此此时 count = 3

std::ignore = std::move(ptrs[0]);
// 注意,这里的 std::move 没有拿走 ptrs[0] 的所有权
ptrs[1] = std::move(ptrs[1]);
ptrs[1] = std::move(ptrs[2]);
ASSERT(observer.use_count() == 2, "");
// ptrs[1]=std::move(ptrs[1]) 没有改变所有权
// 而 ptrs[1]=std::move(ptrs[2]) 把 ptrs[2] 置空,而 ptrs[1] 扔掉
// 原有的所有权,把原来 ptrs[2] 的所有权拿了过来
// 所以,此时只有 ptrs[0], ptrs[1] 有所有权,故 count=2

shared = observer.lock();
ASSERT(observer.use_count() == 3, "");
// 由于 observer 指向的空间有 shared_ptr 指着,所以资源没有被释放
// weak_ptr.lock() 于是成功新创建了一个shared_ptr指向这块资源,于是 count+=1
// 此时为 shared + ptrs[1] + ptrs[0] 拥有所有权

shared = nullptr;
for (auto &ptr : ptrs) ptr = nullptr;
ASSERT(observer.use_count() == 0, "");
// 这里把全部 shared_ptr 置空,于是资源被自动释放

shared = observer.lock();
ASSERT(observer.use_count() == 0, "");
// 对于 weak_ptr 而言,其观察的资源已经被释放了
// 所以 .lock() 没有成功创建 shared_ptr,故 count=0

return 0;
}