Skip to content

Commit b99155e

Browse files
feat(vol3): add passages for vector/string/char8_t (#52)
* feat(vol3): add passages for vector/string/char8_t 正文系列(卷三 01/02/03): - 01-vector-deep-dive:三指针内部表示、扩容摊还常数与三家增长因子 (libstdc++/libc++ 2×、MSVC 1.5×)、迭代器失效全景、move_if_noexcept 异常安全、C++20 constexpr vector (P0784R7+P1004R2)、erase/erase_if (P1209R0) - 02-string-memory-deep-dive:SSO vs COW 历史 (N2668)、SSO 阈值、 C++23 resize_and_overwrite (P1072R10) - 03-char8-t-utf8:C++20 char8_t (P0482R6) 类型变更、两个迁移坑、 C++23 P2513R4 DR 配套: - 3 个 OnlineCompilerDemo 源 (15_vector/16_string/17_char8_t) - 卷三 index.md 分区:正文系列 + 「待重写文章」分区 站点改进(顺带修复): - mermaid 渲染:节点标签 padding/line-height + foreignObject 不裁剪, 解决 CJK 多行文字被节点框遮挡 - dev-only vite plugin:dev 下服务 code/examples(含路径穿越防护), 让 OnlineCompilerDemo 在 dev 也能运行,build 不受影响 * fix: ci issue
1 parent 2b8d44a commit b99155e

11 files changed

Lines changed: 800 additions & 10 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
---
2020

2121
<!-- COVERAGE_START -->
22-
![English Coverage](https://img.shields.io/badge/en_coverage-100%25-green.svg) 420/420 docs translated
22+
![English Coverage](https://img.shields.io/badge/en_coverage-99%25-green.svg) 420/423 docs translated
2323
<!-- COVERAGE_END -->
2424

2525
## 这是什么项目
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Standard: C++20
2+
// vector 实现层深入:扩容追踪 / 迭代器失效 / move_if_noexcept / constexpr vector / erase_if
3+
#include <iostream>
4+
#include <vector>
5+
6+
// ---- move_if_noexcept 观察:move 构造故意不标 noexcept ----
7+
class Tracked {
8+
public:
9+
int id;
10+
static int move_count;
11+
static int copy_count;
12+
13+
explicit Tracked(int i) : id(i) {}
14+
Tracked(const Tracked& o) : id(o.id) { ++copy_count; }
15+
Tracked(Tracked&& o) noexcept(false) : id(o.id) { ++move_count; }
16+
};
17+
int Tracked::move_count = 0;
18+
int Tracked::copy_count = 0;
19+
20+
// ---- constexpr vector:编译期当临时工作区,只返回标量 ----
21+
// transient allocation:常量求值期分配的内存必须在该求值内释放,
22+
// 所以不能定义持久 constexpr vector 变量,只能让缓冲在函数内自然析构。
23+
constexpr int sum_first_n(int n) {
24+
std::vector<int> v;
25+
for (int i = 0; i < n; ++i) {
26+
v.push_back(i + 1);
27+
}
28+
int sum = 0;
29+
for (int x : v) {
30+
sum += x;
31+
}
32+
return sum;
33+
}
34+
static_assert(sum_first_n(100) == 5050); // 全程编译期完成
35+
36+
int main() {
37+
std::cout << "== 扩容追踪(push_back 17 次)==\n";
38+
std::vector<int> v;
39+
for (int i = 0; i < 17; ++i) {
40+
std::size_t cap_before = v.capacity();
41+
v.push_back(i);
42+
if (v.capacity() != cap_before) {
43+
std::cout << "push " << i << ": capacity " << cap_before << " -> " << v.capacity()
44+
<< '\n';
45+
}
46+
}
47+
48+
std::cout << "\n== 迭代器失效 ==\n";
49+
std::vector<int> w{1, 2, 3};
50+
w.reserve(3);
51+
const int* p = &w[1];
52+
w.push_back(4); // 余量足够,不扩容 → 指针仍有效
53+
std::cout << "push_back 不扩容,指针有效? " << (p == &w[1]) << '\n';
54+
w.reserve(100); // 超过 capacity → 换缓冲 → 指针失效
55+
std::cout << "reserve 超容量后,指针有效? " << (p == &w[1]) << '\n';
56+
57+
std::cout << "\n== move_if_noexcept ==\n";
58+
std::vector<Tracked> t;
59+
t.reserve(2);
60+
t.emplace_back(1);
61+
t.emplace_back(2);
62+
t.emplace_back(3); // 触发扩容
63+
std::cout << "扩容时 moves=" << Tracked::move_count << " copies=" << Tracked::copy_count
64+
<< "(noexcept(false) → 扩容倾向 copy;改成 noexcept 则变 move)\n";
65+
66+
std::cout << "\n== constexpr vector (C++20) ==\n";
67+
std::cout << "sum_first_n(100) = " << sum_first_n(100) << "(编译期 static_assert 已验证)\n";
68+
69+
std::cout << "\n== erase_if (C++20) ==\n";
70+
std::vector<int> e{1, 2, 3, 4, 5, 6};
71+
std::size_t removed = std::erase_if(e, [](int x) { return x % 2 == 0; });
72+
std::cout << "removed=" << removed << " left:";
73+
for (int x : e) {
74+
std::cout << ' ' << x;
75+
}
76+
std::cout << '\n';
77+
return 0;
78+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Standard: C++23
2+
// string 内存深入:SSO 观察 + resize_and_overwrite 缓冲复用
3+
#include <algorithm>
4+
#include <cstring>
5+
#include <iostream>
6+
#include <string>
7+
8+
// 判断 string 的 data() 是否落在对象内联缓冲里(SSO 的标志)
9+
bool points_inside_object(const std::string& s) {
10+
const char* obj = reinterpret_cast<const char*>(&s);
11+
return s.data() >= obj && s.data() < obj + sizeof(std::string);
12+
}
13+
14+
// 模拟一个 C API:向 buf 最多写 n 字节,返回实际写入数
15+
std::size_t fake_read(char* buf, std::size_t n) {
16+
static const char msg[] = "hello";
17+
std::size_t len = std::min(n, sizeof(msg) - 1);
18+
std::memcpy(buf, msg, len);
19+
return len;
20+
}
21+
22+
int main() {
23+
std::cout << "sizeof(std::string) = " << sizeof(std::string) << '\n';
24+
25+
std::string short_s = "hi"; // 很可能走 SSO
26+
std::string long_s(64, 'x'); // 超过 SSO 阈值,出堆
27+
std::cout << "short_s.data() 在对象内? " << points_inside_object(short_s) << "(SSO)\n";
28+
std::cout << "long_s.data() 在对象内? " << points_inside_object(long_s) << "(出堆)\n";
29+
30+
std::cout << "\n== resize() 旧写法:先把 64 字符全部值初始化(清零),再截断 ==\n";
31+
std::string old_buf;
32+
old_buf.resize(64);
33+
std::size_t got = fake_read(old_buf.data(), old_buf.size());
34+
old_buf.resize(got);
35+
std::cout << "old: '" << old_buf << "' (len=" << old_buf.size() << ")\n";
36+
37+
std::cout << "\n== resize_and_overwrite (C++23):不清零多余字符,回调报告实际长度 ==\n";
38+
std::string buf;
39+
buf.resize_and_overwrite(64, [](char* p, std::size_t n) noexcept {
40+
return fake_read(p, n); // 只写实际字节,返回新长度(r ∈ [0, n])
41+
});
42+
std::cout << "new: '" << buf << "' (len=" << buf.size() << ")\n";
43+
return 0;
44+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Standard: C++20
2+
// char8_t 两坑(用注释封印,取消注释即编译失败)+ 两种正确写法
3+
#include <iostream>
4+
#include <string>
5+
6+
// 坑一:u8"" 在 C++20 起类型变为 const char8_t[],不再隐式转 const char*
7+
// const char* p = u8"text"; // ill-formed since C++20
8+
9+
// 坑二:标准库显式 =delete 了 char8_t / const char8_t* 的 ostream 插入重载
10+
// std::cout << u8"text"; // ill-formed since C++20
11+
// std::cout << u8'z'; // ill-formed since C++20
12+
13+
// 正确写法之一:显式逐字节转换(内容不变,仅切换指针类型视角)
14+
void print_as_char(const char* s) {
15+
std::cout << s << '\n';
16+
}
17+
18+
// 正确写法之二:用 std::u8string 类型安全地持有 UTF-8,并自定义打印
19+
std::ostream& operator<<(std::ostream& os, const std::u8string& s) {
20+
return os << reinterpret_cast<const char*>(s.data());
21+
}
22+
23+
int main() {
24+
// 路线 A:把 u8 字面量当 const char* 用(适合喂给只认窄字符的旧接口)
25+
print_as_char(reinterpret_cast<const char*>(u8"text"));
26+
27+
// 路线 B:u8string 全程保持 UTF-8 类型,打印时再转
28+
std::u8string u8s = u8"UTF-8 text";
29+
std::cout << u8s << '\n';
30+
31+
std::cout << "__cpp_char8_t = " << __cpp_char8_t << '\n';
32+
return 0;
33+
}

0 commit comments

Comments
 (0)