Skip to content

Commit a05851d

Browse files
feat(vol2): 优化 ch00/05 与 ch01 智能指针全章(de-AI+清死链+实测验证)
ch00/05 移动语义实战: - 修正 benchmark 数据硬伤(原文「285ms/3倍」与实测矛盾)→ GCC16 实测重写 - 补全 noexcept 验证代码(原缺 ThrowingType,代码与输出对不上) - 清死链 code/volumn_codes/vol2/ch00-move-semantics(空目录) - 新增 2 个 OnlineCompilerDemo(push_back_emplace、noexcept_sort_vs_realloc) ch01 智能指针 6 篇 CN: - 人称统一(笔者/咱们/您)、删「## 小结」换承接、清 AI 味词、工具链 GCC16 - 修实测硬伤: · raii-deep-dive:exit 验证缺 main(给了输出却链接不过) · unique-ptr:sizeof 代码缺 StatefulDeleter 但输出里有;5 处死链 + PIMPL 死链 · weak-ptr:lock() 实测慢 ~32 倍(原文写 10-15 倍,把直接访问耗时写高了) · custom-deleter:11 处死链 + 末尾整段「## 验证代码」死链 + 535-536 重复行 · scope-guard:验证段死链 新增 code/examples/vol2: noexcept_sort_vs_realloc.cpp、move_benchmark.cpp、push_back_emplace.cpp(均 GCC16 编译验证) EN 同步 ch00/05 + ch01/01-04
1 parent 04289b2 commit a05851d

15 files changed

Lines changed: 654 additions & 399 deletions

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// move_benchmark.cpp -- 拷贝 vs 移动性能对比(分离构造开销)
2+
// Standard: C++17
3+
// 对应文档:vol2-modern-features/ch00-move-semantics/05-move-in-practice.md
4+
//
5+
// 关键设计:把"构造"这一固定开销单独测出来作为 baseline,再用
6+
// (构造+拷贝) - 构造 和 (构造+移动) - 构造 得到纯粹的拷贝/移动耗时,
7+
// 避免构造开销稀释掉移动操作本身"接近零"的事实。
8+
//
9+
// 注意:绝对耗时是机器相关的,但"纯移动 ≈ 0、纯拷贝 >> 0"的结论稳定。
10+
#include <chrono>
11+
#include <iostream>
12+
#include <numeric>
13+
#include <string>
14+
#include <vector>
15+
16+
class BigData {
17+
std::vector<double> payload_;
18+
19+
public:
20+
explicit BigData(std::size_t n) : payload_(n) {
21+
std::iota(payload_.begin(), payload_.end(), 0.0);
22+
}
23+
24+
BigData(const BigData& other) : payload_(other.payload_) {}
25+
BigData(BigData&& other) noexcept = default;
26+
BigData& operator=(const BigData&) = default;
27+
BigData& operator=(BigData&&) noexcept = default;
28+
};
29+
30+
/// @brief 测量函数执行时间的辅助模板
31+
template <typename Func> double measure_ms(Func&& func, int iterations) {
32+
auto start = std::chrono::high_resolution_clock::now();
33+
for (int i = 0; i < iterations; ++i) {
34+
func();
35+
}
36+
auto end = std::chrono::high_resolution_clock::now();
37+
return std::chrono::duration<double, std::milli>(end - start).count();
38+
}
39+
40+
int main() {
41+
constexpr std::size_t kDataSize = 1000000; // 100 万个 double,约 8MB
42+
constexpr int kIterations = 100;
43+
44+
std::cout << "数据大小: " << kDataSize * sizeof(double) / 1024 << " KB\n";
45+
std::cout << "迭代次数: " << kIterations << "\n\n";
46+
47+
// 测试 0:仅构造(baseline)
48+
auto construct_time = measure_ms(
49+
[&]() {
50+
BigData source(kDataSize);
51+
(void)source;
52+
},
53+
kIterations);
54+
55+
std::cout << "仅构造(baseline): " << construct_time << " ms\n";
56+
57+
// 测试 1:构造 + 拷贝
58+
auto copy_time = measure_ms(
59+
[&]() {
60+
BigData source(kDataSize);
61+
BigData copy = source; // 拷贝构造
62+
(void)copy;
63+
},
64+
kIterations);
65+
66+
std::cout << "构造 + 拷贝: " << copy_time << " ms\n";
67+
68+
// 测试 2:构造 + 移动
69+
auto move_time = measure_ms(
70+
[&]() {
71+
BigData source(kDataSize);
72+
BigData moved = std::move(source); // 移动构造
73+
(void)moved;
74+
},
75+
kIterations);
76+
77+
std::cout << "构造 + 移动: " << move_time << " ms\n\n";
78+
79+
// 分离出纯粹的拷贝/移动耗时
80+
double actual_copy = copy_time - construct_time;
81+
double actual_move = move_time - construct_time;
82+
83+
std::cout << "=== 分离后的实际耗时 ===\n";
84+
std::cout << "纯拷贝: " << actual_copy << " ms\n";
85+
std::cout << "纯移动: " << actual_move << " ms\n";
86+
87+
if (actual_move > 0.01) {
88+
std::cout << "加速比: " << actual_copy / actual_move << "x\n";
89+
} else {
90+
std::cout << "移动耗时在测量噪声范围内(接近零)\n";
91+
}
92+
93+
return 0;
94+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// noexcept_sort_vs_realloc.cpp -- 验证 noexcept 对 std::sort 和 vector 扩容的影响
2+
// Standard: C++17
3+
// 对应文档:vol2-modern-features/ch00-move-semantics/05-move-in-practice.md
4+
//
5+
// 核心结论:
6+
// - std::sort 只用移动,不区分移动操作是否 noexcept(两种类型都是 拷贝=0)
7+
// - vector 扩容通过 move_if_noexcept 选择策略:noexcept 类型用移动,
8+
// 非 noexcept 类型退回拷贝(强异常安全)
9+
#include <algorithm>
10+
#include <iostream>
11+
#include <string>
12+
#include <vector>
13+
14+
// 移动操作带 noexcept 的类型
15+
struct NoexceptType {
16+
std::string payload;
17+
int value;
18+
19+
static int copy_count;
20+
static int move_count;
21+
22+
NoexceptType(int v) : payload("data"), value(v) {}
23+
NoexceptType(const NoexceptType& o) : payload(o.payload + "_c"), value(o.value) {
24+
++copy_count;
25+
}
26+
NoexceptType(NoexceptType&& o) noexcept : payload(std::move(o.payload)), value(o.value) {
27+
o.payload = "(moved)";
28+
++move_count;
29+
}
30+
NoexceptType& operator=(NoexceptType&& o) noexcept {
31+
payload = std::move(o.payload);
32+
value = o.value;
33+
o.payload = "(moved)";
34+
++move_count;
35+
return *this;
36+
}
37+
NoexceptType& operator=(const NoexceptType& o) {
38+
payload = o.payload + "_c";
39+
value = o.value;
40+
++copy_count;
41+
return *this;
42+
}
43+
bool operator<(const NoexceptType& rhs) const { return value < rhs.value; }
44+
static void reset() {
45+
copy_count = 0;
46+
move_count = 0;
47+
}
48+
};
49+
50+
// ThrowingType 与 NoexceptType 完全相同,唯一区别是移动操作没有 noexcept
51+
struct ThrowingType {
52+
std::string payload;
53+
int value;
54+
55+
static int copy_count;
56+
static int move_count;
57+
58+
ThrowingType(int v) : payload("data"), value(v) {}
59+
ThrowingType(const ThrowingType& o) : payload(o.payload + "_c"), value(o.value) {
60+
++copy_count;
61+
}
62+
ThrowingType(ThrowingType&& o) // 注意:没有 noexcept
63+
: payload(std::move(o.payload)), value(o.value) {
64+
o.payload = "(moved)";
65+
++move_count;
66+
}
67+
ThrowingType& operator=(ThrowingType&& o) // 注意:没有 noexcept
68+
{
69+
payload = std::move(o.payload);
70+
value = o.value;
71+
o.payload = "(moved)";
72+
++move_count;
73+
return *this;
74+
}
75+
ThrowingType& operator=(const ThrowingType& o) {
76+
payload = o.payload + "_c";
77+
value = o.value;
78+
++copy_count;
79+
return *this;
80+
}
81+
bool operator<(const ThrowingType& rhs) const { return value < rhs.value; }
82+
static void reset() {
83+
copy_count = 0;
84+
move_count = 0;
85+
}
86+
};
87+
88+
int NoexceptType::copy_count = 0;
89+
int NoexceptType::move_count = 0;
90+
int ThrowingType::copy_count = 0;
91+
int ThrowingType::move_count = 0;
92+
93+
int main() {
94+
const int kCount = 5000;
95+
96+
// Test 1: std::sort(noexcept 类型)
97+
{
98+
std::vector<NoexceptType> vec;
99+
vec.reserve(kCount);
100+
for (int i = 0; i < kCount; ++i)
101+
vec.emplace_back(kCount - i);
102+
NoexceptType::reset();
103+
std::sort(vec.begin(), vec.end());
104+
std::cout << "noexcept sort: 拷贝=" << NoexceptType::copy_count
105+
<< " 移动=" << NoexceptType::move_count << "\n";
106+
}
107+
108+
// Test 2: std::sort(非 noexcept 类型)
109+
{
110+
std::vector<ThrowingType> vec;
111+
vec.reserve(kCount);
112+
for (int i = 0; i < kCount; ++i)
113+
vec.emplace_back(kCount - i);
114+
ThrowingType::reset();
115+
std::sort(vec.begin(), vec.end());
116+
std::cout << "非noexcept sort: 拷贝=" << ThrowingType::copy_count
117+
<< " 移动=" << ThrowingType::move_count << "\n";
118+
}
119+
120+
std::cout << "\n";
121+
122+
// Test 3: vector 扩容(noexcept 类型,无 reserve)
123+
{
124+
NoexceptType::reset();
125+
std::vector<NoexceptType> vec;
126+
for (int i = 0; i < 200; ++i)
127+
vec.emplace_back(i);
128+
std::cout << "noexcept 扩容: 拷贝=" << NoexceptType::copy_count
129+
<< " 移动=" << NoexceptType::move_count << "\n";
130+
}
131+
132+
// Test 4: vector 扩容(非 noexcept 类型,无 reserve)
133+
// ThrowingType 的扩容会退回拷贝,因为 move_if_noexcept 不选中它的移动
134+
{
135+
ThrowingType::reset();
136+
std::vector<ThrowingType> vec;
137+
for (int i = 0; i < 200; ++i)
138+
vec.emplace_back(i);
139+
std::cout << "非noexcept扩容: 拷贝=" << ThrowingType::copy_count
140+
<< " 移动=" << ThrowingType::move_count << "\n";
141+
}
142+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// push_back_emplace.cpp -- push_back(拷贝/移动) vs emplace_back(原位构造) 对比
2+
// Standard: C++17
3+
// 对应文档:vol2-modern-features/ch00-move-semantics/05-move-in-practice.md
4+
//
5+
// 核心结论:
6+
// - push_back(lvalue) 触发拷贝构造
7+
// - push_back(std::move(rvalue)) 触发移动构造
8+
// - emplace_back(构造参数) 连移动都省了,直接原位构造
9+
#include <iostream>
10+
#include <string>
11+
#include <vector>
12+
13+
class Heavy {
14+
std::string name_;
15+
std::vector<int> data_;
16+
17+
public:
18+
explicit Heavy(std::string name, std::size_t n) : name_(std::move(name)), data_(n, 42) {
19+
std::cout << " [" << name_ << "] 构造,数据量: " << data_.size() << "\n";
20+
}
21+
22+
Heavy(const Heavy& other) : name_(other.name_ + "_copy"), data_(other.data_) {
23+
std::cout << " [" << name_ << "] 拷贝构造\n";
24+
}
25+
26+
Heavy(Heavy&& other) noexcept : name_(std::move(other.name_)), data_(std::move(other.data_)) {
27+
other.name_ = "(moved-from)";
28+
std::cout << " [" << name_ << "] 移动构造\n";
29+
}
30+
31+
~Heavy() { std::cout << " [" << name_ << "] 析构,数据量: " << data_.size() << "\n"; }
32+
33+
const std::string& name() const { return name_; }
34+
std::size_t data_size() const { return data_.size(); }
35+
};
36+
37+
int main() {
38+
std::vector<Heavy> items;
39+
items.reserve(4);
40+
41+
std::cout << "=== push_back 左值(拷贝)===\n";
42+
Heavy h1("Alpha", 10000);
43+
items.push_back(h1);
44+
45+
std::cout << "\n=== push_back 右值(移动)===\n";
46+
Heavy h2("Beta", 10000);
47+
items.push_back(std::move(h2));
48+
49+
std::cout << "\n=== emplace_back 原位构造 ===\n";
50+
items.emplace_back("Gamma", 10000);
51+
52+
std::cout << "\n=== 程序结束 ===\n";
53+
return 0;
54+
}

0 commit comments

Comments
 (0)