268
268
< ul class ="nav flex-column ">
269
269
</ ul >
270
270
</ li >
271
- < li class ="nav-item " data-bs-level ="2 "> < a href ="#_7 " class ="nav-link "> 提前返回</ a >
271
+ < li class ="nav-item " data-bs-level ="2 "> < a href ="#_7 " class ="nav-link "> 继承构造函数</ a >
272
+ < ul class ="nav flex-column ">
273
+ </ ul >
274
+ </ li >
275
+ < li class ="nav-item " data-bs-level ="2 "> < a href ="#_8 " class ="nav-link "> 提前返回</ a >
272
276
< ul class ="nav flex-column ">
273
277
</ ul >
274
278
</ li >
320
324
< ul class ="nav flex-column ">
321
325
</ ul >
322
326
</ li >
323
- < li class ="nav-item " data-bs-level ="2 "> < a href ="#_8 " class ="nav-link "> 读取整个文件到字符串</ a >
327
+ < li class ="nav-item " data-bs-level ="2 "> < a href ="#_9 " class ="nav-link "> 读取整个文件到字符串</ a >
324
328
< ul class ="nav flex-column ">
325
329
</ ul >
326
330
</ li >
327
- < li class ="nav-item " data-bs-level ="2 "> < a href ="#_9 " class ="nav-link "> 逐行读取文本文件</ a >
331
+ < li class ="nav-item " data-bs-level ="2 "> < a href ="#_10 " class ="nav-link "> 逐行读取文本文件</ a >
328
332
< ul class ="nav flex-column ">
329
333
</ ul >
330
334
</ li >
331
- < li class ="nav-item " data-bs-level ="2 "> < a href ="#_10 " class ="nav-link "> 字符串切片</ a >
335
+ < li class ="nav-item " data-bs-level ="2 "> < a href ="#_11 " class ="nav-link "> 字符串切片</ a >
332
336
< ul class ="nav flex-column ">
333
337
</ ul >
334
338
</ li >
344
348
< ul class ="nav flex-column ">
345
349
</ ul >
346
350
</ li >
347
- < li class ="nav-item " data-bs-level ="2 "> < a href ="#_11 " class ="nav-link "> 智能指针防止大对象移动</ a >
351
+ < li class ="nav-item " data-bs-level ="2 "> < a href ="#_12 " class ="nav-link "> 智能指针防止大对象移动</ a >
348
352
< ul class ="nav flex-column ">
349
353
</ ul >
350
354
</ li >
368
372
< ul class ="nav flex-column ">
369
373
</ ul >
370
374
</ li >
371
- < li class ="nav-item " data-bs-level ="2 "> < a href ="#_13 " class ="nav-link "> 救命!为什么我的全局函数不能作为函数对象?</ a >
375
+ < li class ="nav-item " data-bs-level ="2 "> < a href ="#_14 " class ="nav-link "> 救命!为什么我的全局函数不能作为函数对象?</ a >
372
376
< ul class ="nav flex-column ">
373
377
</ ul >
374
378
</ li >
384
388
< ul class ="nav flex-column ">
385
389
</ ul >
386
390
</ li >
387
- < li class ="nav-item " data-bs-level ="2 "> < a href ="#_14 " class ="nav-link "> 函数默认参数求值的位置是调用者</ a >
391
+ < li class ="nav-item " data-bs-level ="2 "> < a href ="#_15 " class ="nav-link "> 函数默认参数求值的位置是调用者</ a >
388
392
< ul class ="nav flex-column ">
389
393
</ ul >
390
394
</ li >
391
395
< li class ="nav-item " data-bs-level ="2 "> < a href ="#locale-utf8 " class ="nav-link "> 设置 locale 为 .utf8</ a >
392
396
< ul class ="nav flex-column ">
393
397
</ ul >
394
398
</ li >
395
- < li class ="nav-item " data-bs-level ="2 "> < a href ="#_15 " class ="nav-link "> 花括号实现安全的类型转换检查</ a >
399
+ < li class ="nav-item " data-bs-level ="2 "> < a href ="#_16 " class ="nav-link "> 花括号实现安全的类型转换检查</ a >
396
400
< ul class ="nav flex-column ">
397
401
</ ul >
398
402
</ li >
404
408
< ul class ="nav flex-column ">
405
409
</ ul >
406
410
</ li >
407
- < li class ="nav-item " data-bs-level ="2 "> < a href ="#_16 " class ="nav-link "> 函数默认参数求值的位置是调用者</ a >
411
+ < li class ="nav-item " data-bs-level ="2 "> < a href ="#_17 " class ="nav-link "> 函数默认参数求值的位置是调用者</ a >
408
412
< ul class ="nav flex-column ">
409
413
</ ul >
410
414
</ li >
411
- < li class ="nav-item " data-bs-level ="2 "> < a href ="#_17 " class ="nav-link "> 花括号实现安全的类型转换检查</ a >
415
+ < li class ="nav-item " data-bs-level ="2 "> < a href ="#_18 " class ="nav-link "> 花括号实现安全的类型转换检查</ a >
412
416
< ul class ="nav flex-column ">
413
417
</ ul >
414
418
</ li >
415
- < li class ="nav-item " data-bs-level ="2 "> < a href ="#_18 " class ="nav-link "> 临时右值转左值</ a >
419
+ < li class ="nav-item " data-bs-level ="2 "> < a href ="#_19 " class ="nav-link "> 临时右值转左值</ a >
416
420
< ul class ="nav flex-column ">
417
421
</ ul >
418
422
</ li >
452
456
< ul class ="nav flex-column ">
453
457
</ ul >
454
458
</ li >
455
- < li class ="nav-item " data-bs-level ="2 "> < a href ="#_19 " class ="nav-link "> 多线程通信应基于队列,而不是共享全局变量</ a >
459
+ < li class ="nav-item " data-bs-level ="2 "> < a href ="#_20 " class ="nav-link "> 多线程通信应基于队列,而不是共享全局变量</ a >
456
460
< ul class ="nav flex-column ">
457
461
</ ul >
458
462
</ li >
@@ -485,7 +489,8 @@ <h1 id="c">应知应会 C++ 小技巧</h1>
485
489
< li > < a href ="#_4 "> 别再 [] 啦!</ a > </ li >
486
490
< li > < a href ="#_5 "> 别再写构造函数啦!</ a > </ li >
487
491
< li > < a href ="#_6 "> 别再写拷贝构造函数啦!</ a > </ li >
488
- < li > < a href ="#_7 "> 提前返回</ a > </ li >
492
+ < li > < a href ="#_7 "> 继承构造函数</ a > </ li >
493
+ < li > < a href ="#_8 "> 提前返回</ a > </ li >
489
494
< li > < a href ="#lambda "> 立即调用的 Lambda</ a > </ li >
490
495
< li > < a href ="#lambda_1 "> Lambda 复用代码</ a > </ li >
491
496
< li > < a href ="#inline "> 类内静态成员 inline</ a > </ li >
@@ -498,35 +503,35 @@ <h1 id="c">应知应会 C++ 小技巧</h1>
498
503
< li > < a href ="#c_1 "> C++ 随机数的正确生成方式</ a > </ li >
499
504
< li > < a href ="#const "> const 居然应该后置…</ a > </ li >
500
505
< li > < a href ="#auto "> 函数参数也可以 auto</ a > </ li >
501
- < li > < a href ="#_8 "> 读取整个文件到字符串</ a > </ li >
502
- < li > < a href ="#_9 "> 逐行读取文本文件</ a > </ li >
503
- < li > < a href ="#_10 "> 字符串切片</ a > </ li >
506
+ < li > < a href ="#_9 "> 读取整个文件到字符串</ a > </ li >
507
+ < li > < a href ="#_10 "> 逐行读取文本文件</ a > </ li >
508
+ < li > < a href ="#_11 "> 字符串切片</ a > </ li >
504
509
< li > < a href ="#cout-endl "> cout 不需要 endl</ a > </ li >
505
510
< li > < a href ="#cout "> 多线程中 cout 出现乱序?</ a > </ li >
506
511
< li > < a href ="#cerr-cout "> cerr 与 cout 的抉择</ a > </ li >
507
- < li > < a href ="#_11 "> 智能指针防止大对象移动</ a > </ li >
512
+ < li > < a href ="#_12 "> 智能指针防止大对象移动</ a > </ li >
508
513
< li > < a href ="#optional "> optional 实现延迟初始化</ a > </ li >
509
514
< li > < a href ="#if-auto-while-auto "> if-auto 与 while-auto</ a > </ li >
510
515
< li > < a href ="#bind-lambda "> bind 是历史糟粕,应该由 Lambda 表达式取代</ a > < ul >
511
516
< li > < a href ="#bind "> bind 的历史</ a > </ li >
512
517
< li > < a href ="#thread "> thread 膝盖中箭</ a > </ li >
513
- < li > < a href ="#_12 "> 举个绑定随机数生成器例子</ a > </ li >
518
+ < li > < a href ="#_13 "> 举个绑定随机数生成器例子</ a > </ li >
514
519
</ ul >
515
520
</ li >
516
521
< li > < a href ="#forward-fwd "> forward 迷惑性地不好用,建议改用 FWD 宏</ a > </ li >
517
522
< li > < a href ="#bind-lambda-bind_front "> bind 绑定成员函数是陋习,改用 lambda 或 bind_front</ a > </ li >
518
- < li > < a href ="#_13 "> 救命!为什么我的全局函数不能作为函数对象?</ a > </ li >
523
+ < li > < a href ="#_14 "> 救命!为什么我的全局函数不能作为函数对象?</ a > </ li >
519
524
< li > < a href ="#map-any "> map + any 外挂属性</ a > </ li >
520
525
< li > < a href ="#shared_ptr-deleter "> 自定义 shared_ptr 的 deleter</ a > </ li >
521
526
< li > < a href ="#check_cuda "> CHECK_CUDA 类错误检测宏</ a > </ li >
522
- < li > < a href ="#_14 "> 函数默认参数求值的位置是调用者</ a > </ li >
527
+ < li > < a href ="#_15 "> 函数默认参数求值的位置是调用者</ a > </ li >
523
528
< li > < a href ="#locale-utf8 "> 设置 locale 为 .utf8</ a > </ li >
524
- < li > < a href ="#_15 "> 花括号实现安全的类型转换检查</ a > </ li >
529
+ < li > < a href ="#_16 "> 花括号实现安全的类型转换检查</ a > </ li >
525
530
< li > < a href ="#this "> 成员函数针对 this 的移动重载</ a > </ li >
526
531
< li > < a href ="#check_cuda_1 "> CHECK_CUDA 类错误检测宏</ a > </ li >
527
- < li > < a href ="#_16 "> 函数默认参数求值的位置是调用者</ a > </ li >
528
- < li > < a href ="#_17 "> 花括号实现安全的类型转换检查</ a > </ li >
529
- < li > < a href ="#_18 "> 临时右值转左值</ a > </ li >
532
+ < li > < a href ="#_17 "> 函数默认参数求值的位置是调用者</ a > </ li >
533
+ < li > < a href ="#_18 "> 花括号实现安全的类型转换检查</ a > </ li >
534
+ < li > < a href ="#_19 "> 临时右值转左值</ a > </ li >
530
535
< li > < a href ="#ostringstream "> ostringstream 格式化字符串</ a > </ li >
531
536
< li > < a href ="#adl "> ADL 机制实现静态多态</ a > </ li >
532
537
< li > < a href ="#shared_from_this "> shared_from_this</ a > </ li >
@@ -536,7 +541,7 @@ <h1 id="c">应知应会 C++ 小技巧</h1>
536
541
< li > < a href ="#bit-field "> 位域(bit-field)</ a > </ li >
537
542
< li > < a href ="#vector-unordered_map-lru-cache "> vector + unordered_map = LRU cache</ a > </ li >
538
543
< li > < a href ="#lambda-unique_ptr-function "> Lambda 捕获 unique_ptr 导致 function 报错怎么办</ a > </ li >
539
- < li > < a href ="#_19 "> 多线程通信应基于队列,而不是共享全局变量</ a > </ li >
544
+ < li > < a href ="#_20 "> 多线程通信应基于队列,而不是共享全局变量</ a > </ li >
540
545
< li > < a href ="#raii-finally "> RAII 的 finally 帮手类</ a > </ li >
541
546
< li > < a href ="#swap-mutex "> swap 缩小 mutex 区间代价</ a > </ li >
542
547
< li > < a href ="#namespace "> namespace 别名</ a > </ li >
@@ -756,7 +761,61 @@ <h2 id="_6">别再写拷贝构造函数啦!</h2>
756
761
< p > 总之,很多 C++ 教材把拷贝/移动构造函数过于夸大,搞得好像每个类都需要自己定义一样。</ p >
757
762
< p > 实际上,只有在“自己实现容器”的情况下,才需要自定义拷贝构造函数。可是谁会整天手搓容器?</ p >
758
763
< p > 大多数情况下,我们只需要在类里面存 vector、string 等封装好的容器,编译器默认生成的拷贝构造函数会自动调用他们的拷贝构造函数,用户只需专注于业务逻辑即可,不需要操心底层细节。</ p >
759
- < h2 id ="_7 "> 提前返回</ h2 >
764
+ < p > 对于持有资源的 RAII 类,我们都会直接删除其拷贝构造函数和拷贝赋值函数:</ p >
765
+ < pre > < code class ="language-cpp "> struct RAIIHandle {
766
+ int handle;
767
+ RAIIHandle() {
768
+ handle = CreateObject();
769
+ }
770
+ RAIIHandle(RAIIHandle const &) = delete;
771
+ RAIIHandle &operator=(RAIIHandle const &) = delete;
772
+ RAIIHandle() {
773
+ DeleteObject(handle);
774
+ }
775
+ };
776
+ </ code > </ pre >
777
+ < h2 id ="_7 "> 继承构造函数</ h2 >
778
+ < p > C++ 特色:子类不会自动继承父类的构造函数!(除非父类的构造函数是没有参数的默认构造函数)</ p >
779
+ < pre > < code class ="language-cpp "> struct Parent {
780
+ Parent(int age, const char *name) { ... }
781
+ void parent_func() { ... }
782
+ };
783
+
784
+ struct Child : Parent {
785
+ void child_func() { ... }
786
+ };
787
+
788
+ Child child(23, "peng"); // 错误!Child 没有构造函数!
789
+ </ code > </ pre >
790
+ < p > 可以在子类里面写 < code > using 父类::父类</ code > ,就能自动继承父类所有的构造函数了。</ p >
791
+ < pre > < code class ="language-cpp "> struct Parent {
792
+ Parent(int age, const char *name) { ... }
793
+ void parent_func() { ... }
794
+ };
795
+
796
+ struct Child : Parent {
797
+ using Parent::Parent; // 加上这一行!
798
+ void child_func() { ... }
799
+ };
800
+
801
+ Child child(23, "peng"); // 编译通过,自动调用到父类的构造函数 Parent(int, const char *)
802
+ </ code > </ pre >
803
+ < p > 在 C++98 中,没有 using 的这个语法,只能自己定义一个构造函数,然后使用“委任构造”的语法转发所有参数给父类,非常繁琐。</ p >
804
+ < pre > < code class ="language-cpp "> struct Parent {
805
+ Parent(int age, const char *name) { ... }
806
+ void parent_func() { ... }
807
+ };
808
+
809
+ struct Child : Parent {
810
+ Child(int age, const char *name)
811
+ : Parent(age, name)
812
+ { ... }
813
+ void child_func() { ... }
814
+ };
815
+
816
+ Child child(23, "peng"); // 编译通过,调用到子类的构造函数后转发到父类
817
+ </ code > </ pre >
818
+ < h2 id ="_8 "> 提前返回</ h2 >
760
819
< pre > < code class ="language-cpp "> void babysitter(Baby *baby) {
761
820
if (!baby->is_alive()) {
762
821
puts("宝宝已经去世了");
@@ -1174,7 +1233,7 @@ <h2 id="auto">函数参数也可以 auto</h2>
1174
1233
return x * x;
1175
1234
}
1176
1235
</ code > </ pre >
1177
- < h2 id ="_8 "> 读取整个文件到字符串</ h2 >
1236
+ < h2 id ="_9 "> 读取整个文件到字符串</ h2 >
1178
1237
< pre > < code class ="language-cpp "> std::string file_get_content(std::string const &filename) {
1179
1238
std::ifstream ifs(filename, std::ios::in | std::ios::binary);
1180
1239
std::istreambuf_iterator<char> iit(ifs), iite;
@@ -1191,14 +1250,14 @@ <h2 id="_8">读取整个文件到字符串</h2>
1191
1250
< blockquote >
1192
1251
< 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 >
1193
1252
</ blockquote >
1194
- < h2 id ="_9 "> 逐行读取文本文件</ h2 >
1253
+ < h2 id ="_10 "> 逐行读取文本文件</ h2 >
1195
1254
< pre > < code class ="language-cpp "> std::ifstream fin("test.txt");
1196
1255
std::string line;
1197
1256
while (std::getline(fin, line)) {
1198
1257
std::cout << "读取到一行:" << line << '\n';
1199
1258
}
1200
1259
</ code > </ pre >
1201
- < h2 id ="_10 "> 字符串切片</ h2 >
1260
+ < h2 id ="_11 "> 字符串切片</ h2 >
1202
1261
< pre > < code class ="language-cpp "> #include <sstream>
1203
1262
#include <string>
1204
1263
#include <vector>
@@ -1341,7 +1400,7 @@ <h2 id="cerr-cout">cerr 与 cout 的抉择</h2>
1341
1400
1 3 5 7
1342
1401
11 13 17 19
1343
1402
</ code > </ pre >
1344
- < h2 id ="_11 "> 智能指针防止大对象移动</ h2 >
1403
+ < h2 id ="_12 "> 智能指针防止大对象移动</ h2 >
1345
1404
< p > 我们说一个类型大,有两种情况。</ p >
1346
1405
< ol >
1347
1406
< li > 类本身很大:例如 array</ li >
@@ -1619,7 +1678,7 @@ <h3 id="thread">thread 膝盖中箭</h3>
1619
1678
t.join();
1620
1679
printf("%d\n", x); // 42
1621
1680
</ code > </ pre >
1622
- < h3 id ="_12 "> 举个绑定随机数生成器例子</ h3 >
1681
+ < h3 id ="_13 "> 举个绑定随机数生成器例子</ h3 >
1623
1682
< p > bind 写法:</ p >
1624
1683
< pre > < code class ="language-cpp "> std::mt19937 gen(seed);
1625
1684
std::uniform_real_distribution<double> uni(0, 1);
@@ -1831,7 +1890,7 @@ <h2 id="bind-lambda-bind_front">bind 绑定成员函数是陋习,改用 lambda
1831
1890
auto memfn = BIND(world, this); // 小彭老师的 BIND 宏,C++14 起可用
1832
1891
</ code > </ pre >
1833
1892
< p > 你更喜欢哪一种呢?</ p >
1834
- < h2 id ="_13 "> 救命!为什么我的全局函数不能作为函数对象?</ h2 >
1893
+ < h2 id ="_14 "> 救命!为什么我的全局函数不能作为函数对象?</ h2 >
1835
1894
< p > 当你的全局函数是模板函数,或带有重载的函数时:</ p >
1836
1895
< pre > < code class ="language-cpp "> template <class T>
1837
1896
T square(T const t) {
@@ -1903,14 +1962,14 @@ <h2 id="map-any">map + any 外挂属性</h2>
1903
1962
< p > TODO</ p >
1904
1963
< h2 id ="shared_ptr-deleter "> 自定义 shared_ptr 的 deleter</ h2 >
1905
1964
< h2 id ="check_cuda "> CHECK_CUDA 类错误检测宏</ h2 >
1906
- < h2 id ="_14 "> 函数默认参数求值的位置是调用者</ h2 >
1965
+ < h2 id ="_15 "> 函数默认参数求值的位置是调用者</ h2 >
1907
1966
< h2 id ="locale-utf8 "> 设置 locale 为 .utf8</ h2 >
1908
- < h2 id ="_15 "> 花括号实现安全的类型转换检查</ h2 >
1967
+ < h2 id ="_16 "> 花括号实现安全的类型转换检查</ h2 >
1909
1968
< h2 id ="this "> 成员函数针对 this 的移动重载</ h2 >
1910
1969
< h2 id ="check_cuda_1 "> CHECK_CUDA 类错误检测宏</ h2 >
1911
- < h2 id ="_16 "> 函数默认参数求值的位置是调用者</ h2 >
1912
- < h2 id ="_17 "> 花括号实现安全的类型转换检查</ h2 >
1913
- < h2 id ="_18 "> 临时右值转左值</ h2 >
1970
+ < h2 id ="_17 "> 函数默认参数求值的位置是调用者</ h2 >
1971
+ < h2 id ="_18 "> 花括号实现安全的类型转换检查</ h2 >
1972
+ < h2 id ="_19 "> 临时右值转左值</ h2 >
1914
1973
< p > C++ 有个特性:支持纯右值(prvalue)隐式转换成 const 的左值引用。</ p >
1915
1974
< p > 翻译:< code > int &&</ code > 可以自动转换成 < code > int const &</ code > 。</ p >
1916
1975
< pre > < code class ="language-cpp "> void func(int const &i);
@@ -1995,7 +2054,7 @@ <h2 id="bit-field">位域(bit-field)</h2>
1995
2054
</ code > </ pre >
1996
2055
< h2 id ="vector-unordered_map-lru-cache "> vector + unordered_map = LRU cache</ h2 >
1997
2056
< h2 id ="lambda-unique_ptr-function "> Lambda 捕获 unique_ptr 导致 function 报错怎么办</ h2 >
1998
- < h2 id ="_19 "> 多线程通信应基于队列,而不是共享全局变量</ h2 >
2057
+ < h2 id ="_20 "> 多线程通信应基于队列,而不是共享全局变量</ h2 >
1999
2058
< h2 id ="raii-finally "> RAII 的 finally 帮手类</ h2 >
2000
2059
< h2 id ="swap-mutex "> swap 缩小 mutex 区间代价</ h2 >
2001
2060
< h2 id ="namespace "> namespace 别名</ h2 > </ div >
0 commit comments