Skip to content

Commit 99450d4

Browse files
polish two passages (#105)
1 parent 3875e05 commit 99450d4

6 files changed

Lines changed: 569 additions & 84 deletions

File tree

code/examples/vol2/01_rvalue_reference.cpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// 01_rvalue_reference.cpp
22
// 右值引用与值类别:构造、拷贝、移动、析构全过程追踪
3+
// Standard: C++17
34

45
#include <iostream>
56
#include <string>
@@ -12,17 +13,35 @@ class Tracker {
1213
explicit Tracker(std::string name) : name_(std::move(name)) {
1314
std::cout << " [" << name_ << "] 构造\n";
1415
}
16+
1517
Tracker(const Tracker& other) : name_(other.name_ + "_copy") {
1618
std::cout << " [" << name_ << "] 拷贝构造\n";
1719
}
20+
1821
Tracker(Tracker&& other) noexcept : name_(std::move(other.name_)) {
1922
other.name_ = "(moved-from)";
2023
std::cout << " [" << name_ << "] 移动构造\n";
2124
}
25+
2226
~Tracker() { std::cout << " [" << name_ << "] 析构\n"; }
27+
28+
Tracker& operator=(const Tracker& other) {
29+
name_ = other.name_ + "_copy";
30+
std::cout << " [" << name_ << "] 拷贝赋值\n";
31+
return *this;
32+
}
33+
34+
Tracker& operator=(Tracker&& other) noexcept {
35+
name_ = std::move(other.name_);
36+
other.name_ = "(moved-from)";
37+
std::cout << " [" << name_ << "] 移动赋值\n";
38+
return *this;
39+
}
40+
2341
const std::string& name() const { return name_; }
2442
};
2543

44+
/// @brief 返回临时对象(prvalue)
2645
Tracker make_tracker(std::string name) {
2746
return Tracker(std::move(name));
2847
}
@@ -34,16 +53,23 @@ int main() {
3453

3554
std::cout << "=== 2. 拷贝构造 ===\n";
3655
Tracker b = a;
37-
std::cout << " a.name = " << a.name() << "\n b.name = " << b.name() << "\n\n";
56+
std::cout << " a.name = " << a.name() << "\n";
57+
std::cout << " b.name = " << b.name() << "\n\n";
3858

3959
std::cout << "=== 3. 移动构造(显式 std::move)===\n";
4060
Tracker c = std::move(a);
41-
std::cout << " a.name = " << a.name() << "\n c.name = " << c.name() << "\n\n";
61+
std::cout << " a.name = " << a.name() << "\n";
62+
std::cout << " c.name = " << c.name() << "\n\n";
4263

4364
std::cout << "=== 4. 返回临时对象 ===\n";
4465
Tracker d = make_tracker("D");
4566
std::cout << " d.name = " << d.name() << "\n\n";
4667

47-
std::cout << "=== 5. 程序结束,析构顺序 ===\n";
68+
std::cout << "=== 5. 移动赋值 ===\n";
69+
d = std::move(b);
70+
std::cout << " b.name = " << b.name() << "\n";
71+
std::cout << " d.name = " << d.name() << "\n\n";
72+
73+
std::cout << "=== 6. 程序结束,析构顺序 ===\n";
4874
return 0;
4975
}

code/examples/vol2/02_move_semantics.cpp

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// 02_move_semantics.cpp
2-
// 移动构造与移动赋值:Buffer 资源所有权转移与 vector 操作对比
2+
// 移动构造与移动赋值:Buffer 资源转移全过程追踪
3+
// Standard: C++17
34

45
#include <cstring>
56
#include <iostream>
@@ -17,24 +18,40 @@ class Buffer {
1718
: data_(new char[capacity]), size_(0), capacity_(capacity) {
1819
std::cout << " [Buffer] 分配 " << capacity << " 字节\n";
1920
}
21+
2022
~Buffer() {
2123
if (data_) {
2224
std::cout << " [Buffer] 释放 " << capacity_ << " 字节\n";
2325
delete[] data_;
2426
}
2527
}
28+
2629
Buffer(const Buffer& other)
2730
: data_(new char[other.capacity_]), size_(other.size_), capacity_(other.capacity_) {
2831
std::memcpy(data_, other.data_, size_);
2932
std::cout << " [Buffer] 拷贝构造 " << capacity_ << " 字节\n";
3033
}
34+
3135
Buffer(Buffer&& other) noexcept
3236
: data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
3337
other.data_ = nullptr;
3438
other.size_ = 0;
3539
other.capacity_ = 0;
3640
std::cout << " [Buffer] 移动构造(指针转移)\n";
3741
}
42+
43+
Buffer& operator=(const Buffer& other) {
44+
if (this != &other) {
45+
delete[] data_;
46+
data_ = new char[other.capacity_];
47+
size_ = other.size_;
48+
capacity_ = other.capacity_;
49+
std::memcpy(data_, other.data_, size_);
50+
std::cout << " [Buffer] 拷贝赋值 " << capacity_ << " 字节\n";
51+
}
52+
return *this;
53+
}
54+
3855
Buffer& operator=(Buffer&& other) noexcept {
3956
if (this != &other) {
4057
delete[] data_;
@@ -48,17 +65,20 @@ class Buffer {
4865
}
4966
return *this;
5067
}
68+
5169
void append(const char* str, std::size_t len) {
5270
if (size_ + len <= capacity_) {
5371
std::memcpy(data_ + size_, str, len);
5472
size_ += len;
5573
}
5674
}
75+
5776
std::size_t size() const { return size_; }
77+
std::size_t capacity() const { return capacity_; }
5878
};
5979

6080
int main() {
61-
std::cout << "=== 1. 创建缓冲区 ===\n";
81+
std::cout << "=== 1. 创建两个缓冲区 ===\n";
6282
Buffer a(1024);
6383
a.append("Hello", 5);
6484
Buffer b(2048);
@@ -71,17 +91,25 @@ int main() {
7191

7292
std::cout << "=== 3. 移动构造 ===\n";
7393
Buffer d = std::move(b);
74-
std::cout << " d.size() = " << d.size() << "\n\n";
94+
std::cout << " d.size() = " << d.size() << "\n";
95+
std::cout << " b.capacity() = " << b.capacity() << "\n\n";
7596

76-
std::cout << "=== 4. vector 中的移动 ===\n";
97+
std::cout << "=== 4. 移动赋值 ===\n";
98+
a = std::move(d);
99+
std::cout << " a.size() = " << a.size() << "\n";
100+
std::cout << " d.capacity() = " << d.capacity() << "\n\n";
101+
102+
std::cout << "=== 5. vector 中的移动 ===\n";
77103
std::vector<Buffer> buffers;
78104
buffers.reserve(4);
79105
std::cout << " push_back 左值:\n";
80-
buffers.push_back(c);
106+
buffers.push_back(c); // 拷贝
81107
std::cout << " push_back std::move:\n";
82-
buffers.push_back(std::move(c));
108+
buffers.push_back(std::move(c)); // 移动
83109
std::cout << " emplace_back 原位构造:\n";
84-
buffers.emplace_back(512);
110+
buffers.emplace_back(512); // 直接在 vector 中构造
111+
std::cout << '\n';
85112

113+
std::cout << "=== 6. 程序结束 ===\n";
86114
return 0;
87115
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// noexcept_vector_realloc.cpp -- noexcept 移动 vs 非 noexcept 移动 在 vector 扩容时的差异
2+
// Standard: C++17
3+
4+
#include <iostream>
5+
#include <string>
6+
#include <type_traits>
7+
#include <utility>
8+
#include <vector>
9+
10+
// 用模板参数 NoexceptMove 切换移动构造是否标 noexcept,其余代码完全相同
11+
template <bool NoexceptMove> class TrackedBuffer {
12+
char* data_;
13+
std::size_t capacity_;
14+
std::string tag_;
15+
16+
public:
17+
explicit TrackedBuffer(std::size_t cap, std::string tag)
18+
: data_(new char[cap]), capacity_(cap), tag_(std::move(tag)) {}
19+
20+
~TrackedBuffer() { delete[] data_; }
21+
22+
TrackedBuffer(const TrackedBuffer& other)
23+
: data_(new char[other.capacity_]), capacity_(other.capacity_), tag_(other.tag_) {
24+
std::cout << " [" << tag_ << "] 拷贝构造\n";
25+
}
26+
27+
// 唯一的区别:noexcept 标记
28+
TrackedBuffer(TrackedBuffer&& other) noexcept(NoexceptMove)
29+
: data_(other.data_), capacity_(other.capacity_), tag_(std::move(other.tag_)) {
30+
other.data_ = nullptr;
31+
other.capacity_ = 0;
32+
std::cout << " [" << tag_ << "] 移动构造\n";
33+
}
34+
35+
TrackedBuffer& operator=(const TrackedBuffer&) = delete;
36+
TrackedBuffer& operator=(TrackedBuffer&&) = delete;
37+
};
38+
39+
int main() {
40+
using NB = TrackedBuffer<true>; // 移动构造标了 noexcept
41+
using TB = TrackedBuffer<false>; // 移动构造没标 noexcept
42+
43+
static_assert(std::is_nothrow_move_constructible_v<NB>, "NB 的移动构造是 noexcept");
44+
static_assert(!std::is_nothrow_move_constructible_v<TB>, "TB 的移动构造不是 noexcept");
45+
46+
std::cout << "=== noexcept 移动 + vector 扩容 ===\n";
47+
{
48+
std::vector<NB> v;
49+
v.reserve(1); // 先预留 1 个槽位
50+
v.emplace_back(64, "Noexcept版"); // 占住唯一的槽位
51+
std::cout << "--- 触发扩容 ---\n";
52+
v.emplace_back(64, "Noexcept版"); // 超出容量,必须扩容搬运
53+
}
54+
55+
std::cout << "\n=== 非 noexcept 移动 + vector 扩容 ===\n";
56+
{
57+
std::vector<TB> v;
58+
v.reserve(1);
59+
v.emplace_back(64, "Throwing版");
60+
std::cout << "--- 触发扩容 ---\n";
61+
v.emplace_back(64, "Throwing版"); // 扩容时 vector 不敢用移动,退回拷贝
62+
}
63+
64+
return 0;
65+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// rule_of_five_fallback.cpp -- 只有析构函数时,std::move 退化为拷贝构造
2+
// Standard: C++17
3+
4+
#include <iostream>
5+
#include <type_traits>
6+
#include <utility>
7+
8+
// 只定义了析构函数,没有声明任何拷贝/移动操作
9+
class OnlyDestructor {
10+
char* data_;
11+
12+
public:
13+
explicit OnlyDestructor(std::size_t n) : data_(new char[n]) {}
14+
15+
~OnlyDestructor() { delete[] data_; }
16+
// 注意:这里既没有声明移动构造,也没有声明拷贝构造
17+
};
18+
19+
// 编译期验证:它「能移动构造」,但不是因为真有移动构造函数
20+
static_assert(!std::is_trivially_move_constructible_v<OnlyDestructor>,
21+
"没有真正的(平凡的)移动构造函数");
22+
static_assert(std::is_move_constructible_v<OnlyDestructor>,
23+
"但 is_move_constructible 为 true —— 编译器退回到拷贝构造来满足");
24+
25+
int main() {
26+
std::cout
27+
<< "is_trivially_move_constructible_v: "
28+
<< std::is_trivially_move_constructible_v<OnlyDestructor> << " (没有真正的移动构造)\n";
29+
std::cout << "is_move_constructible_v: "
30+
<< std::is_move_constructible_v<OnlyDestructor> << " (但能用拷贝构造蒙混过关)\n";
31+
32+
// 真要执行 OnlyDestructor b = std::move(a),隐式拷贝构造做浅拷贝,
33+
// a 和 b 的 data_ 指向同一块内存,两者析构时 double free。
34+
// 这里不真的跑(会崩),编译期 static_assert 已经给出结论。
35+
return 0;
36+
}

0 commit comments

Comments
 (0)