Skip to content

Commit 8ac8c2f

Browse files
committed
deploy: 15eaa4f
1 parent 6e783de commit 8ac8c2f

File tree

4 files changed

+147
-81
lines changed

4 files changed

+147
-81
lines changed

cpp_tricks/index.html

+81-46
Original file line numberDiff line numberDiff line change
@@ -248,23 +248,27 @@
248248
<ul class="nav flex-column">
249249
</ul>
250250
</li>
251-
<li class="nav-item" data-bs-level="2"><a href="#raii" class="nav-link">RAII 地分配一段内存空间</a>
251+
<li class="nav-item" data-bs-level="2"><a href="#_2" class="nav-link">安全地分配一段内存空间</a>
252252
<ul class="nav flex-column">
253253
</ul>
254254
</li>
255-
<li class="nav-item" data-bs-level="2"><a href="#_2" class="nav-link">读取整个文件到字符串</a>
255+
<li class="nav-item" data-bs-level="2"><a href="#_3" class="nav-link">地板除与天花板除</a>
256256
<ul class="nav flex-column">
257257
</ul>
258258
</li>
259-
<li class="nav-item" data-bs-level="2"><a href="#_3" class="nav-link">别再写构造函数啦!</a>
259+
<li class="nav-item" data-bs-level="2"><a href="#_4" class="nav-link">读取整个文件到字符串</a>
260260
<ul class="nav flex-column">
261261
</ul>
262262
</li>
263-
<li class="nav-item" data-bs-level="2"><a href="#_4" class="nav-link">别再写拷贝构造函数啦</a>
263+
<li class="nav-item" data-bs-level="2"><a href="#_5" class="nav-link">别再写构造函数啦</a>
264264
<ul class="nav flex-column">
265265
</ul>
266266
</li>
267-
<li class="nav-item" data-bs-level="2"><a href="#_5" class="nav-link">提前返回</a>
267+
<li class="nav-item" data-bs-level="2"><a href="#_6" class="nav-link">别再写拷贝构造函数啦!</a>
268+
<ul class="nav flex-column">
269+
</ul>
270+
</li>
271+
<li class="nav-item" data-bs-level="2"><a href="#_7" class="nav-link">提前返回</a>
268272
<ul class="nav flex-column">
269273
</ul>
270274
</li>
@@ -280,7 +284,7 @@
280284
<ul class="nav flex-column">
281285
</ul>
282286
</li>
283-
<li class="nav-item" data-bs-level="2"><a href="#_6" class="nav-link">别再 [] 啦!</a>
287+
<li class="nav-item" data-bs-level="2"><a href="#_8" class="nav-link">别再 [] 啦!</a>
284288
<ul class="nav flex-column">
285289
</ul>
286290
</li>
@@ -332,7 +336,7 @@
332336
<ul class="nav flex-column">
333337
</ul>
334338
</li>
335-
<li class="nav-item" data-bs-level="2"><a href="#_7" class="nav-link">智能指针防止大对象移动</a>
339+
<li class="nav-item" data-bs-level="2"><a href="#_9" class="nav-link">智能指针防止大对象移动</a>
336340
<ul class="nav flex-column">
337341
</ul>
338342
</li>
@@ -356,7 +360,7 @@
356360
<ul class="nav flex-column">
357361
</ul>
358362
</li>
359-
<li class="nav-item" data-bs-level="2"><a href="#_9" class="nav-link">救命!为什么我的全局函数不能作为函数对象?</a>
363+
<li class="nav-item" data-bs-level="2"><a href="#_11" class="nav-link">救命!为什么我的全局函数不能作为函数对象?</a>
360364
<ul class="nav flex-column">
361365
</ul>
362366
</li>
@@ -372,15 +376,15 @@
372376
<ul class="nav flex-column">
373377
</ul>
374378
</li>
375-
<li class="nav-item" data-bs-level="2"><a href="#_10" class="nav-link">函数默认参数求值的位置是调用者</a>
379+
<li class="nav-item" data-bs-level="2"><a href="#_12" class="nav-link">函数默认参数求值的位置是调用者</a>
376380
<ul class="nav flex-column">
377381
</ul>
378382
</li>
379383
<li class="nav-item" data-bs-level="2"><a href="#locale-utf8" class="nav-link">设置 locale 为 .utf8</a>
380384
<ul class="nav flex-column">
381385
</ul>
382386
</li>
383-
<li class="nav-item" data-bs-level="2"><a href="#_11" class="nav-link">花括号实现安全的类型转换检查</a>
387+
<li class="nav-item" data-bs-level="2"><a href="#_13" class="nav-link">花括号实现安全的类型转换检查</a>
384388
<ul class="nav flex-column">
385389
</ul>
386390
</li>
@@ -392,15 +396,15 @@
392396
<ul class="nav flex-column">
393397
</ul>
394398
</li>
395-
<li class="nav-item" data-bs-level="2"><a href="#_12" class="nav-link">函数默认参数求值的位置是调用者</a>
399+
<li class="nav-item" data-bs-level="2"><a href="#_14" class="nav-link">函数默认参数求值的位置是调用者</a>
396400
<ul class="nav flex-column">
397401
</ul>
398402
</li>
399-
<li class="nav-item" data-bs-level="2"><a href="#_13" class="nav-link">花括号实现安全的类型转换检查</a>
403+
<li class="nav-item" data-bs-level="2"><a href="#_15" class="nav-link">花括号实现安全的类型转换检查</a>
400404
<ul class="nav flex-column">
401405
</ul>
402406
</li>
403-
<li class="nav-item" data-bs-level="2"><a href="#_14" class="nav-link">临时右值转左值</a>
407+
<li class="nav-item" data-bs-level="2"><a href="#_16" class="nav-link">临时右值转左值</a>
404408
<ul class="nav flex-column">
405409
</ul>
406410
</li>
@@ -440,7 +444,7 @@
440444
<ul class="nav flex-column">
441445
</ul>
442446
</li>
443-
<li class="nav-item" data-bs-level="2"><a href="#_15" class="nav-link">多线程通信应基于队列,而不是共享全局变量</a>
447+
<li class="nav-item" data-bs-level="2"><a href="#_17" class="nav-link">多线程通信应基于队列,而不是共享全局变量</a>
444448
<ul class="nav flex-column">
445449
</ul>
446450
</li>
@@ -464,15 +468,16 @@ <h1 id="c">应知应会 C++ 小技巧</h1>
464468
<ul>
465469
<li><a href="#c">应知应会 C++ 小技巧</a><ul>
466470
<li><a href="#_1">交换两个变量</a></li>
467-
<li><a href="#raii">RAII 地分配一段内存空间</a></li>
468-
<li><a href="#_2">读取整个文件到字符串</a></li>
469-
<li><a href="#_3">别再写构造函数啦!</a></li>
470-
<li><a href="#_4">别再写拷贝构造函数啦!</a></li>
471-
<li><a href="#_5">提前返回</a></li>
471+
<li><a href="#_2">安全地分配一段内存空间</a></li>
472+
<li><a href="#_3">地板除与天花板除</a></li>
473+
<li><a href="#_4">读取整个文件到字符串</a></li>
474+
<li><a href="#_5">别再写构造函数啦!</a></li>
475+
<li><a href="#_6">别再写拷贝构造函数啦!</a></li>
476+
<li><a href="#_7">提前返回</a></li>
472477
<li><a href="#lambda">立即调用的 Lambda</a></li>
473478
<li><a href="#lambda_1">Lambda 复用代码</a></li>
474479
<li><a href="#inline">类内静态成员 inline</a></li>
475-
<li><a href="#_6">别再 [] 啦!</a></li>
480+
<li><a href="#_8">别再 [] 啦!</a></li>
476481
<li><a href="#make_pair">别再 make_pair 啦!</a></li>
477482
<li><a href="#insert">insert 不会替换现有值哦</a></li>
478483
<li><a href="#map">一边遍历 map,一边删除?</a></li>
@@ -485,29 +490,29 @@ <h1 id="c">应知应会 C++ 小技巧</h1>
485490
<li><a href="#cout-endl">cout 不需要 endl</a></li>
486491
<li><a href="#cout">多线程中 cout 出现乱序?</a></li>
487492
<li><a href="#cerr-cout">cerr 与 cout 的抉择</a></li>
488-
<li><a href="#_7">智能指针防止大对象移动</a></li>
493+
<li><a href="#_9">智能指针防止大对象移动</a></li>
489494
<li><a href="#optional">optional 实现延迟初始化</a></li>
490495
<li><a href="#if-auto-while-auto">if-auto 与 while-auto</a></li>
491496
<li><a href="#bind-lambda">bind 是历史糟粕,应该由 Lambda 表达式取代</a><ul>
492497
<li><a href="#bind">bind 的历史</a></li>
493498
<li><a href="#thread">thread 膝盖中箭</a></li>
494-
<li><a href="#_8">举个绑定随机数生成器例子</a></li>
499+
<li><a href="#_10">举个绑定随机数生成器例子</a></li>
495500
</ul>
496501
</li>
497502
<li><a href="#forward-fwd">forward 迷惑性地不好用,建议改用 FWD 宏</a></li>
498503
<li><a href="#bind-lambda-bind_front">bind 绑定成员函数是陋习,改用 lambda 或 bind_front</a></li>
499-
<li><a href="#_9">救命!为什么我的全局函数不能作为函数对象?</a></li>
504+
<li><a href="#_11">救命!为什么我的全局函数不能作为函数对象?</a></li>
500505
<li><a href="#map-any">map + any 外挂属性</a></li>
501506
<li><a href="#shared_ptr-deleter">自定义 shared_ptr 的 deleter</a></li>
502507
<li><a href="#check_cuda">CHECK_CUDA 类错误检测宏</a></li>
503-
<li><a href="#_10">函数默认参数求值的位置是调用者</a></li>
508+
<li><a href="#_12">函数默认参数求值的位置是调用者</a></li>
504509
<li><a href="#locale-utf8">设置 locale 为 .utf8</a></li>
505-
<li><a href="#_11">花括号实现安全的类型转换检查</a></li>
510+
<li><a href="#_13">花括号实现安全的类型转换检查</a></li>
506511
<li><a href="#this">成员函数针对 this 的移动重载</a></li>
507512
<li><a href="#check_cuda_1">CHECK_CUDA 类错误检测宏</a></li>
508-
<li><a href="#_12">函数默认参数求值的位置是调用者</a></li>
509-
<li><a href="#_13">花括号实现安全的类型转换检查</a></li>
510-
<li><a href="#_14">临时右值转左值</a></li>
513+
<li><a href="#_14">函数默认参数求值的位置是调用者</a></li>
514+
<li><a href="#_15">花括号实现安全的类型转换检查</a></li>
515+
<li><a href="#_16">临时右值转左值</a></li>
511516
<li><a href="#ostringstream">ostringstream 格式化字符串</a></li>
512517
<li><a href="#adl">ADL 机制实现静态多态</a></li>
513518
<li><a href="#shared_from_this">shared_from_this</a></li>
@@ -517,7 +522,7 @@ <h1 id="c">应知应会 C++ 小技巧</h1>
517522
<li><a href="#bit-field">位域(bit-field)</a></li>
518523
<li><a href="#vector-unordered_map-lru-cache">vector + unordered_map = LRU cache</a></li>
519524
<li><a href="#lambda-unique_ptr-function">Lambda 捕获 unique_ptr 导致 function 报错怎么办</a></li>
520-
<li><a href="#_15">多线程通信应基于队列,而不是共享全局变量</a></li>
525+
<li><a href="#_17">多线程通信应基于队列,而不是共享全局变量</a></li>
521526
<li><a href="#raii-finally">RAII 的 finally</a></li>
522527
<li><a href="#swap-mutex">swap 缩小 mutex 区间代价</a></li>
523528
</ul>
@@ -540,8 +545,8 @@ <h2 id="_1">交换两个变量</h2>
540545
<blockquote>
541546
<p><img src="../img/bulb.png" height="30px" width="auto" style="margin: 0; border: none"/> 只需要 <code>#include &lt;utility&gt;</code> 就可以使用!</p>
542547
</blockquote>
543-
<h2 id="raii">RAII 地分配一段内存空间</h2>
544-
<p>小彭老师:不要让我看到 new 和 delete。</p>
548+
<h2 id="_2">安全地分配一段内存空间</h2>
549+
<p>小彭老师:不要出现 new 和 delete,不安全</p>
545550
<p>同学:我想要<strong>分配一段内存空间</strong>,你不让我 new,我还能怎么办呢?</p>
546551
<pre><code class="language-cpp">char *mem = new char[1024]; // 同学想要 1024 字节的缓冲区
547552
read(1, mem, 1024); // 用于供 C 语言的读文件函数使用
@@ -579,7 +584,37 @@ <h2 id="raii">RAII 地分配一段内存空间</h2>
579584
return ws;
580585
}
581586
</code></pre>
582-
<h2 id="_2">读取整个文件到字符串</h2>
587+
<h2 id="_3">地板除与天花板除</h2>
588+
<p>众所周知,C语言中 <code>int</code> 相除 <code>/</code>,得到的结果也是 <code>int</code>,如果除法产生了余数,那么只会保留整数部分。</p>
589+
<p>例如 <code>14 / 5</code>,本来应该得到 2.8。但是因为 C 语言的除法返回 <code>int</code>,结果会自动向下取整,导致得到 2。</p>
590+
<pre><code class="language-cpp">int a, b;
591+
int c = a / b;
592+
</code></pre>
593+
<p>等价于</p>
594+
<pre><code class="language-cpp">int c = floor((float)a / b);
595+
</code></pre>
596+
<p>如果 <code>a</code> 除以 <code>b</code> 除不尽,那么会找到比他大的第一个整数作为结果,这就是<strong>地板除 (floor div)</strong></p>
597+
<p>C 语言默认的就是地板除。</p>
598+
<p>如果我想要的是向上取整,该怎么写?</p>
599+
<p>最原始的写法是先转成浮点数来除,然后ceil函数向上取整:</p>
600+
<pre><code class="language-cpp">int c = ceil((float)a / b);
601+
</code></pre>
602+
<p>但是浮点数不仅低效,还有糟糕的浮点数精度误差!对于很大的整数(大于 <span class="arithmatex"><span class="MathJax_Preview">2^{23}</span><script type="math/tex">2^{23}</script></span>)会产生错误的结果。</p>
603+
<p>更合理的写法是先把 <code>a</code> 加上 <code>b - 1</code>,然后再下取整地除以 <code>b</code></p>
604+
<pre><code class="language-cpp">int c = (a + b - 1) / b;
605+
</code></pre>
606+
<p>这样就能产生一个向上取整的除法了。</p>
607+
<p>如果 <code>a</code> 除以 <code>b</code> 除不尽,那么会找到比他大的第一个整数作为结果,这就是<strong>天花板除 (ceil div)</strong></p>
608+
<p>试试看:14 除以 5,应该得到 2.8;如果用地板除,会得到 2;如果用天花板除,会得到 3。</p>
609+
<pre><code class="language-cpp">14 / 5 = 2
610+
(14 + 5 - 1) / 5 = (14 + 4) / 5 = 18 / 5 = 3
611+
</code></pre>
612+
<p>试试看:10 除以 5,应该得到 2;那么无论是地板除还是天花板除,都应该得到 2。</p>
613+
<pre><code class="language-cpp">10 / 5 = 2
614+
(10 + 5 - 1) / 5 = (10 + 4) / 5 = 14 / 5 = 2
615+
</code></pre>
616+
<p>这就是 C 语言中实现天花板除的业界公认标准方式。</p>
617+
<h2 id="_4">读取整个文件到字符串</h2>
583618
<pre><code class="language-cpp">std::string file_get_content(std::string const &amp;filename) {
584619
std::ifstream ifs(filename, std::ios::in | std::ios::binary);
585620
std::istreambuf_iterator&lt;char&gt; iit(ifs), iite;
@@ -596,7 +631,7 @@ <h2 id="_2">读取整个文件到字符串</h2>
596631
<blockquote>
597632
<p><img src="../img/bulb.png" height="30px" width="auto" style="margin: 0; border: none"/> 推荐用 <code>std::ios::binary</code> 选项打开二进制文件,否则字符串中出现 <code>'\n'</code> 时,会被 MSVC 标准库自动转换成 <code>'\r\n'</code> 来写入,妨碍我们跨平台。</p>
598633
</blockquote>
599-
<h2 id="_3">别再写构造函数啦!</h2>
634+
<h2 id="_5">别再写构造函数啦!</h2>
600635
<pre><code class="language-cpp">// C++98
601636
struct Student {
602637
string name;
@@ -619,7 +654,7 @@ <h2 id="_3">别再写构造函数啦!</h2>
619654

620655
Student stu{&quot;小彭老师&quot;, 24, 123};
621656
</code></pre>
622-
<p>这被称为<strong>聚合初始化</strong> (aggergate initialize)。只要你的类没有自定义构造函数,没有 private 成员,都可以用 <code>{}</code> 聚合初始化。</p>
657+
<p>这被称为<strong>聚合初始化</strong> (aggregate initialize)。只要你的类没有自定义构造函数,没有 private 成员,都可以用 <code>{}</code> 聚合初始化。</p>
623658
<p>好消息:C++20 中,聚合初始化也支持 <code>()</code> 了,用起来就和传统的 C++98 构造函数一样!</p>
624659
<pre><code class="language-cpp">// C++20
625660
Student stu(&quot;小彭老师&quot;, 24, 123);
@@ -645,7 +680,7 @@ <h2 id="_3">别再写构造函数啦!</h2>
645680
<pre><code class="language-cpp">Student stu{.name = &quot;小彭老师&quot;, .age = 24, .id = 9999};
646681
Student stu{.name = &quot;小彭老师&quot;, .id = 9999, .age = 24};
647682
</code></pre>
648-
<h2 id="_4">别再写拷贝构造函数啦!</h2>
683+
<h2 id="_6">别再写拷贝构造函数啦!</h2>
649684
<p>只有当你需要有“自定义钩子逻辑”的时候,才需要自定义构造函数。</p>
650685
<pre><code class="language-cpp">struct Student {
651686
string name;
@@ -691,7 +726,7 @@ <h2 id="_4">别再写拷贝构造函数啦!</h2>
691726
<p>总之,很多 C++ 教材把拷贝/移动构造函数过于夸大,搞得好像每个类都需要自己定义一样。</p>
692727
<p>实际上,只有在“自己实现容器”的情况下,才需要自定义拷贝构造函数。可是谁会整天手搓容器?</p>
693728
<p>大多数情况下,我们只需要在类里面存 vector、string 等封装好的容器,编译器默认生成的拷贝构造函数会自动调用他们的拷贝构造函数,用户只需专注于业务逻辑即可,不需要操心底层细节。</p>
694-
<h2 id="_5">提前返回</h2>
729+
<h2 id="_7">提前返回</h2>
695730
<pre><code class="language-cpp">void babysitter(Baby *baby) {
696731
if (!baby-&gt;is_alive()) {
697732
puts(&quot;宝宝已经去世了&quot;);
@@ -796,7 +831,7 @@ <h2 id="inline">类内静态成员 inline</h2>
796831
inline static int member;
797832
};
798833
</code></pre>
799-
<h2 id="_6">别再 <code>[]</code> 啦!</h2>
834+
<h2 id="_8">别再 <code>[]</code> 啦!</h2>
800835
<p>你知道吗?在 map 中使用 <code>[]</code> 查找元素,如果不存在,会自动创建一个默认值。这个特性有时很方便,但如果你不小心写错了,就会在 map 中创建一个多余的默认元素。</p>
801836
<pre><code class="language-cpp">map&lt;string, int&gt; table;
802837
table[&quot;小彭老师&quot;] = 24;
@@ -1249,7 +1284,7 @@ <h2 id="cerr-cout">cerr 与 cout 的抉择</h2>
12491284
1 3 5 7
12501285
11 13 17 19
12511286
</code></pre>
1252-
<h2 id="_7">智能指针防止大对象移动</h2>
1287+
<h2 id="_9">智能指针防止大对象移动</h2>
12531288
<p>我们说一个类型大,有两种情况。</p>
12541289
<ol>
12551290
<li>类本身很大:例如 array</li>
@@ -1527,7 +1562,7 @@ <h3 id="thread">thread 膝盖中箭</h3>
15271562
t.join();
15281563
printf(&quot;%d\n&quot;, x); // 42
15291564
</code></pre>
1530-
<h3 id="_8">举个绑定随机数生成器例子</h3>
1565+
<h3 id="_10">举个绑定随机数生成器例子</h3>
15311566
<p>bind 写法:</p>
15321567
<pre><code class="language-cpp">std::mt19937 gen(seed);
15331568
std::uniform_real_distribution&lt;double&gt; uni(0, 1);
@@ -1739,7 +1774,7 @@ <h2 id="bind-lambda-bind_front">bind 绑定成员函数是陋习,改用 lambda
17391774
auto memfn = BIND(world, this); // 小彭老师的 BIND 宏,C++14 起可用
17401775
</code></pre>
17411776
<p>你更喜欢哪一种呢?</p>
1742-
<h2 id="_9">救命!为什么我的全局函数不能作为函数对象?</h2>
1777+
<h2 id="_11">救命!为什么我的全局函数不能作为函数对象?</h2>
17431778
<p>当你的全局函数是模板函数,或带有重载的函数时:</p>
17441779
<pre><code class="language-cpp">template &lt;class T&gt;
17451780
T square(T const t) {
@@ -1811,14 +1846,14 @@ <h2 id="map-any">map + any 外挂属性</h2>
18111846
<p>TODO</p>
18121847
<h2 id="shared_ptr-deleter">自定义 shared_ptr 的 deleter</h2>
18131848
<h2 id="check_cuda">CHECK_CUDA 类错误检测宏</h2>
1814-
<h2 id="_10">函数默认参数求值的位置是调用者</h2>
1849+
<h2 id="_12">函数默认参数求值的位置是调用者</h2>
18151850
<h2 id="locale-utf8">设置 locale 为 .utf8</h2>
1816-
<h2 id="_11">花括号实现安全的类型转换检查</h2>
1851+
<h2 id="_13">花括号实现安全的类型转换检查</h2>
18171852
<h2 id="this">成员函数针对 this 的移动重载</h2>
18181853
<h2 id="check_cuda_1">CHECK_CUDA 类错误检测宏</h2>
1819-
<h2 id="_12">函数默认参数求值的位置是调用者</h2>
1820-
<h2 id="_13">花括号实现安全的类型转换检查</h2>
1821-
<h2 id="_14">临时右值转左值</h2>
1854+
<h2 id="_14">函数默认参数求值的位置是调用者</h2>
1855+
<h2 id="_15">花括号实现安全的类型转换检查</h2>
1856+
<h2 id="_16">临时右值转左值</h2>
18221857
<p>C++ 有个特性:支持纯右值(prvalue)隐式转换成 const 的左值引用。</p>
18231858
<p>翻译:<code>int &amp;&amp;</code> 可以自动转换成 <code>int const &amp;</code></p>
18241859
<pre><code class="language-cpp">void func(int const &amp;i);
@@ -1903,7 +1938,7 @@ <h2 id="bit-field">位域(bit-field)</h2>
19031938
</code></pre>
19041939
<h2 id="vector-unordered_map-lru-cache">vector + unordered_map = LRU cache</h2>
19051940
<h2 id="lambda-unique_ptr-function">Lambda 捕获 unique_ptr 导致 function 报错怎么办</h2>
1906-
<h2 id="_15">多线程通信应基于队列,而不是共享全局变量</h2>
1941+
<h2 id="_17">多线程通信应基于队列,而不是共享全局变量</h2>
19071942
<h2 id="raii-finally">RAII 的 finally</h2>
19081943
<h2 id="swap-mutex">swap 缩小 mutex 区间代价</h2></div>
19091944
</div>

0 commit comments

Comments
 (0)