Skip to content

Commit e57cdf8

Browse files
authored
Update cpp_tricks.md
1 parent 15eaa4f commit e57cdf8

File tree

1 file changed

+97
-65
lines changed

1 file changed

+97
-65
lines changed

docs/cpp_tricks.md

+97-65
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,14 @@ std::wstring utf8_to_wstring(std::string const &s) {
8787
例如 `14 / 5`,本来应该得到 2.8。但是因为 C 语言的除法返回 `int`,结果会自动向下取整,导致得到 2。
8888

8989
```cpp
90-
int a, b;
91-
int c = a / b;
90+
int a = 14, b = 5;
91+
int c = a / b; // c = 14 / 5 = 3
9292
```
9393

9494
等价于
9595

9696
```cpp
97-
int c = floor((float)a / b);
97+
int c = floor((float)a / b); // c = floor(2.8) = 3
9898
```
9999

100100
如果 `a` 除以 `b` 除不尽,那么会找到比他大的第一个整数作为结果,这就是**地板除 (floor div)**
@@ -135,27 +135,55 @@ int c = (a + b - 1) / b;
135135
(10 + 5 - 1) / 5 = (10 + 4) / 5 = 14 / 5 = 2
136136
```
137137

138-
这就是 C 语言中实现天花板除的业界公认标准方式
138+
这就是 C 语言中实现天花板除的业界公认方式
139139

140-
## 读取整个文件到字符串
140+
## 别再 `[]` 啦!
141+
142+
你知道吗?在 map 中使用 `[]` 查找元素,如果不存在,会自动创建一个默认值。这个特性有时很方便,但如果你不小心写错了,就会在 map 中创建一个多余的默认元素。
141143

142144
```cpp
143-
std::string file_get_content(std::string const &filename) {
144-
std::ifstream ifs(filename, std::ios::in | std::ios::binary);
145-
std::istreambuf_iterator<char> iit(ifs), iite;
146-
std::string content(iit, iite);
147-
return content;
148-
}
145+
map<string, int> table;
146+
table["小彭老师"] = 24;
149147

150-
void file_put_content(std::string const &filename, std::string const &content) {
151-
std::ofstream ofs(filename, std::ios::out | std::ios::binary);
152-
ofs << content;
148+
cout << table["侯捷老师"];
149+
```
150+
151+
table 中明明没有 "侯捷老师" 这个元素,但由于 `[]` 的特性,他会默认返回一个 0,不会爆任何错误!
152+
153+
改用更安全的 `at()` 函数,当查询的元素不存在时,会抛出异常,方便你调试:
154+
155+
```cpp
156+
map<string, int> table;
157+
table.at("小彭老师") = 24;
158+
159+
cout << table.at("侯捷老师"); // 抛出异常
160+
```
161+
162+
`[]` 真正的用途是“写入新元素”时,如果元素不存在,他可以自动帮你创建一个默认值,供你以引用的方式赋值进去。
163+
164+
检测元素是否存在可以用 `count`
165+
166+
```cpp
167+
if (table.count("小彭老师")) {
168+
return table.at("小彭老师");
169+
} else {
170+
return 0;
153171
}
154172
```
155173

156-
这样就可以把整个文件读取到内存中,进行处理后再写回文件。
174+
即使你想要默认值 0 这一特性,`count` + `at` 也比 `[]` 更好,因为 `[]` 的默认值是会对 table 做破坏性修改的,这导致 `[]` 需要 `map` 的声明不为 `const`
157175

158-
> {{ icon.tip }} 推荐用 `std::ios::binary` 选项打开二进制文件,否则字符串中出现 `'\n'` 时,会被 MSVC 标准库自动转换成 `'\r\n'` 来写入,妨碍我们跨平台。
176+
```cpp
177+
map<string, int> table;
178+
return table["小彭老师"]; // 如果"小彭老师"这一键不存在,会创建"小彭老师"并设为默认值 0
179+
```
180+
181+
```cpp
182+
const map<string, int> table;
183+
return table["小彭老师"]; // 编译失败![] 需要非 const 的 map 对象,因为他会破坏性修改
184+
```
185+
186+
> {{ icon.tip }} 更多 map 知识请看我们的 [map 专题课](stl_map.md)
159187
160188
## 别再写构造函数啦!
161189

@@ -414,54 +442,6 @@ struct Class {
414442
};
415443
```
416444

417-
## 别再 `[]` 啦!
418-
419-
你知道吗?在 map 中使用 `[]` 查找元素,如果不存在,会自动创建一个默认值。这个特性有时很方便,但如果你不小心写错了,就会在 map 中创建一个多余的默认元素。
420-
421-
```cpp
422-
map<string, int> table;
423-
table["小彭老师"] = 24;
424-
425-
cout << table["侯捷老师"];
426-
```
427-
428-
table 中明明没有 "侯捷老师" 这个元素,但由于 `[]` 的特性,他会默认返回一个 0,不会爆任何错误!
429-
430-
改用更安全的 `at()` 函数,当查询的元素不存在时,会抛出异常,方便你调试:
431-
432-
```cpp
433-
map<string, int> table;
434-
table.at("小彭老师") = 24;
435-
436-
cout << table.at("侯捷老师"); // 抛出异常
437-
```
438-
439-
`[]` 真正的用途是“写入新元素”时,如果元素不存在,他可以自动帮你创建一个默认值,供你以引用的方式赋值进去。
440-
441-
检测元素是否存在可以用 `count`
442-
443-
```cpp
444-
if (table.count("小彭老师")) {
445-
return table.at("小彭老师");
446-
} else {
447-
return 0;
448-
}
449-
```
450-
451-
即使你想要默认值 0 这一特性,`count` + `at` 也比 `[]` 更好,因为 `[]` 的默认值是会对 table 做破坏性修改的,这导致 `[]` 需要 `map` 的声明不为 `const`
452-
453-
```cpp
454-
map<string, int> table;
455-
return table["小彭老师"]; // 如果"小彭老师"这一键不存在,会创建"小彭老师"并设为默认值 0
456-
```
457-
458-
```cpp
459-
const map<string, int> table;
460-
return table["小彭老师"]; // 编译失败![] 需要非 const 的 map 对象,因为他会破坏性修改
461-
```
462-
463-
> {{ icon.tip }} 更多 map 知识请看我们的 [map 专题课](stl_map.md)
464-
465445
## 别再 make_pair 啦!
466446

467447
```cpp
@@ -900,6 +880,56 @@ T square(T x) {
900880
}
901881
```
902882
883+
## 读取整个文件到字符串
884+
885+
```cpp
886+
std::string file_get_content(std::string const &filename) {
887+
std::ifstream ifs(filename, std::ios::in | std::ios::binary);
888+
std::istreambuf_iterator<char> iit(ifs), iite;
889+
std::string content(iit, iite);
890+
return content;
891+
}
892+
893+
void file_put_content(std::string const &filename, std::string const &content) {
894+
std::ofstream ofs(filename, std::ios::out | std::ios::binary);
895+
ofs << content;
896+
}
897+
```
898+
899+
这样就可以把整个文件读取到内存中,很方便地进行处理后再写回文件。
900+
901+
> {{ icon.tip }} 推荐用 `std::ios::binary` 选项打开二进制文件,否则字符串中出现 `'\n'` 时,会被 MSVC 标准库自动转换成 `'\r\n'` 来写入,妨碍我们跨平台。
902+
903+
## 逐行读取文本文件
904+
905+
```cpp
906+
std::ifstream fin("test.txt");
907+
std::string line;
908+
while (std::getline(fin, line)) {
909+
std::cout << "读取到一行:" << line << '\n';
910+
}
911+
```
912+
913+
## 字符串切片
914+
915+
```cpp
916+
#include <sstream>
917+
#include <string>
918+
#include <vector>
919+
920+
std::vector<std::string> split_str(std::string const &str, char ch) {
921+
std::stringstream ss(str);
922+
std::string line;
923+
std::vector<std::string> res;
924+
while (std::getline(ss, line, ch)) {
925+
res.push_back(std::move(line));
926+
}
927+
return res;
928+
}
929+
930+
auto res = split_str("hello world", ' '); // res = {"hello", "world"}
931+
```
932+
903933
## cout 不需要 endl
904934

905935
```cpp
@@ -1952,6 +1982,8 @@ int b = f >> 4; // 0x2
19521982

19531983
## 多线程通信应基于队列,而不是共享全局变量
19541984

1955-
## RAII 的 finally
1985+
## RAII 的 finally 帮手类
19561986

19571987
## swap 缩小 mutex 区间代价
1988+
1989+
## namespace 别名

0 commit comments

Comments
 (0)