[
  {
    "path": ".gitignore",
    "content": "com.sh\nbazel-CPlusPlusThings\nbazel-bin\nbazel-out\nbazel-testlogs\nbazel-cplusplus_bazel\n.vscode"
  },
  {
    "path": "README.md",
    "content": "\n- [C++ 那些事](#c-那些事)\n    - [重磅项目](#重磅项目)\n    - [关于作者](#关于作者)\n    - [项目运行](#项目运行)\n      - [方式1: vscode + bazel](#方式1-vscode--bazel)\n      - [方式2: docker](#方式2-docker)\n      - [方式3: g++](#方式3-g)\n    - [视频学习本项目](#视频学习本项目)\n    - [飞书知识库](#飞书知识库)\n    - [基础进阶](#基础进阶)\n    - [实战系列](#实战系列)\n      - [10 日狂练](#10-日狂练)\n      - [重点实战练习](#重点实战练习)\n    - [C++2.0 新特性](#c20-新特性)\n      - [概况](#概况)\n      - [C++11 新特性](#c11-新特性)\n      - [C++14/17/20](#c141720)\n    - [设计模式](#设计模式)\n    - [STL 源码剖析](#stl-源码剖析)\n    - [并发编程](#并发编程)\n      - [C++ Concurrency in Action](#c-concurrency-in-action)\n      - [多线程与多进程](#多线程与多进程)\n        - [Threading In C++](#threading-in-c)\n    - [C++ 惯用法](#c-惯用法)\n        - [你最喜欢的 c++ 编程风格惯用法是什么?](#你最喜欢的-c-编程风格惯用法是什么)\n    - [学习课程](#学习课程)\n      - [极客时间《现代 C++ 实战 30 讲》](#极客时间现代-c-实战-30-讲)\n    - [工具篇](#工具篇)\n      - [容器快捷输出工具](#容器快捷输出工具)\n      - [像 Python 一样简单输出（Jupyter Notebook）](#像-python-一样简单输出jupyter-notebook)\n      - [观察编译过程变化](#观察编译过程变化)\n      - [C++ 的 Debug 工具 dbg-macro](#c-的-debug-工具-dbg-macro)\n      - [在 Linux 上的 Debug 工具 rr - 拥有回到过去的能力](#在-linux-上的-debug-工具-rr---拥有回到过去的能力)\n    - [拓展部分](#拓展部分)\n      - [一些问题](#一些问题)\n    - [贡献者](#贡献者)\n\n# C++ 那些事\n\n\n感谢各位对《C++ 那些事》的支持，现将内容也同步至B站，以视频的方式展示，可以打开网站直接阅读~欢迎大家 star、转发、PR。\n\n在线个人博客：[光城的实验室](https://light-city.github.io/)\n\n在线学习网站：[C++那些事](https://light-city.github.io/stories_things/)\n\n- 中文名：**C++ 那些事**\n- 英文名：**Stories About C Plus Plus**\n\n这是一个适合初学者从<u>**入门到进阶**</u>的仓库，解决了<u>**面试者与学习者**</u>想要<u>**深入 C++**</u> 及如何<u>**入坑 C++**</u> 的问题。除此之外，本仓库拓展了更加深入的源码分析，多线程并发等的知识，是一个比较全面的 C++ 学习从入门到进阶提升的仓库。\n\n\n### 重磅项目\n\n推出一系列重磅项目，带大家实战学习C++，配合本开源项目学习，成长飞快!\n\n直通点：[戳这里](./proj/README.md)\n\n### 关于作者\n\n公众号已开放两大入口，分别是专辑与菜单，可以直接在微信公众号上阅读《C++ 那些事》内容，配上该仓库的代码，将会非常的爽，建议大家关注一波。\n\n个人公众号：guangcity\n\n或者扫下面的二维码，欢迎大家反馈及 C++ 交流，本人在微信创建了 C++ 那些事交流群，优质的 C++ 资源交流区，期待大家关注上面公众号，点击公众号右下角->联系我，拉你入群。\n\n---\n\n>关注我\n\n如果觉得不错，关注我吧~\n\n<table>\n  <tbody>\n    <tr>\n      <th align=\"center\" style=\"height=200 width=\"200\">\n          <img src=\"./img/cpp.jpg\" height=\"200\" width=\"200\" ><br>\n          星球\n      </th>\n      <th align=\"center\" style=\"height=200 width=\"200\">\n          <img src=\"./img/wechat.jpg\" height=\"200\" width=\"200\" ><br>\n          微信公众号\n      </th>\n    </tr>\n  </tbody>\n</table>\n\n\n### 项目运行\n\n\n#### 方式1: vscode + bazel\n\n#### 方式2: docker\n\n新推出免开发环境，docker环境，大家可以pull下面镜像：\n\n```\ndocker pull xingfranics/cplusplusthings:latest\n```\n#### 方式3: g++\n\n\n### 视频学习本项目\n\n[第一期：Step By Step上手编译本项目](https://www.bilibili.com/video/BV1Rv4y1H7LB/?vd_source=bb6532dcd5b1d6b26125da900adb618e)\n\n[第二期：免开发环境docker](https://www.bilibili.com/video/BV1oz4y1a7Pu/?vd_source=bb6532dcd5b1d6b26125da900adb618e)\n\n[第三期：手把手共读HashTable，彻底搞懂C++ STL](https://www.bilibili.com/video/BV1o8411U7vy/?vd_source=bb6532dcd5b1d6b26125da900adb618e)\n\n[第四期：手把手共读STL之enable_shared_from_this](https://www.bilibili.com/video/BV1du4y1w7Mg/?spm_id_from=333.788&vd_source=bb6532dcd5b1d6b26125da900adb618e)\n\n[第五期：手把手共读STL之线程，从C++11 thread到C++20 jthread](https://www.bilibili.com/video/BV1DH4y1g7gS/?vd_source=bb6532dcd5b1d6b26125da900adb618e)\n\n[第六期：手把手共读STL之共读condition_variable、condition_variable_any](https://www.bilibili.com/video/BV13b421b7Am/?spm_id_from=333.999.0.0&vd_source=bb6532dcd5b1d6b26125da900adb618e)\n\n[第七期：手把手共读STL之Mutex](https://www.bilibili.com/video/BV1xm42157pq/?spm_id_from=333.999.0.0&vd_source=bb6532dcd5b1d6b26125da900adb618e)\n\n[第八期：手把手共读STL之RAII Lock](https://www.bilibili.com/video/BV1Ls421g7iq/?spm_id_from=333.788&vd_source=bb6532dcd5b1d6b26125da900adb618e)\n\n### 飞书知识库\n\n[互联网大厂面试实录](https://hmpy6adnp5.feishu.cn/docx/OitBdRB4KozIhTxQt7Ec7iFDnkc)\n\n[拿下offer之必备面经](https://hmpy6adnp5.feishu.cn/docx/B1aCdVTUgoyJGYxtWV7cdvgRnxv)\n\n\n### 基础进阶\n\n- [`const` 那些事](./basic_content/const)\n- [`static` 那些事](./basic_content/static)\n- [`this` 那些事](./basic_content/this)\n- [`inline` 那些事](./basic_content/inline)\n- [`sizeof` 那些事](./basic_content/sizeof)\n- [函数指针那些事](./basic_content/func_pointer)\n- [纯虚函数和抽象类那些事](./basic_content/abstract)\n- [`vptr_vtable` 那些事](./basic_content/vptr_vtable)\n- [`virtual` 那些事](./basic_content/virtual)\n- [`volatile` 那些事](./basic_content/volatile)\n- [`assert` 那些事](./basic_content/assert)\n- [位域那些事](./basic_content/bit)\n- [`extern` 那些事](./basic_content/extern)\n- [`struct` 那些事](./basic_content/struct)\n- [`struct` 与 `class` 那些事](./basic_content/struct_class)\n- [`union` 那些事](./basic_content/union)\n- [`c` 实现 `c++` 多态那些事](./basic_content/c_poly)\n- [`explicit` 那些事](./basic_content/explicit)\n- [`friend` 那些事](./basic_content/friend)\n- [`using` 那些事](./basic_content/using)\n- [`::` 那些事](./basic_content/maohao)\n- [`enum` 那些事](./basic_content/enum)\n- [`decltype` 那些事](./basic_content/decltype)\n- [引用与指针那些事](./basic_content/pointer_refer)\n- [宏那些事](./basic_content/macro)\n\n### 实战系列\n\n#### [10 日狂练](./practical_exercises/10_day_practice)\n\n- [day1-基本语法](practical_exercises/10_day_practice/day1)\n\n- [day2-递归、结构体、枚举、静态变量等](practical_exercises/10_day_practice/day2)\n\n- [day3-函数1](practical_exercises/10_day_practice/day3)\n\n- [day4-函数2](practical_exercises/10_day_practice/day4)\n\n- [day5-继承多态](practical_exercises/10_day_practice/day5)\n\n- [day6-虚函数与抽象类](practical_exercises/10_day_practice/day6)\n\n- [day7-运算符重载](practical_exercises/10_day_practice/day7)\n\n- [day8-模版与STL](practical_exercises/10_day_practice/day8)\n\n- [day9-异常](practical_exercises/10_day_practice/day9)\n\n- [day10-文件与流](practical_exercises/10_day_practice/day10)\n\n#### [重点实战练习](./practical_exercises/key_exercises/)\n\n- [中括号重载.cpp](./practical_exercises/key_exercises/bracket_overloading.cpp)\n\n- [时钟++运算符重载.cpp](./practical_exercises/key_exercises/clock.cpp)\n\n- [运算符重载之强制转换.cpp](./practical_exercises/key_exercises/operator_cast.cpp)\n\n- [重载圆括号的时钟.cpp](./practical_exercises/key_exercises/operator_circle.cpp)\n\n- [函数模板.cpp](./practical_exercises/key_exercises/func_temp.cpp)\n\n- [动态数组.cpp](./practical_exercises/key_exercises/array.cpp)\n\n- [字典插入与查找.cpp](./practical_exercises/key_exercises/map_insert_look.cpp)\n\n- [异常捕获.cpp](./practical_exercises/key_exercises/try.cpp)\n\n- [类模板之栈.cpp](./practical_exercises/key_exercises/stack.cpp)\n\n- [类模板特化之数组.cpp](./practical_exercises/key_exercises/array_template.cpp)\n\n- [继承与封装.cpp](./practical_exercises/key_exercises/override.cpp)\n\n- [读写文件综合题.cpp](./practical_exercises/key_exercises/read_file.cpp)\n\n- [输入输出运算符重载.cpp](./practical_exercises/key_exercises/io_operator_overload.cpp)\n\n- [输入输出重载.cpp](./practical_exercises/key_exercises/io_operator.cpp)\n\n- [输出格式.cpp](./practical_exercises/key_exercises/output.cpp)\n\n\n### [C++2.0 新特性](./cpp2.0)\n\n#### 概况\n\nC++2.0 是一个简称，意为「现代 C++」，包括 C++11/14/17/20。\n\n#### [C++11 新特性](./cpp2.0/cpp11)\n\n- [Variadic Templates](./cpp2.0/cpp11/variadic)\n\n- Spaces in Template Expressions\n\n  ```cpp\n  vector<list<int> > //ok in each C++ version\n  vector<list<int>> // before c++ 11 error error: ‘>>’ should be ‘> >’ within a nested template argument list,c++11后可以正常通过\n  ```\n\n- [nullptr and nullptr_t](./cpp2.0/cpp11/nullptr.cpp)\n\n- [Automatic Type Deduction with auto](./cpp2.0/cpp11/auto.cpp)\n\n- [Uniform Initialization ](./cpp2.0/cpp11/uniform_initialization.cpp)\n\n- [initializer_list](./cpp2.0/cpp11/initializer.cpp)\n\n- [explicit for ctors taking more than one argument](./cpp2.0/cpp11/explicit.cpp)\n\n- [range-based for statement](./cpp2.0/cpp11/auto.cpp)\n\n  ```cpp\n  for(decl:col) {\n      statement\n  }\n  ```\n\n- [=default,=delete](./cpp2.0/cpp11/default_delete.cpp)\n\n  如果你自行定义了一个 `ctor`, 那么编译器就不会给你一个 `default ctor`\n  如果强制加上 `=default`, 就可以重新获得并使用 `default ctor`.\n\n- Alias(化名) Template (template typedef)\n\n  - [alias.cpp](./cpp2.0/cpp11/alias.cpp)\n  - [template_template.cpp](./cpp2.0/cpp11/template_template.cpp)\n\n- [template template parameter](./cpp2.0/template_template.cpp)\n\n- [type alias](./cpp2.0/cpp11/type_alias.cpp)\n\n- [noexcept](./cpp2.0/cpp11/noexcept.cpp)\n\n- [override](./cpp2.0/cpp11/override.cpp)\n\n- [final](./cpp2.0/cpp11/final.cpp)\n\n- [decltype](./cpp2.0/cpp11/decltype.cpp)\n\n- [lambda](./cpp2.0/cpp11/lambda.cpp)\n\n- [Rvalue reference](./cpp2.0/cpp11/rvalue.cpp)\n\n- [move aware class](./cpp2.0/cpp11/move.cpp)\n\n- 容器-结构与分类\n\n  - (1) 序列式容器包括：`array`(C++2.0 新引入),`vector`,`deque`,`list`,`forward_list`(C++2.0 新引入)\n  - (2) 关联式容器包括：`set/multiset`,`map/multimap`\n  - (3) 无序容器(C++2.0 新引入,更换原先 `hash_xxx` 为 `unordered_xxx`)包括：`unordered_map/unordered_multimap,unordered_set/unordered_multiset`\n\n- [Hash Function](./cpp2.0/cpp11/hash.cpp)\n\n- [tuple](./cpp2.0/cpp11/tuple.cpp)\n\n  学习资料：https://www.bilibili.com/video/av51863195?from=search&seid=3610634846288253061\n\n#### C++14/17/20\n\n待更新...\n\n### 设计模式\n\n- [单例模式](./design_pattern/singleton)\n- [生产消费者模式](./design_pattern/producer_consumer)\n\n### [STL 源码剖析](./src_analysis/stl)\n\n**STL 源码剖析：gcc 4.9.1**\n\n- [array](./src_analysis/stl/array.md)\n- [deque](./src_analysis/stl/deque.md)\n- [queue and stack](./src_analysis/stl/queue_stack.md)\n- [list](./src_analysis/stl/list.md)\n- [vector](./src_analysis/stl/vector.md)\n- [typename](./src_analysis/stl/typename.md)\n- [traits](./src_analysis/stl/traits.md)\n- [iterator](./src_analysis/stl/iterator.md)\n- [谈谈 STL 设计之 EBO 优化](./src_analysis/stl/谈谈STL设计之EBO优化.md)\n- [rb_tree](./src_analysis/stl/rb_tree.md)\n- [set and multiset](./src_analysis/stl/set_multiset.md)\n- [map and multimap](./src_analysis/stl/map_multimap.md)\n- [hashtable](./src_analysis/stl/hashtable.md)\n- [myhashtable](./src_analysis/stl/myhashtable.md)\n- [unordered_map](./src_analysis/stl/unordered_map.md)\n\n### [并发编程](./concurrency)\n\n#### C++ Concurrency in Action\n\n- [第一章](./concurrency/concurrency_v1/chapter1)\n- [第二章](./concurrency/concurrency_v1/chapter2)\n\n学习资料：https://downdemo.gitbook.io/cpp-concurrency-in-action-2ed/\n\n#### 多线程与多进程\n\n##### Threading In C++\n\n- [介绍](./concurrency/Threading_In_CPlusPlus/1.thread)\n- [创建线程的五种类型](./concurrency/Threading_In_CPlusPlus/2.create_type)\n- [Join 与 Detachs](./concurrency/Threading_In_CPlusPlus/3.join_detach)\n- [mutex in C++ Threading](./concurrency/Threading_In_CPlusPlus/4.mutex)\n\n> 学习自：\n>\n> https://www.youtube.com/watch?v=eZ8yKZo-PGw&list=PLk6CEY9XxSIAeK-EAh3hB4fgNvYkYmghp&index=4\n\n### [C++ 惯用法](./codingStyleIdioms)\n\n##### 你最喜欢的 c++ 编程风格惯用法是什么?\n\n- [1.类初始化列表](./codingStyleIdioms/1_classInitializers)\n- [2.枚举类替换命名空间](./codingStyleIdioms/2_enumclass_namespace)\n- [3.RAII (资源获取即初始化)](./codingStyleIdioms/3_RAII)\n- [4.copy and swap](./codingStyleIdioms/4_copy-swap)\n- [5.pImpl (指针指向具体实现)](./codingStyleIdioms/5_pImpl)\n\n### 学习课程\n\n#### [极客时间《现代 C++ 实战 30 讲》](https://time.geekbang.org/channel/home)\n\n- [堆、栈、RAII：C++ 里该如何管理资源？](./learn_class/modern_cpp_30/RAII)\n  - [堆](./learn_class/modern_++_30/RAII/heap.cpp)\n  - [栈](./learn_class/modern_cpp_30/RAII/stack.cpp)\n  - [RAII](./learn_class/modern_cpp_30/RAII/RAII.cpp)\n- [自己动手，实现 C++ 的智能指针](./learn_class/modern_cpp_30/smart_ptr)\n  - [auto_ptr、scope_ptr](./learn_class/modern_cpp_30/smart_ptr/auto_scope.cpp)\n  - [unique_ptr](./learn_class/modern_cpp_30/smart_ptr/unique_ptr.cpp)\n  - [shared_ptr](./learn_class/modern_cpp_30/smart_ptr/shared_ptr.cpp)\n- [右值和移动究竟解决了什么问题？](./learn_class/modern_cpp_30/reference)\n  - [左值与右值](./learn_class/modern_cpp_30/reference/reference.cpp)\n  - [延长声明周期](./learn_class/modern_cpp_30/reference/lifetime.cpp)\n  - [引用折叠](./learn_class/modern_cpp_30/reference/collapses.cpp)\n  - [完美转发](./learn_class/modern_cpp_30/reference/forward.cpp)\n  - [不要返回本地变量的引用](./learn_class/modern_cpp_30/reference/don'treturnReference.cpp)\n- [容器 1](./learn_class/modern_cpp_30/container1)\n- [容器 2](./learn_class/modern_cpp_30/container2)\n- [异常](./learn_class/modern_cpp_30/exception)\n- [字面量、静态断言和成员函数说明符](./learn_class/modern_cpp_30/literalAssert)\n- [是不是应该返回对象？](./learn_class/modern_cpp_30/returnObj)\n- [编译期多态：泛型编程和模板入门](./learn_class/modern_cpp_30/compilerpoly)\n- [译期能做些什么？一个完整的计算世界](./learn_class/modern_cpp_30/compilercompute)\n- [SFINAE：不是错误的替换失败是怎么回事?](./learn_class/modern_cpp_30/SFINAE)\n- [constexpr：一个常态的世界](./learn_class/modern_cpp_30/constexpr)\n- [函数对象和 lambda：进入函数式编程](./learn_class/modern_cpp_30/functionLambda)\n- [内存模型和 atomic：理解并发的复杂性](./learn_class/modern_cpp_30/memorymodel_atomic)\n\n### 工具篇\n\n#### [容器快捷输出工具](./tool/output)\n\n对吴老师的代码进行了修改，[点击此处直通代码](./tool/output/output_container.h)\n\n输入：\n\n```cpp\nmap<int, int> mp{\n            {1, 1},\n            {2, 4},\n            {3, 9}};\n    cout << mp << endl;\n```\n\n输出结果显示：\n\n```cpp\n{ 1 => 1, 2 => 4, 3 => 9 }\n```\n\n#### 像 Python 一样简单输出（Jupyter Notebook）\n\n- [像 Python 一样玩 C/C++](./tool/像Python一样玩CC++.md)\n\n#### 观察编译过程变化\n\n-  [https://cppinsights.io](https://cppinsights.io/)\n\n#### C++ 的 Debug 工具 dbg-macro\n\n- [C++ 的 Debug 工具 dbg-macro](./tool/C++的Debug工具dbg-macro.md)\n\n#### 在 Linux 上的 Debug 工具 rr - 拥有回到过去的能力\n- [用 rr 来进行 debug](./tool/用rr来进行debug.md)\n\n### 拓展部分\n\n#### 一些问题\n\n- [C++ 中如何将 string 类型转换为 int 类型？](./extension/some_problem/string_int.md)\n\n### 贡献者\n\n\n\n| 贡献人  | 地址                          |\n| ------- | ----------------------------- |\n| 光城    | https://github.com/Light-City |\n| ChungZH | https://github.com/ChungZH    |\n| xliu79  | https://github.com/xliu79     |\n\n\n\n\n\n"
  },
  {
    "path": "README_EN.md",
    "content": "# Stories About C Plus Plus\n\nThanks for all the support about << Stories About C ++ >>~~, right now you could open this link(https://light-city.club/sc/) to read this article.~~\nAny star, retweet or pr will be welcomed.\n\n**Notice: The website is not available now.**\n\n\n-----------------------\n\n\n### 0. Directory\n\n- Chinese version：**C++ 那些事**\n- English version：**Stories About C Plus Plus**\n\nThis repository meets the need of people who wants to really know about C++, and may help you in the interview. Besides, it also provides\nother details,such as in-depth source code analysis and multi-threaded concurrency. It is a comprehensive C ++ repository from entry to advanced improvement\n\n### 1.Foundation\n\n- [Stories About const](./basic_content/const)\n- [Stories About static](./basic_content/static)\n- [Stories about this](./basic_content/this)\n- [Stories About inline](./basic_content/inline)\n- [Stories About sizeof](./basic_content/sizeof)\n- [Stories About pointer of function](./basic_content/func_pointer)\n- [Stories About pure virtual function and abstract](./basic_content/abstract)\n- [Stories About vptr_vtable](./basic_content/vptr_vtable)\n- [Stories About virtual](./basic_content/virtual)\n- [Stories About volatile](./basic_content/volatile)\n- [Stories About assert](./basic_content/assert)\n- [Stories About bit](./basic_content/bit)\n- [Stories About extern](./basic_content/extern)\n- [Stories About struct](./basic_content/struct)\n- [Stories About struct and class](./basic_content/struct_class)\n- [Stories About union](./basic_content/union)\n- [Stories About polymorphism](./basic_content/c_poly)\n- [Stories About explicit](./basic_content/explicit)\n- [Stories About friend](./basic_content/friend)\n- [Stories About using](./basic_content/using)\n- [Stories About :: ](./basic_content/maohao)\n- [Stories About enum](./basic_content/enum)\n- [Stories About decltype](./basic_content/decltype)\n- [Stories About pointer_refer](./basic_content/pointer_refer)\n- [Stories About macro](./basic_content/macro)\n\n### 2.Code Samples\n\n#### 2.1 10 Days Practice\n\n- [x] day1\n\nBasic Grammar About C ++\n- [x] day2\n\nRecursive、Struct、Enumerate、Static Variable\n\n- [x] day3\n\nFunction\n\n- [x] day4\n\nThorough About Function \n\n- [x] day5\n\nInheritance and Polymorphism \n\n- [x] day6\n\nVirtual Function、Abstruct \n\n- [x] day7\n\nOperator overloading\n\n- [x] day8\n\nTemplate And STL\n\n- [x] day9\n\nException\n\n- [x] day10\n\nFile And Stream\n\n#### 2.2 Practical Exercises\n\n├── [Square brackets overload .cpp](./practical_exercises/key_exercises/中括号重载.cpp)\n├── [Clock++operator overloading.cpp](./practical_exercises/key_exercises/时钟++运算符重载.cpp)\n├── [Mandatory conversion of operator overloading.cpp](./practical_exercises/key_exercises/运算符重载之强制转换.cpp)\n└── [Clock with overloaded parenthesis.cpp](./practical_exercises/key_exercises/重载圆括号的时钟.cpp)\n\n├── [Template of Function.cpp](./practical_exercises/key_exercises/函数模板.cpp)\n\n├── [Dynamic array.cpp](./practical_exercises/key_exercises/动态数组.cpp)\n\n├── [Dictionary insertion and search.cpp](./practical_exercises/key_exercises/字典插入与查找.cpp)\n\n├── [Catch Exception.cpp](./practical_exercises/key_exercises/异常捕获.cpp)\n\n├── [Template of Stack.cpp](./practical_exercises/key_exercises/类模板之栈.cpp)\n\n├── [Template of Array.cpp](./practical_exercises/key_exercises/类模板特化之数组.cpp)\n\n├── [Inheritance And Package.cpp](./practical_exercises/key_exercises/继承与封装.cpp)\n\n├── [Read And Write files.cpp](./practical_exercises/key_exercises/读写文件综合题.cpp)\n├── [Operator Overloading About Input and Output.cpp](./practical_exercises/key_exercises/输入输出运算符重载.cpp)\n├── [Input And Output Overloading.cpp](./practical_exercises/key_exercises/输入输出重载.cpp)\n├── [Input Format.cpp](./practical_exercises/key_exercises/输出格式.cpp)\n\n### 2.[C++2.0 New Features](./c++2.0)\n\n#### 2.0 Overview\n\nC++2.0 is an Abbreviation, meaning「modern C++」，including C++11/14/17/20.\n\n#### 2.1 [C++11 New Features](./c++2.0/c++11)\n\n- [Variadic Templates](./c++2.0/variadic)\n\n- Spaces in Template Expressions\n\n  ```cpp\n  vector<list<int> > //ok in each C++ version\n  vector<list<int>> // before c++ 11 error error: ‘>>’ should be ‘> >’ within a nested template argument list,version beyond c++ 11 could   compile successfully\n  ```\n\n- [nullptr and nullptr_t](./c++2.0/nullptr.cpp)\n\n- [Automatic Type Deduction with auto](./c++2.0/auto.cpp)\n\n- [Uniform Initialization ](./c++2.0/uniform_initialization.cpp)\n\n- [initializer_list](./c++2.0/initializer.cpp)\n\n- [explicit for ctors taking more than one argument](./c++2.0/explicit.cpp)\n\n- [range-based for statement](./c++2.0/auto.cpp)\n\n  ```cpp\n  for(decl:col) {\n      statement\n  }\n  ```\n\n- [=default,=delete](./c++2.0/default_delete.cpp)\n\n  If you define a ctor by yourself, compiler won't compile the default ctor.\n  If you add =default, you could recatch and use default ctor.\n\n- Alias(化名)Template(template typedef)\n\n  - [alias.cpp](./c++2.0/alias.cpp) \n  - [template_template.cpp](./c++2.0/template_template.cpp)\n\n- [template template parameter](./c++2.0/template_template.cpp)\n\n- [type alias](./c++2.0/type_alias.cpp)\n\n- [noexcept](./c++2.0/noexcept.cpp)\n\n- [override](./c++2.0/override.cpp)\n\n- [final](./c++2.0/final.cpp)\n\n- [decltype](./c++2.0/decltype.cpp)\n\n- [lambda](./c++2.0/lambda.cpp)\n\n- [Rvalue reference](./c++2.0/rvalue.cpp)\n\n- [move aware class](./c++2.0/move.cpp)\n\n- Container-Struct And Classify\n\n  - (1) Serial containers include：`array`(C++2.0 Newly Introduced),`vector`,`deque`,`list`,`forward_list`(C++2.0 Newly Introduced)\n  - (2) Associated containers include：`set/multiset`,`map/multimap`\n  - (3) Unordered container(C++2.0 Newly Introduced,replace `hash_xxx` to `unordered_xxx`) include：`unordered_map/unordered_multimap,unordered_set/unordered_multiset`\n\n- [Hash Function](./c++2.0/hash.cpp)\n\n- [tuple](./c++2.0/tuple.cpp)\n\n  Learning Material：https://www.bilibili.com/video/av51863195?from=search&seid=3610634846288253061\n\n### 2.2 C++14/17/20\n\nTo Be Continued ...\n\n### 3.Design Pattern\n\n- [Singleton pattern](./design_pattern/singleton)\n\n### 4. [STL Source Code Analysis](./src_analysis/stl)\n\n**STL Source Code Analysis：gcc 4.9.1**\n\n- [array](./src_analysis/stl/array.md)\n- [deque](./src_analysis/stl/deque.md)\n- [queue and stack](./src_analysis/stl/queue_stack.md)\n- [list](./src_analysis/stl/list.md)\n- [vector](./src_analysis/stl/vector.md)\n- [typename](./src_analysis/stl/typename.md)\n- [traits](./src_analysis/stl/traits.md)\n- [iterator](./src_analysis/stl/iterator.md)\n- [Talking about STL design and EBO optimization](./src_analysis/stl/谈谈STL设计之EBO优化.md)\n- [rb_tree](./src_analysis/stl/rb_tree.md)\n- [set and multiset](set_multiset.md)\n- [map and multimap](./src_analysis/stl/map_multimap.md)\n- [hashtable](./src_analysis/stl/hashtable.md)\n- [myhashtable](./src_analysis/stl/myPhashtable.md)\n- [unordered_map](./src_analysis/stl/unordered_map.md)\n\n### 4. [Concurrent Programming](./concurrency)\n\n#### 4.1 C++ Concurrency in Action\n\n- [Chapter One](./concurrency/concurrency_v1/chapter1)\n- [Chapter Two](./concurrency/concurrency_v1/chapter2)\n\nLearning materials: https://chenxiaowei.gitbook.io/cpp_concurrency_in_action/\n\n#### 4.2 Multithreading And Multiprocess\n\n##### 4.2.1 Threading In C++\n\n- [Introduction](./concurrency/Threading_In_CPlusPlus/1.thread)\n- [Five Types of Thread Creation](./concurrency/Threading_In_CPlusPlus/2.create_type)\n- [Join And Detaches](./concurrency/Threading_In_CPlusPlus/3.join_detach)\n- [mutex in C++ Threading](./concurrency/Threading_In_CPlusPlus/4.mutex)\n\n> From：\n>\n> https://www.youtube.com/watch?v=eZ8yKZo-PGw&list=PLk6CEY9XxSIAeK-EAh3hB4fgNvYkYmghp&index=4\n\n### 5. [C++ Conventional method](./codingStyleIdioms)\n\n##### What is your favorite custom for c ++ programming style?\n\n- [1.ClassInitializers](./codingStyleIdioms/1_classInitializers)\n- [2.Replace Enumclass with Namespace](./codingStyleIdioms/2_enumclass_namespace)\n- [3.RAII(Resource Acquisition Initialization)](./codingStyleIdioms/3_RAII)\n- [4.Copy and Swap](./codingStyleIdioms/4_copy-swap)\n- [5.pImpl(Pointer Implement)](./codingStyleIdioms/5_pImpl)\n\n### 6.Learning Courses\n\n#### 6.1 [Chinese Name:极客时间《现代 C++ 实战 30 讲》](https://time.geekbang.org/channel/home)\n\n- [heap、stack、RAII：How to manage resources for C ++ ?](./learn_class/modern_cpp_30/RAII)\n  - [heap](./modern_++_30/RAII/heap.cpp)\n  - [stack](./learn_class/modern_cpp_30/RAII/stack.cpp)\n  - [RAII](./learn_class/modern_cpp_30/RAII/RAII.cpp)\n- [Implementing smart pointers for C ++](./learn_class/modern_cpp_30/smart_ptr)\n  - [auto_ptr、scope_ptr](./learn_class/modern_cpp_30/smart_ptr/auto_scope.cpp)\n  - [unique_ptr](./learn_class/modern_cpp_30/smart_ptr/unique_ptr.cpp)\n  - [shared_ptr](./learn_class/modern_cpp_30/smart_ptr/shared_ptr.cpp)\n- [What exactly does r value and move solve？](./learn_class/modern_cpp_30/reference)\n  - [L value and R value](./learn_class/modern_cpp_30/reference/reference.cpp)\n  - [Extend the declaration cycle](./learn_class/modern_cpp_30/reference/lifetime.cpp)\n  - [Reference folding](./learn_class/modern_cpp_30/reference/collapses.cpp)\n  - [Perfect forward](./learn_class/modern_cpp_30/reference/forward.cpp)\n  - [Do not return Reference](./learn_class/modern_cpp_30/reference/don'treturnReference.cpp)\n- [Container 1](./learn_class/modern_cpp_30/container1)\n- [Container 2](./learn_class/modern_cpp_30/container2)\n- [Exception](./learn_class/modern_cpp_30/exception)\n- [Literal、Static Assertion And Member Function Specifier](./learn_class/modern_cpp_30/literalAssert)\n- [Return Object？](./learn_class/modern_cpp_30/returnObj)c\n- [Getting started with generic programming and templates](./learn_class/modern_cpp_30/compilerpoly)\n- [A whole Compiler Compute World](./learn_class/modern_cpp_30/compilercompute)\n- [SFINAE：What is it if it is not replace error?](./learn_class/modern_cpp_30/SFINAE)\n- [constexpr：A Normal World](./learn_class/modern_cpp_30/constexpr)\n- [Function object and Lambda：functionLambda](./learn_class/modern_cpp_30/functionLambda)\n- [Memory Model and Atomic：Understanding the complexity of concurrency](./learn_class/modern_cpp_30/memorymodel_atomic)\n\n### 7.Tools\n\n#### 7.1 [Container shortcut output tool](./tool/output)\n\nModified the code, [Click here for the code](./tool/output/output_container.h)\n\nInput：\n\n```cpp\nmap<int, int> mp{\n            {1, 1},\n            {2, 4},\n            {3, 9}};\n    cout << mp << endl;\n```\n\nOutput：\n\n```cpp\n{ 1 => 1, 2 => 4, 3 => 9 }\n```\n\n#### 7.2 Output Like Python（Jupyter Notebook）\n\n- [How to output like python in C/C++](./tool/像Python一样玩CC++.md)\n\n#### 7.3 Observe the changes in the compilation process\n\n-  [https://cppinsights.io](https://cppinsights.io/)\n\n#### 7.4 Debug Tools For C ++：dbg-macro\n\n- [Debug Tool: dbg-macro](./tool/C++的Debug工具dbg-macro.md)\n\n### 8.Expansion\n\n#### 8.1 Other Problems\n- [How to convert string to in C ++？](./extension/some_problem/string_int.md)\n\n\n### 9.Circumstance\n\n\n- **Running Circumstance**\n\n  Ubuntu 18.04\n\n- **IDE**\n\n  CLion  gcc/g++\n\n### 10.Contributor\n\n\n\n| 贡献人  | 地址                          |\n| ------- | ----------------------------- |\n| 光城    | https://github.com/Light-City |\n| ChungZH | https://github.com/ChungZH    |\n| xliu79  | https://github.com/xliu79     |\n\n\n\n### 11.About The Writer\n\n个人公众号：\n\n![](./img/wechat.jpg)\n"
  },
  {
    "path": "WORKSPACE",
    "content": ""
  },
  {
    "path": "basic_content/abstract/BUILD",
    "content": "# you can run some main program, just replace binary name\n# such as: `bazel run basic_content/abstract:interesting_facts1`\n#          `bazel run basic_content/abstract:interesting_facts2`\n# etc...\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\n# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.\ncc_binary(\n    name = \"interesting_facts1\",\n    srcs = [\"interesting_facts1.cpp\"],\n)\n\ncc_binary(\n    name = \"interesting_facts2\",\n    srcs = [\"interesting_facts2.cpp\"],\n)\n\n# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.\ncc_binary(\n    name = \"interesting_facts3\",\n    srcs = [\"interesting_facts3.cpp\"],\n)\n\ncc_binary(\n    name = \"interesting_facts4\",\n    srcs = [\"interesting_facts4.cpp\"],\n)\n\ncc_binary(\n    name = \"interesting_facts5\",\n    srcs = [\"interesting_facts5.cpp\"],\n)\n\n# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.\ncc_binary(\n    name = \"pure_virtual\",\n    srcs = [\"pure_virtual.cpp\"],\n)\n\ncc_binary(\n    name = \"derived_full\",\n    srcs = [\"derived_full.cpp\"],\n)"
  },
  {
    "path": "basic_content/abstract/README.md",
    "content": "# 纯虚函数和抽象类\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 1.纯虚函数与抽象类\n\nC++中的纯虚函数(或抽象函数)是我们没有实现的虚函数！我们只需声明它! 通过声明中赋值0来声明纯虚函数！\n```cpp\n// 抽象类\nClass A {\npublic: \n    virtual void show() = 0; // 纯虚函数\n    /* Other members */\n}; \n```\n\n * 纯虚函数：没有函数体的虚函数\n * 抽象类：包含纯虚函数的类\n\n抽象类只能作为基类来派生新类使用，不能创建抽象类的对象,抽象类的指针和引用->由抽象类派生出来的类的对象！\n\n> 代码样例：[abstract_base.h](./abstract_base.h)、[pure_virtual.cpp](./pure_virtual.cpp)\n\n## 2.实现抽象类\n\n抽象类中：在成员函数内可以调用纯虚函数，在构造函数/析构函数内部不能使用纯虚函数。\n\n如果一个类从抽象类派生而来，它必须实现了基类中的所有纯虚函数，才能成为非抽象类。\n```cpp\n// A为抽象类\nclass A {\npublic:\n    virtual void f() = 0;  // 纯虚函数\n    void g(){ this->f(); }\n    A(){}  // 构造函数\n};\n\nclass B : public A{\npublic:\n    void f(){ cout<<\"B:f()\"<<endl;}  // 实现了抽象类的纯虚函数\n};\n```\n\n> 代码样例：[abstract.cpp](./abstract.cpp)\n\n## 3.重要点\n\n- [纯虚函数使一个类变成抽象类](./interesting_facts1.cpp)\n```cpp\n// 抽象类至少包含一个纯虚函数\nclass Base{\npublic: \n    virtual void show() = 0; // 纯虚函数\n    int getX() { return x; } // 普通成员函数\n\nprivate:\n     int x; \n}; \n```\n\n- [抽象类类型的指针和引用](./interesting_facts2.cpp)\n```cpp\nclass Derived : public Base { \npublic: \n    void show() { cout << \"In Derived \\n\"; } // 实现抽象类的纯虚函数\n    Derived(){} // 构造函数\n}; \n\nint main(void) \n{ \n    //Base b;  // error! 不能创建抽象类的对象\n    //Base *b = new Base(); error!\n    \n    Base *bp = new Derived(); // 抽象类的指针和引用 -> 由抽象类派生出来的类的对象\n    bp->show();\n    return 0; \n}\n```\n\n- [如果我们不在派生类中覆盖纯虚函数，那么派生类也会变成抽象类](./interesting_facts3.cpp)\n```cpp\n// Derived为抽象类\nclass Derived: public Base \n{ \npublic: \n//    void show() {}\n}; \n```\n\n- [抽象类可以有构造函数](./interesting_facts4.cpp)\n```cpp\n// 抽象类\nclass Base { \n    protected: \n        int x; \n    public: \n        virtual void fun() = 0; \n        Base(int i) { x = i; }  // 构造函数\n}; \n// 派生类\nclass Derived: public Base \n{ \n    int y; \npublic: \n    Derived(int i, int j) : Base(i) { y = j; } // 构造函数\n    void fun() { cout << \"x = \" << x << \", y = \" << y; }\n}; \n```\n\n- [构造函数不能是虚函数，而析构函数可以是虚析构函数](./interesting_facts5.cpp)\n```cpp\n// 抽象类\nclass Base  {\npublic:\n    Base(){ cout << \"Constructor: Base\" << endl; }\n    virtual ~Base(){ cout << \"Destructor : Base\" << endl; }\n    \n    virtual void func() = 0;\n};\n\nclass Derived: public Base {\npublic:\n    Derived(){ cout << \"Constructor: Derived\" << endl; }\n    ~Derived(){ cout << \"Destructor : Derived\" << endl;}\n    \n    void func(){cout << \"In Derived.func().\" << endl;}\n};\n```\n>当基类指针指向派生类对象并删除对象时，我们可能希望调用适当的析构函数。\n> 如果析构函数不是虚拟的，则只能调用基类析构函数。\n\n## 4.完整实例\n\n抽象类由派生类继承实现！\n\n> 代码样例：[derived_full.cpp](./derived_full.cpp)"
  },
  {
    "path": "basic_content/abstract/abstract.cpp",
    "content": "/**\n * @file abstract.cpp\n * @brief\n * 抽象类中：在成员函数内可以调用纯虚函数，在构造函数/析构函数内部不能使用纯虚函数\n * 如果一个类从抽象类派生而来，它必须实现了基类中的所有纯虚函数，才能成为非抽象类\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include <iostream>\nusing namespace std;\n\nclass A {\npublic:\n  virtual void f() = 0; // 纯虚函数\n  void g() { this->f(); }\n  A() {}\n};\nclass B : public A {\npublic:\n  void f() { cout << \"B:f()\" << endl; }\n};\nint main() {\n  B b;\n  b.g();\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/abstract/abstract_base.h",
    "content": "/**\n * @file abstreact_base.cpp\n * @brief\n * C++中的纯虚函数(或抽象函数)是我们没有实现的虚函数！我们只需声明它!通过声明中赋值0来声明纯虚函数！\n * 纯虚函数：没有函数体的虚函数\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n/**\n * @brief 抽象类\n */\nclass AbstractBase {\n  // Data members of class\npublic:\n  // Pure Virtual Function\n  virtual void show() = 0;\n\n  /* Other members */\n};\n"
  },
  {
    "path": "basic_content/abstract/derived_full.cpp",
    "content": "/**\n * @file derived_full.cpp\n * @brief 完整示例！抽象类由派生类继承实现！\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include <iostream>\nusing namespace std;\n\nclass Base {\n  int x;\n\npublic:\n  virtual void fun() = 0;\n  int getX() { return x; }\n};\n\nclass Derived : public Base {\npublic:\n  void fun() { cout << \"fun() called\"; } // 实现了fun()函数\n};\n\nint main(void) {\n  Derived d;\n  d.fun();\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/abstract/interesting_facts1.cpp",
    "content": "/**\n * @file interesting_facts1.cpp\n * @brief 纯虚函数使一个类变成抽象类\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include <iostream>\nusing namespace std;\n\n/**\n * @brief 抽象类至少包含一个纯虚函数\n */\nclass Test {\n  int x;\n\npublic:\n  virtual void show() = 0;\n  int getX() { return x; }\n};\n\nint main(void) {\n  Test t; // error! 不能创建抽象类的对象\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/abstract/interesting_facts2.cpp",
    "content": "/**\n * @file interesting_facts2.cpp\n * @brief 抽象类类型的指针和引用\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include <iostream>\nusing namespace std;\n\n/**\n * @brief 抽象类至少包含一个纯虚函数\n */\nclass Base {\n  int x;\n\npublic:\n  virtual void show() = 0;\n  int getX() { return x; }\n};\nclass Derived : public Base {\npublic:\n  void show() { cout << \"In Derived \\n\"; }\n  Derived() {}\n};\nint main(void) {\n  // Base b;  //error! 不能创建抽象类的对象\n  // Base *b = new Base(); error!\n  Base *bp = new Derived(); // 抽象类的指针和引用 -> 由抽象类派生出来的类的对象\n  bp->show();\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/abstract/interesting_facts3.cpp",
    "content": "/**\n * @file interesting_facts3.cpp\n * @brief 如果我们不在派生类中覆盖纯虚函数，那么派生类也会变成抽象类。\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include <iostream>\nusing namespace std;\n\nclass Base {\n  int x;\n\npublic:\n  virtual void show() = 0;\n  int getX() { return x; }\n};\nclass Derived : public Base {\npublic:\n  //    void show() { }\n};\nint main(void) {\n  Derived\n      d; // error!\n         // 派生类没有实现纯虚函数，那么派生类也会变为抽象类，不能创建抽象类的对象\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/abstract/interesting_facts4.cpp",
    "content": "/**\n * @file interesting_facts4.cpp\n * @brief 抽象类可以有构造函数\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include <iostream>\nusing namespace std;\n\n// An abstract class with constructor\nclass Base {\nprotected:\n  int x;\n\npublic:\n  virtual void fun() = 0;\n  Base(int i) { x = i; }\n};\n\nclass Derived : public Base {\n  int y;\n\npublic:\n  Derived(int i, int j) : Base(i) { y = j; }\n  void fun() { cout << \"x = \" << x << \", y = \" << y; }\n};\n\nint main(void) {\n  Derived d(4, 5);\n  d.fun();\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/abstract/interesting_facts5.cpp",
    "content": "\n/**\n * @file interesting_facts5.cpp\n * @brief 构造函数不能是虚函数，而析构函数可以是虚析构函数。\n * 例如：当基类指针指向派生类对象并删除对象时，我们可能希望调用适当的析构函数。如果析构函数不是虚拟的，则只能调用基类析构函数。\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n#include <iostream>\nusing namespace std;\n\nclass Base {\npublic:\n  Base() { cout << \"Constructor: Base\" << endl; }\n  virtual ~Base() { cout << \"Destructor : Base\" << endl; }\n};\n\nclass Derived : public Base {\npublic:\n  Derived() { cout << \"Constructor: Derived\" << endl; }\n  ~Derived() { cout << \"Destructor : Derived\" << endl; }\n};\n\nint main() {\n  Base *Var = new Derived();\n  delete Var;\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/abstract/pure_virtual.cpp",
    "content": "/**\n * @file pure_virtual.cpp\n * @brief 纯虚函数：没有函数体的虚函数\n * 抽象类：包含纯虚函数的类\n *\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include <iostream>\n\nusing namespace std;\nclass A {\nprivate:\n  int a;\n\npublic:\n  virtual void show() = 0; // 纯虚函数\n};\n\nint main() {\n  /*\n   * 1. 抽象类只能作为基类来派生新类使用\n   * 2. 抽象类的指针和引用->由抽象类派生出来的类的对象！\n   */\n  A a; // error 抽象类，不能创建对象\n\n  A *a1; // ok 可以定义抽象类的指针\n\n  A *a2 = new A(); // error, A是抽象类，不能创建对象\n}\n"
  },
  {
    "path": "basic_content/assert/BUILD",
    "content": "# you can run some main program, just replace binary name\n# such as: `bazel run basic_content/assert:ignore_assert`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"ignore_assert\",\n    srcs = [\"ignore_assert.c\"],\n)\n\ncc_binary(\n    name = \"assert\",\n    srcs = [\"assert.c\"],\n)\n"
  },
  {
    "path": "basic_content/assert/README.md",
    "content": "# assert那些事\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n\n\n## 1.第一个断言案例\n\n断言，**是宏，而非函数**。\n\nassert 宏的原型定义在 <assert.h>（C）、<cassert>（C++）中。其作用是如果它的条件返回错误，则终止程序执行。\n\n可以通过定义 `NDEBUG` 来关闭 assert，**但是需要在源代码的开头，include <assert.h> 之前。**\n\n```c\nvoid assert(int expression);\n```\n\n> 代码样例：[assert.c](./assert.c)\n```c\n#include <stdio.h> \n#include <assert.h> \n\nint main() \n{ \n    int x = 7; \n\n    /*  Some big code in between and let's say x  \n    is accidentally changed to 9  */\n    x = 9; \n\n    // Programmer assumes x to be 7 in rest of the code \n    assert(x==7); \n\n    /* Rest of the code */\n\n    return 0; \n} \n```\n输出：\n```c\nassert: assert.c:13: main: Assertion 'x==7' failed.\n```\n可以看到输出会把源码文件，行号错误位置，提示出来！\n\n## 2.断言与正常错误处理\n\n+ 断言主要用于检查逻辑上不可能的情况。\n\n>例如，它们可用于检查代码在开始运行之前所期望的状态，或者在运行完成后检查状态。与正常的错误处理不同，断言通常在运行时被禁用。\n\n+ 忽略断言，在代码开头加上：\n```c++\n#define NDEBUG          // 加上这行，则 assert 不可用\n```\n\n> 样例代码：[ignore_assert.c](./ignore_assert.c)\n"
  },
  {
    "path": "basic_content/assert/assert.c",
    "content": "#include <stdio.h> \n#include <assert.h> \n\nint main() \n{ \n    int x = 7; \n\n    /*  Some big code in between and let's say x  \n     *  is accidentally changed to 9  \n     */\n    x = 9; \n\n    // Programmer assumes x to be 7 in rest of the code \n    assert(x==7); \n\n    /* Rest of the code */\n\n    return 0; \n} \n"
  },
  {
    "path": "basic_content/assert/ignore_assert.c",
    "content": "/**\n * @file ignore_assert.c\n * @brief 忽略断言\n * @author 光城\n * @version v1\n * @date 2019-07-25\n */\n\n# define NDEBUG // 忽略断言\n\n#include<assert.h>\n\nint main(){\n    assert(x==5);\n    return 0;\n}\n"
  },
  {
    "path": "basic_content/bit/BUILD",
    "content": "# please run `bazel run basic_content/bit:bit`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"bit\",\n    srcs = [\"bit.cpp\"],\n)"
  },
  {
    "path": "basic_content/bit/README.md",
    "content": "## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## Bit field 是什么？\n\n“ 位域 “ 或 “ 位段 “(Bit field)为一种数据结构，可以把数据以位的形式紧凑的储存，并允许程序员对此结构的位进行操作。这种数据结构的一个好处是它可以使数据单元节省储存空间，当程序需要成千上万个数据单元时，这种方法就显得尤为重要。第二个好处是位段可以很方便的访问一个整数值的部分内容从而可以简化程序源代码。而这种数据结构的缺点在于，位段实现依赖于具体的机器和系统，在不同的平台可能有不同的结果，这导致了位段在本质上是不可移植的。\n\n- 位域在内存中的布局是与机器有关的\n- 位域的类型必须是整型或枚举类型，带符号类型中的位域的行为将因具体实现而定\n- 取地址运算符（&）不能作用于位域，任何指针都无法指向类的位域\n\n## 位域使用\n\n位域通常使用结构体声明， 该结构声明为每个位域成员设置名称，并决定其宽度：\n\n```\nstruct bit_field_name\n{\n\ttype member_name : width;\n};\n```\n\n\n\n| Elements       | Description                                                  |\n| -------------- | ------------------------------------------------------------ |\n| bit_field_name | 位域结构名                                                   |\n| type           | 位域成员的类型，必须为 int、signed int 或者 unsigned int 类型 |\n| member_name    | 位域成员名                                                   |\n| width          | 规定成员所占的位数                                           |\n\n例如声明如下一个位域:\n\n```\nstruct _PRCODE\n{\n\tunsigned int code1: 2;\n\tunsigned int cdde2: 2;\n\tunsigned int code3: 8;\n};\nstruct _PRCODE prcode;\n```\n\n该定义使 `prcode`包含 2 个 2 Bits 位域和 1 个 8 Bits 位域，我们可以使用结构体的成员运算符对其进行赋值\n\n```\nprcode.code1 = 0;\nprcode.code2 = 3;\nprocde.code3 = 102;\n```\n\n\n赋值时要注意值的大小不能超过位域成员的容量，例如 prcode.code3 为 8 Bits 的位域成员，其容量为 2^8 = 256，即赋值范围应为 [0,255]。\n\n## 位域的大小和对齐\n\n### 位域的大小\n\n例如以下位域：\n\n```\nstruct box \n{\n\tunsigned int a: 1;\n\tunsigned int  : 3;\n\tunsigned int b: 4;\n};\n```\n\n该位域结构体中间有一个未命名的位域，占据 3 Bits，仅起填充作用，并无实际意义。 填充使得该结构总共使用了 8 Bits。但 C 语言使用 unsigned int 作为位域的基本单位，即使一个结构的唯一成员为 1 Bit 的位域，该结构大小也和一个 unsigned int 大小相同。 有些系统中，unsigned int 为 16 Bits，在 x86 系统中为 32 Bits。文章以下均默认 unsigned int 为 32 Bits。\n\n### 位域的对齐\n\n一个位域成员不允许跨越两个 unsigned int 的边界，如果成员声明的总位数超过了一个 unsigned int 的大小， 那么编辑器会自动移位位域成员，使其按照 unsigned int 的边界对齐。\n例如：\n\n```\nstruct stuff \n{\n\tunsigned int field1: 30;\n\tunsigned int field2: 4;\n\tunsigned int field3: 3;\n};\n```\n\n\n\n`field1` + `field2` = 34 Bits，超出 32 Bits, 编译器会将`field2`移位至下一个 unsigned int 单元存放， stuff.field1 和 stuff.field2 之间会留下一个 2 Bits 的空隙， stuff.field3 紧跟在 stuff.field2 之后，该结构现在大小为 2 * 32 = 64 Bits。\n\n这个空洞可以用之前提到的未命名的位域成员填充，我们也可以使用一个宽度为 0 的未命名位域成员令下一位域成员与下一个整数对齐。\n例如:\n\n```\nstruct stuff \n{\n\tunsigned int field1: 30;\n\tunsigned int       : 2;\n\tunsigned int field2: 4;\n\tunsigned int       : 0;\n\tunsigned int field3: 3; \n};\n```\n\n\n\n这里 stuff.field1 与 stuff.field2 之间有一个 2 Bits 的空隙，stuff.field3 则存储在下一个 unsigned int 中，该结构现在大小为 3 * 32 = 96 Bits。\n\n学习代码见：[bit.cpp](bit.cpp)\n\n## 位域的初始化和位的重映射\n\n### 初始化\n\n位域的初始化与普通结构体初始化的方法相同，这里列举两种，如下:\n\n```\nstruct stuff s1= {20,8,6};\n```\n\n或者直接为位域成员赋值\n\n```\nstruct stuff s1;\ns1.field1 = 20;\ns1.field2 = 8;\ns1.field3 = 4;\n```\n\n### 位域的重映射 (Re-mapping)\n\n声明一个 大小为 32 Bits 的位域\n\n```\nstruct box {\n\tunsigned int ready:     2;\n\tunsigned int error:     2;\n\tunsigned int command:   4;\n\tunsigned int sector_no: 24;\n}b1;\n```\n\n#### 利用重映射将位域归零\n\n```\nint* p = (int *) &b1;  // 将 \"位域结构体的地址\" 映射至 \"整形（int*) 的地址\" \n*p = 0;                // 清除 s1，将各成员归零\n```\n\n#### 利用联合 (union) 将 32 Bits 位域 重映射至 unsigned int 型\n\n先简单介绍一下联合\n\n> “联合” 是一种特殊的类，也是一种构造类型的数据结构。在一个 “联合” 内可以定义多种不同的数据类型， 一个被说明为该 “联合” 类型的变量中，允许装入该 “联合” 所定义的任何一种数据，这些数据共享同一段内存，以达到节省空间的目的\n>\n> “联合” 与 “结构” 有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间， 一个结构变量的总长度是各成员长度之和（空结构除外，同时不考虑边界调整）。而在 “联合” 中，各成员共享一段内存空间， 一个联合变量的长度等于各成员中最长的长度。应该说明的是， 这里所谓的共享不是指把多个成员同时装入一个联合变量内， 而是指该联合变量可被赋予任一成员值，但每次只能赋一种值， 赋入新值则冲去旧值。\n\n我们可以声明以下联合:\n\n```\nunion u_box {\n  struct box st_box;     \n  unsigned int ui_box;\n};\n```\n\n\n\nx86 系统中 unsigned int 和 box 都为 32 Bits, 通过该联合使 st_box 和 ui_box 共享一块内存。具体位域中哪一位与 unsigned int 哪一位相对应，取决于编译器和硬件。\n利用联合将位域归零，代码如下：\n\n```\nunion u_box u;\nu.ui_box = 0;\n```\n\n> 学习参考自：<http://www.yuan-ji.me/C-C-%E4%BD%8D%E5%9F%9F-Bit-fields-%E5%AD%A6%E4%B9%A0%E5%BF%83%E5%BE%97/>"
  },
  {
    "path": "basic_content/bit/bit.cpp",
    "content": "#include <iostream>\n\nusing namespace std;\nstruct stuff {\n  unsigned int field1 : 30;\n  unsigned int : 2;\n  unsigned int field2 : 4;\n  unsigned int : 0;\n  unsigned int field3 : 3;\n};\nint main() {\n  struct stuff s = {1, 3, 5};\n  cout << s.field1 << endl;\n  cout << s.field2 << endl;\n  cout << s.field3 << endl;\n  cout << sizeof(s) << endl;\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/c_poly/BUILD",
    "content": "# please run `bazel run basic_content/c_poly:c_examp`\n# please run `bazel run basic_content/c_poly:c++_examp`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"c_examp\",\n    srcs = [\"c_examp.c\"],\n)\n\ncc_binary(\n    name = \"c++_examp\",\n    srcs = [\"c++_examp.cpp\"],\n)"
  },
  {
    "path": "basic_content/c_poly/README.md",
    "content": "# C实现C++的面向对象特性\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 1.C++实现案例\n\nC++中的多态:在C++中会维护一张虚函数表，根据赋值兼容规则，我们知道父类的指针或者引用是可以指向子类对象的。\n\n如果一个父类的指针或者引用调用父类的虚函数则该父类的指针会在自己的虚函数表中查找自己的函数地址，如果该父类对象的指针或者引用指向的是子类的对象，而且该子类已经重写了父类的虚函数，则该指针会调用子类的已经重写的虚函数。\n\n学习案例代码见:[c++_examp.cpp](./c++_examp.cpp)\n\n\n\n## 2.C实现\n\n- 封装\n\nC语言中是没有class类这个概念的，但是有struct结构体，我们可以考虑使用struct来模拟；\n\n使用函数指针把属性与方法封装到结构体中。\n\n- 继承\n\n结构体嵌套\n\n- 多态\n\n类与子类方法的函数指针不同\n\n在C语言的结构体内部是没有成员函数的，如果实现这个父结构体和子结构体共有的函数呢？我们可以考虑使用函数指针来模拟。但是这样处理存在一个缺陷就是：父子各自的函数指针之间指向的不是类似C++中维护的虚函数表而是一块物理内存，如果模拟的函数过多的话就会不容易维护了。\n\n模拟多态，必须保持函数指针变量对齐(在内容上完全一致，而且变量对齐上也完全一致)。否则父类指针指向子类对象，运行崩溃！\n\n\n\n学习案例代码见:[c_examp.c](./c_examp.c)\n"
  },
  {
    "path": "basic_content/c_poly/c++_examp.cpp",
    "content": "/**\n * @file c++_examp.cpp\n * @brief c++中的多态\n * @author 光城\n * @version v1\n * @date 2019-08-06\n */\n\n#include <iostream>\n\nusing namespace std;\nclass A {\npublic:\n  virtual void f() //虚函数实现\n  {\n    cout << \"Base A::f() \" << endl;\n  }\n};\n\nclass B : public A // 必须为共有继承，否则后面调用不到，class默认为私有继承！\n{\npublic:\n  virtual void f() //虚函数实现,子类中virtual关键字可以没有\n  {\n    cout << \"Derived B::f() \" << endl;\n  }\n};\n\nint main() {\n  A a; //基类对象\n  B b; //派生类对象\n\n  A *pa = &a; //父类指针指向父类对象\n  pa->f();    //调用父类的函数\n\n  pa = &b; //父类指针指向子类对象，多态实现\n  pa->f(); //调用派生类同名函数\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/c_poly/c_examp.c",
    "content": "/**\n * @file c_examp.c\n * @brief C实现多态\n * @author 光城\n * @version v1\n * @date 2019-08-06\n */\n\n#include <stdio.h>\n\n/// 重定义一个函数指针类型\ntypedef void (*pf) ();\n\n/**\n * @brief 父类 \n */ \ntypedef struct _A\n{\n    pf _f;\n}A;\n\n\n/**\n * @brief 子类\n */\ntypedef struct _B\n{ \n    A _b; ///< 在子类中定义一个基类的对象即可实现对父类的继承。 \n}B;\n\nvoid FunA() \n{\n    printf(\"%s\\n\",\"Base A::fun()\");\n}\n\nvoid FunB() \n{\n    printf(\"%s\\n\",\"Derived B::fun()\");\n}\n\n\nint main() \n{\n    A a;\n    B b;\n\n    a._f = FunA;\n    b._b._f = FunB;\n\n    A *pa = &a;\n    pa->_f();\n    pa = (A *)&b;   /// 让父类指针指向子类的对象,由于类型不匹配所以要进行强转 \n    pa->_f();\n    return 0;\n}\n"
  },
  {
    "path": "basic_content/const/BUILD",
    "content": "# please run `bazel run basic_content/const:const_function`\n# please run `bazel run basic_content/const:const_num`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\n# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.\ncc_binary(\n    name = \"const_function\",\n    srcs = [\"const_function.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\n# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.\ncc_binary(\n    name = \"const_num\",\n    srcs = [\"const_num.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/const/README.md",
    "content": "## 关于作者\n\n微信公众号：\n\n![](../img/wechat.jpg)\n\n## 1.const含义\n\n常类型是指使用类型修饰符**const**说明的类型，常类型的变量或对象的值是不能被更新的。\n\n## 2.const作用\n\n+ 可以定义常量\n\n```cpp\nconst int a=100;\n```\n\n+ 类型检查\n\n    + const常量与`#define`宏定义常量的区别：\n    > <strong><s>const常量具有类型，编译器可以进行安全检查；#define宏定义没有数据类型，只是简单的字符串替换，不能进行安全检查。</s></strong>感谢两位大佬指出这里问题，见：[issue](https://github.com/Light-City/CPlusPlusThings/issues/5)\n\n    + const常量支持所有类型。\n    + 其他情况下它只是一个 `const` 限定的变量，不要将与常量混淆。\n\n+ 防止修改，起保护作用，增加程序健壮性\n\n```cpp\nvoid f(const int i){\n    i++; // error!\n}\n```\n\n+ 可以节省空间，避免不必要的内存分配\n\n    + const定义常量从汇编的角度来看，只是给出了对应的内存地址，而不是像`#define`一样给出的是立即数。\n    + const定义的常量在程序运行过程中只有一份拷贝，而`#define`定义的常量在内存中有若干个拷贝。\n\n## 3.const对象默认为文件局部变量\n\n<p><font style=\"color:red\">注意：非const变量默认为extern。要使const变量能够在其他文件中访问，必须在文件中显式地指定它为extern。</font></p>\n\n> 未被const修饰的变量在不同文件的访问\n\n```cpp\n// file1.cpp\nint ext;\n// file2.cpp\n#include<iostream>\n\nextern int ext;\nint main(){\n    std::cout<<(ext+10)<<std::endl;\n}\n```\n\n> const常量在不同文件的访问\n\n```cpp\n// extern_file1.cpp\nextern const int ext=12;\n// extern_file2.cpp\n#include<iostream>\nextern const int ext;\nint main(){\n    std::cout<<ext<<std::endl;\n}\n```\n\n> 小结：<br>可以发现未被const修饰的变量不需要extern显式声明！而const常量需要显式声明extern，并且需要做初始化！因为常量在定义后就不能被修改，所以定义时必须初始化。</font></p>\n\n## 4.定义常量\n\n```c++\nconst int b = 10;\nb = 0; // error: assignment of read-only variable ‘b’\nconst string s = \"helloworld\";\nconst int i,j=0 // error: uninitialized const ‘i’\n```\n\n上述有两个错误：\n+ b 为常量，不可更改！\n+ i 为常量，必须进行初始化！(因为常量在定义后就不能被修改，所以定义时必须初始化。)\n\n## 5.指针与const\n\n与指针相关的const有四种：\n\n```c++\nconst char * a; // 指向const对象的指针或者说指向常量的指针。\nchar const * a; // 同上\nchar * const a; // 指向类型对象的const指针。或者说常指针、const指针。\nconst char * const a; // 指向const对象的const指针。\n```\n\n> **小结：** <br>\n> 如果*const*位于`*`的左侧，则const就是用来修饰指针所指向的变量，即指针指向为常量；<br>如果const位于`*`的右侧，*const*就是修饰指针本身，即指针本身是常量。\n\n**另一种解读方式**<br>\n利用英文从右边往左边读，并且以to为分界，to之前为描述指针的特性，to之后为描述目标的特性<br>\n```c++\nconst char * p; //p is a pointer to const char\nchar const * p; //同上\nchar * const p; //p is a const pointer to char\nconst char * const p; //p is a const pointer to const char\n```\n当指针被加上const特性，则指针不可改变指向的地址<br>\n当指向的目标特性为char，则内容可以透过指针被修改，如: *char='y';<br>\n当指向的目标特性为const char，则内容不可透过指针修改<br>\n\n具体使用如下：\n\n（1） **指向常量的指针**\n\n```cpp\nconst int *ptr;\n*ptr = 10; // error\n```\n\nptr是一个指向int类型const对象的指针，const定义的是int类型，也就是ptr所指向的对象类型，而不是ptr本身，所以ptr可以不用赋初始值。但是不能通过ptr去修改所指对象的值。\n\n除此之外，也不能使用void`*`指针保存const对象的地址，必须使用const void`*`类型的指针保存const对象的地址。\n\n```c++\nconst int p = 10;\nconst void * vp = &p;\nvoid *vp = &p; // error\n```\n\n另外一个重点是：**允许把非const对象的地址赋给指向const对象的指针**。\n\n将非const对象的地址赋给const对象的指针:\n\n```c++\nconst int *ptr;\nint val = 3;\nptr = &val; // ok\n```\n\n我们不能通过ptr指针来修改val的值，即使它指向的是非const对象!\n\n我们不能使用指向const对象的指针修改基础对象，然而如果该指针指向了非const对象，可用其他方式修改其所指的对象。可以修改const指针所指向的值的，但是不能通过const对象指针来进行而已！如下修改：\n\n```c++\nint *ptr1 = &val;\n*ptr1=4;\ncout<<*ptr<<endl;\n```\n\n> 小结：<br>1.对于指向常量的指针，不能通过指针来修改对象的值。<br>2.不能使用void`*`指针保存const对象的地址，必须使用const void`*`类型的指针保存const对象的地址。<br>3.允许把非const对象的地址赋值给const对象的指针，如果要修改指针所指向的对象值，必须通过其他方式修改，不能直接通过当前指针直接修改。\n\n（2） **常指针**\n\nconst指针必须进行初始化，且const指针指向的值能修改，但指向不能修改。\n\n```cpp\n#include<iostream>\nusing namespace std;\nint main(){\n    int num=0, num1=1;\n    int * const ptr=&num; // const指针必须初始化！且const指针的指向不能修改\n    ptr = &num1; // error! const指针不能修改指向！\n    cout<<*ptr<<endl;\n}\n```\n\n代码出现编译错误：const指针不能修改指向。\n\n```cpp\n#include<iostream>\nusing namespace std;\nint main(){\n    int num=0, num1=1;\n    int * const ptr=&num; // const指针必须初始化！且const指针的指向不能修改\n    *ptr = 1;\n    cout<<*ptr<<endl;\n}\n```\n\n代码无事发生，正常输出1。\n\n最后，当把一个const常量的地址赋值给ptr时候，由于ptr指向的是一个变量，而不是const常量，所以会报错，出现：const int`*` -> int `*`错误！\n\n```cpp\n#include<iostream>\nusing namespace std;\nint main(){\n    const int num=0;\n    int * const ptr=&num; // error! const int* -> int*\n    cout<<*ptr<<endl;\n}\n```\n\n上述若改为 const int `*`ptr或者改为const int `*`const ptr，都可以正常！\n\n（3）**指向常量的常指针**\n\n理解完前两种情况，下面这个情况就比较好理解了：\n\n```cpp\nconst int p = 3;\nconst int * const ptr = &p; \n```\n\nptr是一个const指针，然后指向了一个int 类型的const对象。\n\n## 6.函数中使用const\n\n> const修饰函数返回值\n\n这个跟const修饰普通变量以及指针的含义基本相同：\n\n（1）**const int**\n\n```cpp\nconst int func1();\n```\n\n这个本身无意义，因为参数返回本身就是赋值给其他的变量！\n\n（2）**const int***\n\n```cpp\nconst int* func2();\n```\n\n指针指向的内容不变。\n\n（3）int *const\n\n```cpp\nint *const func2();\n```\n\n指针本身不可变。\n\n> const修饰函数参数\n\n（1）传递过来的参数及指针本身在函数内不可变，无意义！\n\n```cpp\nvoid func(const int var); // 传递过来的参数不可变\nvoid func(int *const var); // 指针本身不可变\n```\n\n表明参数在函数体内不能被修改，但此处没有任何意义，var本身就是形参，在函数内不会改变。包括传入的形参是指针也是一样。\n\n输入参数采用“值传递”，由于函数将自动产生临时变量用于复制该参数，该输入参数本来就无需保护，所以不要加const 修饰。\n\n（2）**参数指针所指内容为常量不可变**\n\n```cpp\nvoid StringCopy(char *dst, const char *src);\n```\n\n其中src 是输入参数，dst 是输出参数。给src加上const修饰后，如果函数体内的语句试图改动src的内容，编译器将指出错误。这就是加了const的作用之一。\n\n（3）**参数为引用，为了增加效率同时防止修改。**\n\n```cpp\nvoid func(const A &a)\n```\n\n对于非内部数据类型的参数而言，像void func(A a) 这样声明的函数注定效率比较低。因为函数体内将产生A 类型的临时对象用于复制参数a，而临时对象的构造、复制、析构过程都将消耗时间。\n\n为了提高效率，可以将函数声明改为void func(A &a)，因为“引用传递”仅借用一下参数的别名而已，不需要产生临\n时对象。\n\n> 但是函数void func(A &a) 存在一个缺点：<br><br>“引用传递”有可能改变参数a，这是我们不期望的。解决这个问题很容易，加const修饰即可，因此函数最终成为\nvoid func(const A &a)。\n\n以此类推，是否应将void func(int x) 改写为void func(const int &x)，以便提高效率？完全没有必要，因为内部数据类型的参数不存在构造、析构的过程，而复制也非常快，“值传递”和“引用传递”的效率几乎相当。\n\n> 小结：<br>1.对于非内部数据类型的输入参数，应该将“值传递”的方式改为“const 引用传递”，目的是提高效率。例如将void func(A a) 改为void func(const A &a)。<br><br>2.对于内部数据类型的输入参数，不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的，又降低了函数的可理解性。例如void func(int x) 不应该改为void func(const int &x)。</font></p>\n\n以上解决了两个面试问题：\n\n+ 如果函数需要传入一个指针，是否需要为该指针加上const，把const加在指针不同的位置有什么区别；\n+ 如果写的函数需要传入的参数是一个复杂类型的实例，传入值参数或者引用参数有什么区别，什么时候需要为传入的引用参数加上const。 \n\n## 7.类中使用const\n\n在一个类中，任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时，不慎修改 数据成员，或者调用了其它非const成员函数，编译器将指出错误，这无疑会提高程序的健壮性。\n\n使用const关键字进行说明的成员函数，称为常成员函数。只有常成员函数才有资格操作常量或常对象，没有使用const关键字进行说明的成员函数不能用来操作常对象。\n\n对于类中的const成员变量必须通过初始化列表进行初始化，如下所示：\n\n```cpp\nclass Apple{\nprivate:\n    int people[100];\npublic:\n    Apple(int i); \n    const int apple_number;\n};\n\nApple::Apple(int i):apple_number(i)\n{\n\n}\n```\n\nconst对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数.\n\n例如：\n\n```cpp\n// apple.cpp\nclass Apple\n{\nprivate:\n    int people[100];\npublic:\n    Apple(int i); \n    const int apple_number;\n    void take(int num) const;\n    int add();\n    int add(int num) const;\n    int getCount() const;\n\n};\n// apple.cpp\nApple::Apple(int i) : apple_number(i)\n{\n}\nint Apple::add(int num)\n{\n    take(num);\n    return 0;\n}\nint Apple::add(int num) const\n{\n    take(num);\n    return 0;\n}\nvoid Apple::take(int num) const\n{\n    std::cout << \"take func \" << num << std::endl;\n}\nint Apple::getCount() const\n{\n    take(1);\n    //    add(); // error\n    return apple_number;\n}\nint main()\n{\n    Apple a(2);\n    cout << a.getCount() << endl;\n    a.add(10);\n    const Apple b(3);\n    b.add(100);\n    return 0;\n}\n// main.cpp\n```\n> 编译：bazel run basic_content/const/class_const/first_example:main<br>\n\n此时报错，上面getCount()方法中调用了一个add方法，而add方法并非const修饰，所以运行报错。也就是说const成员函数只能访问const成员函数。\n\n当调用改为：\n\n```\nconst Apple b(3);\nb.add(); // error\n```\n此时，可以证明的是const对象只能访问const成员函数。\n\n我们除了上述的初始化const常量用初始化列表方式外，也可以通过下面方法：\n\n第一：将常量定义与static结合，也就是：\n\n```cpp\nstatic const int apple_number\n```\n\n第二：在外面初始化：\n\n```cpp\nconst int Apple::apple_number=10;\n```\n\n当然，如果你使用c++11进行编译，直接可以在定义出初始化，可以直接写成：\n\n```cpp\nstatic const int apple_number=10;\n// 或者\nconst int apple_number=10;\n```\n\n这两种都在c++11中支持！\n\n编译的时候加上`-std=c++11`即可！\n\n这里提到了static，下面简单的说一下：\n\n在C++中，static静态成员变量不能在类的内部初始化。在类的内部只是声明，定义必须在类定义体的外部，通常在类的实现文件中初始化。\n\n在类中声明：\n\n```cpp\nstatic int ap;\n```\n\n在类实现文件中使用：\n\n```cpp\nint Apple::ap=666\n```\n\n对于此项，c++11不能进行声明并初始化，也就是上述使用方法。\n"
  },
  {
    "path": "basic_content/const/class_const/c++11_example/BUILD",
    "content": "# please run `bazel run basic_content/const/class_const/c++11_example:main`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\")\n\ncc_library(\n    name = \"apple\",\n    srcs = [\"apple.cpp\"],\n    hdrs = [\"apple.h\"],\n)\n\ncc_binary(\n    name = \"main\",\n    srcs = [\"main.cpp\"],\n    deps = [\n        \":apple\",\n    ],\n)"
  },
  {
    "path": "basic_content/const/class_const/c++11_example/apple.cpp",
    "content": "#include \"apple.h\"\n#include <iostream>\n\nusing namespace std;\nApple::Apple(int i) {}\nint Apple::add(int num) {\n  take(num);\n  return 0;\n}\nint Apple::add(int num) const {\n  take(num);\n  return 0;\n}\nvoid Apple::take(int num) const { cout << \"take func \" << num << endl; }\nint Apple::getCount() const {\n  take(1);\n  //    add(); //error\n  return apple_number;\n}"
  },
  {
    "path": "basic_content/const/class_const/c++11_example/apple.h",
    "content": "#pragma once\n\nclass Apple {\npublic:\n  Apple(int i);\n  // 使用c++11标准编译\n  static const int apple_number = 10;\n  // const int apple_number=10;\n  void take(int num) const;\n  int add(int num);\n  int add(int num) const;\n  int getCount() const;\n};\n"
  },
  {
    "path": "basic_content/const/class_const/c++11_example/main.cpp",
    "content": "#include \"apple.h\"\n#include <iostream>\nusing namespace std;\nint main() {\n  Apple a(2);\n  cout << a.getCount() << endl;\n  a.add(10);\n  const Apple b(3);\n  b.add(100);\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/const/class_const/first_example/BUILD",
    "content": "# please run `bazel run basic_content/const/class_const/first_example:main`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\")\n\ncc_library(\n    name = \"apple\",\n    srcs = [\"apple.cpp\"],\n    hdrs = [\"apple.h\"],\n)\n\ncc_binary(\n    name = \"main\",\n    srcs = [\"main.cpp\"],\n    deps = [\n        \":apple\",\n    ],\n)"
  },
  {
    "path": "basic_content/const/class_const/first_example/apple.cpp",
    "content": "#include \"apple.h\"\n#include <iostream>\n\nApple::Apple(int i) : apple_number(i) {}\nint Apple::add(int num) {\n  take(num);\n  return 0;\n}\nint Apple::add(int num) const {\n  take(num);\n  return 0;\n}\nvoid Apple::take(int num) const {\n  std::cout << \"take func \" << num << std::endl;\n}\nint Apple::getCount() const {\n  take(1);\n  //    add(); //error\n  return apple_number;\n}"
  },
  {
    "path": "basic_content/const/class_const/first_example/apple.h",
    "content": "#pragma once\n\nclass Apple {\npublic:\n  Apple(int i);\n  const int apple_number;\n  void take(int num) const;\n  int add(int num);\n  int add(int num) const;\n  int getCount() const;\n};\n"
  },
  {
    "path": "basic_content/const/class_const/first_example/main.cpp",
    "content": "#include \"apple.h\"\n#include <iostream>\nusing namespace std;\n\nint main() {\n  Apple a(2);\n  cout << a.getCount() << endl;\n  a.add(10);\n  const Apple b(3);\n  b.add(100);\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/const/class_const/overload_example/BUILD",
    "content": "# please run `bazel run basic_content/const/class_const/overload_example:main`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\")\n\ncc_library(\n    name = \"apple\",\n    srcs = [\"apple.cpp\"],\n    hdrs = [\"apple.h\"],\n)\n\n# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.\ncc_binary(\n    name = \"main\",\n    srcs = [\"main.cpp\"],\n    deps = [\n        \":apple\",\n    ],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/const/class_const/overload_example/apple.cpp",
    "content": "#include \"apple.h\"\n#include <iostream>\n\nint Apple::apple_number = 10;\n\nApple::Apple(int i) { apple_number = i; }\nint Apple::add() {\n  take(1);\n  return 0;\n}\nint Apple::add(int num) const {\n  take(num);\n  return num;\n}\nvoid Apple::take(int num) const {\n  std::cout << \"take func \" << num << std::endl;\n}\nint Apple::getCount() const {\n  take(1);\n  add(); // error\n  return apple_number;\n}"
  },
  {
    "path": "basic_content/const/class_const/overload_example/apple.h",
    "content": "#pragma once\n\nclass Apple {\npublic:\n  Apple(int i);\n  static int apple_number;\n  void take(int num) const;\n  int add();\n  int add(int num) const;\n  int getCount() const;\n};\n"
  },
  {
    "path": "basic_content/const/class_const/overload_example/main.cpp",
    "content": "#include \"apple.h\"\n#include <iostream>\nusing namespace std;\n\nint main() {\n  Apple a(2);\n  cout << a.getCount() << endl;\n  a.add(10);\n  //     const Apple b(3);\n  //     b.add(); // error\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/const/class_const/static_example/BUILD",
    "content": "# please run `bazel run basic_content/const/class_const/static_example:main`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\")\n\ncc_library(\n    name = \"apple\",\n    srcs = [\"apple.cpp\"],\n    hdrs = [\"apple.h\"],\n)\n\ncc_binary(\n    name = \"main\",\n    srcs = [\"main.cpp\"],\n    deps = [\n        \":apple\",\n    ],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/const/class_const/static_example/apple.cpp",
    "content": "#include \"apple.h\"\n#include <iostream>\n\nconst int Apple::apple_number = 10;\nint Apple::ap = 666;\nApple::Apple(int i) {}\nint Apple::add(int num) {\n  take(num);\n  return 0;\n}\nint Apple::add(int num) const {\n  take(num);\n  return 0;\n}\nvoid Apple::take(int num) const {\n  std::cout << \"take func \" << num << std::endl;\n}\nint Apple::getCount() const {\n  take(1);\n  //    add(); //error\n  return apple_number;\n}"
  },
  {
    "path": "basic_content/const/class_const/static_example/apple.h",
    "content": "#pragma once\nclass Apple {\npublic:\n  Apple(int i);\n  static int ap; // 在类实现文件中定义并初始化\n  static const int apple_number;\n  void take(int num) const;\n  int add(int num);\n  int add(int num) const;\n  int getCount() const;\n};\n"
  },
  {
    "path": "basic_content/const/class_const/static_example/main.cpp",
    "content": "#include \"apple.h\"\n#include <iostream>\nint main() {\n  Apple a(2);\n  std::cout << a.getCount() << std::endl;\n  std::cout << a.ap << std::endl;\n  a.add(10);\n  const Apple b(3);\n  b.add(100);\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/const/const_function.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nvoid f(const int i) {\n  i = 10; // error: assignment of read-only parameter ‘i’\n  cout << i << endl;\n}\n\nint main() { f(1); }\n"
  },
  {
    "path": "basic_content/const/const_num.cpp",
    "content": "#include <iostream>\nusing namespace std;\nint main() {\n  const int b = 10;\n  b = 0; // error\n  const string s = \"helloworld\";\n  const int i, j = 0;\n}\n"
  },
  {
    "path": "basic_content/const/extern_const/BUILD",
    "content": "# please run `bazel run basic_content/const/extern_const:const_file1` and `# please run `bazel run basic_content/const/extern_const:file`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"const_file1\",\n    srcs = [\"const_file1.cpp\", \"const_file2.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"file\",\n    srcs = [\"file1.cpp\", \"file2.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/const/extern_const/const_file1.cpp",
    "content": "extern const int ext = 12;"
  },
  {
    "path": "basic_content/const/extern_const/const_file2.cpp",
    "content": "#include <iostream>\n/**\n * by 光城\n * compile: g++ -o file const_file2.cpp const_file1.cpp\n * execute: ./file\n */\nextern const int ext;\nint main() { std::cout << ext << std::endl; }"
  },
  {
    "path": "basic_content/const/extern_const/file1.cpp",
    "content": "int ext;"
  },
  {
    "path": "basic_content/const/extern_const/file2.cpp",
    "content": "#include <iostream>\n/**\n * by 光城\n * compile: g++ -o file file2.cpp file1.cpp\n * execute: ./file\n */\nextern int ext;\nint main() { std::cout << (ext + 10) << std::endl; }"
  },
  {
    "path": "basic_content/const/funciton_const/condition1/BUILD",
    "content": "# please run `bazel run basic_content/const/funciton_const/condition2:condition1`\n# please run `bazel run basic_content/const/funciton_const/condition2:condition2`\n# please run `bazel run basic_content/const/funciton_const/condition2:condition3`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\n# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.\ncc_binary(\n    name = \"condition1\",\n    srcs = [\"condition1.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\n# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.\ncc_binary(\n    name = \"condition2\",\n    srcs = [\"condition2.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"condition3\",\n    srcs = [\"condition3.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/const/funciton_const/condition1/condition1.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nint main() {\n  const int *ptr;\n  *ptr = 10; // error\n}\n"
  },
  {
    "path": "basic_content/const/funciton_const/condition1/condition2.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nint main() {\n  const int p = 10;\n  // const void *vp = &p;  // const pointer to const data\n  void *vp = (void*)&p; // ✅ C-style cast\n}\n"
  },
  {
    "path": "basic_content/const/funciton_const/condition1/condition3.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nint main() {\n  const int *ptr;\n  int val = 3;\n  ptr = &val; // ok\n  int *ptr1 = &val;\n  *ptr1 = 4;\n  cout << *ptr << endl;\n}\n"
  },
  {
    "path": "basic_content/const/funciton_const/condition2/BUILD",
    "content": "# please run `bazel run basic_content/const/funciton_const/condition2:condition1`\n# please run `bazel run basic_content/const/funciton_const/condition2:condition2`\n# please run `bazel run basic_content/const/funciton_const/condition2:condition3`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"condition1\",\n    srcs = [\"condition1.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\n# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.\ncc_binary(\n    name = \"condition2\",\n    srcs = [\"condition2.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"condition3\",\n    srcs = [\"condition3.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/const/funciton_const/condition2/condition1.cpp",
    "content": "#include <iostream>\nusing namespace std;\nint main() {\n  int num = 0;\n  int *const ptr = &num; // const指针必须初始化！且const指针的指向不能修改\n  int *t = &num;\n  *t = 1;\n  cout << *ptr << endl;\n}\n"
  },
  {
    "path": "basic_content/const/funciton_const/condition2/condition2.cpp",
    "content": "#include <iostream>\nusing namespace std;\nint main() {\n  const int num = 0;\n  int *const ptr = &num; // error! const int* -> int*\n  cout << *ptr << endl;\n}\n"
  },
  {
    "path": "basic_content/const/funciton_const/condition2/condition3.cpp",
    "content": "#include <iostream>\nusing namespace std;\nint main() {\n  const int num = 10;\n  const int *const ptr = &num;\n  cout << *ptr << endl;\n}\n"
  },
  {
    "path": "basic_content/const/funciton_const/condition3/BUILD",
    "content": "# please run `bazel run basic_content/const/funciton_const/condition3:condition1`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\n# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.\ncc_binary(\n    name = \"condition1\",\n    srcs = [\"condition1.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/const/funciton_const/condition3/condition1.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nint main() {\n  const int p = 3;\n  const int *const ptr = &p;\n  cout << *ptr << endl;\n}\n"
  },
  {
    "path": "basic_content/decltype/BUILD",
    "content": "# please run `bazel run basic_content/decltype:decltype`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"decltype\",\n    srcs = [\"decltype.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n"
  },
  {
    "path": "basic_content/decltype/README.md",
    "content": "# decltype那些事\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 1.基本使用\ndecltype的语法是:\n\n```\ndecltype (expression)\n```\n\n这里的括号是必不可少的,decltype的作用是“查询表达式的类型”，因此，上面语句的效果是，返回 expression 表达式的类型。注意，decltype 仅仅“查询”表达式的类型，并不会对表达式进行“求值”。\n\n\n### 1.1 推导出表达式类型\n\n```\nint i = 4;\ndecltype(i) a; //推导结果为int。a的类型为int。\n```\n\n### 1.2 与using/typedef合用，用于定义类型。\n\n```c++\nusing size_t = decltype(sizeof(0));//sizeof(a)的返回值为size_t类型\nusing ptrdiff_t = decltype((int*)0 - (int*)0);\nusing nullptr_t = decltype(nullptr);\nvector<int >vec;\ntypedef decltype(vec.begin()) vectype;\nfor (vectype i = vec.begin; i != vec.end(); i++)\n{\n//...\n}\n```\n\n这样和auto一样，也提高了代码的可读性。\n\n### 1.3 重用匿名类型\n\n在C++中，我们有时候会遇上一些匿名类型，如:\n\n```c++\nstruct \n{\n    int d ;\n    double b;\n}anon_s;\n```\n\n而借助decltype，我们可以重新使用这个匿名的结构体：\n\n```c++\ndecltype(anon_s) as ;//定义了一个上面匿名的结构体\n```\n\n### 1.4 泛型编程中结合auto，用于追踪函数的返回值类型\n\n这也是decltype最大的用途了。\n\n```c++\ntemplate <typename T>\nauto multiply(T x, T y)->decltype(x*y)\n{\n\treturn x*y;\n}\n```\n\n完整代码见：[decltype.cpp](decltype.cpp)\n\n## 2.判别规则\n\n对于decltype(e)而言，其判别结果受以下条件的影响：\n\n如果e是一个没有带括号的标记符表达式或者类成员访问表达式，那么的decltype（e）就是e所命名的实体的类型。此外，如果e是一个被重载的函数，则会导致编译错误。\n否则 ，假设e的类型是T，如果e是一个将亡值，那么decltype（e）为T&&\n否则，假设e的类型是T，如果e是一个左值，那么decltype（e）为T&。\n否则，假设e的类型是T，则decltype（e）为T。\n\n标记符指的是除去关键字、字面量等编译器需要使用的标记之外的程序员自己定义的标记，而单个标记符对应的表达式即为标记符表达式。例如：\n```c++\nint arr[4]\n```\n则arr为一个标记符表达式，而arr[3]+0不是。\n\n举例如下：\n\n```c++\nint i = 4;\nint arr[5] = { 0 };\nint *ptr = arr;\nstruct S{ double d; }s ;\nvoid Overloaded(int);\nvoid Overloaded(char);//重载的函数\nint && RvalRef();\nconst bool Func(int);\n\n//规则一：推导为其类型\ndecltype (arr) var1; //int[] 标记符表达式\n\ndecltype (ptr) var2;//int *  标记符表达式\n\ndecltype(s.d) var3;//doubel 成员访问表达式\n\n//decltype(Overloaded) var4;//重载函数。编译错误。\n\n//规则二：将亡值。推导为类型的右值引用。\n\ndecltype (RvalRef()) var5 = 1;\n\n//规则三：左值，推导为类型的引用。\n\ndecltype ((i))var6 = i;     //int&\n\ndecltype (true ? i : i) var7 = i; //int&  条件表达式返回左值。\n\ndecltype (++i) var8 = i; //int&  ++i返回i的左值。\n\ndecltype(arr[5]) var9 = i;//int&. []操作返回左值\n\ndecltype(*ptr)var10 = i;//int& *操作返回左值\n\ndecltype(\"hello\")var11 = \"hello\"; //const char(&)[9]  字符串字面常量为左值，且为const左值。\n\n\n//规则四：以上都不是，则推导为本类型\n\ndecltype(1) var12;//const int\n\ndecltype(Func(1)) var13=true;//const bool\n\ndecltype(i++) var14 = i;//int i++返回右值\n```\n\n学习参考：https://www.cnblogs.com/QG-whz/p/4952980.html\n"
  },
  {
    "path": "basic_content/decltype/decltype.cpp",
    "content": "#include <iostream>\n#include <vector>\nusing namespace std;\n/**\n * 泛型编程中结合auto，用于追踪函数的返回值类型\n */\ntemplate <typename T>\n\nauto multiply(T x, T y) -> decltype(x * y) {\n  return x * y;\n}\n\nint main() {\n  int nums[] = {1, 2, 3, 4};\n  vector<int> vec(nums, nums + 4);\n  vector<int>::iterator it;\n\n  for (it = vec.begin(); it != vec.end(); it++)\n    cout << *it << \" \";\n  cout << endl;\n\n  using nullptr_t = decltype(nullptr);\n  nullptr_t nu;\n  int *p = NULL;\n  if (p == nu)\n    cout << \"NULL\" << endl;\n\n  typedef decltype(vec.begin()) vectype;\n\n  for (vectype i = vec.begin(); i != vec.end(); i++)\n    cout << *i << \" \";\n  cout << endl;\n\n  /**\n   * 匿名结构体\n   */\n  struct {\n    int d;\n    double b;\n  } anon_s;\n\n  decltype(anon_s) as{1, 2.0}; // 定义了一个上面匿名的结构体\n\n  cout << multiply(11, 2) << \":\" << as.b << endl;\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/enum/BUILD",
    "content": "# please run `bazel run basic_content/enum:classic_practice`\n# please run `bazel run basic_content/enum:tradition_color`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"classic_practice\",\n    srcs = [\"classic_practice.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\n# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.\ncc_binary(\n    name = \"tradition_color\",\n    srcs = [\"tradition_color.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/enum/README.md",
    "content": "# 从初级到高级的enum那些事\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n\n## 传统行为\n\n\n枚举有如下问题：\n\n- 作用域不受限,会容易引起命名冲突。例如下面无法编译通过的：\n\n```c++\nenum Color\n{\n    RED,\n    BLUE\n};\nenum Feeling\n{\n    EXCITED,\n    BLUE\n};\n\nint main()\n{\n    Color a = BLUE; // error\n    Feeling b = EXCITED;\n    std::cout << a << \":\" << b << std::endl;\n    return 0;\n}\n```\n- 会隐式转换为int\n- 用来表征枚举变量的实际类型不能明确指定，从而无法支持枚举类型的前向声明。\n\n具体实现见：[tradition_color.cpp](tradition_color.cpp)\n\n## 经典做法\n\n解决作用域不受限带来的命名冲突问题的一个简单方法是，给枚举变量命名时加前缀，如上面例子改成 COLOR_BLUE 以及 FEELING_BLUE。\n\n一般说来，为了一致性我们会把所有常量统一加上前缀。但是这样定义枚举变量的代码就显得累赘。C 程序中可能不得不这样做。不过 C++ 程序员恐怕都不喜欢这种方法。替代方案是命名空间:\n```c++\nnamespace Color \n{\n    enum Type\n    {\n        RED=15,\n        YELLOW,\n        BLUE\n    };\n};\n```\n\n这样之后就可以用 `Color::Type c = Color::RED;` 来定义新的枚举变量了。如果 `using namespace Color` 后，前缀还可以省去，使得代码简化。不过，因为命名空间是可以随后被扩充内容的，所以它提供的作用域封闭性不高。在大项目中，还是有可能不同人给不同的东西起同样的枚举类型名。\n\n更“有效”的办法是用一个类或结构体来限定其作用域，例如：定义新变量的方法和上面命名空间的相同。不过这样就不用担心类在别处被修改内容。这里用结构体而非类，是因为本身希望这些常量可以公开访问。\n\n```c++\nstruct Color1\n{\n    enum Type\n    {\n        RED=102,\n        YELLOW,\n        BLUE\n    };\n};\n```\n\n具体实现见：[classic_practice.cpp](classic_practice.cpp)\n\n## C++11 的枚举类\n\n上面的做法解决了第一个问题，但对于后两个仍无能为力。庆幸的是，C++11 标准中引入了“枚举类”(enum class)，可以较好地解决上述问题。\n\n- 新的enum的作用域不在是全局的\n- 不能隐式转换成其他类型\n\n```c++\n/**\n * @brief C++11的枚举类\n * 下面等价于enum class Color2:int\n */\nenum class Color2\n{\n    RED=2,\n    YELLOW,\n    BLUE\n};\nColor2 c2 = Color2::RED;\ncout << static_cast<int>(c2) << endl; //必须转！\n```\n\n- 可以指定用特定的类型来存储enum\n\n```c++\nenum class Color3:char;  // 前向声明\n\n// 定义\nenum class Color3:char \n{\n    RED='r',\n    BLUE\n};\nchar c3 = static_cast<char>(Color3::RED);\n```\n\n具体实现见：[classic_practice.cpp](classic_practice.cpp)\n\n## 类中的枚举类型\n\n有时我们希望某些常量只在类中有效。 由于#define 定义的宏常量是全局的，不能达到目的，于是想到使用const 修饰数据成员来实现。而const 数据成员的确是存在的，但其含义却不是我们所期望的。\n\nconst 数据成员只在某个对象生存期内是常量，而对于整个类而言却是可变的，因为类可以创建多个对象，不同的对象其 const 数据成员的值可以不同。 \n\n不能在类声明中初始化 const 数据成员。以下用法是错误的，因为类的对象未被创建时，编译器不知道 SIZE 的值是什么。(c++11标准前)\n\n```c++\nclass A \n{\n  const int SIZE = 100;   // 错误，企图在类声明中初始化 const 数据成员 \n  int array[SIZE];  // 错误，未知的 SIZE \n}; \n```\n\n正确应该在类的构造函数的初始化列表中进行：\n\n```c++\nclass A \n{\n  A(int size);  // 构造函数 \n  const int SIZE ;    \n}; \nA::A(int size) : SIZE(size)  // 构造函数的定义\n{ \n\n} \nA  a(100); // 对象 a 的 SIZE 值为 100 \nA  b(200); // 对象 b 的 SIZE 值为 200 \n```\n\n怎样才能建立在整个类中都恒定的常量呢？\n\n别指望 const 数据成员了，应该用类中的枚举常量来实现。例如:\n\n```c++\nclass Person{\npublic:\n    typedef enum {\n        BOY = 0,\n        GIRL\n    }SexType;\n};\n//访问的时候通过，Person::BOY或者Person::GIRL来进行访问。\n```\n\n枚举常量不会占用对象的存储空间，它们在编译时被全部求值。\n\n枚举常量的缺点是：它的隐含数据类型是整数，其最大值有限，且不能表示浮点。\n"
  },
  {
    "path": "basic_content/enum/classic_practice.cpp",
    "content": "#include <iostream>\nusing namespace std;\n/**\n * @brief namespace解决作用域不受限\n */\nnamespace Color {\nenum Type { RED = 15, YELLOW, BLUE };\n};\n\n/**\n * @brief 上述如果 using namespace Color 后，前缀还可以省去，使得代码简化。\n * 不过，因为命名空间是可以随后被扩充内容的，所以它提供的作用域封闭性不高。\n * 在大项目中，还是有可能不同人给不同的东西起同样的枚举类型名。\n * 更“有效”的办法是用一个类或结构体来限定其作用域。\n *\n * 定义新变量的方法和上面命名空间的相同。\n * 不过这样就不用担心类在别处被修改内容。\n * 这里用结构体而非类，一是因为本身希望这些常量可以公开访问，\n * 二是因为它只包含数据没有成员函数。\n */\nstruct Color1 {\n  enum Type { RED = 102, YELLOW, BLUE };\n};\n\n/**\n * @brief C++11的枚举类\n * 下面等价于enum class Color2:int\n */\nenum class Color2 { RED = 2, YELLOW, BLUE };\n\nenum class Color3 : char; // 前向声明\n\n// 定义\nenum class Color3 : char { RED = 'r', BLUE };\n\nint main() {\n  // 定义新的枚举变量\n  Color::Type c = Color::RED;\n  cout << c << endl;\n  /**\n   * 上述的另一种方法：\n   * using namespace Color; // 定义新的枚举变量\n   * Type c = RED;\n   */\n  Color1 c1;\n  cout << c1.RED << endl;\n\n  Color1::Type c11 = Color1::BLUE;\n  cout << c11 << endl;\n\n  Color2 c2 = Color2::RED;\n  cout << static_cast<int>(c2) << endl;\n\n  char c3 = static_cast<char>(Color3::RED);\n  cout << c3 << endl;\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/enum/tradition_color.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nenum Color { RED, BLUE };\nenum Feeling { EXCITED, BLUE };\n\nint main() {\n  Color a = BLUE; // error\n  Feeling b = EXCITED;\n  std::cout << a << \":\" << b << std::endl;\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/explicit/BUILD",
    "content": "# please run `bazel run basic_content/explicit:explicit`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\n# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.\ncc_binary(\n    name = \"explicit\",\n    srcs = [\"explicit.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/explicit/README.md",
    "content": "# explicit(显式)关键字那些事\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n- explicit 修饰构造函数时，可以防止隐式转换和复制初始化\n- explicit 修饰转换函数时，可以防止隐式转换，但按语境转换除外\n\n\n代码参见:[.explicit.cpp](./explicit.cpp)\n\n参考链接：\n> https://stackoverflow.com/questions/4600295/what-is-the-meaning-of-operator-bool-const\n"
  },
  {
    "path": "basic_content/explicit/explicit.cpp",
    "content": "#include <iostream>\n\nusing namespace std;\n\nstruct A {\n  A(int) {}\n  operator bool() const { return true; }\n};\n\nstruct B {\n  explicit B(int) {}\n  explicit operator bool() const { return true; }\n};\n\nvoid doA(A a) {}\n\nvoid doB(B b) {}\n\nint main() {\n  A a1(1);     // OK：直接初始化\n  A a2 = 1;    // OK：复制初始化\n  A a3{1};     // OK：直接列表初始化\n  A a4 = {1};  // OK：复制列表初始化\n  A a5 = (A)1; // OK：允许 static_cast 的显式转换\n  doA(1);      // OK：允许从 int 到 A 的隐式转换\n  if (a1)\n    ; // OK：使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换\n  bool a6(a1); // OK：使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换\n  bool a7 = a1; // OK：使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换\n  bool a8 = static_cast<bool>(a1); // OK ：static_cast 进行直接初始化\n\n  B b1(1);     // OK：直接初始化\n               //    B b2 = 1;        // 错误：被 explicit\n               //    修饰构造函数的对象不可以复制初始化\n  B b3{1};     // OK：直接列表初始化\n               //    B b4 = { 1 };        // 错误：被 explicit\n               //    修饰构造函数的对象不可以复制列表初始化\n  B b5 = (B)1; // OK：允许 static_cast 的显式转换\n               //    doB(1);            // 错误：被 explicit\n               //    修饰构造函数的对象不可以从 int 到 B 的隐式转换\n  if (b1)\n    ; // OK：被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool\n      // 的按语境转换\n  bool b6(b1); // OK：被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B\n               // 到 bool 的按语境转换\n               //    bool b7 = b1;        // 错误：被 explicit 修饰转换函数\n               //    B::operator bool() 的对象不可以隐式转换\n  bool b8 = static_cast<bool>(b1); // OK：static_cast 进行直接初始化\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/extern/README.md",
    "content": "# extern \"C\"\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 1.C++与C编译区别\n\n在C++中常在头文件见到extern \"C\"修饰函数，那有什么作用呢？ 是用于C++链接在C语言模块中定义的函数。\n\nC++虽然兼容C，但C++文件中函数编译后生成的符号与C语言生成的不同。因为C++支持函数重载，C++函数编译后生成的符号带有函数参数类型的信息，而C则没有。\n\n例如`int add(int a, int b)`函数经过C++编译器生成.o文件后，`add`会变成形如`add_int_int`之类的, 而C的话则会是形如`_add`, 就是说：相同的函数，在C和C++中，编译后生成的符号不同。\n\n这就导致一个问题：如果C++中使用C语言实现的函数，在编译链接的时候，会出错，提示找不到对应的符号。此时`extern \"C\"`就起作用了：告诉链接器去寻找`_add`这类的C语言符号，而不是经过C++修饰的符号。\n\n## 2.C++调用C函数\n\nC++调用C函数的例子: 引用C的头文件时，需要加`extern \"C\"`\n\n```c++\n//add.h\n#ifndef ADD_H\n#define ADD_H\nint add(int x,int y);\n#endif\n\n//add.c\n#include \"add.h\"\n\nint add(int x,int y) {\n    return x+y;\n}\n\n//add.cpp\n#include <iostream>\n#include \"add.h\"\nusing namespace std;\nint main() {\n    add(2,3);\n    return 0;\n}\n```\n\n编译：\n\n```\n//Generate add.o file\ngcc -c add.c\n```\n\n链接：\n\n```\ng++ add.cpp add.o -o main\n```\n\n没有添加extern \"C\" 报错：\n\n```c++\n> g++ add.cpp add.o -o main                                   \nadd.o：在函数‘main’中：\nadd.cpp:(.text+0x0): `main'被多次定义\n/tmp/ccH65yQF.o:add.cpp:(.text+0x0)：第一次在此定义\n/tmp/ccH65yQF.o：在函数‘main’中：\nadd.cpp:(.text+0xf)：对‘add(int, int)’未定义的引用\nadd.o：在函数‘main’中：\nadd.cpp:(.text+0xf)：对‘add(int, int)’未定义的引用\ncollect2: error: ld returned 1 exit status\n```\n\n添加extern \"C\"后：\n\n`add.cpp`\n\n```c++\n#include <iostream>\nusing namespace std;\nextern \"C\" {\n    #include \"add.h\"\n}\nint main() {\n    std::cout << add(2, 3) << std::endl;\n    return 0;\n}\n```\n\n编译的时候一定要注意，先通过gcc生成中间文件add.o。\n\n```\ngcc -c add.c \n```\n\n然后编译：\n\n```\ng++ add.cpp add.o -o main\n```\n\n如果使用bazel编译，运行:`bazel run basic_content/extern/extern_c++:main`\n\n而通常为了C代码能够通用，即既能被C调用，又能被C++调用，头文件通常会有如下写法：\n\n```c\n#ifdef __cplusplus\nextern \"C\"{\n#endif\nint add(int x,int y);\n#ifdef __cplusplus\n}\n#endif\n```\n\n即在C++调用该接口时，会以C接口的方式调用。这种方式使得C++者不需要额外的extern C，而标准库头文件通常也是类似的做法，否则你为何不需要extern C就可以直接使用stdio.h中的C函数呢？\n\n\n上述案例源代码见：\n\n- [add.h](extern_c++/add.h)\n\n- [add.c](extern_c++/add.c)\n\n- [add.cpp](extern_c++/add.cpp)\n\n## 2.C中调用C++函数\n\n`extern \"C\"`在C中是语法错误，需要放在C++头文件中。\n\n```c\n// add.h\n#ifndef ADD_H\n#define ADD_H\nextern \"C\" {\n    int add(int x,int y);\n}\n#endif\n\n// add.cpp\n#include \"add.h\"\n\nint add(int x,int y) {\n    return x+y;\n}\n\n// add.c\nextern int add(int x,int y);\nint main() {\n    add(2,3);\n    return 0;\n}\n```\n\n编译：\n\n```c\ng++ -c add.cpp\n```\n\n链接：\n\n```\ngcc add.c add.o -o main\n```\n\n上述案例源代码见：\n\n- [add.h](extern_c/add.h)\n\n- [add.c](extern_c/add.c)\n\n- [add.cpp](extern_c/add.cpp)\n\n综上，总结出使用方法，在C语言的头文件中，对其外部函数只能指定为extern类型，C语言中不支持extern \"C\"声明，在.c文件中包含了extern \"C\"时会出现编译语法错误。所以使用extern \"C\"全部都放在于cpp程序相关文件或其头文件中。\n\n总结出如下形式：\n\n（1）C++调用C函数：\n\n```c++\n//xx.h\nextern int add(...)\n\n//xx.c\nint add(){\n    \n}\n\n//xx.cpp\nextern \"C\" {\n    #include \"xx.h\"\n}\n```\n\n（2）C调用C++函数\n\n```c\n//xx.h\nextern \"C\"{\n    int add();\n}\n//xx.cpp\nint add(){\n    \n}\n//xx.c\nextern int add();\n```\n\n\n不过与C++调用C接口不同，C++确实是能够调用编译好的C函数，而这里C调用C++，不过是把C++代码当成C代码编译后调用而已。也就是说，C并不能直接调用C++库函数。\n"
  },
  {
    "path": "basic_content/extern/extern_c/BUILD",
    "content": "# please run `bazel run basic_content/extern/extern_c:main`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\")\n\ncc_library(\n    name = \"add\",\n    srcs = [\"add.cpp\"],\n    hdrs = [\"add.h\"],\n)\n\ncc_binary(\n    name = \"main\",\n    srcs = [\"add.c\"],\n    deps = [\":add\"],\n)"
  },
  {
    "path": "basic_content/extern/extern_c/add.c",
    "content": "extern int add(int x,int y);\nint main() {\n    add(2,3);\n    return 0;\n}\n"
  },
  {
    "path": "basic_content/extern/extern_c/add.cpp",
    "content": "#include \"add.h\"\n\nint add(int x, int y) { return x + y; }\n"
  },
  {
    "path": "basic_content/extern/extern_c/add.h",
    "content": "#ifndef ADD_H\n#define ADD_H\nextern \"C\" {\nint add(int x, int y);\n}\n#endif\n"
  },
  {
    "path": "basic_content/extern/extern_c++/BUILD",
    "content": "# please run `bazel run basic_content/extern/extern_c++:main`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\")\n\ncc_library(\n    name = \"add\",\n    srcs = [\"add.c\"],\n    hdrs = [\"add.h\"],\n)\n\ncc_binary(\n    name = \"main\",\n    srcs = [\"add.cpp\"],\n    deps = [\":add\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/extern/extern_c++/add.c",
    "content": "#include \"add.h\"\n\nint add(int x,int y) {\n    return x+y;\n}\n"
  },
  {
    "path": "basic_content/extern/extern_c++/add.cpp",
    "content": "#include <iostream>\nusing namespace std;\nextern \"C\" {\n#include \"add.h\"\n}\nint main() {\n  std::cout << add(2, 3) << std::endl;\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/extern/extern_c++/add.h",
    "content": "#ifndef ADD_H\n#define ADD_H\nextern int add(int x, int y);\n#endif\n"
  },
  {
    "path": "basic_content/friend/BUILD",
    "content": "# please run `bazel run basic_content/friend:friend_class`\n# please run `bazel run basic_content/friend:friend_func`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"friend_class\",\n    srcs = [\"friend_class.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"friend_func\",\n    srcs = [\"friend_func.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/friend/README.md",
    "content": "# 友元函数与友元类\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 0.概述\n\n友元提供了一种 普通函数或者类成员函数 访问另一个类中的私有或保护成员 的机制。也就是说有两种形式的友元：\n\n（1）友元函数：普通函数对一个访问某个类中的私有或保护成员。\n\n（2）友元类：类A中的成员函数访问类B中的私有或保护成员\n\n优点：提高了程序的运行效率。\n\n缺点：破坏了类的封装性和数据的透明性。\n\n总结：\n- 能访问私有成员\n- 破坏封装性\n- 友元关系不可传递\n- 友元关系的单向性\n- 友元声明的形式及数量不受限制\n\n## 1.友元函数\n\n在类声明的任何区域中声明，而定义则在类的外部。\n\n```\nfriend <类型><友元函数名>(<参数表>);\n```\n\n注意，友元函数只是一个普通函数，并不是该类的类成员函数，它可以在任何地方调用，友元函数中通过对象名来访问该类的私有或保护成员。\n\n具体代码见：[friend_func.cpp](friend_func.cpp)\n\n```c++\n#include <iostream>\n\nusing namespace std;\n\nclass A\n{\npublic:\n    A(int _a):a(_a){};\n    friend int geta(A &ca);  ///< 友元函数\nprivate:\n    int a;\n};\n\nint geta(A &ca) \n{\n    return ca.a;\n}\n\nint main()\n{\n    A a(3);    \n    cout<<geta(a)<<endl;\n\n    return 0;\n}\n```\n\n## 2.友元类\n友元类的声明在该类的声明中，而实现在该类外。\n\n```\nfriend class <友元类名>;\n```\n\n类B是类A的友元，那么类B可以直接访问A的私有成员。\n\n具体代码见：[friend_class.cpp](friend_class.cpp)\n```c++\n#include <iostream>\n\nusing namespace std;\n\nclass A\n{\npublic:\n    A(int _a):a(_a){};\n    friend class B;\nprivate:\n    int a;\n};\n\nclass B\n{\npublic:\n    int getb(A ca) {\n        return  ca.a; \n    };\n};\n\nint main() \n{\n    A a(3);\n    B b;\n    cout<<b.getb(a)<<endl;\n    return 0;\n}\n```\n\n\n## 3.注意\n- 友元关系没有继承性\n假如类B是类A的友元，类C继承于类A，那么友元类B是没办法直接访问类C的私有或保护成员。\n\n- 友元关系没有传递性\n假如类B是类A的友元，类C是类B的友元，那么友元类C是没办法直接访问类A的私有或保护成员，也就是不存在“友元的友元”这种关系。\n"
  },
  {
    "path": "basic_content/friend/friend_class.cpp",
    "content": "#include <iostream>\n\nusing namespace std;\n\nclass A {\npublic:\n  A(int _a) : a(_a){};\n  friend class B;\n\nprivate:\n  int a;\n};\n\nclass B {\npublic:\n  int getb(A ca) { return ca.a; };\n};\n\nint main() {\n  A a(3);\n  B b;\n  cout << b.getb(a) << endl;\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/friend/friend_func.cpp",
    "content": "#include <iostream>\n\nusing namespace std;\n\nclass A {\npublic:\n  A(int _a) : a(_a){};\n  friend int geta(A &ca); ///< 友元函数\nprivate:\n  int a;\n};\n\nint geta(A &ca) { return ca.a; }\n\nint main() {\n  A a(3);\n  cout << geta(a) << endl;\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/func_pointer/BUILD",
    "content": "# please run `bazel run basic_content/func_pointer:func_pointer`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"func_pointer\",\n    srcs = [\"func_pointer.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/func_pointer/func_pointer.cpp",
    "content": "/**\n * @file func_pointer.cpp\n * @brief 函数指针的使用！\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include <iostream>\nusing namespace std;\n\n/**\n * @brief\n * 定义了一个变量pFun，这个变量是个指针，指向返回值为空和参数为int的函数的指针！\n */\nvoid (*pFun)(int);\n\n/**\n * @brief 代表一种新类型，不是变量！所以与上述的pFun不一样！\n */\ntypedef void (*func)(void);\n\nvoid myfunc(void) { cout << \"asda\" << endl; }\n\nvoid glFun(int a) { cout << a << endl; }\nint main() {\n  func pfun = myfunc; /*赋值*/\n  pfun();             /*调用*/\n  pFun = glFun;\n  (*pFun)(2);\n}\n"
  },
  {
    "path": "basic_content/inline/BUILD",
    "content": "# please run `bazel run basic_content/inline:inline_virtual`\n# please run `bazel run basic_content/inline:main`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\")\n\ncc_library(\n    name = \"inline\",\n    hdrs = [\"inline.h\"],\n)\n\ncc_binary(\n    name = \"inline_virtual\",\n    srcs = [\"inline_virtual.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"main\",\n    srcs = [\"inline.cpp\"],\n    deps = [\n        \":inline\",\n    ],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/inline/README.md",
    "content": "# inline那些事\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 1.类中内联\n\n头文件中声明方法\n\n```c++\n\nclass A\n{\npublic:\n    void f1(int x); \n\n    /**\n     * @brief 类中定义了的函数是隐式内联函数,声明要想成为内联函数，必须在实现处(定义处)加inline关键字。\n     *\n     * @param x\n     * @param y\n     */\n    void Foo(int x,int y) ///< 定义即隐式内联函数！\n    {\n    \n    };\n    void f2(int x); ///< 声明后，要想成为内联函数，必须在定义处加inline关键字。  \n};\n```\n\n实现文件中定义内联函数：\n\n```c++\n#include <iostream>\n#include \"inline.h\"\n\nusing namespace std;\n\n/**\n * @brief inline要起作用,inline要与函数定义放在一起,inline是一种“用于实现的关键字,而不是用于声明的关键字”\n *\n * @param x\n * @param y\n *\n * @return \n */\nint Foo(int x,int y);  // 函数声明\ninline int Foo(int x,int y) // 函数定义\n{\n    return x+y;\n}\n\n// 定义处加inline关键字，推荐这种写法！\ninline void A::f1(int x){\n\n}\n\nint main()\n{\n\n    \n    cout<<Foo(1,2)<<endl;\n\n}\n/**\n * 编译器对 inline 函数的处理步骤\n * 将 inline 函数体复制到 inline 函数调用点处；\n * 为所用 inline 函数中的局部变量分配内存空间；\n * 将 inline 函数的的输入参数和返回值映射到调用方法的局部变量空间中；\n * 如果 inline 函数有多个返回点，将其转变为 inline 函数代码块末尾的分支（使用 GOTO）。\n */\n\n```\n\n内联能提高函数效率，但并不是所有的函数都定义成内联函数！内联是以代码膨胀(复制)为代价，仅仅省去了函数调用的开销，从而提高函数的执行效率。\n\n- 如果执行函数体内代码的时间相比于函数调用的开销较大，那么效率的收获会更少！\n\n- 另一方面，每一处内联函数的调用都要复制代码，将使程序的总代码量增大，消耗更多的内存空间。\n\n以下情况不宜用内联：\n\n（1）如果函数体内的代码比较长，使得内联将导致内存消耗代价比较高。\n\n（2）如果函数体内出现循环，那么执行函数体内代码的时间要比函数调用的开销大。\n\n## 2.虚函数（virtual）可以是内联函数（inline）吗？\n\n- 虚函数可以是内联函数，内联是可以修饰虚函数的，但是当虚函数表现多态性的时候不能内联。\n- 内联是在编译期建议编译器内联，而虚函数的多态性在运行期，编译器无法知道运行期调用哪个代码，因此虚函数表现为多态性时（运行期）不可以内联。\n- `inline virtual` 唯一可以内联的时候是：编译器知道所调用的对象是哪个类（如 `Base::who()`），这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。\n\n```c++\n#include <iostream>  \nusing namespace std;\nclass Base\n{\npublic:\n    inline virtual void who()\n    {\n        cout << \"I am Base\\n\";\n    }\n    virtual ~Base() {}\n};\nclass Derived : public Base\n{\npublic:\n    inline void who()  // 不写inline时隐式内联\n    {\n        cout << \"I am Derived\\n\";\n    }\n};\n\nint main()\n{\n    // 此处的虚函数 who()，是通过类（Base）的具体对象（b）来调用的，编译期间就能确定了，所以它可以是内联的，但最终是否内联取决于编译器。 \n    Base b;\n    b.who();\n\n    // 此处的虚函数是通过指针调用的，呈现多态性，需要在运行时期间才能确定，所以不能为内联。  \n    Base *ptr = new Derived();\n    ptr->who();\n\n    // 因为Base有虚析构函数（virtual ~Base() {}），所以 delete 时，会先调用派生类（Derived）析构函数，再调用基类（Base）析构函数，防止内存泄漏。\n    delete ptr;\n    ptr = nullptr;\n\n    \n    return 0;\n} \n```\n\n\n\n"
  },
  {
    "path": "basic_content/inline/inline.cpp",
    "content": "#include \"inline.h\"\n#include <iostream>\n\nusing namespace std;\n\n/**\n * @brief\n * inline要起作用,inline要与函数定义放在一起,inline是一种“用于实现的关键字,而不是用于声明的关键字”\n *\n * @param x\n * @param y\n *\n * @return\n */\nint Foo(int x, int y);       // 函数声明\ninline int Foo(int x, int y) // 函数定义\n{\n  return x + y;\n}\n\n// 定义处加inline关键字，推荐这种写法！\ninline void A::f1(int x) {}\n\n/**\n * @brief\n * 内联能提高函数效率，但并不是所有的函数都定义成内联函数！内联是以代码膨胀(复制)为代价，仅仅省去了函数调用的开销，从而提高函数的执行效率。\n * 如果执行函数体内代码的时间相比于函数调用的开销较大，那么效率的收货会更少！另一方面，每一处内联函数的调用都要复制代码，将使程序的总代码量增大，消耗更多的内存空间。\n * 以下情况不宜用内联：\n *  （1） 如果函数体内的代码比较长，使得内联将导致内存消耗代价比较高。\n *   (2)  如果函数体内出现循环，那么执行函数体内代码的时间要比函数调用的开销大。\n *\n * @return\n */\nint main() { cout << Foo(1, 2) << endl; }\n/**\n * 编译器对 inline 函数的处理步骤\n * 将 inline 函数体复制到 inline 函数调用点处；\n * 为所用 inline 函数中的局部变量分配内存空间；\n * 将 inline 函数的的输入参数和返回值映射到调用方法的局部变量空间中；\n * 如果 inline 函数有多个返回点，将其转变为 inline 函数代码块末尾的分支（使用\n * GOTO）。\n */\n"
  },
  {
    "path": "basic_content/inline/inline.h",
    "content": "#pragma once\nclass A {\npublic:\n  void f1(int x);\n\n  /**\n   * @brief\n   * 类中定义了的函数是隐式内联函数,声明要想成为内联函数，必须在实现处(定义处)加inline关键字。\n   *\n   * @param x\n   * @param y\n   */\n  void Foo(int x, int y) ///< 定义即隐式内联函数！\n      {\n\n      };\n  void f2(int x); ///< 声明后，要想成为内联函数，必须在定义处加inline关键字。\n};\n"
  },
  {
    "path": "basic_content/inline/inline_virtual.cpp",
    "content": "#include <iostream>\nusing namespace std;\nclass Base {\npublic:\n  inline virtual void who() { cout << \"I am Base\\n\"; }\n  virtual ~Base() {}\n};\nclass Derived : public Base {\npublic:\n  inline void who() // 不写inline时隐式内联\n  {\n    cout << \"I am Derived\\n\";\n  }\n};\n\nint main() {\n  // 此处的虚函数\n  // who()，是通过类（Base）的具体对象（b）来调用的，编译期间就能确定了，所以它可以是内联的，但最终是否内联取决于编译器。\n  Base b;\n  b.who();\n\n  // 此处的虚函数是通过指针调用的，呈现多态性，需要在运行时期间才能确定，所以不能为内联。\n  Base *ptr = new Derived();\n  ptr->who();\n\n  // 因为Base有虚析构函数（virtual ~Base() {}），所以 delete\n  // 时，会先调用派生类（Derived）析构函数，再调用基类（Base）析构函数，防止内存泄漏。\n  delete ptr;\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/macro/BUILD",
    "content": "# please run `bazel run basic_content/macro:do_while`\n# please run `bazel run basic_content/sig_examp:sig_examp`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"do_while\",\n    srcs = [\"do_while.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n\ncc_binary(\n    name = \"sig_examp\",\n    srcs = [\"sig_examp.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n"
  },
  {
    "path": "basic_content/macro/README.md",
    "content": "# 宏那些事\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 1.宏中包含特殊符号\n\n分为几种：`#`，`##`，`\\`\n\n### 1.1 字符串化操作符（#）\n\n**在一个宏中的参数前面使用一个#,预处理器会把这个参数转换为一个字符数组**，换言之就是：**#是“字符串化”的意思，出现在宏定义中的#是把跟在后面的参数转换成一个字符串**。\n\n**注意：其只能用于有传入参数的宏定义中，且必须置于宏定义体中的参数名前。**\n\n例如：\n\n```c++\n#define exp(s) printf(\"test s is:%s\\n\",s)\n#define exp1(s) printf(\"test s is:%s\\n\",#s)\n#define exp2(s) #s \nint main() {\n    exp(\"hello\");\n    exp1(hello);\n\n    string str = exp2(   bac );\n    cout<<str<<\" \"<<str.size()<<endl;\n    /**\n     * 忽略传入参数名前面和后面的空格。\n     */\n    string str1 = exp2( asda  bac );\n    /**\n     * 当传入参数名间存在空格时，编译器将会自动连接各个子字符串，\n     * 用每个子字符串之间以一个空格连接，忽略剩余空格。\n     */\n    cout<<str1<<\" \"<<str1.size()<<endl;\n    return 0;\n}\n```\n\n上述代码给出了基本的使用与空格处理规则，空格处理规则如下：\n\n- 忽略传入参数名前面和后面的空格。\n\n```c++\nstring str = exp2(   bac );\ncout<<str<<\" \"<<str.size()<<endl;\n```\n\n输出：\n\n```\nbac 3\n```\n\n- 当传入参数名间存在空格时，编译器将会自动连接各个子字符串，用每个子字符串之间以一个空格连接，忽略剩余空格。\n\n```c++\nstring str1 = exp2( asda  bac );\ncout<<str1<<\" \"<<str1.size()<<endl;\n```\n\n输出：\n\n```\nasda bac 8\n```\n\n### 1.2 符号连接操作符（##）\n\n**“##”是一种分隔连接方式，它的作用是先分隔，然后进行强制连接。将宏定义的多个形参转换成一个实际参数名。**\n\n注意事项：\n\n**（1）当用##连接形参时，##前后的空格可有可无。**\n\n**（2）连接后的实际参数名，必须为实际存在的参数名或是编译器已知的宏定义。**\n\n**（3）如果##后的参数本身也是一个宏的话，##会阻止这个宏的展开。**\n\n示例：\n\n```c++\n\n#define expA(s) printf(\"前缀加上后的字符串为:%s\\n\",gc_##s)  //gc_s必须存在\n// 注意事项2\n#define expB(s) printf(\"前缀加上后的字符串为:%s\\n\",gc_  ##  s)  //gc_s必须存在\n// 注意事项1\n#define gc_hello1 \"I am gc_hello1\"\nint main() {\n    // 注意事项1\n    const char * gc_hello = \"I am gc_hello\";\n    expA(hello);\n    expB(hello1);\n}\n```\n\n### 1.3 续行操作符（\\） \n\n**当定义的宏不能用一行表达完整时，可以用”\\”表示下一行继续此宏的定义。**\n\n**注意 \\ 前留空格。**\n\n```c++\n#define MAX(a,b) ((a)>(b) ? (a) \\\n   :(b))  \nint main() {\n    int max_val = MAX(3,6);\n    cout<<max_val<<endl;\n}\n```\n\n上述代码见：[sig_examp.cpp](sig_examp.cpp)\n\n## 2.do{...}while(0)的使用\n\n### 2.1 避免语义曲解\n\n例如：\n\n```\n#define fun() f1();f2();\nif(a>0)\n\tfun()\n```\n\n这个宏被展开后就是：\n\n```\nif(a>0)\n\tf1();\n\tf2();\n```\n\n本意是a>0执行f1 f2，而实际是f2每次都会执行，所以就错误了。\n\n为了解决这种问题，在写代码的时候，通常可以采用`{}`块。\n\n如：\n\n```c++\n#define fun() {f1();f2();}\nif(a>0)\n\tfun();\n// 宏展开\nif(a>0)\n{\n    f1();\n    f2();\n};\n```\n\n但是会发现上述宏展开后多了一个分号，实际语法不太对。(虽然编译运行没问题，正常没分号)。\n\n### 2.2避免使用goto控制流\n\n在一些函数中，我们可能需要在return语句之前做一些清理工作，比如释放在函数开始处由malloc申请的内存空间，使用goto总是一种简单的方法：\n\n```c++\nint f() {\n    int *p = (int *)malloc(sizeof(int));\n    *p = 10; \n    cout<<*p<<endl;\n#ifndef DEBUG\n    int error=1;\n#endif\n    if(error)\n        goto END;\n    // dosomething\nEND:\n    cout<<\"free\"<<endl;\n    free(p);\n    return 0;\n}\n```\n\n但由于goto不符合软件工程的结构化，而且有可能使得代码难懂，所以很多人都不倡导使用，这个时候我们可以使用do{...}while(0)来做同样的事情：\n\n```c++\nint ff() {\n    int *p = (int *)malloc(sizeof(int));\n    *p = 10; \n    cout<<*p<<endl;\n    do{ \n#ifndef DEBUG\n        int error=1;\n#endif\n        if(error)\n            break;\n        //dosomething\n    }while(0);\n    cout<<\"free\"<<endl;\n    free(p);\n    return 0;\n}\n```\n\n这里将函数主体部分使用do{...}while(0)包含起来，使用break来代替goto，后续的清理工作在while之后，现在既能达到同样的效果，而且代码的可读性、可维护性都要比上面的goto代码好的多了。\n\n### 2.3 避免由宏引起的警告\n\n内核中由于不同架构的限制，很多时候会用到空宏，。在编译的时候，这些空宏会给出warning，为了避免这样的warning，我们可以使用do{...}while(0)来定义空宏：\n\n```\n#define EMPTYMICRO do{}while(0)\n```\n\n### 2.4 **定义单一的函数块来完成复杂的操作**\n\n如果你有一个复杂的函数，变量很多，而且你不想要增加新的函数，可以使用do{...}while(0)，将你的代码写在里面，里面可以定义变量而不用考虑变量名会同函数之前或者之后的重复。\n这种情况应该是指一个变量多处使用（但每处的意义还不同），我们可以在每个do-while中缩小作用域，比如：\n\n```c++\nint fc()\n{\n    int k1 = 10;\n    cout<<k1<<endl;\n    do{\n        int k1 = 100;\n        cout<<k1<<endl;\n    }while(0);\n    cout<<k1<<endl;\n}\n```\n\n上述代码见：[do_while.cpp](do_while.cpp)\n\n学习文章：<https://www.cnblogs.com/lizhenghn/p/3674430.html>\n\n"
  },
  {
    "path": "basic_content/macro/do_while.cpp",
    "content": "#include <iostream>\n#include <malloc.h>\n\nusing namespace std;\n#define f1() cout << \"f1()\" << endl;\n#define f2() cout << \"f2()\" << endl;\n\n#define fun()                                                                  \\\n  {                                                                            \\\n    f1();                                                                      \\\n    f2();                                                                      \\\n  }\n#define fun1()                                                                 \\\n  do {                                                                         \\\n    f1();                                                                      \\\n    f2();                                                                      \\\n  } while (0)\n\nint f() {\n  int *p = (int *)malloc(sizeof(int));\n  *p = 10;\n  cout << *p << endl;\n#ifndef DEBUG\n  int error = 1;\n#endif\n  if (error)\n    goto END;\n\n  // dosomething\nEND:\n  cout << \"free\" << endl;\n  free(p);\n  return 0;\n}\n\nint ff() {\n\n  int *p = (int *)malloc(sizeof(int));\n  *p = 10;\n  cout << *p << endl;\n\n  do {\n#ifndef DEBUG\n    int error = 1;\n#endif\n    if (error)\n      break;\n    // dosomething\n  } while (0);\n\n  cout << \"free\" << endl;\n  free(p);\n  return 0;\n}\n\nint fc() {\n  int k1 = 10;\n  cout << k1 << endl;\n  do {\n    int k1 = 100;\n    cout << k1 << endl;\n  } while (0);\n  cout << k1 << endl;\n  return 0;\n}\n\nint main() {\n\n  if (1 > 0)\n    fun();\n\n  if (2 > 0)\n    fun1();\n\n  f();\n  ff();\n  fc();\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/macro/sig_examp.cpp",
    "content": "#include <cstring>\n#include <iostream>\n#include <stdio.h>\n#include <string>\n\nusing namespace std;\n\n///===========================================\n/**\n * (#)字符串操作符\n */\n///===========================================\n#define exp(s) printf(\"test s is:%s\\n\", s)\n#define exp1(s) printf(\"test s is:%s\\n\", #s)\n\n#define exp2(s) #s\n\n///===========================================\n/**\n *（##）符号连接操作符\n */\n///===========================================\n#define expA(s) printf(\"前缀加上后的字符串为:%s\\n\", gc_##s) // gc_s必须存在\n\n#define expB(s) printf(\"前缀加上后的字符串为:%s\\n\", gc_##s) // gc_s必须存在\n\n#define gc_hello1 \"I am gc_hello1\"\n\n///===========================================\n/**\n * (\\)续行操作符\n */\n///===========================================\n#define MAX(a, b) ((a) > (b) ? (a) : (b))\n\nint main() {\n  ///===========================================\n  /**\n   * (#)字符串操作符\n   */\n  ///===========================================\n  exp(\"hello\");\n  exp1(hello);\n\n  string str = exp2(bac);\n  cout << str << \" \" << str.size() << endl;\n  /**\n   * 忽略传入参数名前面和后面的空格。\n   */\n  string str1 = exp2(asda bac);\n  /**\n   * 当传入参数名间存在空格时，编译器将会自动连接各个子字符串，\n   * 用每个子字符串之间以一个空格连接，忽略剩余空格。\n   */\n  cout << str1 << \" \" << str1.size() << endl;\n\n  ///===========================================\n  /**\n   * (#)字符串操作符\n   */\n  ///===========================================\n\n  const char *gc_hello = \"I am gc_hello\";\n  expA(hello);\n  expB(hello1);\n\n  char var1_p[20];\n  char var2_p[20];\n\n  // 连接后的实际参数名赋值\n  strcpy(var1_p, \"aaaa\");\n  strcpy(var2_p, \"bbbb\");\n\n  ///===========================================\n  /**\n   * (\\)续行操作符\n   */\n  ///===========================================\n  int max_val = MAX(3, 6);\n  cout << max_val << endl;\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/maohao/BUILD",
    "content": "# please run `bazel run basic_content/maohao:maohao`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"maohao\",\n    srcs = [\"maohao.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/maohao/README.md",
    "content": "# :: 范围解析运算符那些事\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n- 全局作用域符（::name）：用于类型名称（类、类成员、成员函数、变量等）前，表示作用域为全局命名空间\n- 类作用域符（class::name）：用于表示指定类型的作用域范围是具体某个类的\n- 命名空间作用域符（namespace::name）:用于表示指定类型的作用域范围是具体某个命名空间的\n\n具体代码见：[maohao.cpp](maohao.cpp)\n\n"
  },
  {
    "path": "basic_content/maohao/maohao.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nint count = 0; // 全局(::)的count\n\nclass A {\npublic:\n  static int count; // 类A的count (A::count)\n};\n// 静态变量必须在此处定义\nint A::count;\nint main() {\n  ::count = 1;  // 设置全局的count为1\n  A::count = 5; // 设置类A的count为2\n  cout << A::count << endl;\n  //    int count=3; // 局部count\n  //    count=4;     // 设置局部的count为4\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/pointer_refer/BUILD",
    "content": "# please run `bazel run basic_content/pointer_refer:copy_construct`\n# please run `bazel run basic_content/pointer_refer:effec`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"copy_construct\",\n    srcs = [\"copy_construct.cpp\"],\n    copts = [\"-std=c++11\", \"-fno-elide-constructors\"]\n)\n\ncc_binary(\n    name = \"effec\",\n    srcs = [\"effec.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/pointer_refer/README.md",
    "content": "# 引用与指针那些事\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 1.引用与指针\n\n总论：\n\n| 引用         | 指针         |\n| ------------ | ------------ |\n| 必须初始化   | 可以不初始化 |\n| 不能为空     | 可以为空     |\n| 不能更换目标 | 可以更换目标 |\n\n> 引用必须初始化，而指针可以不初始化。\n\n我们在定义一个引用的时候必须为其指定一个初始值，但是指针却不需要。\n\n```c++\nint &r;    //不合法，没有初始化引用\nint *p;    //合法，但p为野指针，使用需要小心\n```\n\n> 引用不能为空，而指针可以为空。\n\n由于引用不能为空，所以我们在使用引用的时候不需要测试其合法性，而在使用指针的时候需要首先判断指针是否为空指针，否则可能会引起程序崩溃。\n\n```c++\nvoid test_p(int* p)\n{\n  \tif(p != null_ptr)    //对p所指对象赋值时需先判断p是否为空指针\n    \t*p = 3;\n    return;\n}\nvoid test_r(int& r)\n{\n    r = 3;    //由于引用不能为空，所以此处无需判断r的有效性就可以对r直接赋值\n    return;\n}\n```\n\n> 引用不能更换目标\n\n指针可以随时改变指向，但是引用只能指向初始化时指向的对象，无法改变。\n\n```\nint a = 1;\nint b = 2;\n\nint &r = a;    //初始化引用r指向变量a\nint *p = &a;   //初始化指针p指向变量a\n\np = &b;        //指针p指向了变量b\nr = b;         //引用r依然指向a，但a的值变成了b\n```\n\n## 2.引用\n\n#### 左值引用\n\n常规引用，一般表示对象的身份。\n\n#### 右值引用\n\n右值引用就是必须绑定到右值（一个临时对象、将要销毁的对象）的引用，一般表示对象的值。\n\n右值引用可实现转移语义（Move Sementics）和精确传递（Perfect Forwarding），它的主要目的有两个方面：\n\n- 消除两个对象交互时不必要的对象拷贝，节省运算存储资源，提高效率。\n- 能够更简洁明确地定义泛型函数。\n\n#### 引用折叠\n\n- `X& &`、`X& &&`、`X&& &` 可折叠成 `X&`\n- `X&& &&` 可折叠成 `X&&`\n\nC++的引用**在减少了程序员自由度的同时提升了内存操作的安全性和语义的优美性**。比如引用强制要求必须初始化，可以让我们在使用引用的时候不用再去判断引用是否为空，让代码更加简洁优美，避免了指针满天飞的情形。除了这种场景之外引用还用于如下两个场景：\n\n> 引用型参数\n\n一般我们使用const reference参数作为只读形参，这种情况下既可以避免参数拷贝还可以获得与传值参数一样的调用方式。\n\n```c++\nvoid test(const vector<int> &data)\n{\n    //...\n}\nint main()\n{\n  \tvector<int> data{1,2,3,4,5,6,7,8};\n    test(data);\n}\n```\n\n> 引用型返回值\n\nC++提供了重载运算符的功能，我们在重载某些操作符的时候，使用引用型返回值可以获得跟该操作符原来语法相同的调用方式，保持了操作符语义的一致性。一个例子就是operator []操作符，这个操作符一般需要返回一个引用对象，才能正确的被修改。\n\n```c++\nvector<int> v(10);\nv[5] = 10;    //[]操作符返回引用，然后vector对应元素才能被修改\n              //如果[]操作符不返回引用而是指针的话，赋值语句则需要这样写\n*v[5] = 10;   //这种书写方式，完全不符合我们对[]调用的认知，容易产生误解\n```\n\n## 3.指针与引用的性能差距\n\n指针与引用之间有没有性能差距呢？这种问题就需要进入汇编层面去看一下。我们先写一个test1函数，参数传递使用指针：\n\n```c++\nvoid test1(int* p)\n{\n    *p = 3;    //此处应该首先判断p是否为空，为了测试的需要，此处我们没加。\n    return;\n}\n```\n\n该代码段对应的汇编代码如下：\n\n```c++\n(gdb) disassemble \nDump of assembler code for function test1(int*):\n   0x0000000000400886 <+0>:  push   %rbp\n   0x0000000000400887 <+1>:  mov    %rsp,%rbp\n   0x000000000040088a <+4>:  mov    %rdi,-0x8(%rbp)\n=> 0x000000000040088e <+8>:  mov    -0x8(%rbp),%rax\n   0x0000000000400892 <+12>: movl   $0x3,(%rax)\n   0x0000000000400898 <+18>: nop\n   0x0000000000400899 <+19>: pop    %rbp\n   0x000000000040089a <+20>: retq   \nEnd of assembler dump.\n\n```\n\n上述代码1、2行是参数调用保存现场操作；第3行是参数传递，函数调用第一个参数一般放在rdi寄存器，此行代码把rdi寄存器值（指针p的值）写入栈中；第4行是把栈中p的值写入rax寄存器；第5行是把立即数3写入到**rax寄存器值所指向的内存**中，此处要注意(%rax)两边的括号，这个括号并并不是可有可无的，(%rax)和%rax完全是两种意义，(%rax)代表rax寄存器中值所代表地址部分的内存，即相当于C++代码中的*p，而%rax代表rax寄存器，相当于C++代码中的p值，所以汇编这里使用了(%rax)而不是%rax。\n\n我们再写出参数传递使用引用的C++代码段test2：\n\n```c++\nvoid test2(int& r)\n{\n    r = 3;    //赋值前无需判断reference是否为空\n    return;\n}\n```\n\n这段代码对应的汇编代码如下：\n\n```c++\n(gdb) disassemble \nDump of assembler code for function test2(int&):\n   0x000000000040089b <+0>:  push   %rbp\n   0x000000000040089c <+1>:  mov    %rsp,%rbp\n   0x000000000040089f <+4>:  mov    %rdi,-0x8(%rbp)\n=> 0x00000000004008a3 <+8>:  mov    -0x8(%rbp),%rax\n   0x00000000004008a7 <+12>: movl   $0x3,(%rax)\n   0x00000000004008ad <+18>: nop\n   0x00000000004008ae <+19>: pop    %rbp\n   0x00000000004008af <+20>: retq   \nEnd of assembler dump.\n\n```\n\n我们发现test2对应的汇编代码和test1对应的汇编代码完全相同，这说明C++编译器在编译程序的时候将指针和引用编译成了完全一样的机器码。所以C++中的引用只是C++对指针操作的一个“语法糖”，在底层实现时C++编译器实现这两种操作的方法完全相同。\n\n## 3.总结\n\nC++中引入了引用操作，在对引用的使用加了更多限制条件的情况下，保证了引用使用的安全性和便捷性，还可以保持代码的优雅性。在适合的情况使用适合的操作，引用的使用可以一定程度避免“指针满天飞”的情况，对于提升程序稳定性也有一定的积极意义。最后，指针与引用底层实现都是一样的，不用担心两者的性能差距。\n\n上述部分参考自：<http://irootlee.com/juicer_pointer_reference/#>\n"
  },
  {
    "path": "basic_content/pointer_refer/copy_construct.cpp",
    "content": "/**\n * @file copy_construct.cpp\n * @brief g++ -o copy_construct copy_construct.cpp -fno-elide-constructors\n * -fno-elide-constructors选项(关闭返回值优化)\n * @author 光城\n * @version v1\n * @date 2019-08-09\n */\n\n#include <iostream>\nusing namespace std;\n\nclass Copyable {\npublic:\n  Copyable() {}\n  Copyable(const Copyable &o) { cout << \"Copied\" << endl; }\n};\nCopyable ReturnRvalue() {\n  return Copyable(); // 返回一个临时对象\n}\nvoid AcceptVal(Copyable a) {}\nvoid AcceptRef(const Copyable &a) {}\n\nint main() {\n  cout << \"pass by value: \" << endl;\n  AcceptVal(ReturnRvalue()); // 应该调用两次拷贝构造函数\n  cout << \"pass by reference: \" << endl;\n  AcceptRef(ReturnRvalue()); // 应该只调用一次拷贝构造函数\n}\n"
  },
  {
    "path": "basic_content/pointer_refer/effec.cpp",
    "content": "#include <iostream>\nusing namespace std;\nvoid test1(int *p) {\n  *p = 3; //此处应该首先判断p是否为空，为了测试的需要，此处我们没加。\n  return;\n}\n\nvoid test2(int &p) {\n  p = 3; //此处应该首先判断p是否为空，为了测试的需要，此处我们没加。\n  return;\n}\n\nint main() {\n  int a = 10;\n  int *p = &a;\n  test1(p);\n  test2(a);\n  cout << a << endl;\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/sizeof/BUILD",
    "content": "# please run `bazel run basic_content/sizeof:blackclass`\n# please run `bazel run basic_content/sizeof:genA`\n# please run `bazel run basic_content/sizeof:geninhe`\n# please run `bazel run basic_content/sizeof:moreinhe`\n# please run `bazel run basic_content/sizeof:morevir`\n# please run `bazel run basic_content/sizeof:static`\n# please run `bazel run basic_content/sizeof:virinhe`\n# please run `bazel run basic_content/sizeof:virmoreinhe`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"blackclass\",\n    srcs = [\"blackclass.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"genA\",\n    srcs = [\"genA.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"geninhe\",\n    srcs = [\"geninhe.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"moreinhe\",\n    srcs = [\"moreinhe.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"static\",\n    srcs = [\"static.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"virinhe\",\n    srcs = [\"virinhe.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"virmoreinhe\",\n    srcs = [\"virmoreinhe.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/sizeof/README.md",
    "content": "# 类大小计算\n\n首先来个总结，然后下面给出实际例子，实战！\n\n- 空类的大小为1字节\n- 一个类中，虚函数本身、成员函数（包括静态与非静态）和静态数据成员都是不占用类对象的存储空间。\n- 对于包含虚函数的类，不管有多少个虚函数，只有一个虚指针,vptr的大小。\n- 普通继承，派生类继承了所有基类的函数与成员，要按照字节对齐来计算大小\n- 虚函数继承，不管是单继承还是多继承，都是继承了基类的vptr。(32位操作系统4字节，64位操作系统 8字节)！\n- 虚继承,继承基类的vptr。\n\n## 1.原则1\n\n```c++\n/**\n * @file blackclass.cpp\n * @brief 空类的大小为1字节\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n#include<iostream>\nusing namespace std;\nclass A{};\nint main()\n{\n    cout<<sizeof(A)<<endl;\n    return 0;\n}\n```\n\n## 2.原则2\n\n```c++\n/**\n * @file static.cpp\n * @brief 静态数据成员\n * 静态数据成员被编译器放在程序的一个global data members中，它是类的一个数据成员，但不影响类的大小。不管这个类产生了多少个实例，还是派生了多少新的类，静态数据成员只有一个实例。静态数据成员，一旦被声明，就已经存在。 \n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n#include<iostream>\nusing namespace std;\nclass A\n{\n    public:\n        char b;\n        virtual void fun() {};\n        static int c;\n        static int d;\n        static int f;\n};\n\nint main()\n{\n    /**\n     * @brief 16  字节对齐、静态变量不影响类的大小、vptr指针=8\n     */\n    cout<<sizeof(A)<<endl; \n    return 0;\n}\n```\n\n## 3.原则3\n\n```c++\n/**\n * @file morevir.cpp\n * @brief 对于包含虚函数的类，不管有多少个虚函数，只有一个虚指针,vptr的大小。\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n#include<iostream>\nusing namespace std;\nclass A{\n    virtual void fun();\n    virtual void fun1();\n    virtual void fun2();\n    virtual void fun3();\n};\nint main()\n{\n    cout<<sizeof(A)<<endl; // 8\n    return 0;\n}\n```\n\n## 4.原则4与5\n\n```c++\n/**\n * @file geninhe.cpp\n * @brief 1.普通单继承,继承就是基类+派生类自身的大小(注意字节对齐)\n * 注意：类的数据成员按其声明顺序加入内存，无访问权限无关，只看声明顺序。\n * 2.虚单继承，派生类继承基类vptr\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include<iostream>\n\nusing namespace std;\n\nclass A\n{\n    public:\n        char a;\n        int b;\n};\n\n/**\n * @brief 此时B按照顺序：\n * char a\n * int b\n * short a\n * long b\n * 根据字节对齐4+4+8+8=24\n * \n * 或编译器优化\n * char a\n * short a\n * int b\n * long b\n * 根据字节对齐2+2+4+8=16\n */\nclass B:A\n{\n    public:\n        short a;\n        long b;\n};\n/**\n* 把A的成员拆开看，char为1，int为4，所以是1+（3）+4+1+（3）=12，（）为字节补齐\n*/\nclass C\n{\n    A a;\n    char c;\n};\n\nclass A1\n{\n    virtual void fun(){}\n};\nclass C1:public A1\n{\n};\n\n\nint main()\n{\n    cout<<sizeof(A)<<endl; // 8\n    cout<<sizeof(B)<<endl; // 16 或 24\n    cout<<sizeof(C)<<endl; // 12\n\n    /**\n     * @brief 对于虚单函数继承，派生类也继承了基类的vptr，所以是8字节\n     */\n    cout<<sizeof(C1)<<endl; // 8 \n    return 0;\n}\n```\n\n## 5.原则6\n\n```c++\n/**\n * @file virnhe.cpp\n * @brief 虚继承\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include<iostream>\nusing namespace std;\nclass A\n{\n    virtual void fun() {}\n};\nclass B\n{\n    virtual void fun2() {}\n};\nclass C : virtual public  A, virtual public B\n{\n    public:\n        virtual void fun3() {}\n};\n\nint main()\n{\n    /**\n     * @brief 8 8 16  派生类虚继承多个虚函数，会继承所有虚函数的vptr\n     */\n    cout<<sizeof(A)<<\" \"<<sizeof(B)<<\" \"<<sizeof(C);\n\n    return 0;\n}\n```\n\n"
  },
  {
    "path": "basic_content/sizeof/blackclass.cpp",
    "content": "/**\n * @file blackclass.cpp\n * @brief 空类的大小为1字节\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include <bits/stdc++.h>\n#include <iostream>\nusing namespace std;\nclass A {};\nint main() {\n  cout << sizeof(A) << endl;\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/sizeof/genA.cpp",
    "content": "/**\n * @file genA.cpp\n * @brief\n * 普通成员函数，大小为1,一个类中，虚函数本身、成员函数（包括静态与非静态）和静态数据成员都是不占用类对象的存储空间。\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include <iostream>\n\nusing namespace std;\n\nclass A {\npublic:\n  A();\n  ~A();\n  static int a;\n  static void fun3();\n  void fun();\n  void fun1();\n};\n\nint main() {\n  cout << sizeof(A) << endl; // 1\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/sizeof/geninhe.cpp",
    "content": "/**\n * @file geninhe.cpp\n * @brief 1.普通单继承,继承就是基类+派生类自身的大小(注意字节对齐)\n * 注意：类的数据成员按其声明顺序加入内存，无访问权限无关，只看声明顺序。\n * 2.虚单继承，派生类继承基类vptr\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include <iostream>\n\nusing namespace std;\n\nclass A {\npublic:\n  char a;\n  int b;\n};\n\n/**\n * @brief 此时B按照顺序：\n * char a\n * int b\n * short a\n * long b\n * 根据字节对齐4+4+8+8=24\n *\n * 或编译器优化\n * char a\n * short a\n * int b\n * long b\n * 根据字节对齐2+2+4+8=16\n */\nclass B : A {\npublic:\n  short a;\n  long b;\n};\n/**\n * 把A的成员拆开看，char为1，int为4，所以是1+（3）+4+1+（3）=12，（）为字节补齐\n */\nclass C {\n  A a;\n  char c;\n};\n\nclass A1 {\n  virtual void fun() {}\n};\nclass C1 : public A1 {};\n\nint main() {\n  cout << sizeof(A) << endl; // 8\n  cout << sizeof(B) << endl; // 16\n  cout << sizeof(C) << endl; // 12\n\n  /**\n   * @brief 对于虚单函数继承，派生类也继承了基类的vptr，所以是8字节\n   */\n  cout << sizeof(C1) << endl; // 8\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/sizeof/moreinhe.cpp",
    "content": "/**\n * @file moreinhe.cpp\n * @brief 普通多继承与虚函数多继承\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include <iostream>\n\nusing namespace std;\n\nclass A {\npublic:\n  char a;\n  int b;\n};\n\nclass B {\npublic:\n  short a;\n  long b;\n};\n\n/**\n * @brief 8+16+8=32\n */\nclass C : A, B {\n  char c;\n};\n\nint main() {\n  cout << sizeof(A) << endl; // 8\n  cout << sizeof(B) << endl; // 16\n  cout << sizeof(C) << endl; // 32\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/sizeof/morevir.cpp",
    "content": "/**\n * @file morevir.cpp\n * @brief 对于包含虚函数的类，不管有多少个虚函数，只有一个虚指针,vptr的大小。\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include <iostream>\n\nusing namespace std;\n\nclass A {\n\n  virtual void fun();\n  virtual void fun1();\n  virtual void fun2();\n  virtual void fun3();\n};\nint main() {\n  cout << sizeof(A) << endl;\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/sizeof/static.cpp",
    "content": "/**\n * @file static.cpp\n * @brief 静态数据成员\n * 静态数据成员被编译器放在程序的一个global data\n * members中，它是类的一个数据成员，但不影响类的大小。不管这个类产生了多少个实例，还是派生了多少新的类，静态数据成员只有一个实例。静态数据成员，一旦被声明，就已经存在。\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include <iostream>\nusing namespace std;\nclass A {\npublic:\n  char b;\n  virtual void fun(){};\n  static int c;\n  static int d;\n  static int f;\n};\n\nint main() {\n\n  /**\n   * @brief 16  字节对齐、静态变量不影响类的大小、vptr指针=8\n   */\n  cout << sizeof(A) << endl;\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/sizeof/virinhe.cpp",
    "content": "/**\n * @file virnhe.cpp\n * @brief 虚继承\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include <iostream>\n\nusing namespace std;\n\nclass A {\n  virtual void fun() {}\n};\n\nclass B {\n  virtual void fun2() {}\n};\nclass C : virtual public A, virtual public B {\npublic:\n  virtual void fun3() {}\n};\n\nint main() {\n\n  /**\n   * @brief 8 8 16  派生类虚继承多个虚函数，会继承所有虚函数的vptr\n   */\n  cout << sizeof(A) << \" \" << sizeof(B) << \" \" << sizeof(C);\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/sizeof/virmoreinhe.cpp",
    "content": "/**\n * @file virmoreinhe.cpp\n * @brief 虚函数多继承\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include <iostream>\n\nusing namespace std;\n\nclass A {\n  virtual void fun() {}\n};\n\nclass B {\n  virtual void fun2() {}\n};\nclass C : public A, public B {\npublic:\n  virtual void fun3() {}\n};\n\nint main() {\n\n  /**\n   * @brief 8 8 16  派生类继承多个虚函数，会继承所有虚函数的vptr\n   */\n  cout << sizeof(A) << \" \" << sizeof(B) << \" \" << sizeof(C);\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/static/BUILD",
    "content": "# please run `bazel run basic_content/static:nostatic_class`\n# please run `bazel run basic_content/static:static_class`\n# please run `bazel run basic_content/static:static_demo`\n# please run `bazel run basic_content/static:static_error_variable`\n# please run `bazel run basic_content/static:static_funciton`\n# please run `bazel run basic_content/static:static_variable`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"nostatic_class\",\n    srcs = [\"nostatic_class.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"static_class\",\n    srcs = [\"static_class.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"static_demo\",\n    srcs = [\"static_demo.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"sstatic_error_variable\",\n    srcs = [\"static_error_variable.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"static_funciton\",\n    srcs = [\"static_funciton.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"static_variable\",\n    srcs = [\"static_variable.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/static/README.md",
    "content": "# static那些事\n\n## 关于作者\n\n微信公众号：\n\n![](../img/wechat.jpg)\n\n当与不同类型一起使用时，Static关键字具有不同的含义。我们可以使用static关键字：\n\n**静态变量：** 函数中的变量，类中的变量\n\n**静态类的成员：** 类对象和类中的函数\n\n现在让我们详细看一下静态的这些用法：\n\n**静态变量**\n\n- 函数中的静态变量\n\n当变量声明为static时，空间**将在程序的生命周期内分配**。即使多次调用该函数，静态变量的空间也**只分配一次**，前一次调用中的变量值通过下一次函数调用传递。这对于在C / C ++或需要存储先前函数状态的任何其他应用程序非常有用。\n\n```c++\n#include <iostream> \n#include <string> \nusing namespace std; \n\nvoid demo() \n{ \n\t// static variable \n\tstatic int count = 0; \n\tcout << count << \" \"; \n\t\n\t// value is updated and \n\t// will be carried to next \n\t// function calls \n\tcount++; \n} \n\nint main() \n{ \n\tfor (int i=0; i<5; i++)\t \n\t\tdemo(); \n\treturn 0; \n} \n```\n\n输出：\n\n```\n0 1 2 3 4 \n```\n\n您可以在上面的程序中看到变量count被声明为static。因此，它的值通过函数调用来传递。每次调用函数时，都不会对变量计数进行初始化。\n\n- 类中的静态变量\n\n由于声明为static的变量只被初始化一次，因为它们在单独的静态存储中分配了空间，因此类中的静态变量**由对象共享**。对于不同的对象，不能有相同静态变量的多个副本。也是因为这个原因，静态变量不能使用构造函数初始化。\n\n```c++\n\n#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \npublic: \n\tstatic int i; \n\t\n\tApple() \n\t{ \n\t\t// Do nothing \n\t}; \n}; \n\nint main() \n{ \nApple obj1; \nApple obj2; \nobj1.i =2; \nobj2.i = 3; \n\t\n// prints value of i \ncout << obj1.i<<\" \"<<obj2.i; \n} \n```\n\n您可以在上面的程序中看到我们已经尝试为多个对象创建静态变量i的多个副本。但这并没有发生。因此，类中的静态变量应由用户使用类外的类名和范围解析运算符显式初始化，如下所示：\n\n```c++\n\n#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \npublic: \n\tstatic int i; \n\t\n\tApple() \n\t{ \n\t\t// Do nothing \n\t}; \n}; \n\nint Apple::i = 1; \n\nint main() \n{ \n\tApple obj; \n\t// prints value of i \n\tcout << obj.i; \n} \n```\n\n输出：\n\n```\n1\n```\n\n**静态成员**\n\n- 类对象为静态\n\n就像变量一样，对象也在声明为static时具有范围，直到程序的生命周期。\n\n考虑以下程序，其中对象是非静态的。\n\n```c++\n#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \n\tint i; \n\tpublic: \n\t\tApple() \n\t\t{ \n\t\t\ti = 0; \n\t\t\tcout << \"Inside Constructor\\n\"; \n\t\t} \n\t\t~Apple() \n\t\t{ \n\t\t\tcout << \"Inside Destructor\\n\"; \n\t\t} \n}; \n\nint main() \n{ \n\tint x = 0; \n\tif (x==0) \n\t{ \n\t\tApple obj; \n\t} \n\tcout << \"End of main\\n\"; \n} \n\n```\n\n\n输出：\n\n```c++\nInside Constructor\nInside Destructor\nEnd of main\n```\n\n在上面的程序中，对象在if块内声明为非静态。因此，变量的范围仅在if块内。因此，当创建对象时，将调用构造函数，并且在if块的控制权越过析构函数的同时调用，因为对象的范围仅在声明它的if块内。\n如果我们将对象声明为静态，现在让我们看看输出的变化。\n\n```c++\n#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \n\tint i; \n\tpublic: \n\t\tApple() \n\t\t{ \n\t\t\ti = 0; \n\t\t\tcout << \"Inside Constructor\\n\"; \n\t\t} \n\t\t~Apple() \n\t\t{ \n\t\t\tcout << \"Inside Destructor\\n\"; \n\t\t} \n}; \n\nint main() \n{ \n\tint x = 0; \n\tif (x==0) \n\t{ \n\t\tstatic Apple obj; \n\t} \n\tcout << \"End of main\\n\"; \n} \n\n```\n\n\n输出：\n\n```\nInside Constructor\nEnd of main\nInside Destructor\n```\n\n您可以清楚地看到输出的变化。现在，在main结束后调用析构函数。这是因为静态对象的范围是贯穿程序的生命周期。\n\n- 类中的静态函数\n\n就像类中的静态数据成员或静态变量一样，静态成员函数也不依赖于类的对象。我们被允许使用对象和'.'来调用静态成员函数。但建议使用类名和范围解析运算符调用静态成员。\n\n允许静态成员函数仅访问静态数据成员或其他静态成员函数，它们无法访问类的非静态数据成员或成员函数。\n\n```c++\n#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \n    public: \n        // static member function \n        static void printMsg() \n        {\n            cout<<\"Welcome to Apple!\"; \n        }\n}; \n\n// main function \nint main() \n{ \n    // invoking a static member function \n    Apple::printMsg(); \n} \n```\n\n输出：\n\n```\nWelcome to Apple!\n```\n\n**限定访问范围**\nstatic还有限定访问范围的作用（类似于匿名名字空间）。来自issue:https://github.com/Light-City/CPlusPlusThings/issues/142\n```cpp\n// source1.cpp\nextern void sayHello();\nconst char* msg = \"Hello World!\\n\";\nint main()\n{\n    sayHello();\n    return 0;\n}\n\n// source2.cpp\n#include <cstdio>\nextern char* msg;\nvoid sayHello()\n{\n    printf(\"%s\", msg);\n}\n```\ng++对于上面两个代码文件是可以正常编译并且打印Hello World!，但如果给source1.cpp中的msg加上static，则会导致undefined reference to 'msg'的编译错误：\n```cpp\n// source1.cpp\nextern void sayHello();\nstatic const char* msg = \"Hello World!\\n\";\nint main()\n{\n    sayHello();\n    return 0;\n}\n```\n"
  },
  {
    "path": "basic_content/static/nostatic_class.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nclass Apple {\n  int i;\n\npublic:\n  Apple() {\n    i = 0;\n    cout << \"Inside Constructor\\n\";\n  }\n  ~Apple() { cout << \"Inside Destructor\\n\"; }\n};\n\nint main() {\n  int x = 0;\n  if (x == 0) {\n    Apple obj;\n  }\n  cout << \"End of main\\n\";\n}\n"
  },
  {
    "path": "basic_content/static/static_class.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nclass Apple {\n  int i;\n\npublic:\n  Apple() {\n    i = 0;\n    cout << \"Inside Constructor\\n\";\n  }\n  ~Apple() { cout << \"Inside Destructor\\n\"; }\n};\n\nint main() {\n  int x = 0;\n  if (x == 0) {\n    static Apple obj;\n  }\n  cout << \"End of main\\n\";\n}\n"
  },
  {
    "path": "basic_content/static/static_demo.cpp",
    "content": "// the use of static Static\n// variables in a Function\n#include <iostream>\n#include <string>\nusing namespace std;\n\nvoid demo() {\n  // static variable\n  static int count = 0;\n  cout << count << \" \";\n\n  // value is updated and\n  // will be carried to next\n  // function calls\n  count++;\n}\n\nint main() {\n  for (int i = 0; i < 5; i++)\n    demo();\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/static/static_error_variable.cpp",
    "content": "// variables inside a class\n\n#include <iostream>\nusing namespace std;\n\nclass Apple {\npublic:\n  static int i;\n\n  Apple(){\n      // Do nothing\n  };\n};\n\nint main() {\n  Apple obj1;\n  Apple obj2;\n  obj1.i = 2;\n  obj2.i = 3;\n\n  // prints value of i\n  cout << obj1.i << \" \" << obj2.i;\n}\n"
  },
  {
    "path": "basic_content/static/static_funciton.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nclass Apple {\npublic:\n  // static member function\n  static void printMsg() { cout << \"Welcome to Apple!\"; }\n};\n\n// main function\nint main() {\n  // invoking a static member function\n  Apple::printMsg();\n}\n"
  },
  {
    "path": "basic_content/static/static_variable.cpp",
    "content": "// variables inside a class\n\n#include <iostream>\nusing namespace std;\n\nclass GfG {\npublic:\n  static int i;\n\n  GfG(){\n      // Do nothing\n  };\n};\n\nint GfG::i = 1;\n\nint main() {\n  GfG obj;\n  // prints value of i\n  cout << obj.i;\n}\n"
  },
  {
    "path": "basic_content/struct/BUILD",
    "content": "# please run `bazel run basic_content/struct:ext_struct_func`\n# please run `bazel run basic_content/struct:struct_func_func`\n# please run `bazel run basic_content/struct:struct_func`\n# please run `bazel run basic_content/struct:struct_func_cpp`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"ext_struct_func\",\n    srcs = [\"ext_struct_func.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"struct_func_func\",\n    srcs = [\"struct_func_func.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"struct_func\",\n    srcs = [\"struct_func.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"struct_func_cpp\",\n    srcs = [\"struct_func.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/struct/README.md",
    "content": "# 一文搞懂C和C++中struct\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 1.C中struct\n\n- 在C中struct只单纯的用作数据的复合类型，也就是说，在结构体声明中只能将数据成员放在里面，而不能将函数放在里面。 \n- 在C结构体声明中不能使用C++访问修饰符，如：public、protected、private 而在C++中可以使用。\n- 在C中定义结构体变量，如果使用了下面定义必须加struct。\n- C的结构体不能继承（没有这一概念）。\n- 若结构体的名字与函数名相同，可以正常运行且正常的调用！例如：可以定义与 struct Base 不冲突的 void Base() {}。\n\n完整案例：\n\n```c\n#include<stdio.h>\n\nstruct Base {            // public\n    int v1; \n//    public:      //error\n        int v2; \n    //private:\n        int v3; \n    //void print(){       // c中不能在结构体中嵌入函数\n    //    printf(\"%s\\n\",\"hello world\");\n    //};    //error!\n};\n\nvoid Base(){\n    printf(\"%s\\n\",\"I am Base func\");\n}\n//struct Base base1;  //ok\n//Base base2; //error\nint main() {\n    struct Base base;\n    base.v1=1;\n    //base.print();\n    printf(\"%d\\n\",base.v1);\n    Base();\n    return 0;\n}\n```\n\n最后输出：\n\n```\n1\nI am Base func\n```\n\n完整代码见：[struct_func.c](./struct_func.c)\n\n## 2.C++中struct\n\n与C对比如下：\n\n- C++结构体中不仅可以定义数据，还可以定义函数。\n- C++结构体中可以使用访问修饰符，如：public、protected、private 。\n- C++结构体使用可以直接使用不带struct。\n- C++继承\n- 若结构体的名字与函数名相同，可以正常运行且正常的调用！但是定义结构体变量时候只能用带struct的！\n\n例如：\n\n> 情形1：不适用typedef定义结构体别名\n\n未添加同名函数前：\n\n```c++\nstruct Student {\n    \n};\n//Student(){}\nstruct Student s; //ok\nStudent s;  //ok\n```\n\n添加同名函数后：\n\n```c++\nstruct Student {\n    \n};\nStudent(){}\nstruct Student s; //ok\nStudent s;  //error\n```\n\n> 情形二：使用typedef定义结构体别名\n\n```c++\ntypedef struct Base1 {         \n    int v1;\n    int v3;\n    public:     //显示声明public\n    int v2;\n    void print(){       \n        printf(\"%s\\n\",\"hello world\");\n    };    \n}B;\n//void B() {}  //error! 符号 \"B\" 已经被定义为一个 \"struct Base1\" 的别名\n```\n\n> 前三种案例\n\n```c++\n#include<iostream>\n#include<stdio.h>\n\nstruct Base {         \n    int v1;\n//    private:   //error!\n        int v3;\n    public:     //显示声明public\n        int v2;\n    void print(){       \n        printf(\"%s\\n\",\"hello world\");\n    };    \n};\n\nint main() {\n    struct Base base1;  //ok\n    Base base2; //ok\n    Base base;\n    base.v1=1;\n    base.v3=2;\n    base.print();\n    printf(\"%d\\n\",base.v1);\n    printf(\"%d\\n\",base.v3);\n    return 0;\n}\n```\n\n完整代码见：[struct_func.cpp](struct_func.cpp)\n\n> 继承案例\n\n```c++\n#include<iostream>\n#include<stdio.h>\nstruct Base {         \n    int v1;\n//    private:   //error!\n        int v3;\n    public:   //显示声明public\n        int v2;\n    virtual void print(){       \n        printf(\"%s\\n\",\"Base\");\n    };    \n};\nstruct Derived:Base {         \n\n    public:\n        int v2;\n    void print(){       \n        printf(\"%s\\n\",\"Derived\");\n    };    \n};\nint main() {\n    Base *b=new Derived();\n    b->print();\n    return 0;\n}\n```\n\n完整代码见：[ext_struct_func.cpp](./ext_struct_func.cpp)\n\n> 同名函数\n\n```c++\n#include<iostream>\n#include<stdio.h>\n\nstruct Base {         \n    int v1;\n//    private:   //error!\n        int v3;\n    public:     //显示声明public\n        int v2;\n    void print(){       \n        printf(\"%s\\n\",\"hello world\");\n    };    \n};\n\ntypedef struct Base1 {         \n    int v1;\n//    private:   //error!\n        int v3;\n    public:     //显示声明public\n        int v2;\n    void print(){       \n        printf(\"%s\\n\",\"hello world\");\n    };    \n}B;\nvoid Base(){\n    printf(\"%s\\n\",\"I am Base func\");\n}\n//void B() {}  //error! 符号 \"B\" 已经被定义为一个 \"struct Base1\" 的别名\nint main() {\n    struct Base base;  //ok\n    //Base base1;  // error!\n    base.v1=1;\n    base.v3=2;\n    base.print();\n    printf(\"%d\\n\",base.v1);\n    printf(\"%d\\n\",base.v3);\n    Base();\n    return 0;\n}\n```\n完整代码见：[struct_func_func.cpp](./struct_func_func.cpp)\n\n## 3.总结\n\n### C和C++中的Struct区别\n\n| C                                                      | C++                                                          |\n| ------------------------------------------------------ | ------------------------------------------------------------ |\n| 不能将函数放在结构体声明                               | 能将函数放在结构体声明                                       |\n| 在C结构体声明中不能使用C++访问修饰符。                 | public、protected、private 在C++中可以使用。                 |\n| 在C中定义结构体变量，如果使用了下面定义必须加struct。  | 可以不加struct                                               |\n| 结构体不能继承（没有这一概念）。                       | 可以继承                                                     |\n| 若结构体的名字与函数名相同，可以正常运行且正常的调用！ | 若结构体的名字与函数名相同，使用结构体，只能使用带struct定义！ |\n"
  },
  {
    "path": "basic_content/struct/ext_struct_func.cpp",
    "content": "#include <iostream>\n#include <stdio.h>\nusing namespace std;\nstruct Base {\n  int v1;\n  //    private:   //error!\n  int v3;\n\npublic: // 显示声明public\n  int v2;\n  virtual void print() { printf(\"%s\\n\", \"Base\"); };\n  Base() { cout << \"Base construct\" << endl; };\n  virtual ~Base() { cout << \"Base deconstruct\" << endl; };\n};\n\nstruct Derived : Base {\n\n  Derived() { cout << \"Derived construct\" << endl; };\n  virtual ~Derived() { cout << \"Derived deconstruct\" << endl; };\n\npublic:\n  int v2;\n  void print() { printf(\"%s\\n\", \"Derived\"); };\n};\n\nint main() {\n  Base *b = new Derived();\n  b->print();\n  delete b;\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/struct/struct_func.c",
    "content": "#include <stdio.h>\n\nstruct Base\n{ // public\n    int v1;\n    //    public:      //error\n    int v2;\n    // private:\n    int v3;\n    // void print(){       // c中不能在结构体中嵌入函数\n    //     printf(\"%s\\n\",\"hello world\");\n    // };    //error!\n};\n\nvoid Base()\n{\n    printf(\"%s\\n\", \"I am Base func\");\n}\n// struct Base base1;  //ok\n// Base base2; //error\n\nint main()\n{\n    struct Base base;\n    base.v1 = 1;\n    // base.print();\n    printf(\"%d\\n\", base.v1);\n    Base();\n    return 0;\n}\n"
  },
  {
    "path": "basic_content/struct/struct_func.cpp",
    "content": "#include <iostream>\n#include <stdio.h>\n\nstruct Base {\n  int v1;\n  //    private:   //error!\n  int v3;\n\npublic: // 显示声明public\n  int v2;\n  void print() { printf(\"%s\\n\", \"hello world\"); };\n};\n\nint main() {\n  struct Base base1; // ok\n  Base base2;        // ok\n  Base base;\n  base.v1 = 1;\n  base.v3 = 2;\n  base.print();\n  printf(\"%d\\n\", base.v1);\n  printf(\"%d\\n\", base.v3);\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/struct/struct_func_func.cpp",
    "content": "#include <iostream>\n#include <stdio.h>\n\nstruct Base {\n  int v1;\n  //    private:   //error!\n  int v3;\n\npublic: // 显示声明public\n  int v2;\n  void print() { printf(\"%s\\n\", \"hello world\"); };\n};\n\ntypedef struct Base1 {\n  int v1;\n  //    private:   //error!\n  int v3;\n\npublic: // 显示声明public\n  int v2;\n  void print() { printf(\"%s\\n\", \"hello world\"); };\n} B;\nvoid Base() { printf(\"%s\\n\", \"I am Base func\"); }\n// void B() {}  //error! 符号 \"B\" 已经被定义为一个 \"struct Base1\" 的别名\nint main() {\n  struct Base base; // ok\n  // Base base1;  // error!\n  base.v1 = 1;\n  base.v3 = 2;\n  base.print();\n  printf(\"%d\\n\", base.v1);\n  printf(\"%d\\n\", base.v3);\n  Base();\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/struct_class/README.md",
    "content": "# struct与class区别\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n关于C与C++中struct内容：见[struct那些事](../struct)\n\n总的来说，struct 更适合看成是一个数据结构的实现体，class 更适合看成是一个对象的实现体。\n\n区别:\n\n最本质的一个区别就是默认的访问控制\n\n默认的继承访问权限。struct 是 public 的，class 是 private 的。\n\nstruct 作为数据结构的实现体，它默认的数据访问控制是 public 的，而 class 作为对象的实现体，它默认的成员变量访问控制是 private 的。\n"
  },
  {
    "path": "basic_content/this/BUILD",
    "content": "# please run `bazel run basic_content/this:person`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"person\",\n    srcs = [\"person.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/this/README.md",
    "content": "# this指针那些事\n\n## 关于作者\n\n微信公众号：\n\n![](../img/wechat.jpg)\n\n## 1.this指针\n\n相信在坐的很多人，都在学Python，对于Python来说有self，类比到C++中就是this指针，那么下面一起来深入分析this指针在类中的使用！\n\n首先来谈谈this指针的用处：\n\n（1）一个对象的this指针并不是对象本身的一部分，不会影响sizeof(对象)的结果。\n\n（2）this作用域是在类内部，当在类的非静态成员函数中访问类的非静态成员的时候，编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说，即使你没有写上this指针，编译器在编译的时候也是加上this的，它作为非静态成员函数的隐含形参，对各成员的访问均通过this进行。\n\n其次，this指针的使用：\n\n（1）在类的非静态成员函数中返回类对象本身的时候，直接使用 return *this。\n\n（2）当参数与成员变量名相同时，如this->n = n （不能写成n = n)。\n\n另外，在网上大家会看到this会被编译器解析成`A *const `，`A const * `，究竟是哪一个呢？下面通过断点调试分析：\n\n现有如下例子：\n\n```c++\n#include<iostream>\n#include<cstring>\n\n\nusing namespace std;\nclass Person{\npublic:\n    typedef enum {\n        BOY = 0, \n        GIRL \n    }SexType;\n    Person(char *n, int a,SexType s){\n        name=new char[strlen(n)+1];\n        strcpy(name,n);\n        age=a;\n        sex=s;\n    }\n    int get_age() const{\n    \n        return this->age; \n    }\n    Person& add_age(int a){\n        age+=a;\n        return *this; \n    }\n    ~Person(){\n        delete [] name;\n    }\nprivate:\n    char * name;\n    int age;\n    SexType sex;\n};\n\n\nint main(){\n    Person p(\"zhangsan\",20,Person::BOY); \n    cout<<p.get_age()<<endl;\n\tcout<<p.add_age(10).get_age()<<endl;\n    return 0;\n}\n```\n\n对于这个简单的程序，相信大家没得问题吧，就是定义了一个类，然后初始化构造函数，并获取这个人的年龄，设置后，再获取！\n\n为了验证this指针是哪一个，现在在`add_age`处添加断点，运行后如下：\n\n![thiscontrust](./img/thiscontrust.png)\n\n![genthis](./img/genthis.png)\n\n会发现编译器自动为我们加上`A* const`，而不是`A const *this`！\n\n紧接着，上述还有个常函数，那么我们在对`get_age`添加断点，如下：\n\n![constthis](./img/constthis.png)\n\n会发现编译器把上述的this，变为`const A* const`，这个大家也能想到，因为这个函数是const函数，那么针对const函数，它只能访问const变量与const函数，不能修改其他变量的值，所以需要一个this指向不能修改的变量，那就是`const A*`,又由于本身this是`const`指针，所以就为`const A* const`!\n\n总结：this在成员函数的开始执行前构造，在成员的执行结束后清除。上述的get_age函数会被解析成`get_age(const A * const this)`,`add_age`函数会被解析成`add_age(A* const this,int a)`。在C++中类和结构是只有一个区别的：类的成员默认是private，而结构是public。this是类的指针，如果换成结构，那this就是结构的指针了。\n"
  },
  {
    "path": "basic_content/this/person.cpp",
    "content": "#include <cstring>\n#include <iostream>\n\nusing namespace std;\nclass Person {\npublic:\n  typedef enum { BOY = 0, GIRL } SexType;\n  Person(char *n, int a, SexType s) {\n    name = new char[strlen(n) + 1];\n    strcpy(name, n);\n    age = a;\n    sex = s;\n  }\n  int get_age() const { return this->age; }\n  Person &add_age(int a) {\n    age += a;\n    return *this;\n  }\n  ~Person() { delete[] name; }\n\nprivate:\n  char *name;\n  int age;\n  SexType sex;\n};\n\nint main() {\n  Person p(\"zhangsan\", 20, Person::BOY);\n  cout << p.get_age() << endl;\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/union/BUILD",
    "content": "# please run `bazel run basic_content/union:union`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"union\",\n    srcs = [\"union.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/union/README.md",
    "content": "# UNION那些事\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n联合（union）是一种节省空间的特殊的类，一个 union 可以有多个数据成员，但是在任意时刻只有一个数据成员可以有值。当某个成员被赋值后其他成员变为未定义状态。联合有如下特点：\n\n- 默认访问控制符为 public\n- 可以含有构造函数、析构函数\n- 不能含有引用类型的成员\n- 不能继承自其他类，不能作为基类\n- 不能含有虚函数\n- 匿名 union 在定义所在作用域可直接访问 union 成员\n- 匿名 union 不能包含 protected 成员或 private 成员\n- 全局匿名联合必须是静态（static）的\n"
  },
  {
    "path": "basic_content/union/union.cpp",
    "content": "/**\n * @file union.cpp\n * @brief UNION\n * @author 光城\n * @version v1\n * @date 2019-08-06\n */\n\n#include <iostream>\n/**\n * 默认访问控制符为public\n */\nunion UnionTest {\n  /**\n   * 可以含有构造函数、析构函数\n   */\n  UnionTest() : i(10) { print(i); };\n  ~UnionTest(){};\n  int i;\n\nprivate:\n  void print(int i) { std::cout << i << std::endl; };\n};\n/**\n * 全局匿名联合必须是静态的\n */\nstatic union {\n  int i;\n  double d;\n};\n\nint main() {\n  UnionTest u;\n\n  union {\n    int i;\n    double d;\n  };\n\n  std::cout << u.i << std::endl; // 输出 UnionTest 联合的 10\n\n  ::i = 20;\n  std::cout << ::i << std::endl; // 输出全局静态匿名联合的 20\n  /**\n   * 匿名union在定义所在作用域可直接访问union成员\n   */\n  i = 30;\n  std::cout << i << std::endl; // 输出局部匿名联合的 30\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/using/BUILD",
    "content": "# please run `bazel run basic_content/using:derived_base`\n# please run `bazel run basic_content/using:using_derived`\n# please run `bazel run basic_content/using:using_global`\n# please run `bazel run basic_content/using:using_typedef`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\n# Don't panic if you get compilation errors, this is what this code demonstrates, as expected.\ncc_binary(\n    name = \"derived_base\",\n    srcs = [\"derived_base.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"using_derived\",\n    srcs = [\"using_derived.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"using_global\",\n    srcs = [\"using_global.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"using_typedef\",\n    srcs = [\"using_typedef.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/using/README.md",
    "content": "# using那些事\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 基本使用\n\n局部与全局using，具体操作与使用见下面案例：\n\n```c++\n#include <iostream>\n#define isNs1 1\n//#define isGlobal 2\nusing namespace std;\nvoid func() \n{\n    cout<<\"::func\"<<endl;\n}\n\nnamespace ns1 {\n    void func()\n    {\n        cout<<\"ns1::func\"<<endl; \n    }\n}\n\nnamespace ns2 {\n#ifdef isNs1 \n    using ns1::func;    /// ns1中的函数\n#elif isGlobal\n    using ::func; /// 全局中的函数\n#else\n    void func() \n    {\n        cout<<\"other::func\"<<endl; \n    }\n#endif\n}\n\nint main() \n{\n    /**\n     * 这就是为什么在c++中使用了cmath而不是math.h头文件\n     */\n    ns2::func(); // 会根据当前环境定义宏的不同来调用不同命名空间下的func()函数\n    return 0;\n}\n```\n完整代码见：[using_global.cpp](using_global.cpp)\n## 改变访问性\n\n```\nclass Base{\npublic:\n std::size_t size() const { return n;  }\nprotected:\n std::size_t n;\n};\nclass Derived : private Base {\npublic:\n using Base::size;\nprotected:\n using Base::n;\n};\n```\n\n类Derived私有继承了Base，对于它来说成员变量n和成员函数size都是私有的，如果使用了using语句，可以改变他们的可访问性，如上述例子中，size可以按public的权限访问，n可以按protected的权限访问。\n完整代码见：[derived_base.cpp](derived_base.cpp)\n## 函数重载\n\n在继承过程中，派生类可以覆盖重载函数的0个或多个实例，一旦定义了一个重载版本，那么其他的重载版本都会变为不可见。\n\n如果对于基类的重载函数，我们需要在派生类中修改一个，又要让其他的保持可见，必须要重载所有版本，这样十分的繁琐。\n\n```c++\n#include <iostream>\nusing namespace std;\n\nclass Base{\n    public:\n        void f(){ cout<<\"f()\"<<endl;\n        }\n        void f(int n){\n            cout<<\"Base::f(int)\"<<endl;\n        }\n};\n\nclass Derived : private Base {\n    public:\n        using Base::f;\n        void f(int n){\n            cout<<\"Derived::f(int)\"<<endl;\n        }\n};\n\nint main()\n{\n    Base b;\n    Derived d;\n    d.f();\n    d.f(1);\n    return 0;\n}\n```\n如上代码中，在派生类中使用using声明语句指定一个名字而不指定形参列表，所以一条基类成员函数的using声明语句就可以把该函数的所有重载实例添加到派生类的作用域中。此时，派生类只需要定义其特有的函数就行了，而无需为继承而来的其他函数重新定义。\n\n完整代码见：[using_derived.cpp](using_derived.cpp)\n## 取代typedef\n\nC中常用typedef A B这样的语法，将B定义为A类型，也就是给A类型一个别名B\n\n对应typedef A B，使用using B=A可以进行同样的操作。\n\n```c++\ntypedef vector<int> V1; \nusing V2 = vector<int>;\n```\n完整代码见：[using_typedef.cpp](using_typedef.cpp)\n"
  },
  {
    "path": "basic_content/using/derived_base.cpp",
    "content": "#include <iostream>\n\nusing namespace std;\n\nclass Base1 {\npublic:\n  Base1() : value(10) {}\n  virtual ~Base1() {}\n  void test1() { cout << \"Base test1...\" << endl; }\n\nprotected: // 保护\n  int value;\n};\n// 默认为私有继承\nclass Derived1 : Base1 {\npublic:\n  void test2() { cout << \"value is \" << value << endl; }\n};\n\nclass Base {\npublic:\n  Base() : value(20) {}\n  virtual ~Base() {}\n  void test1() { cout << \"Base test1...\" << endl; }\n\nprivate: // 私有\n  int value;\n};\n\n/**\n * 子类对父类成员的访问权限跟如何继承没有任何关系，\n * “子类可以访问父类的public和protected成员，不可以访问父类的private成员”——这句话对任何一种继承都是成立的。\n *\n */\nclass Derived : Base {\npublic:\n  using Base::value;\n  void test2() { cout << \"value is \" << value << endl; }\n};\n\nint main() {\n  Derived1 d1;\n  d1.test2();\n\n  Derived d;\n  d.test2();\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/using/using_derived.cpp",
    "content": "/**\n * @file using_derived.cpp\n * @brief 函数重装\n * @author 光城\n * @version v1\n * @date 2019-08-07\n */\n\n#include <iostream>\nusing namespace std;\n\nclass Base {\npublic:\n  void f() { cout << \"f()\" << endl; }\n  void f(int n) { cout << \"Base::f(int)\" << endl; }\n};\n\nclass Derived : private Base {\npublic:\n  using Base::f;\n  void f(int n) { cout << \"Derived::f(int)\" << endl; }\n};\n\nint main() {\n  Derived d;\n  d.f();\n  d.f(1);\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/using/using_global.cpp",
    "content": "/**\n * @file using_global.cpp\n * @brief using各种使用\n * @author 光城\n * @version v1\n * @date 2019-08-07\n */\n\n#include <iostream>\n#define isNs1 1\n// #define isGlobal 2\nusing namespace std;\nvoid func() { cout << \"::func\" << endl; }\n\nnamespace ns1 {\nvoid func() { cout << \"ns1::func\" << endl; }\n} // namespace ns1\n\nnamespace ns2 {\n#ifdef isNs1\nusing ns1::func; /// ns1中的函数\n#elif isGlobal\nusing ::func; /// 全局中的函数\n#else\nvoid func() { cout << \"other::func\" << endl; }\n#endif\n} // namespace ns2\n\nint main() {\n  /**\n   * 这就是为什么在c++中使用了cmath而不是math.h头文件\n   */\n  ns2::func(); // 会根据当前环境定义宏的不同来调用不同命名空间下的func()函数\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/using/using_typedef.cpp",
    "content": "/**\n * @file using_typedef.cpp\n * @brief bazel run basic_content/using:using_typedef\n * 取代typedef，使用using来定义别名\n * @author 光城\n * @version v1\n * @date 2019-08-07\n */\n\n#include <iostream>\n#include <vector>\nusing namespace std;\n\ntypedef vector<int> V1;\nusing V2 = vector<int>;\n\nint main() {\n  int nums1[] = {1, 2, 3, 4, 5, 6};\n  V1 vec1(nums1, nums1 + sizeof(nums1) / sizeof(int));\n  int nums2[] = {5, 7, 6};\n  V2 vec2(nums2, nums2 + sizeof(nums2) / sizeof(int));\n\n  for (auto i : vec1)\n    cout << i << \" \";\n  cout << endl;\n\n  for (auto i : vec2)\n    cout << i << \" \";\n  cout << endl;\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/virtual/README.md",
    "content": "# virtual那些事\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 1.虚函数与运行多态\n\n对应的代码：[emp.cpp](./set1/emp.cpp)\n\n**虚函数的调用取决于指向或者引用的对象的类型，而不是指针或者引用自身的类型。**\n\n## 2.vptr与vtable\n\n见[vptr_vtable那些事](../vptr_vtable)\n\n## 3.虚函数中默认参数\n\n对应的代码：[default_arg.cpp](./set2/default_arg.cpp)\n\n**默认参数是静态绑定的，虚函数是动态绑定的。 默认参数的使用需要看指针或者引用本身的类型，而不是对象的类型**。\n\n## 4.可以不可以\n\n（1） **静态函数可以声明为虚函数吗？**\n\n原因主要有两方面：\n\n**静态函数不可以声明为虚函数，同时也不能被const 和 volatile关键字修饰**\n\nstatic成员函数不属于任何类对象或类实例，所以即使给此函数加上virutal也是没有任何意义\n\n虚函数依靠vptr和vtable来处理。vptr是一个指针，在类的构造函数中创建生成，并且只能用this指针来访问它，静态成员函数没有this指针，所以无法访问vptr。\n\n代码学习：[static_error.cpp  ](./set3/static_error.cpp  )\n\n（2）**构造函数可以为虚函数吗？**\n\n构造函数不可以声明为虚函数。同时除了inline|explicit之外，构造函数不允许使用其它任何关键字。\n\n为什么构造函数不可以为虚函数？\n\n尽管虚函数表vtable是在编译阶段就已经建立的，但指向虚函数表的指针vptr是在运行阶段实例化对象时才产生的。 如果类含有虚函数，编译器会在构造函数中添加代码来创建vptr。 问题来了，如果构造函数是虚的，那么它需要vptr来访问vtable，可这个时候vptr还没产生。 因此，构造函数不可以为虚函数。\n\n我们之所以使用虚函数，是因为需要在信息不全的情况下进行多态运行。而构造函数是用来初始化实例的，实例的类型必须是明确的。 因此，构造函数没有必要被声明为虚函数。\n\n代码学习：\n\n- [copy_consrtuct.cpp](./set3/copy_consrtuct.cpp) \n\n- [vir_con.cpp](./set3/vir_con.cpp) \n\n（3）**析构函数可以为虚函数吗？**\n\n**析构函数可以声明为虚函数。如果我们需要删除一个指向派生类的基类指针时，应该把析构函数声明为虚函数。 事实上，只要一个类有可能会被其它类所继承， 就应该声明虚析构函数(哪怕该析构函数不执行任何操作)。**\n\n代码学习：\n\n- [full_virde.cpp](./set3/full_virde.cpp)\n\n- [vir_de.cpp ](./set3/vir_de.cpp)      \n\n（4）**虚函数可以为私有函数吗？**\n\n- 基类指针指向继承类对象，则调用继承类对象的函数；\n- int main()必须声明为Base类的友元，否则编译失败。 编译器报错： ptr无法访问私有函数。 当然，把基类声明为public， 继承类为private，该问题就不存在了。\n\n代码学习：\n\n- [virtual_function.cpp](./set3/virtual_function.cpp)\n- [virtual_function1.cpp](./set3/virtual_function1.cpp)\n\n（5）**虚函数可以被内联吗？**\n\n**通常类成员函数都会被编译器考虑是否进行内联。 但通过基类指针或者引用调用的虚函数必定不能被内联。 当然，实体对象调用虚函数或者静态调用时可以被内联，虚析构函数的静态调用也一定会被内联展开。**\n\n- 虚函数可以是内联函数，内联是可以修饰虚函数的，但是当虚函数表现多态性的时候不能内联。\n- 内联是在编译器建议编译器内联，而虚函数的多态性在运行期，编译器无法知道运行期调用哪个代码，因此虚函数表现为多态性时（运行期）不可以内联。\n- `inline virtual` 唯一可以内联的时候是：编译器知道所调用的对象是哪个类（如 `Base::who()`），这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。\n\n代码学习：\n\n- [virtual_inline.cpp](./set3/virtual_inline.cpp)\n\n- [inline_virtual.cpp](./set3/inline_virtual.cpp)\n\n## 5.RTTI与dynamic_cast\n\nRTTI（Run-Time Type Identification)，通过运行时类型信息程序能够使用[基类](https://baike.baidu.com/item/%E5%9F%BA%E7%B1%BB/9589663)的[指针](https://baike.baidu.com/item/%E6%8C%87%E9%92%88/2878304)或引用来检查这些指针或引用所指的对象的实际[派生类](https://baike.baidu.com/item/%E6%B4%BE%E7%94%9F%E7%B1%BB)型。\n\n在面向对象程序设计中，有时我们需要在运行时查询一个对象是否能作为某种多态类型使用。与Java的instanceof，以及C#的as、is运算符类似，C++提供了dynamic_cast函数用于动态转型。相比C风格的强制类型转换和C++ reinterpret_cast，dynamic_cast提供了类型安全检查，是一种基于能力查询(Capability Query)的转换，所以在多态类型间进行转换更提倡采用dynamic_cast。\n\n代码学习：\n\n- [rtti.cpp](./set4/rtti.cpp)\n- [warn_rtti.cpp](./set4/warn_rtti.cpp)\n\n## 6.纯虚函数和抽象类\n\n见[纯虚函数和抽象类那些事](../abstract)\n"
  },
  {
    "path": "basic_content/virtual/set1/BUILD",
    "content": "# please run `bazel run basic_content/virtual/set1:emp`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"emp\",\n    srcs = [\"emp.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/virtual/set1/emp.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nclass Employee {\npublic:\n  virtual void raiseSalary() { cout << 0 << endl; }\n\n  virtual void promote() { /* common promote code */\n  }\n};\n\nclass Manager : public Employee {\n  virtual void raiseSalary() { cout << 100 << endl; }\n\n  virtual void promote() { /* Manager specific promote */\n  }\n};\nclass Engineer : public Employee {\n  virtual void raiseSalary() { cout << 200 << endl; }\n\n  virtual void promote() { /* Manager specific promote */\n  }\n};\n\n// Similarly, there may be other types of employees\n// We need a very simple function to increment salary of all employees\n// Note that emp[] is an array of pointers and actual pointed objects can\n// be any type of employees. This function should ideally be in a class\n// like Organization, we have made it global to keep things simple\nvoid globalRaiseSalary(Employee *emp[], int n) {\n  for (int i = 0; i < n; i++)\n    emp[i]->raiseSalary(); // Polymorphic Call: Calls raiseSalary()\n                           // according to the actual object, not\n                           // according to the type of pointer\n}\nint main() {\n  Employee *emp[] = {new Manager(), new Engineer};\n  globalRaiseSalary(emp, 2);\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/virtual/set2/BUILD",
    "content": "# please run `bazel run basic_content/virtual/set2:default_arg`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"default_arg\",\n    srcs = [\"default_arg.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/virtual/set2/default_arg.cpp",
    "content": "/**\n * @file first_example.cpp\n * @brief 虚函数中默认参数\n * 规则：虚函数是动态绑定的，默认参数是静态绑定的。默认参数的使用需要看指针或者应用本身的类型，而不是对象的类型！\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\n#include <iostream>\nusing namespace std;\n\nclass Base {\npublic:\n  virtual void fun(int x = 10) { cout << \"Base::fun(), x = \" << x << endl; }\n};\n\nclass Derived : public Base {\npublic:\n  virtual void fun(int x = 20) { cout << \"Derived::fun(), x = \" << x << endl; }\n};\n\nint main() {\n  Derived d1;\n  Base *bp = &d1;\n  bp->fun(); // 10\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/virtual/set3/BUILD",
    "content": "# please run `bazel run //basic_content/virtual/set3:copy_consrtuct`\n# please run `bazel run //basic_content/virtual/set3:full_virde`\n# please run `bazel run //basic_content/virtual/set3:inline_virtual`\n# please run `bazel run //basic_content/virtual/set3:vir_con`\n# please run `bazel run //basic_content/virtual/set3:vir_de`\n# please run `bazel run //basic_content/virtual/set3:virtual_function`\n# please run `bazel run //basic_content/virtual/set3:virtual_function1`\n# please run `bazel run //basic_content/virtual/set3:virtual_inline`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"copy_consrtuct\",\n    srcs = [\"copy_consrtuct.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"full_virde\",\n    srcs = [\"full_virde.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"inline_virtual\",\n    srcs = [\"inline_virtual.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"vir_con\",\n    srcs = [\"vir_con.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"vir_de\",\n    srcs = [\"vir_de.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"virtual_function\",\n    srcs = [\"virtual_function.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"virtual_function1\",\n    srcs = [\"virtual_function1.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"virtual_inline\",\n    srcs = [\"virtual_inline.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/virtual/set3/copy_consrtuct.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nclass Base {\npublic:\n};\n\nclass Derived : public Base {\npublic:\n  Derived() { cout << \"Derived created\" << endl; }\n\n  Derived(const Derived &rhs) {\n    cout << \"Derived created by deep copy\" << endl;\n  }\n\n  ~Derived() { cout << \"Derived destroyed\" << endl; }\n};\n\nint main() {\n  Derived s1;\n\n  Derived s2 = s1; // Compiler invokes \"copy constructor\"\n  // Type of s1 and s2 are concrete to compiler\n\n  // How can we create Derived1 or Derived2 object\n  // from pointer (reference) to Base class pointing Derived object?\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/virtual/set3/full_virde.cpp",
    "content": "/**\n * @file full_virde.cpp\n * @brief 将基类的析构函数声明为虚函数\n * 输出结果：\n *      Constructing base\n *      Constructing derived\n *      Destructing derived\n *      Destructing base\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n#include <iostream>\n\nusing namespace std;\n\nclass base {\npublic:\n  base() { cout << \"Constructing base \\n\"; }\n  virtual ~base() { cout << \"Destructing base \\n\"; }\n};\n\nclass derived : public base {\npublic:\n  derived() { cout << \"Constructing derived \\n\"; }\n  ~derived() { cout << \"Destructing derived \\n\"; }\n};\n\nint main(void) {\n  derived *d = new derived();\n  base *b = d;\n  delete b;\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/virtual/set3/inline_virtual.cpp",
    "content": "#include <iostream>\nusing namespace std;\nclass Base {\npublic:\n  inline virtual void who() { cout << \"I am Base\\n\"; }\n  virtual ~Base() {}\n};\nclass Derived : public Base {\npublic:\n  inline void who() // 不写inline时隐式内联\n  {\n    cout << \"I am Derived\\n\";\n  }\n};\n\nint main() {\n  // 此处的虚函数\n  // who()，是通过类（Base）的具体对象（b）来调用的，编译期间就能确定了，所以它可以是内联的，但最终是否内联取决于编译器。\n  Base b;\n  b.who();\n\n  // 此处的虚函数是通过指针调用的，呈现多态性，需要在运行时期间才能确定，所以不能为内联。\n  Base *ptr = new Derived();\n  ptr->who();\n\n  // 因为Base有虚析构函数（virtual ~Base() {}），所以 delete\n  // 时，会先调用派生类（Derived）析构函数，再调用基类（Base）析构函数，防止内存泄漏。\n  delete ptr;\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/virtual/set3/static_error.cpp",
    "content": "/**\n * @file static_error.cpp\n * @brief 静态函数不可以声明为虚函数，同时也不能被const和volatile关键字修饰!\n * 原因如下：\n * static成员函数不属于任何类对象或类实例，所以即使给此函数加上virutal也是没有任何意义\n * 虚函数依靠vptr和vtable来处理。vptr是一个指针，在类的构造函数中创建生成，并且只能用this指针来访问它，静态成员函数没有this指针，所以无法访问vptr。\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\nvirtual static void fun() {}\nstatic void fun() const {}\n"
  },
  {
    "path": "basic_content/virtual/set3/vir_con.cpp",
    "content": "/**\n * @file vir_con.cpp\n * @brief\n * 构造函数不可以声明为虚函数。同时除了inline之外，构造函数不允许使用其它任何关键字。\n *\n * 为什么构造函数不可以为虚函数？\n *\n * 尽管虚函数表vtable是在编译阶段就已经建立的，但指向虚函数表的指针vptr是在运行阶段实例化对象时才产生的。\n * 如果类含有虚函数，编译器会在构造函数中添加代码来创建vptr。\n * 问题来了，如果构造函数是虚的，那么它需要vptr来访问vtable，可这个时候vptr还没产生。\n * 因此，构造函数不可以为虚函数。\n * 我们之所以使用虚函数，是因为需要在信息不全的情况下进行多态运行。而构造函数是用来初始化实例的，实例的类型必须是明确的。\n * 因此，构造函数没有必要被声明为虚函数。\n * 尽管构造函数不可以为虚函数，但是有些场景下我们确实需要 “Virtual Copy\n * Constructor”。\n * “虚复制构造函数”的说法并不严谨，其只是一个实现了对象复制的功能的类内函数。\n * 举一个应用场景，比如剪切板功能。\n * 复制内容作为基类，但派生类可能包含文字、图片、视频等等。\n * 我们只有在程序运行的时候才知道我们需要复制的具体是什么类型的数据。\n *\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\n#include <iostream>\nusing namespace std;\n\n//// LIBRARY SRART\nclass Base {\npublic:\n  Base() {}\n\n  virtual // Ensures to invoke actual object destructor\n      ~Base() {}\n\n  virtual void ChangeAttributes() = 0;\n\n  // The \"Virtual Constructor\"\n  static Base *Create(int id);\n\n  // The \"Virtual Copy Constructor\"\n  virtual Base *Clone() = 0;\n};\n\nclass Derived1 : public Base {\npublic:\n  Derived1() { cout << \"Derived1 created\" << endl; }\n\n  Derived1(const Derived1 &rhs) {\n    cout << \"Derived1 created by deep copy\" << endl;\n  }\n\n  ~Derived1() { cout << \"~Derived1 destroyed\" << endl; }\n\n  void ChangeAttributes() { cout << \"Derived1 Attributes Changed\" << endl; }\n\n  Base *Clone() { return new Derived1(*this); }\n};\n\nclass Derived2 : public Base {\npublic:\n  Derived2() { cout << \"Derived2 created\" << endl; }\n\n  Derived2(const Derived2 &rhs) {\n    cout << \"Derived2 created by deep copy\" << endl;\n  }\n\n  ~Derived2() { cout << \"~Derived2 destroyed\" << endl; }\n\n  void ChangeAttributes() { cout << \"Derived2 Attributes Changed\" << endl; }\n\n  Base *Clone() { return new Derived2(*this); }\n};\n\nclass Derived3 : public Base {\npublic:\n  Derived3() { cout << \"Derived3 created\" << endl; }\n\n  Derived3(const Derived3 &rhs) {\n    cout << \"Derived3 created by deep copy\" << endl;\n  }\n\n  ~Derived3() { cout << \"~Derived3 destroyed\" << endl; }\n\n  void ChangeAttributes() { cout << \"Derived3 Attributes Changed\" << endl; }\n\n  Base *Clone() { return new Derived3(*this); }\n};\n\n// We can also declare \"Create\" outside Base.\n// But is more relevant to limit it's scope to Base\nBase *Base::Create(int id) {\n  // Just expand the if-else ladder, if new Derived class is created\n  // User need not be recompiled to create newly added class objects\n\n  if (id == 1) {\n    return new Derived1;\n  } else if (id == 2) {\n    return new Derived2;\n  } else {\n    return new Derived3;\n  }\n}\n//// LIBRARY END\n\n//// UTILITY SRART\nclass User {\npublic:\n  User() : pBase(0) {\n    // Creates any object of Base heirarchey at runtime\n\n    int input;\n\n    cout << \"Enter ID (1, 2 or 3): \";\n    cin >> input;\n\n    while ((input != 1) && (input != 2) && (input != 3)) {\n      cout << \"Enter ID (1, 2 or 3 only): \";\n      cin >> input;\n    }\n\n    // Create objects via the \"Virtual Constructor\"\n    pBase = Base::Create(input);\n  }\n\n  ~User() {\n    if (pBase) {\n      delete pBase;\n      pBase = 0;\n    }\n  }\n\n  void Action() {\n    // Duplicate current object\n    Base *pNewBase = pBase->Clone();\n\n    // Change its attributes\n    pNewBase->ChangeAttributes();\n\n    // Dispose the created object\n    delete pNewBase;\n  }\n\nprivate:\n  Base *pBase;\n};\n\n//// UTILITY END\n\n//// Consumer of User (UTILITY) class\nint main() {\n  User *user = new User();\n\n  user->Action();\n\n  delete user;\n}\n"
  },
  {
    "path": "basic_content/virtual/set3/vir_de.cpp",
    "content": "/**\n * @file vir_de.cpp\n * @brief 派生类的析构函数没有被调用!\n * 输出结果：\n *      Constructing base\n *      Constructing derived\n *      Destructing base\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\n// CPP program without virtual destructor\n// causing undefined behavior\n#include <iostream>\n\nusing namespace std;\n\nclass base {\npublic:\n  base() { cout << \"Constructing base \\n\"; }\n  ~base() { cout << \"Destructing base \\n\"; }\n};\n\nclass derived : public base {\npublic:\n  derived() { cout << \"Constructing derived \\n\"; }\n  ~derived() { cout << \"Destructing derived \\n\"; }\n};\n\nint main(void) {\n  derived *d = new derived();\n  base *b = d;\n  delete b;\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/virtual/set3/virtual_function.cpp",
    "content": "/**\n * @file virtual_function.cpp\n * @brief 虚函数可以被私有化，但有一些细节需要注意。\n * 基类指针指向继承类对象，则调用继承类对象的函数；\n * int main()必须声明为Base类的友元，否则编译失败。 编译器报错：\n * ptr无法访问私有函数。 当然，把基类声明为public，\n * 继承类为private，该问题就不存在了。----> 见另外一个例子！\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\n#include <iostream>\nusing namespace std;\n\nclass Derived;\n\nclass Base {\nprivate:\n  virtual void fun() { cout << \"Base Fun\"; }\n  friend int main();\n};\n\nclass Derived : public Base {\npublic:\n  void fun() { cout << \"Derived Fun\"; }\n};\n\nint main() {\n  Base *ptr = new Derived;\n  ptr->fun();\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/virtual/set3/virtual_function1.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nclass Derived;\n\nclass Base {\npublic:\n  virtual void fun() { cout << \"Base Fun\"; }\n  //   friend int main();\n};\n\nclass Derived : public Base {\nprivate:\n  void fun() { cout << \"Derived Fun\"; }\n};\n\nint main() {\n  Base *ptr = new Derived;\n  ptr->fun();\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/virtual/set3/virtual_inline.cpp",
    "content": "/**\n * @file virtual_inline.cpp\n * @brief 通常类成员函数都会被编译器考虑是否进行内联。\n * 但通过基类指针或者引用调用的虚函数必定不能被内联。\n * 当然，实体对象调用虚函数或者静态调用时可以被内联，虚析构函数的静态调用也一定会被内联展开。\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\n#include <iostream>\nusing namespace std;\nclass Base {\npublic:\n  virtual void who() { cout << \"I am Base\\n\"; }\n};\nclass Derived : public Base {\npublic:\n  void who() { cout << \"I am Derived\\n\"; }\n};\n\nint main() {\n  // note here virtual function who() is called through\n  // object of the class (it will be resolved at compile\n  // time) so it can be inlined.\n  Base b;\n  b.who();\n\n  // Here virtual function is called through pointer,\n  // so it cannot be inlined\n  Base *ptr = new Derived();\n  ptr->who();\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/virtual/set4/BUILD",
    "content": "# please run `bazel run basic_content/virtual/set4:rtti`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"rtti\",\n    srcs = [\"rtti.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/virtual/set4/rtti.cpp",
    "content": "/**\n * @file rtti.cpp\n * @brief\n * 在面向对象程序设计中，有时我们需要在运行时查询一个对象是否能作为某种多态类型使用。与Java的instanceof，以及C#的as、is运算符类似，C++提供了dynamic_cast函数用于动态转型。相比C风格的强制类型转换和C++\n * reinterpret_cast，dynamic_cast提供了类型安全检查，是一种基于能力查询(Capability\n * Query)的转换，所以在多态类型间进行转换更提倡采用dynamic_cast\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\n// CPP program to illustrate\n// Run Time Type Identification\n#include <iostream>\n#include <typeinfo>\nusing namespace std;\nclass B {\n  virtual void fun() {}\n};\nclass D : public B {};\n\nint main() {\n  B *b = new D; // 向上转型\n  B &obj = *b;\n  D *d = dynamic_cast<D *>(b); // 向下转型\n  if (d != NULL)\n    cout << \"works\" << endl;\n  else\n    cout << \"cannot cast B* to D*\";\n\n  try {\n    D &dobj = dynamic_cast<D &>(obj);\n    cout << \"works\" << endl;\n  } catch (bad_cast bc) { // ERROR\n    cout << bc.what() << endl;\n  }\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/virtual/set4/warn_rtti.cpp",
    "content": "// 在使用时需要注意：被转换对象obj的类型T1必须是多态类型，即T1必须公有继承自其它类，或者T1拥有虚函数（继承或自定义）。若T1为非多态类型，使用dynamic_cast会报编译错误。\n\n// A为非多态类型\n\nclass A {};\n\n// B为多态类型\n\nclass B {\n\npublic:\n  virtual ~B() {}\n};\n\n// D为非多态类型\n\nclass D : public A {};\n\n// E为非多态类型\n\nclass E : private A {};\n\n// F为多态类型\n\nclass F : private B {};\n"
  },
  {
    "path": "basic_content/volatile/BUILD",
    "content": "# please run `bazel run basic_content/volatile:noopt_vola`\n# please run `bazel run basic_content/volatile:volatile`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"noopt_vola\",\n    srcs = [\"noopt_vola.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n\ncc_binary(\n    name = \"volatile\",\n    srcs = [\"volatile.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/volatile/README.md",
    "content": "## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 1.volatile\n\n被 `volatile` 修饰的变量，在对其进行读写操作时，会引发一些**可观测的副作用**。而这些可观测的副作用，是由**程序之外的因素决定的**。\n\n## 2.volatile应用\n（1）并行设备的硬件寄存器（如状态寄存器）。\n假设要对一个设备进行初始化，此设备的某一个寄存器为0xff800000。\n\n```c++\nint  *output = (unsigned  int *)0xff800000; //定义一个IO端口；  \nint   init(void)  \n{  \n    int i;  \n    for(i=0;i< 10;i++)\n    {  \n    *output = i;  \n    }  \n}\n```\n经过编译器优化后，编译器认为前面循环半天都是废话，对最后的结果毫无影响，因为最终只是将output这个指针赋值为 9，所以编译器最后给你编译编译的代码结果相当于：\n```c++\nint  init(void)  \n{  \n    *output = 9;  \n}\n```\n如果你对此外部设备进行初始化的过程是必须是像上面代码一样顺序的对其赋值，显然优化过程并不能达到目的。反之如果你不是对此端口反复写操作，而是反复读操作，其结果是一样的，编译器在优化后，也许你的代码对此地址的读操作只做了一次。然而从代码角度看是没有任何问题的。这时候就该使用volatile通知编译器这个变量是一个不稳定的，在遇到此变量时候不要优化。\n\n（2）一个中断服务子程序中访问到的变量；\n\n```c++\nstatic int i=0;\n\nint main()\n{\n    while(1)\n    {\n    if(i) dosomething();\n    }\n}\n\n/* Interrupt service routine */\nvoid IRS()\n{\n\ti=1;\n}\n```\n上面示例程序的本意是产生中断时，由中断服务子程序IRS响应中断，变更程序变量i，使在main函数中调用dosomething函数，但是，由于编译器判断在main函数里面没有修改过i，因此可能只执行一次对从i到某寄存器的读操作，然后每次if判断都只使用这个寄存器里面的“i副本”，导致dosomething永远不会被调用。如果将变量i加上volatile修饰，则编译器保证对变量i的读写操作都不会被优化，从而保证了变量i被外部程序更改后能及时在原程序中得到感知。\n\n（3）多线程应用中被多个任务共享的变量。\n当多个线程共享某一个变量时，该变量的值会被某一个线程更改，应该用 volatile 声明。作用是防止编译器优化把变量从内存装入CPU寄存器中，当一个线程更改变量后，未及时同步到其它线程中导致程序出错。volatile的意思是让编译器每次操作该变量时一定要从内存中真正取出，而不是使用已经存在寄存器中的值。示例如下：\n```c++\nvolatile  bool bStop=false;  //bStop 为共享全局变量  \n//第一个线程\nvoid threadFunc1()\n{\n    ...\n    while(!bStop){...}\n}\n//第二个线程终止上面的线程循环\nvoid threadFunc2()\n{\n    ...\n    bStop = true;\n}\n```\n要想通过第二个线程终止第一个线程循环，如果bStop不使用volatile定义，那么这个循环将是一个死循环，因为bStop已经读取到了寄存器中，寄存器中bStop的值永远不会变成FALSE，加上volatile，程序在执行时，每次均从内存中读出bStop的值，就不会死循环了。\n\n是否了解volatile的应用场景是区分C/C++程序员和嵌入式开发程序员的有效办法，搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道，这些都要求用到volatile变量，不懂得volatile将会带来程序设计的灾难。\n\n## 3.volatile常见问题\n下面的问题可以看一下面试者是不是真正了解volatile。\n（1）一个参数既可以是const还可以是volatile吗？为什么？\n可以。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。\n\n（2）一个指针可以是volatile吗？为什么？\n可以。尽管这并不常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。\n\n（3）下面的函数有什么错误？\n```c++\nint square(volatile int *ptr) \n{ \nreturn *ptr * *ptr; \n} \n```\n这段代码有点变态，其目的是用来返回指针ptr指向值的平方，但是，由于ptr指向一个volatile型参数，编译器将产生类似下面的代码：\n```c++\nint square(volatile int *ptr) \n{ \nint a,b; \na = *ptr; \nb = *ptr; \nreturn a * b; \n} \n```\n由于*ptr的值可能被意想不到地改变，因此a和b可能是不同的。结果，这段代码可能返回的不是你所期望的平方值！正确的代码如下：\n```c++\nlong square(volatile int *ptr) \n{ \nint a=*ptr; \nreturn a * a; \n} \n```\n## 4.volatile使用\n\n- volatile 关键字是一种类型修饰符，用它声明的类型变量表示可以被某些编译器未知的因素（操作系统、硬件、其它线程等）更改。所以使用 volatile 告诉编译器不应对这样的对象进行优化。\n\n- volatile 关键字声明的变量，每次访问时都必须从内存中取出值（没有被 volatile 修饰的变量，可能由于编译器的优化，从 CPU 寄存器中取值）\n- const 可以是 volatile （如只读的状态寄存器）\n- 指针可以是 volatile\n\n代码学习：\n\n- [noopt_vola.cpp](./noopt_vola.cpp)\n- [volatile.cpp](./volatile.cpp)\n\n"
  },
  {
    "path": "basic_content/volatile/noopt_vola.cpp",
    "content": "/* Compile code without optimization option */\n#include <stdio.h>\nint main(void) {\n  const int local = 10;\n  int *ptr = (int *)&local;\n\n  printf(\"Initial value of local : %d \\n\", local);\n\n  *ptr = 100;\n\n  printf(\"Modified value of local: %d \\n\", local);\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/volatile/volatile.cpp",
    "content": "/* Compile code with optimization option */\n#include <stdio.h>\n\nint main(void) {\n  const volatile int local = 10;\n  int *ptr = (int *)&local;\n\n  printf(\"Initial value of local : %d \\n\", local);\n\n  *ptr = 100;\n\n  printf(\"Modified value of local: %d \\n\", local);\n\n  return 0;\n}\n"
  },
  {
    "path": "basic_content/vptr_vtable/BUILD",
    "content": "# please run `bazel run basic_content/vptr_vtable:vptr1`\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"vptr1\",\n    srcs = [\"vptr1.cpp\"],\n    copts = [\"-std=c++11\"]\n)"
  },
  {
    "path": "basic_content/vptr_vtable/README.md",
    "content": "# 深入浅出C++虚函数的vptr与vtable\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n\n\n## 1.基础理论\n\n为了实现虚函数，C ++使用一种称为虚拟表的特殊形式的后期绑定。该虚拟表是用于解决在动态/后期绑定方式的函数调用函数的查找表。虚拟表有时会使用其他名称，例如“vtable”，“虚函数表”，“虚方法表”或“调度表”。\n\n虚拟表实际上非常简单，虽然用文字描述有点复杂。首先，**每个使用虚函数的类（或者从使用虚函数的类派生）都有自己的虚拟表**。该表只是编译器在编译时设置的静态数组。虚拟表包含可由类的对象调用的每个虚函数的一个条目。此表中的每个条目只是一个函数指针，指向该类可访问的派生函数。\n\n其次，编译器还会添加一个隐藏指向基类的指针，我们称之为vptr。vptr在创建类实例时自动设置，以便指向该类的虚拟表。与this指针不同，this指针实际上是编译器用来解析自引用的函数参数，vptr是一个真正的指针。\n\n因此，它使每个类对象的分配大一个指针的大小。这也意味着vptr由派生类继承，这很重要。\n\n## 2.实现与内部结构\n\n下面我们来看自动与手动操纵vptr来获取地址与调用虚函数！\n\n开始看代码之前，为了方便大家理解，这里给出调用图：\n\n![base](./img/base.jpg)\n\n代码全部遵循标准的注释风格，相信大家看了就会明白，不明白的话，可以留言！\n\n```c++\n/**\n * @file vptr1.cpp\n * @brief C++虚函数vptr和vtable\n * 编译：g++ -g -o vptr vptr1.cpp -std=c++11\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include <iostream>\n#include <stdio.h>\nusing namespace std;\n\n/**\n * @brief 函数指针\n */\ntypedef void (*Fun)();\n\n/**\n * @brief 基类\n */\nclass Base\n{\n    public:\n        Base(){};\n        virtual void fun1()\n        {\n            cout << \"Base::fun1()\" << endl;\n        }\n        virtual void fun2()\n        {\n            cout << \"Base::fun2()\" << endl;\n        }\n        virtual void fun3(){}\n        ~Base(){};\n};\n\n/**\n * @brief 派生类\n */\nclass Derived: public Base\n{\n    public:\n        Derived(){};\n        void fun1()\n        {\n            cout << \"Derived::fun1()\" << endl;\n        }\n        void fun2()\n        {\n            cout << \"DerivedClass::fun2()\" << endl;\n        }\n        ~Derived(){};\n};\n/**\n * @brief 获取vptr地址与func地址,vptr指向的是一块内存，这块内存存放的是虚函数地址，这块内存就是我们所说的虚表\n *\n * @param obj\n * @param offset\n *\n * @return \n */\nFun getAddr(void* obj,unsigned int offset)\n{\n    cout<<\"=======================\"<<endl;\n    void* vptr_addr = (void *)*(unsigned long *)obj;  //64位操作系统，占8字节，通过*(unsigned long *)obj取出前8字节，即vptr指针\n    printf(\"vptr_addr:%p\\n\",vptr_addr);\n    \n    /**\n     * @brief 通过vptr指针访问virtual table，因为虚表中每个元素(虚函数指针)在64位编译器下是8个字节，因此通过*(unsigned long *)vptr_addr取出前8字节，\n     * 后面加上偏移量就是每个函数的地址！\n     */\n    void* func_addr = (void *)*((unsigned long *)vptr_addr+offset);\n    printf(\"func_addr:%p\\n\",func_addr);\n    return (Fun)func_addr;\n}\nint main(void)\n{\n    Base ptr;\n    Derived d;\n    Base *pt = new Derived(); // 基类指针指向派生类实例\n    Base &pp = ptr; // 基类引用指向基类实例\n    Base &p = d; // 基类引用指向派生类实例\n    cout<<\"基类对象直接调用\"<<endl;\n    ptr.fun1();\n    cout<<\"基类引用指向基类实例\"<<endl;\n    pp.fun1(); \n    cout<<\"基类指针指向派生类实例并调用虚函数\"<<endl;\n    pt->fun1();\n    cout<<\"基类引用指向派生类实例并调用虚函数\"<<endl;\n    p.fun1();\n    \n    // 手动查找vptr 和 vtable\n    Fun f1 = getAddr(pt, 0);\n    (*f1)();\n    Fun f2 = getAddr(pt, 1);\n    (*f2)();\n    delete pt;\n    return 0;\n}\n```\n\n运行结果：\n\n```\n基类对象直接调用\nBase::fun1()\n基类引用指向基类实例\nBase::fun1()\n基类指针指向派生类实例并调用虚函数\nDerived::fun1()\n基类引用指向派生类实例并调用虚函数\nDerived::fun1()\n=======================\nvptr_addr:0x401130\nfunc_addr:0x400ea8\nDerived::fun1()\n=======================\nvptr_addr:0x401130\nfunc_addr:0x400ed4\nDerivedClass::fun2()\n```\n\n我们发现C++的动态多态性是通过虚函数来实现的。简单的说，通过virtual函数，指向子类的基类指针可以调用子类的函数。例如，上述通过基类指针指向派生类实例，并调用虚函数，将上述代码简化为：\n\n```c++\nBase *pt = new Derived(); // 基类指针指向派生类实例\ncout<<\"基类指针指向派生类实例并调用虚函数\"<<endl;\npt->fun1();\n```\n\n其过程为：首先程序识别出fun1()是个虚函数，其次程序使用pt->vptr来获取Derived的虚拟表。第三，它查找Derived虚拟表中调用哪个版本的fun1()。这里就可以发现调用的是Derived::fun1()。因此pt->fun1()被解析为Derived::fun1()!\n\n除此之外，上述代码大家会看到，也包含了手动获取vptr地址，并调用vtable中的函数，那么我们一起来验证一下上述的地址与真正在自动调用vtable中的虚函数，比如上述`pt->fun1()`的时候，是否一致！\n\n这里采用gdb调试，在编译的时候记得加上`-g`。\n\n通过`gdb vptr`进入gdb调试页面，然后输入`b Derived::fun1`对fun1打断点，然后通过输入r运行程序到断点处，此时我们需要查看调用栈中的内存地址，通过`disassemable fun1`可以查看当前有关fun1中的相关汇编代码，我们看到了`0x0000000000400ea8`，然后再对比上述的结果会发现与手动调用的fun1一致，fun2类似，以此证明代码正确!\n\ngdb调试信息如下：\n\n```c++\n(gdb) b Derived::fun1\nBreakpoint 1 at 0x400eb4: file vptr1.cpp, line 23.\n(gdb) r\nStarting program: /home/light/Program/CPlusPlusThings/virtual/pure_virtualAndabstract_class/vptr \n基类对象直接调用\nBase::fun1()\n基类引用指向派生类实例\nBase::fun1()\n基类指针指向派生类实例并调用虚函数\n\nBreakpoint 1, Derived::fun1 (this=0x614c20) at vptr1.cpp:23\n23\t            cout << \"Derived::fun1()\" << endl;\n(gdb) disassemble fun1\nDump of assembler code for function Derived::fun1():\n   0x0000000000400ea8 <+0>:\tpush   %rbp\n   0x0000000000400ea9 <+1>:\tmov    %rsp,%rbp\n   0x0000000000400eac <+4>:\tsub    $0x10,%rsp\n   0x0000000000400eb0 <+8>:\tmov    %rdi,-0x8(%rbp)\n=> 0x0000000000400eb4 <+12>:\tmov    $0x401013,%esi\n   0x0000000000400eb9 <+17>:\tmov    $0x602100,%edi\n   0x0000000000400ebe <+22>:\tcallq  0x4009d0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>\n   0x0000000000400ec3 <+27>:\tmov    $0x400a00,%esi\n   0x0000000000400ec8 <+32>:\tmov    %rax,%rdi\n   0x0000000000400ecb <+35>:\tcallq  0x4009f0 <_ZNSolsEPFRSoS_E@plt>\n   0x0000000000400ed0 <+40>:\tnop\n   0x0000000000400ed1 <+41>:\tleaveq \n   0x0000000000400ed2 <+42>:\tretq   \nEnd of assembler dump.\n(gdb) disassemble fun2\nDump of assembler code for function Derived::fun2():\n   0x0000000000400ed4 <+0>:\tpush   %rbp\n   0x0000000000400ed5 <+1>:\tmov    %rsp,%rbp\n   0x0000000000400ed8 <+4>:\tsub    $0x10,%rsp\n   0x0000000000400edc <+8>:\tmov    %rdi,-0x8(%rbp)\n   0x0000000000400ee0 <+12>:\tmov    $0x401023,%esi\n   0x0000000000400ee5 <+17>:\tmov    $0x602100,%edi\n   0x0000000000400eea <+22>:\tcallq  0x4009d0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>\n   0x0000000000400eef <+27>:\tmov    $0x400a00,%esi\n   0x0000000000400ef4 <+32>:\tmov    %rax,%rdi\n   0x0000000000400ef7 <+35>:\tcallq  0x4009f0 <_ZNSolsEPFRSoS_E@plt>\n   0x0000000000400efc <+40>:\tnop\n   0x0000000000400efd <+41>:\tleaveq \n   0x0000000000400efe <+42>:\tretq   \nEnd of assembler dump.\n```\n\n"
  },
  {
    "path": "basic_content/vptr_vtable/vptr1.cpp",
    "content": "#include <iostream>\n#include <stdio.h>\nusing namespace std;\n\n/**\n * @brief 函数指针\n */\ntypedef void (*Fun)();\n\n/**\n * @brief 基类\n */\nclass Base {\npublic:\n  Base(){};\n  virtual void fun1() { cout << \"Base::fun1()\" << endl; }\n  virtual void fun2() { cout << \"Base::fun2()\" << endl; }\n  virtual void fun3() {}\n  ~Base(){};\n};\n\n/**\n * @brief 派生类\n */\nclass Derived : public Base {\npublic:\n  Derived(){};\n  void fun1() { cout << \"Derived::fun1()\" << endl; }\n  void fun2() { cout << \"DerivedClass::fun2()\" << endl; }\n  ~Derived(){};\n};\n\n/**\n * @brief\n * 获取vptr地址与func地址,vptr指向的是一块内存，这块内存存放的是虚函数地址，这块内存就是我们所说的虚表\n *\n * @param obj\n * @param offset\n *\n * @return\n */\nFun getAddr(void *obj, unsigned int offset) {\n  cout << \"=======================\" << endl;\n  void *vptr_addr =\n      (void *)*(unsigned long *)obj; // 64位操作系统，占8字节，通过*(unsigned\n                                     // long *)obj取出前8字节，即vptr指针\n  printf(\"vptr_addr:%p\\n\", vptr_addr);\n\n  /**\n   * @brief 通过vptr指针访问virtual\n   * table，因为虚表中每个元素(虚函数指针)在64位编译器下是8个字节，因此通过*(unsigned\n   * long *)vptr_addr取出前8字节， 后面加上偏移量就是每个函数的地址！\n   */\n  void *func_addr = (void *)*((unsigned long *)vptr_addr + offset);\n  printf(\"func_addr:%p\\n\", func_addr);\n  return (Fun)func_addr;\n}\nint main(void) {\n  Base ptr;\n  Derived d;\n  Base *pt = new Derived(); // 基类指针指向派生类实例\n  Base &pp = ptr;           // 基类引用指向基类实例\n  Base &p = d;              // 基类引用指向派生类实例\n  cout << \"基类对象直接调用\" << endl;\n  ptr.fun1();\n  cout << \"基类引用指向基类实例\" << endl;\n  pp.fun1();\n  cout << \"基类指针指向派生类实例并调用虚函数\" << endl;\n  pt->fun1();\n  cout << \"基类引用指向派生类实例并调用虚函数\" << endl;\n  p.fun1();\n\n  // 手动查找vptr 和 vtable\n  Fun f1 = getAddr(pt, 0);\n  (*f1)();\n  Fun f2 = getAddr(pt, 1);\n  (*f2)();\n\n  delete pt;\n  return 0;\n}\n"
  },
  {
    "path": "codingStyleIdioms/1_classInitializers/1.1_nest.cpp",
    "content": "/* 1.1_类之间嵌套.cpp */\n//\n// Created by light on 19-12-9.\n//\n\n#include <iostream>\n\nclass Animal {\npublic:\n    Animal() {\n        std::cout << \"Animal() is called\" << std::endl;\n    }\n\n    Animal(const Animal &) {\n        std::cout << \"Animal (const Animal &) is called\" << std::endl;\n    }\n\n    Animal &operator=(const Animal &) {\n        std::cout << \"Animal & operator=(const Animal &) is called\" << std::endl;\n        return *this;\n    }\n\n    ~Animal() {\n        std::cout << \"~Animal() is called\" << std::endl;\n    }\n};\n\nclass Dog {\npublic:\n    // 第一种： 使用初始化列表。\n    Dog(const Animal &animal) : __animal(animal) {\n        std::cout << \"Dog(const Animal &animal) is called\" << std::endl;\n    }\n    // 第二种：构造函数赋值来初始化对象。\n//    Dog(const Animal &animal) {\n//        __animal = animal;\n//        std::cout << \"Dog(const Animal &animal) is called\" << std::endl;\n//    }\n    ~Dog() {\n        std::cout << \"~Dog() is called\" << std::endl;\n    }\n\nprivate:\n    Animal __animal;\n};\n\nint main() {\n    Animal animal;\n    std::cout << std::endl;\n    Dog d(animal);\n    std::cout << std::endl;\n    return 0;\n}"
  },
  {
    "path": "codingStyleIdioms/1_classInitializers/1.2_nodefault_ctor.cpp",
    "content": "/* 1.2_无默认构造的继承.cpp */\n//\n// Created by light on 19-12-9.\n//\n#include <iostream>\nclass Animal {\npublic:\n    Animal(int age) {\n        std::cout << \"Animal(int age) is called\" << std::endl;\n    }\n\n    Animal(const Animal & animal) {\n        std::cout << \"Animal (const Animal &) is called\" << std::endl;\n    }\n\n    Animal &operator=(const Animal & amimal) {\n        std::cout << \"Animal & operator=(const Animal &) is called\" << std::endl;\n        return *this;\n    }\n\n    ~Animal() {\n        std::cout << \"~Animal() is called\" << std::endl;\n    }\n\n};\n\nclass Dog : Animal {\npublic:\n    Dog(int age) : Animal(age) {\n        std::cout << \"Dog(int age) is called\" << std::endl;\n    }\n\n    ~Dog() {\n        std::cout << \"~Dog() is called\" << std::endl;\n    }\n};\n\nint main() {\n    Animal animal(10);\n    std::cout << std::endl;\n    Dog d(100);\n    std::cout << std::endl;\n    return 0;\n}"
  },
  {
    "path": "codingStyleIdioms/1_classInitializers/1.3_const.cpp",
    "content": "/* 1.3_类中const数据成员、引用数据成员.cpp */\n//\n// Created by light on 19-12-9.\n//\n\n#include <iostream>\n\nclass Animal {\npublic:\n    Animal(int& age,std::string name):age_(age),name_(name) {\n        std::cout << \"Animal(int age) is called\" << std::endl;\n    }\nprivate:\n    int &age_;\n    const std::string name_;\n};\n\nint main() {\n    int x = 10;\n    Animal animal(x,\"hh\");\n    return 0;\n}"
  },
  {
    "path": "codingStyleIdioms/1_classInitializers/BUILD",
    "content": "# please run `bazel run //codingStyleIdioms/1_classInitializers:1.2_nodefault_ctor`\n# please run `bazel run //codingStyleIdioms/1_classInitializers:initializer`\n# please run `bazel run //codingStyleIdioms/1_classInitializers:1.1_nest`\n# please run `bazel run //codingStyleIdioms/1_classInitializers:1.3_const`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"1.2_nodefault_ctor\",\n    srcs = [\"1.2_nodefault_ctor.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"initializer\",\n    srcs = [\"initializer.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"1.1_nest\",\n    srcs = [\"1.1_nest.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"1.3_const\",\n    srcs = [\"1.3_const.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n"
  },
  {
    "path": "codingStyleIdioms/1_classInitializers/README.md",
    "content": "# 初始化列表与赋值\n\n- const成员的初始化只能在构造函数初始化列表中进行\n- 引用成员的初始化也只能在构造函数初始化列表中进行\n- 对象成员（对象成员所对应的类没有默认构造函数）的初始化，也只能在构造函数初始化列表中进行\n\n## 类之间嵌套\n\n**第一种： 使用初始化列表。**\n\n\n```cpp\nclass Animal {\npublic:\n    Animal() {\n        std::cout << \"Animal() is called\" << std::endl;\n    }\n\n    Animal(const Animal &) {\n        std::cout << \"Animal (const Animal &) is called\" << std::endl;\n    }\n\n    Animal &operator=(const Animal &) {\n        std::cout << \"Animal & operator=(const Animal &) is called\" << std::endl;\n        return *this;\n    }\n\n    ~Animal() {\n        std::cout << \"~Animal() is called\" << std::endl;\n    }\n};\n\nclass Dog {\npublic:\n    Dog(const Animal &animal) : __animal(animal) {\n        std::cout << \"Dog(const Animal &animal) is called\" << std::endl;\n    }\n\n    ~Dog() {\n        std::cout << \"~Dog() is called\" << std::endl;\n    }\n\nprivate:\n    Animal __animal;\n};\n\nint main() {\n    Animal animal;\n    std::cout << std::endl;\n    Dog d(animal);\n    std::cout << std::endl;\n    return 0;\n}\n```\n\n运行结果：\n```cpp\nAnimal() is called\n\nAnimal (const Animal &) is called\nDog(const Animal &animal) is called\n\n~Dog() is called\n~Animal() is called\n~Animal() is called\n```\n\n依次分析从上到下：\n\nmain函数中`Animal animal;`调用默认构造。\n\n`Dog d(animal);`等价于：\n\n```\nAnimal __animal = animal;\n```\n实际上就是调用了拷贝构造，因此输出了：\n```\nAnimal (const Animal &) is called\n```\n再然后打印Dog的构造函数里面的输出。\n\n最后调用析构，程序结束。\n\n**第二种：构造函数赋值来初始化对象。**\n\n构造函数修改如下：\n```cpp\nDog(const Animal &animal) {\n    __animal = animal;\n    std::cout << \"Dog(const Animal &animal) is called\" << std::endl;\n}\n```\n此时输出结果：\n```\nAnimal() is called\n\nAnimal() is called\nAnimal & operator=(const Animal &) is called\nDog(const Animal &animal) is called\n\n~Dog() is called\n~Animal() is called\n~Animal() is called\n```\n\n于是得出：\n\n当调用`Dog d(animal);`时，等价于：\n\n先定义对象，再进行赋值，因此先调用了默认构造，再调用=操作符重载函数。\n\n```cpp\n// 假设之前已经有了animal对象\nAnimal __animal;\n__animal = animal;\n```\n\n> 小结\n\n通过上述我们得出如下结论：\n\n- **类中包含其他自定义的class或者struct，采用初始化列表，实际上就是创建对象同时并初始化**\n- **而采用类中赋值方式，等价于先定义对象，再进行赋值，一般会先调用默认构造，在调用=操作符重载函数。**\n\n## 无默认构造函数的继承关系中\n\n现考虑把上述的关系改为继承，并修改Animal与Dog的构造函数，如下代码：\n\n```cpp\nclass Animal {\npublic:\n    Animal(int age) {\n        std::cout << \"Animal(int age) is called\" << std::endl;\n    }\n\n    Animal(const Animal & animal) {\n        std::cout << \"Animal (const Animal &) is called\" << std::endl;\n    }\n\n    Animal &operator=(const Animal & amimal) {\n        std::cout << \"Animal & operator=(const Animal &) is called\" << std::endl;\n        return *this;\n    }\n\n    ~Animal() {\n        std::cout << \"~Animal() is called\" << std::endl;\n    }\n};\n\nclass Dog : Animal {\npublic:\n    Dog(int age) : Animal(age) {\n        std::cout << \"Dog(int age) is called\" << std::endl;\n    }\n\n    ~Dog() {\n        std::cout << \"~Dog() is called\" << std::endl;\n    }\n\n};\n```\n\n上述是通过初始化列表给基类带参构造传递参数，如果不通过初始化列表传递，会发生什么影响？\n\n去掉初始化列表\n```\nDog(int age)  {\n    std::cout << \"Dog(int age) is called\" << std::endl;\n}\n```\n运行程序：\n\n```\nerror: no matching function for call to ‘Animal::Animal()’\n```\n\n由于在Animal中没有默认构造函数，所以报错，遇到这种问题属于灾难性的，我们应该尽量避免，可以通过初始化列表给基类的构造初始化。\n\n\n## 类中const数据成员、引用数据成员\n\n特别是引用数据成员，必须用初始化列表初始化，而不能通过赋值初始化！\n\n例如：在上述的Animal中添加私有成员，并修改构造函数：\n```cpp\nclass Animal {\npublic:\n    Animal(int age,std::string name) {\n        std::cout << \"Animal(int age) is called\" << std::endl;\n    }\nprivate:\n    int &age_;\n    const std::string name_;\n};\n```\n\n报下面错误：\n\n```cpp\nerror: uninitialized reference member in ‘int&’\n```\n\n应该改为下面：\n\n```cpp\nAnimal(int age, std::string name) : age_(age), name_(name) {\n    std::cout << \"Animal(int age) is called\" << std::endl;\n}\n```\n\n"
  },
  {
    "path": "codingStyleIdioms/1_classInitializers/initializer.cpp",
    "content": "/* initializer.cpp */\n//\n// Created by light on 19-12-9.\n//\n\n#include <iostream>\n\nusing namespace std;\n\nclass A {\npublic:\n    A(int a) : _a(a), _p(nullptr) {     // 初始化列表\n\n    }\n\nprivate:\n    int _a;\n    int *_p;\n};\n\nint main() {\n    A aa(10);\n    return 0;\n}\n"
  },
  {
    "path": "codingStyleIdioms/2_enumclass/BUILD",
    "content": "# please run `bazel run //codingStyleIdioms/2_enumclass:namespace`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"namespace\",\n    srcs = [\"namespace.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n"
  },
  {
    "path": "codingStyleIdioms/2_enumclass/README.md",
    "content": "# C++惯用法之enum class\n\n在Effective modern C++中Item 10: Prefer scoped enums to unscoped enum，调到要用有范围的enum class代替无范围的enum。\n\n例如：\n\n```cpp\nenum Shape {circle,retangle};\nauto circle = 10;  // error\n```\n\n上述错误是因为两个circle在同一范围。\n对于enum等价于：\n```cpp\n#define circle 0\n#define retangle 1\n```\n因此后面再去定义circle就会出错。\n\n所以不管枚举名是否一样,里面的成员只要有一致的,就会出问题。\n例如：\n```cpp\nenum A {a,b};\nenum B {c,a};\n```\na出现两次,在enum B的a处报错。\n\n根据前面我们知道,enum名在范围方面没有什么作用,因此我们想到了namespace,如下例子:\n```cpp\n// 在创建枚举时，将它们放在名称空间中，以便可以使用有意义的名称访问它们:\nnamespace EntityType {\n    enum Enum {\n        Ground = 0,\n        Human,\n        Aerial,\n        Total\n    };\n}\n\nvoid foo(EntityType::Enum entityType)\n{\n    if (entityType == EntityType::Ground) {\n        /*code*/\n    }\n}\n```\n将命名空间起的有意思点,就可以达到想要的效果。\n\n但是不断的使用命名空间,势必太繁琐,而且如果我不想使用namespace,要达到这样的效果,便会变得不安全,也没有约束。\n\n因此在c++11后,引入enum class。\n\nenum class 解决了为enum成员定义类型、类型安全、约束等问题。\n回到上述例子：\n```cpp\n// enum class\nenum class EntityType {\n    Ground = 0,\n    Human,\n    Aerial,\n    Total\n};\n\nvoid foo(EntityType entityType)\n{\n    if (entityType == EntityType::Ground) {\n        /*code*/\n    }\n}\n```\n这便是这一节要阐述的惯用法:enum class。"
  },
  {
    "path": "codingStyleIdioms/2_enumclass/namespace.cpp",
    "content": "//\n// Created by light on 19-12-9.\n//\n\n#include <iostream>\n\nusing namespace std;\n// 在创建枚举时，将它们放在名称空间中，以便可以使用有意义的名称访问它们:\nnamespace EntityType {\n    enum Enum {\n        Ground = 0,\n        Human,\n        Aerial,\n        Total\n    };\n}\n\nvoid foo(EntityType::Enum entityType)\n{\n    if (entityType == EntityType::Ground) {\n        /*code*/\n    }\n}\n\n// enum class\nenum class EntityType1 {\n    Ground = 0,\n    Human,\n    Aerial,\n    Total\n};\n\nvoid foo(EntityType1 entityType)\n{\n    if (entityType == EntityType1::Ground) {\n        /*code*/\n    }\n}\n\nint main() {\n\n    return 0;\n}\n"
  },
  {
    "path": "codingStyleIdioms/3_RAII/BUILD",
    "content": "# please run `bazel run //codingStyleIdioms/3_RAII:RAII_fstram`\n# please run `bazel run //codingStyleIdioms/3_RAII:c++_example1`\n# please run `bazel run //codingStyleIdioms/3_RAII:c++_example`\n# please run `bazel run //codingStyleIdioms/3_RAII:RAII`\n# please run `bazel run //codingStyleIdioms/3_RAII:c_example`\n# please run `bazel run //codingStyleIdioms/3_RAII:c++_example2`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"RAII_fstram\",\n    srcs = [\"RAII_fstram.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"c++_example1\",\n    srcs = [\"c++_example1.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"c++_example\",\n    srcs = [\"c++_example.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"RAII\",\n    srcs = [\"RAII.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"c_example\",\n    srcs = [\"c_example.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"c++_example2\",\n    srcs = [\"c++_example2.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n"
  },
  {
    "path": "codingStyleIdioms/3_RAII/RAII.cpp",
    "content": "#include <iostream>\n#include <mutex>\n#include <fstream>\nusing namespace std;\n// RAII 资源获取即初始化,例1\n\nenum class shape_type {\n    circle,\n    triangle,\n    rectangle,\n};\n\n\nclass shape {\npublic:\n    shape() { cout << \"shape\" << endl; }\n\n    virtual void print() {\n        cout << \"I am shape\" << endl;\n    }\n\n    virtual ~shape() {}\n};\n\nclass circle : public shape {\npublic:\n    circle() { cout << \"circle\" << endl; }\n\n    void print() {\n        cout << \"I am circle\" << endl;\n    }\n};\n\nclass triangle : public shape {\npublic:\n    triangle() { cout << \"triangle\" << endl; }\n\n    void print() {\n        cout << \"I am triangle\" << endl;\n    }\n};\n\nclass rectangle : public shape {\npublic:\n    rectangle() { cout << \"rectangle\" << endl; }\n\n    void print() {\n        cout << \"I am rectangle\" << endl;\n    }\n};\n\n// 利用多态 上转 如果返回值为shape,会存在对象切片问题。\nshape *create_shape(shape_type type) {\n    switch (type) {\n        case shape_type::circle:\n            return new circle();\n        case shape_type::triangle:\n            return new triangle();\n        case shape_type::rectangle:\n            return new rectangle();\n    }\n}\n\nclass shape_wrapper {\npublic:\n    explicit shape_wrapper(shape *ptr = nullptr) : ptr_(ptr) {}\n\n    ~shape_wrapper() {\n        delete ptr_;\n    }\n\n    shape *get() const {\n        return ptr_;\n    }\n\nprivate:\n    shape *ptr_;\n};\n\n\nvoid foo() {\n    shape_wrapper ptr(create_shape(shape_type::circle));\n    ptr.get()->print();\n}\n\nint main() {\n\n    // 第一种方式\n    shape *sp = create_shape(shape_type::circle);\n    sp->print();\n    delete sp;\n\n    // 第二种方式 RAII\n    foo();\n\n    return 0;\n}\n"
  },
  {
    "path": "codingStyleIdioms/3_RAII/RAII.md",
    "content": "# C++惯用法之消除垃圾收集器-资源获取即初始化方法(RAII)\n\n## 0.导语\n\n在C语言中，有三种类型的内存分配:静态、自动和动态。静态变量是嵌入在源文件中的常数，因为它们有已知的大小并且从不改变，所以它们并不那么有趣。自动分配可以被认为是堆栈分配——当一个词法块进入时分配空间，当该块退出时释放空间。它最重要的特征与此直接相关。在C99之前，自动分配的变量需要在编译时知道它们的大小。这意味着任何字符串、列表、映射以及从这些派生的任何结构都必须存在于堆中的动态内存中。\n\n程序员使用四个基本操作明确地分配和释放动态内存:malloc、realloc、calloc和free。前两个不执行任何初始化，内存可能包含碎片。除了自由，他们都可能失败。在这种情况下，它们返回一个空指针，其访问是未定义的行为；在最好的情况下，你的程序会崩溃。在最坏的情况下，你的程序看起来会工作一段时间，在崩溃前处理垃圾数据。\n\n例如：\t\n\n```cpp\nint main() {\n   char *str = (char *) malloc(7); \n   strcpy(str, \"toptal\");\n   printf(\"char array = \\\"%s\\\" @ %u\\n\", str, str);\n\n   str = (char *) realloc(str, 11);\n   strcat(str, \".com\");\n   printf(\"char array = \\\"%s\\\" @ %u\\n\", str, str);\n\n   free(str);\n   \n   return(0);\n}\n```\n\n输出：\n\n```cpp\nchar array = \"toptal\" @ 2762894960\nchar array = \"toptal.com\" @ 2762894960\n```\n\n尽管代码很简单，但它已经包含了一个反模式和一个有问题的决定。在现实生活中，你不应该直接写字节数，而应该使用sizeof函数。类似地，我们将char *数组精确地分配给我们需要的字符串大小的两倍(比字符串长度多一倍，以说明空终止)，这是一个相当昂贵的操作。一个更复杂的程序可能会构建一个更大的字符串缓冲区，允许字符串大小增长。\n\n## 1.RAII的发明：新希望\n\n至少可以说，所有手动管理都是令人不快的。 在80年代中期，Bjarne Stroustrup为他的全新语言C ++发明了一种新的范例。 他将其称为“资源获取就是初始化”，其基本见解如下：**可以指定对象具有构造函数和析构函数，这些构造函数和析构函数在适当的时候由编译器自动调用，这为管理给定对象的内存提供了更为方便的方法。** 需要，并且该技术对于不是内存的资源也很有用。\n\n意味着上面的例子在c++中更简洁：\n\n```cpp\nint main() {\n   std::string str = std::string (\"toptal\");\n   std::cout << \"string object: \" << str << \" @ \" << &str << \"\\n\";\n   \n   str += \".com\";\n   std::cout << \"string object: \" << str << \" @ \" << &str << \"\\n\";\n   \n   return(0);\n}\n```\n\n输出：\n\n```cpp\nstring object: toptal @ 0x7fffa67b9400\nstring object: toptal.com @ 0x7fffa67b9400\n```\n\n在上述例子中，我们没有手动内存管理！构造string对象，调用重载方法，并在函数退出时自动销毁。不幸的是，同样的简单也会导致其他问题。让我们详细地看一个例子：\n\n```cpp\nvector<string> read_lines_from_file(string &file_name) {\n\tvector<string> lines;\n\tstring line;\n\t\n\tifstream file_handle (file_name.c_str());\n\twhile (file_handle.good() && !file_handle.eof() && file_handle.peek()!=EOF) {\n\t\tgetline(file_handle, line);\n\t\tlines.push_back(line);\n\t}\n\t\n\tfile_handle.close();\n\t\n\treturn lines;\n}\n\nint main(int argc, char* argv[]) {\n\t// get file name from the first argument\n\tstring file_name (argv[1]);\n\tint count = read_lines_from_file(file_name).size();\n\tcout << \"File \" << file_name << \" contains \" << count << \" lines.\";\n\t\n\treturn 0;\n}\n```\n\n输出：\n\n```cpp\nFile makefile contains 37 lines.\n```\n\n这看起来很简单。`vector`被填满、返回和调用。然而，作为关心性能的高效程序员，这方面的一些问题困扰着我们:在return语句中，由于使用了值语义，`vector`在销毁之前不久就被复制到一个新`vector`中。\n\n> 在现代C ++中，这不再是严格的要求了。 C ++ 11引入了移动语义的概念，其中将原点保留在有效状态（以便仍然可以正确销毁）但未指定状态。 对于编译器而言，返回调用是最容易优化以优化语义移动的情况，因为它知道在进行任何进一步访问之前不久将销毁源。 但是，该示例的目的是说明为什么人们在80年代末和90年代初发明了一大堆垃圾收集的语言，而在那个时候C ++ move语义不可用。\n\n对于数据量比较大的文件，这可能会变得昂贵。 让我们对其进行优化，只返回一个指针。 语法进行了一些更改，但其他代码相同：\n\n```cpp\nvector<string> * read_lines_from_file(string &file_name) {\n\tvector<string> * lines;\n\tstring line;\n\t\n\tifstream file_handle (file_name.c_str());\n\twhile (file_handle.good() && !file_handle.eof() && file_handle.peek()!=EOF) {\n\t\tgetline(file_handle, line);\n\t\tlines->push_back(line);\n\t}\n\t\n\tfile_handle.close();\n\t\n\treturn lines;\n}\nint main(int argc, char* argv[]) {\n\t// get file name from the first argument\n\tstring file_name (argv[1]);\n\tint count = read_lines_from_file(file_name).size();\n\tcout << \"File \" << file_name << \" contains \" << count << \" lines.\";\n\t\n\treturn 0;\n}\n```\n\n输出：\n\n```\nSegmentation fault (core dumped)\n```\n\n程序崩溃！我们只需要将上述的`lines`进行内存分配：\n\n```cpp\nvector<string> * lines = new vector<string>;\n```\n\n这样就可以运行了！\n\n不幸的是，尽管这看起来很完美，但它仍然有一个缺陷:它会泄露内存。在C++中，指向堆的指针在不再需要后必须手动删除；否则，一旦最后一个指针超出范围，该内存将变得不可用，并且直到进程结束时操作系统对其进行管理后才会恢复。惯用的现代C++将在这里使用`unique_ptr`，它实现了期望的行为。它删除指针超出范围时指向的对象。然而，这种行为直到C++11才成为语言的一部分。\n\n在这里，可以直接使用C++11之前的语法，只是把main中改一下即可：\n\n```cpp\nvector<string> * read_lines_from_file(string &file_name) {\n\tvector<string> * lines = new vector<string>;\n\tstring line;\n\t\n\tifstream file_handle (file_name.c_str());\n\twhile (file_handle.good() && !file_handle.eof() && file_handle.peek()!=EOF) {\n\t\tgetline(file_handle, line);\n\t\tlines->push_back(line);\n\t}\n\t\n\tfile_handle.close();\n\t\n\treturn lines;\n}\n\nint main(int argc, char* argv[]) {\n\t// get file name from the first argument\n\tstring file_name (argv[1]);\n\tvector<string> * file_lines = read_lines_from_file(file_name);\n\tint count = file_lines->size();\n\tdelete file_lines;\n\tcout << \"File \" << file_name << \" contains \" << count << \" lines.\";\n\t\n\treturn 0;\n}\n```\n\n手动去分配内存与释放内存。\n\n**不幸的是，随着程序扩展到上述范围之外，很快就变得更加难以推理指针应该在何时何地被删除。当一个函数返回指针时，你现在拥有它吗？您应该在完成后自己删除它，还是它属于某个稍后将被一次性释放的数据结构？一方面出错，内存泄漏，另一方面出错，你已经破坏了正在讨论的数据结构和其他可能的数据结构，因为它们试图取消引用现在不再有效的指针。**\n\n## 2.“使用垃圾收集器，flyboy！”\n\n垃圾收集器不是一项新技术。 它们由John McCarthy在1959年为Lisp发明。 1980年，随着Smalltalk-80的出现，垃圾收集开始成为主流。 但是，1990年代代表了该技术的真正发芽：在1990年至2000年之间，发布了多种语言，所有语言都使用一种或另一种垃圾回收：Haskell，Python，Lua，Java，JavaScript，Ruby，OCaml 和C＃是最著名的。\n\n什么是垃圾收集？ 简而言之，这是一组用于自动执行手动内存管理的技术。 它通常作为具有手动内存管理的语言（例如C和C ++）的库提供，但在需要它的语言中更常用。 最大的优点是程序员根本不需要考虑内存。 都被抽象了。 例如，相当于我们上面的文件读取代码的Python就是这样：\n\n```python\ndef read_lines_from_file(file_name):\n\tlines = []\n\twith open(file_name) as fp: \n\t\tfor line in fp:\n\t\t\tlines.append(line)\n\treturn lines\n\t\nif __name__ == '__main__':\n\timport sys\n\tfile_name = sys.argv[1]\n\tcount = len(read_lines_from_file(file_name))\n\tprint(\"File {} contains {} lines.\".format(file_name, count))\n```\n\n行数组是在第一次分配给它时出现的，并且不复制到调用范围就返回。 由于时间不确定，它会在超出该范围后的某个时间被垃圾收集器清理。 有趣的是，在Python中，用于非内存资源的RAII不是惯用语言。 允许-我们可以简单地编写`fp = open（file_name）`而不是使用with块，然后让GC清理。 但是建议的模式是在可能的情况下使用上下文管理器，以便可以在确定的时间释放它们。\n\n尽管简化了内存管理，但要付出很大的代价。 在引用计数垃圾回收中，所有变量赋值和作用域出口都会获得少量成本来更新引用。 在标记清除系统中，在GC清除内存的同时，所有程序的执行都以不可预测的时间间隔暂停。 这通常称为世界停止事件。 同时使用这两种系统的Python之类的实现都会受到两种惩罚。 这些问题降低了垃圾收集语言在性能至关重要或需要实时应用程序的情况下的适用性。 即使在以下玩具程序上，也可以看到实际的性能下降：\n\n```cpp\n$ make cpp && time ./c++ makefile\ng++ -o c++ c++.cpp\nFile makefile contains 38 lines.\nreal    0m0.016s\nuser    0m0.000s\nsys     0m0.015s\n\n$ time python3 python3.py makefile\nFile makefile contains 38 lines.\n\nreal    0m0.041s\nuser    0m0.015s\nsys     0m0.015s\n```\n\nPython版本的实时时间几乎是C ++版本的三倍。 尽管并非所有这些差异都可以归因于垃圾收集，但它仍然是可观的。\n\n## 3.所有权：RAII觉醒\n\n我们知道对象的生存期由其范围决定。 但是，有时我们需要创建一个对象，该对象与创建对象的作用域无关，这是有用的，或者很有用。 在C ++中，运算符new用于创建这样的对象。 为了销毁对象，可以使用运算符delete。 由new操作员创建的对象是动态分配的，即在动态内存（也称为堆或空闲存储）中分配。 因此，由new创建的对象将继续存在，直到使用delete将其明确销毁为止。\n\n使用new和delete时可能发生的一些错误是：\n\n- 对象（或内存）泄漏：使用new分配对象，而忘记删除该对象。\n\n- 过早删除（或悬挂引用）：持有指向对象的另一个指针，删除该对象，然而还有其他指针在引用它。\n\n- 双重删除：尝试两次删除一个对象。\n\n通常，范围变量是首选。 但是，RAII可以用作new和delete的替代方法，以使对象独立于其范围而存在。 这种技术包括将指针分配到在堆上分配的对象，并将其放在句柄/管理器对象中。 后者具有一个析构函数，将负责销毁该对象。 这将确保该对象可用于任何想要访问它的函数，并且该对象在句柄对象的生存期结束时将被销毁，而无需进行显式清理。\n\n来自C ++标准库的使用RAII的示例为std :: string和std :: vector。\n\n考虑这段代码：\n\n```cpp\nvoid fn(const std::string& str)\n{\n    std::vector<char> vec;\n    for (auto c : str)\n        vec.push_back(c);\n    // do something\n}\n```\n\n当创建`vector`,并将元素推入`vector`时，您不必担心分配和取消分配此类元素内存。 `vector`使用new为其堆上的元素分配空间，并使用delete释放该空间。 作为vector的用户，您无需关心实现细节，并且会相信vector不会泄漏。 在这种情况下，向量是其元素的句柄对象。\n\n标准库中使用RAII的其他示例是std :: shared_ptr，std :: unique_ptr和std :: lock_guard。\n\n该技术的另一个名称是SBRM，是范围绑定资源管理的缩写。\n\n现在，我们将上述读取文件例子，进行修改：\n\n```cpp\n#include <iostream>\n#include <vector>\n#include <cstring>\n#include <fstream>\n#include <bits/unique_ptr.h>\n\nusing namespace std;\nunique_ptr<vector<string>> read_lines_from_file(string &file_name) {\n    unique_ptr<vector<string>> lines(new vector<string>);\n    string line;\n\n    ifstream file_handle (file_name.c_str());\n    while (file_handle.good() && !file_handle.eof() && file_handle.peek()!=EOF) {\n        getline(file_handle, line);\n        lines->push_back(line);\n    }\n\n    file_handle.close();\n\n    return lines;\n}\nint main(int argc, char* argv[]) {\n\t// get file name from the first argument\n\tstring file_name (argv[1]);\n\tint count = read_lines_from_file(file_name).get()->size();\n    cout << \"File \" << file_name << \" contains \" << count << \" lines.\";\n\n\treturn 0;\n}\n```\n\n## 4.只有在最后，你才意识到RAII的真正力量。\n\n自从编译器发明以来，手动内存管理是程序员一直在想办法避免的噩梦。 RAII是一种很有前途的模式，但由于没有一些奇怪的解决方法，它根本无法用于堆分配的对象，因此在C ++中会受到影响。 因此，在90年代出现了垃圾收集语言的爆炸式增长，旨在使程序员生活更加愉快，即使以性能为代价。\n\n最后，RAII总结如下：\n\n- 资源在析构函数中被释放\n\n- 该类的实例是堆栈分配的\n- 资源是在构造函数中获取的。 \n\nRAII代表“资源获取是初始化”。\n\n常见的例子有：\n\n- 文件操作\n\n- 智能指针\n\n- 互斥量\n\n## 5.参考文章\n\n> 1.https://www.toptal.com/software/eliminating-garbage-collector#remote-developer-job\n\n> 2.https://stackoverflow.com/questions/2321511/what-is-meant-by-resource-acquisition-is-initialization-raii\n"
  },
  {
    "path": "codingStyleIdioms/3_RAII/RAII_fstram.cpp",
    "content": "//\n// Created by light on 19-12-9.\n//\n#include <iostream>\n#include <fstream>\n\n// RAII 资源获取即初始化,例2\n\n// C ++保证在对象超出范围时调用析构函数，而不管控制如何离开该范围。\n// 即使抛出异常，所有本地对象也会超出范围，因此它们的相关资源将被清除。\n\nvoid foo() {\n    std::fstream file(\"bar.txt\"); // open a file \"bar.txt\"\n    if (rand() % 2) {\n        // if this exception is thrown, we leave the function, and so\n        // file's destructor is called, which closes the file handle.\n        throw std::exception();\n    }\n    // if the exception is not called, we leave the function normally, and so\n    // again, file's destructor is called, which closes the file handle.\n}\n\nint main() {\n    try {\n        foo();\n    } catch (std::exception) {\n        puts(\"exception!\");\n    }\n}"
  },
  {
    "path": "codingStyleIdioms/3_RAII/c++_example.cpp",
    "content": "//\n// Created by light on 19-12-12.\n//\n#include <iostream>\nint main() {\n    std::string str = std::string (\"toptal\");\n    std::cout << \"string object: \" << str << \" @ \" << &str << \"\\n\";\n\n    str += \".com\";\n    std::cout << \"string object: \" << str << \" @ \" << &str << \"\\n\";\n\n    return(0);\n}"
  },
  {
    "path": "codingStyleIdioms/3_RAII/c++_example1.cpp",
    "content": "//\n// Created by light on 19-12-12.\n//\n#include <iostream>\n#include <vector>\n#include <cstring>\n#include <fstream>\n\nusing namespace std;\nvector<string> read_lines_from_file(string &file_name) {\n    vector<string> lines;\n    string line;\n\n    ifstream file_handle (file_name.c_str());\n    //  file_handle.peek()!=EOF 解决多读一行问题\n    while (file_handle.good() && !file_handle.eof() && file_handle.peek()!=EOF) {\n        getline(file_handle, line);\n        lines.push_back(line);\n    }\n\n    file_handle.close();\n\n    return lines;\n}\nvector<string> * read_lines_from_file1(string &file_name) {\n    vector<string> * lines;\n    string line;\n\n    ifstream file_handle (file_name.c_str());\n    //  file_handle.peek()!=EOF\n    while (file_handle.good() && !file_handle.eof() && file_handle.peek()!=EOF) {\n        getline(file_handle, line);\n        lines->push_back(line);\n    }\n\n    file_handle.close();\n\n    return lines;\n}\nvector<string> * read_lines_from_file1_1(string &file_name) {\n    vector<string> * lines=new vector<string>;\n    string line;\n\n    ifstream file_handle (file_name.c_str());\n    while (file_handle.good() && !file_handle.eof() &&  file_handle.peek()!=EOF) {\n        getline(file_handle, line);\n        lines->push_back(line);\n    }\n\n    file_handle.close();\n\n    return lines;\n}\nint main() {\n    // get file name from the first argument\n    string file_name (\"/home/light/CLionProjects/Morden_C++/CMakeLists.txt\");\n    int count = read_lines_from_file(file_name).size();\n    cout << \"File \" << file_name << \" contains \" << count << \" lines.\";\n\n    cout<<endl;\n//    string file_name1 (\"/home/light/CLionProjects/Morden_C++/CMakeLists.txt\");\n//    int count1 = read_lines_from_file1(file_name1)->size();\n//    cout << \"File \" << file_name << \" contains \" << count1 << \" lines.\";\n    string file_name1 (\"/home/light/CLionProjects/Morden_C++/CMakeLists.txt\");\n    int count1 = read_lines_from_file1_1(file_name1)->size();\n    cout << \"File \" << file_name << \" contains \" << count1 << \" lines.\";\n    return 0;\n}\n"
  },
  {
    "path": "codingStyleIdioms/3_RAII/c++_example2.cpp",
    "content": "//\n// Created by light on 19-12-12.\n//\n#include <iostream>\n#include <vector>\n#include <cstring>\n#include <fstream>\n#include <bits/unique_ptr.h>\n\nusing namespace std;\nunique_ptr<vector<string>> read_lines_from_file(string &file_name) {\n    unique_ptr<vector<string>> lines(new vector<string>);\n    string line;\n\n    ifstream file_handle (file_name.c_str());\n    while (file_handle.good() && !file_handle.eof()) {\n        getline(file_handle, line);\n        lines->push_back(line);\n    }\n\n    file_handle.close();\n\n    return lines;\n}\nint main() {\n    // get file name from the first argument\n    string file_name (\"/home/light/CLionProjects/Morden_C++/CMakeLists.txt\");\n    int count = read_lines_from_file(file_name).get()->size();\n    cout << \"File \" << file_name << \" contains \" << count << \" lines.\";\n    cout<<endl;\n    return 0;\n}"
  },
  {
    "path": "codingStyleIdioms/3_RAII/c_example.cpp",
    "content": "//\n// Created by light on 19-12-12.\n//\n#include <stdio.h>\n#include <string.h>\n#include <malloc.h>\nint main() {\n    char *str = (char *) malloc(7);\n    strcpy(str, \"toptal\");\n    printf(\"char array = \\\"%s\\\" @ %u\\n\", str, str);\n\n    str = (char *) realloc(str, 11);\n    strcat(str, \".com\");\n    printf(\"char array = \\\"%s\\\" @ %u\\n\", str, str);\n\n    free(str);\n\n    return(0);\n}"
  },
  {
    "path": "codingStyleIdioms/4_copy-swap/BUILD",
    "content": "# please run `bazel run //codingStyleIdioms/4_copy-swap:copy-swapAndADL`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"copy-swapAndADL\",\n    srcs = [\"copy-swapAndADL.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n"
  },
  {
    "path": "codingStyleIdioms/4_copy-swap/README.md",
    "content": "> 为什么我们需要复制和交换习惯？\n\n任何管理资源的类（包装程序，如智能指针）都需要实现big three。尽管拷贝构造函数和析构函数的目标和实现很简单。\n\n但是复制分配运算符无疑是最细微和最困难的。\n\n> 应该怎么做？需要避免什么陷阱？\n\ncopy-swap是解决方案，可以很好地协助赋值运算符实现两件事：避免代码重复，并提供强大的**异常保证**。\n\n>它是如何工作的？\n\n**从概念上讲，它通过使用拷贝构造函数的功能来创建数据的本地副本，然后使用交换功能获取复制的数据，将旧数据与新数据交换来工作。然后，临时副本将销毁，并随身携带旧数据。我们剩下的是新数据的副本。**\n\n为了使用copy-swap，我们需要三件事：\n\n- 一个有效的拷贝构造函数\n- 一个有效的析构函数（两者都是任何包装程序的基础，因此无论如何都应完整）以及交换功能。\n\n交换函数是一种不抛异常函数，它交换一个类的两个对象或者成员。我们可能很想使用std :: swap而不是提供我们自己的方法，但这是不可能的。 std :: swap在实现中使用了copy-constructor和copy-assignment运算符，我们最终将尝试根据自身定义赋值运算符！\n\n（不仅如此，对swap的无条件调用将使用我们的自定义swap运算符，从而跳过了std :: swap会导致的不必要的类构造和破坏。）\n\n具体例子如下：\n\n```cpp\nnamespace A {\n    template<typename T>\n    class smart_ptr {\n    public:\n        smart_ptr() noexcept : ptr_(new T()) {\n\n        }\n\n        smart_ptr(const T &ptr) noexcept : ptr_(new T(ptr)) {\n\n        }\n\n        smart_ptr(smart_ptr &rhs) noexcept {\n            ptr_ = rhs.release();       // 释放所有权,此时rhs的ptr_指针为nullptr\n        }\n\n        void swap(smart_ptr &rhs) noexcept { // noexcept == throw() 保证不抛出异常\n            using std::swap;\n            swap(ptr_, rhs.ptr_);\n        }\n\n        T *release() noexcept {\n            T *ptr = ptr_;\n            ptr_ = nullptr;\n            return ptr;\n        }\n\n        T *get() const noexcept {\n            return ptr_;\n        }\n\n    private:\n        T *ptr_;\n    };\n\n// 提供一个非成员swap函数for ADL(Argument Dependent Lookup)\n    template<typename T>\n    void swap(A::smart_ptr<T> &lhs, A::smart_ptr<T> &rhs) noexcept {\n        lhs.swap(rhs);\n    }\n}\n// 注释开启,会引发ADL冲突\n//namespace std {\n//    // 提供一个非成员swap函数for ADL(Argument Dependent Lookup)\n//    template<typename T>\n//    void swap(A::smart_ptr<T> &lhs, A::smart_ptr<T> &rhs) noexcept {\n//        lhs.swap(rhs);\n//    }\n//\n//}\n\nint main() {\n\n    using std::swap;\n    A::smart_ptr<std::string> s1(\"hello\"), s2(\"world\");\n    // 交换前\n    std::cout << *s1.get() << \" \" << *s2.get() << std::endl;\n    swap(s1, s2);      // 这里swap 能够通过Koenig搜索或者说ADL根据s1与s2的命名空间来查找swap函数\n    // 交换后\n    std::cout << *s1.get() << \" \" << *s2.get() << std::endl;\n    s1=s2;\n}\n```\n\n现在为了让上述的`s1=s2`完成工作，必须实现赋值运算符。\n\n> 方法1\n\n为了避免自赋值,通常采用下面写法 。\n\n不好!  \n\n不具备异常安全,只具备自我赋值安全性\n\n```cpp\nsmart_ptr &operator=(const smart_ptr &rhs) {\n    if (*this != rhs) {\n        delete ptr_;\n        ptr_ = new T(rhs.ptr_);  // 当new 发生异常,此时ptr_指向的而是一块被删除区域,而不是被赋值对象的区域\n        return *this;\n    }\n    return *this;\n}\n```\n\n> 方法2\n\n如果new出现异常,ptr_会保持原装!  也可以处理自我赋值! 还是不够好!\n\n**这样就会导致代码膨胀，于是导致了另一个问题：代码冗余**\n\n```cpp\n// 方法2：如果new出现异常,ptr_会保持原装!  也可以处理自我赋值! 还是不够好!\nsmart_ptr &operator=(const smart_ptr &rhs) {\n    T *origin = ptr_;\n    ptr_ = new T(rhs.ptr_);\n    delete origin;\n    return *this;\n}\n```\n\n> 方法3\n\ncopy and swap 很好!\n\n```cpp\nsmart_ptr &operator=(smart_ptr &rhs) noexcept {\n    smart_ptr tmp(rhs);\n    swap(tmp);\n    return *this;\n}\n```\n\n> 方法4\n\n改为传值，同方法3！\n\n```cpp\nsmart_ptr &operator=(smart_ptr rhs) noexcept {\n    swap(rhs);\n    return *this;\n}\n```\n\n> C++11 move\n\n我们在big three上加上move ctor与move assignment就构成了big five。\n\n此时再次拓展上述的代码：\n\n```cpp\n// move ctor\nsmart_ptr(smart_ptr &&rhs) noexcept {\n    std::cout << \"move ctor\" << std::endl;\n    ptr_ = rhs.ptr_;\n    if (ptr_)\n        rhs.ptr_ = nullptr;\n}\n\n// move assignment\nsmart_ptr &operator=(smart_ptr &&rhs) noexcept {\n    std::cout << \"move assignment\" << std::endl;\n    smart_ptr tmp(rhs);\n    swap(rhs);\n    return *this;\n}\n```\n\n**实际上，我们比那个不需要多写代码move assignment，copy-and-swap 技巧 和 move-and-swap 技巧是共享同一个函数的。**当copy构造为上述的方法4时，**对于C++ 11，编译器会依据参数是左值还是右值在拷贝构造函数和移动构造函数间进行选择：**\n\n```cpp\nsmart_ptr &operator=(smart_ptr rhs) noexcept {\n    swap(rhs);\n    return *this;\n}\n```\n\n所以当这个同上述写的\n\n```cpp\nsmart_ptr &operator=(smart_ptr &&rhs) noexcept{}\n```\n\n同时存在，就会出现error: ambiguous overload for ‘operator=’ 。\n\n调用处如下：\n\n```cpp\nA::smart_ptr<std::string> s1(\"hello\"), s2(\"world\");\nA::smart_ptr<std::string> s3 = s1;\nA::smart_ptr<std::string> s4 = std::move(s1);\n```\n\n- 如果是 s3 = s1，这样就会调用拷贝构造函数来初始化other（因为s1是左值），赋值操作符会与新创建的对象交换数据，深度拷贝。这就是copy and swap 惯用法的定义：构造一个副本，与副本交换数据，并让副本在作用域内自动销毁。\n- 如果是s4 = std::move(s1)，这样就会调用移动构造函数来初始化rhs（因为std::move(s1)是右值），所以这里没有深度拷贝，只有高效的数据转移。\n\n因此也可以称呼它为“统一赋值操作符”，因为它合并了\"拷贝赋值\"与\"移动赋值\"。\n\n"
  },
  {
    "path": "codingStyleIdioms/4_copy-swap/copy-swapAndADL.cpp",
    "content": "//\n// Created by light on 19-12-9.\n//\n#include <iostream>\n\n// copy and swap : https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom\n// ADL : https://stackoverflow.com/questions/8111677/what-is-argument-dependent-lookup-aka-adl-or-koenig-lookup\n\nnamespace A {\n    template<typename T>\n    class smart_ptr {\n    public:\n        smart_ptr() noexcept : ptr_(new T()) {\n\n        }\n\n        smart_ptr(const T &ptr) noexcept : ptr_(new T(ptr)) {\n\n        }\n\n        smart_ptr(smart_ptr &rhs) noexcept {\n            std::cout << \"copy ctor\" << std::endl;\n            ptr_ = rhs.release();       // 释放所有权,此时rhs的ptr_指针为nullptr\n        }\n\n        // 方法1：为了避免自赋值,通常采用下面写法   不好!  不具备异常安全,只具备自我赋值安全性\n//        smart_ptr &operator=(const smart_ptr &rhs) {\n//            if (*this != rhs) {\n//                delete ptr_;\n//                ptr_ = new T(rhs.ptr_);  // 当new 发生异常,此时ptr_指向的而是一块被删除区域,而不是被赋值对象的区域\n//                return *this;\n//            }\n//            return *this;\n//        }\n        // 方法2：如果new出现异常,ptr_会保持原装!  也可以处理自我赋值! 还是不够好!\n//        smart_ptr &operator=(const smart_ptr &rhs) {\n//            T *origin = ptr_;\n//            ptr_ = new T(rhs.ptr_);\n//            delete origin;\n//            return *this;\n//        }\n        // 方法3：copy and swap 很好!\n//        smart_ptr &operator=(smart_ptr &rhs) noexcept {\n//            smart_ptr tmp(rhs);\n//            swap(tmp);\n//            return *this;\n//        }\n\n        // 方法4：同方法3,改为传值\n        // 既适用于copy ctor也适用于 move ctor\n        smart_ptr &operator=(smart_ptr rhs) noexcept {\n            swap(rhs);\n            return *this;\n        }\n        // move ctor\n        smart_ptr(smart_ptr &&rhs) noexcept {\n            std::cout << \"move ctor\" << std::endl;\n            ptr_ = rhs.ptr_;\n            if (ptr_)\n                rhs.ptr_ = nullptr;\n        }\n\n        // move assignment\n//        smart_ptr &operator=(smart_ptr &&rhs) noexcept {\n//            std::cout << \"move assignment\" << std::endl;\n//            smart_ptr tmp(rhs);\n//            swap(rhs);\n//            return *this;\n//        }\n\n        void swap(smart_ptr &rhs) noexcept { // noexcept == throw() 保证不抛出异常\n            using std::swap;\n            swap(ptr_, rhs.ptr_);\n        }\n\n        T *release() noexcept {\n            T *ptr = ptr_;\n            ptr_ = nullptr;\n            return ptr;\n        }\n\n        T *get() const noexcept {\n            return ptr_;\n        }\n\n    private:\n        T *ptr_;\n    };\n\n// 提供一个非成员swap函数for ADL(Argument Dependent Lookup)\n    template<typename T>\n    void swap(A::smart_ptr<T> &lhs, A::smart_ptr<T> &rhs) noexcept {\n        lhs.swap(rhs);\n    }\n}\n// 注释开启,会引发ADL冲突\n//namespace std {\n//    // 提供一个非成员swap函数for ADL(Argument Dependent Lookup)\n//    template<typename T>\n//    void swap(A::smart_ptr<T> &lhs, A::smart_ptr<T> &rhs) noexcept {\n//        lhs.swap(rhs);\n//    }\n//\n//}\n\nint main() {\n\n    using std::swap;\n    A::smart_ptr<std::string> s1(\"hello\"), s2(\"world\");\n    // 交换前\n    std::cout << *s1.get() << \" \" << *s2.get() << std::endl;\n    swap(s1, s2);      // 这里swap 能够通过Koenig搜索或者说ADL根据s1与s2的命名空间来查找swap函数\n    // 交换后\n    std::cout << *s1.get() << \" \" << *s2.get() << std::endl;\n//    s1 = s2;\n\n    A::smart_ptr<std::string> s3 = s1;\n    A::smart_ptr<std::string> s4 = std::move(s1);\n}"
  },
  {
    "path": "codingStyleIdioms/5_pImpl/BUILD",
    "content": "# please run `bazel run //codingStyleIdioms/5_pImpl:pimpl`\n# please run `bazel run //codingStyleIdioms/5_pImpl:pimplTime`\n# please run `bazel run //codingStyleIdioms/5_pImpl:noPimpl`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"pimpl\",\n    srcs = [\"pimpl.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"pimplTime\",\n    srcs = [\"pimplTime.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"noPimpl\",\n    srcs = [\"noPimpl.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n"
  },
  {
    "path": "codingStyleIdioms/5_pImpl/README.md",
    "content": "# C++惯用法之pImpl\n\n“指向实现的指针”或“pImpl”是一种 C++ 编程技巧,它将类的实现细节从对象表示中移除，放到一个分离的类中，并以一个不透明的指针进行访问。\n\n使用pImpl惯用法的原因如下：\n\n考虑如下例子：\n\n```cpp\nclass X\n{\nprivate:\n  C c;\n  D d;  \n} ;\n```\n\n变成pImpl就是下面这样子\n\n```cpp\nclass X\n{\nprivate:\n  struct XImpl;\n  XImpl* pImpl;       \n};\n```\n\nCPP定义：\n\n```cpp\nstruct X::XImpl\n{\n  C c;\n  D d;\n};\n```\n\n- 二进制兼容性\n\n开发库时，可以在不破坏与客户端的二进制兼容性的情况下向XImpl添加/修改字段（这将导致崩溃！）。 由于在向Ximpl类添加新字段时X类的二进制布局不会更改，因此可以安全地在次要版本更新中向库添加新功能。\n\n当然，您也可以在不破坏二进制兼容性的情况下向X / XImpl添加新的公共/私有非虚拟方法，但这与标准的标头/实现技术相当。\n\n- 数据隐藏\n\n如果您正在开发一个库，尤其是专有库，则可能不希望公开用于实现库公共接口的其他库/实现技术。 要么是由于知识产权问题，要么是因为您认为用户可能会被诱使对实现进行危险的假设，或者只是通过使用可怕的转换技巧来破坏封装。 PIMPL解决/缓解了这一难题。\n\n- 编译时间\n\n编译时间减少了，因为当您向XImpl类添加/删除字段和/或方法时（仅映射到标准技术中添加私有字段/方法的情况），仅需要重建X的源（实现）文件。 实际上，这是一种常见的操作。\n\n使用标准的标头/实现技术（没有PIMPL），当您向X添加新字段时，曾经重新分配X（在堆栈或堆上）的每个客户端都需要重新编译，因为它必须调整分配的大小 。 好吧，每个从未分配X的客户端也都需要重新编译，但这只是开销（客户端上的结果代码是相同的）。\n\n\n\n> https://stackoverflow.com/questions/8972588/is-the-pimpl-idiom-really-used-in-practices"
  },
  {
    "path": "codingStyleIdioms/5_pImpl/noPimpl.cpp",
    "content": "#include <iostream>\n#include <vector>\nusing namespace std;\n\nclass C {\n    vector<int> v;\n    string s;\n};\nclass D {\n    string s;\n};\n\nclass X {\nprivate:\n    C c;\n    D d;\n};\n\n\nint main() {\n\n    X x;\n}\n"
  },
  {
    "path": "codingStyleIdioms/5_pImpl/pimpl.cpp",
    "content": "//\n// Created by light on 19-12-9.\n//\n\n#include <iostream>\nusing namespace std;\n\n// pImpl: Pointer-to-Implementation\n\nclass private_foo;\nclass foo {\npublic:\n    foo();\n\n    ~foo();\n\n    void bar();\n\nprivate:\n    private_foo *pImpl;\n};\n\nclass private_foo {\npublic:\n    void bar() {\n        cout<<\"private_foo invoke bar funciton.\"<<endl;\n    }\n\nprivate:\n    int m1;\n    string m2;\n};\n\nfoo::foo() : pImpl(new private_foo()) {\n}\n\nfoo::~foo() {\n}\n\nvoid foo::bar() {\n    pImpl->bar();\n}\n\n\nint main() {\n    foo f;\n    f.bar();\n}"
  },
  {
    "path": "codingStyleIdioms/5_pImpl/pimplTime.cpp",
    "content": "#include <iostream>\n#include <vector>\nusing namespace std;\n\nclass C {\n    vector<int> v;\n    string s;\n};\nclass D {\n    string s;\n};\n\nclass X {\nprivate:\n    struct XImpl;\n    XImpl* pImpl;\n};\n\nstruct X::XImpl {\n    C c;\n    D d;\n};\n\nint main() {\n    X x;\n}\n"
  },
  {
    "path": "codingStyleIdioms/5_pImpl/pimplTime.h",
    "content": "#include <iostream>\n#include <vector>\nusing namespace std;\n\nclass C {\n    vector<int> v;\n    string s;\n};\nclass D {\n    string s;\n};\n\nclass X {\nprivate:\n    struct XImpl;\n    XImpl* pImpl;\n};\n\n\n"
  },
  {
    "path": "codingStyleIdioms/README.md",
    "content": "# 你最喜欢的c++编程风格惯用法是什么?\n\n在stackoverflow上找到了一篇文章，写的蛮好的，地址如下：\n\n> https://stackoverflow.com/questions/276173/what-are-your-favorite-c-coding-style-idioms#comment60171463_2034439\n\n由于是英文的，且比较重要，于是总结成下面几条！\n\n- [1.类初始化列表](./1_classInitializers)\n- [2.枚举类替换命名空间](./2_enumclass_namespace)\n- [3.RAII(资源获取即初始化)](./3_RAII)\n- [4.copy and swap](./4_copy-swap)\n- [5.pImpl(指针指向具体实现)](./5_pImpl)\n\n"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/1.thread/BUILD",
    "content": "# please run `bazel run //concurrency/Threading_In_CPlusPlus/1.thread:intro`\n# please run `bazel run //concurrency/Threading_In_CPlusPlus/1.thread:thread`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"intro\",\n    srcs = [\"intro.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"thread\",\n    srcs = [\"thread.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/1.thread/intro.cpp",
    "content": "//\n// Created by light on 20-1-31.\n//\n\n#include <iostream>\n#include <chrono>\n\nusing namespace std::chrono;\nusing namespace std;\n\nusing ull = unsigned long long;\null OddSum = 0;\null EvenSum = 0;\n\nvoid findEven(ull start, ull end) {\n    for (ull i = start; i <= end; ++i)\n        if ((i & 1) == 0)\n            EvenSum += i;\n}\n\nvoid findOdd(ull start, ull end) {\n    for (ull i = start; i <= end; ++i)\n        if ((i & 1) == 1)\n            OddSum += i;\n}\n\nint main() {\n\n    ull start = 0, end = 1900000000;\n\n    auto startTime = high_resolution_clock::now();\n    findOdd(start, end);\n    findEven(start, end);\n    auto stopTime = high_resolution_clock::now();\n    auto duration = duration_cast<microseconds>(stopTime - startTime);\n\n    cout << \"OddSum : \" << OddSum << endl;\n    cout << \"EvenSum: \" << EvenSum << endl;\n    cout << \"Sec: \" << duration.count() / 1000000 << endl;\n    return 0;\n}\n"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/1.thread/thread.cpp",
    "content": "//\n// Created by light on 20-1-31.\n//\n\n#include <iostream>\n#include <thread>\n#include <chrono>\n\nusing namespace std::chrono;\nusing namespace std;\n\n\n/**\n * 1.普通函数指针\n * 2.Lambda函数\n * 3.Functors\n * 4.非静态成员函数\n * 5.静态成员函数\n */\n\nusing ull = unsigned long long;\null OddSum = 0;\null EvenSum = 0;\n\nvoid findEven(ull start, ull end) {\n    for (ull i = start; i <= end; ++i)\n        if ((i & 1) == 0)\n            EvenSum += i;\n}\n\nvoid findOdd(ull start, ull end) {\n    for (ull i = start; i <= end; ++i)\n        if ((i & 1) == 1)\n            OddSum += i;\n}\n\nint main() {\n\n    ull start = 0, end = 1900000000;\n\n\n    auto startTime = high_resolution_clock::now();\n    std::thread t1(findEven,start,end);\n    std::thread t2(findOdd,start,end);\n\n    t1.join();\n    t2.join();\n\n    auto stopTime = high_resolution_clock::now();\n    auto duration = duration_cast<microseconds>(stopTime - startTime);\n\n    cout << \"OddSum : \" << OddSum << endl;\n    cout << \"EvenSum: \" << EvenSum << endl;\n    cout << \"Sec: \" << duration.count() / 1000000 << endl;\n    return 0;\n}\n"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/2.create_type/1.function_pointer.cpp",
    "content": "//\n// Created by light on 20-1-31.\n//\n\n// 1.函数指针\n#include <thread>\n#include <iostream>\n\nusing namespace std;\n\nvoid fun(int x) {\n    while (x-- > 0) {\n        cout << x << endl;\n    }\n}\n// 注意：如果我们创建多线程 并不会保证哪一个先开始\nint main() {\n    std::thread t1(fun, 10);\n//    std::1.thread t2(fun, 10);\n    t1.join();\n//    t2.join();\n    return 0;\n}"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/2.create_type/2.lambda_function.cpp",
    "content": "//\n// Created by light on 20-1-31.\n//\n\n// 1.函数指针\n#include <thread>\n#include <iostream>\n\nusing namespace std;\n\n// 注意：如果我们创建多线程 并不会保证哪一个先开始\nint main() {\n    // 2.Lambda函数\n    auto fun = [](int x) {\n        while (x-- > 0) {\n            cout << x << endl;\n        }\n    };\n//    std::1.thread t1(fun, 10);\n    // 也可以写成下面：\n    std::thread t1_1([](int x) {\n        while (x-- > 0) {\n            cout << x << endl;\n        }\n    }, 11);\n//    std::1.thread t2(fun, 10);\n//    t1.join();\n    t1_1.join();\n//    t2.join();\n    return 0;\n}"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/2.create_type/3.functor.cpp",
    "content": "//\n// Created by light on 20-1-31.\n//\n\n\n#include <thread>\n#include <iostream>\n\nusing namespace std;\n\n// 3.functor (Funciton Object)\nclass Base {\npublic:\n    void operator()(int x) {\n        while (x-- > 0) {\n            cout << x << endl;\n        }\n    }\n};\n\nint main() {\n    thread t(Base(), 10);\n    t.join();\n    return 0;\n}"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/2.create_type/4.no_static_member_function.cpp",
    "content": "//\n// Created by light on 20-1-31.\n//\n\n\n#include <thread>\n#include <iostream>\n\nusing namespace std;\n\n// 4.Non-static member function\nclass Base {\npublic:\n    void fun(int x) {\n        while (x-- > 0) {\n            cout << x << endl;\n        }\n    }\n};\n\nint main() {\n    Base b;\n    thread t(&Base::fun,&b, 10);\n    t.join();\n    return 0;\n}"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/2.create_type/5.static_member_function.cpp",
    "content": "//\n// Created by light on 20-1-31.\n//\n\n\n#include <thread>\n#include <iostream>\n\nusing namespace std;\n\n// 4.Non-static member function\nclass Base {\npublic:\n    static void fun(int x) {\n        while (x-- > 0) {\n            cout << x << endl;\n        }\n    }\n};\n\nint main() {\n    thread t(&Base::fun, 10);\n    t.join();\n    return 0;\n}"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/2.create_type/BUILD",
    "content": "# please run `bazel run //concurrency/Threading_In_CPlusPlus/2.create_type:4.no_static_member_function`\n# please run `bazel run //concurrency/Threading_In_CPlusPlus/2.create_type:2.lambda_function`\n# please run `bazel run //concurrency/Threading_In_CPlusPlus/2.create_type:1.function_pointer`\n# please run `bazel run //concurrency/Threading_In_CPlusPlus/2.create_type:3.functor`\n# please run `bazel run //concurrency/Threading_In_CPlusPlus/2.create_type:5.static_member_function`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"4.no_static_member_function\",\n    srcs = [\"4.no_static_member_function.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"2.lambda_function\",\n    srcs = [\"2.lambda_function.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"1.function_pointer\",\n    srcs = [\"1.function_pointer.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"3.functor\",\n    srcs = [\"3.functor.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"5.static_member_function\",\n    srcs = [\"5.static_member_function.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/3.join_detach/BUILD",
    "content": "# please run `bazel run //concurrency/Threading_In_CPlusPlus/3.join_detach:join`\n# please run `bazel run //concurrency/Threading_In_CPlusPlus/3.join_detach:detach`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"join\",\n    srcs = [\"join.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"detach\",\n    srcs = [\"detach.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/3.join_detach/detach.cpp",
    "content": "//\n// Created by light on 20-1-31.\n//\n\n// join 注意点\n\n/**\n * 这用于从父线程分离新创建的线程\n * 在分离线程之前，请务必检查它是否可以joinable，\n * 否则可能会导致两次分离，并且双重detach()将导致程序终止\n * 如果我们有分离的线程并且main函数正在返回，那么分离的线程执行将被挂起\n*/\n#include <iostream>\n#include <thread>\n#include <chrono>\n#include <mutex>\nusing namespace std;\n\nvoid run(int count) {\n    while (count-- > 0) {\n        cout << count << endl;\n    }\n    std::this_thread::sleep_for(chrono::seconds(3));\n}\n\nint main() {\n    thread t1(run, 10);\n    cout << \"main()\" << endl;\n    t1.detach();\n    if(t1.joinable())\n        t1.detach();\n    cout << \"main() after\" << endl;\n    return 0;\n}"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/3.join_detach/join.cpp",
    "content": "//\n// Created by light on 20-1-31.\n//\n\n// join 注意点\n\n/**\n * 一旦线程开始，我们要想等待线程完成，需要在该对象上调用join()\n * 双重join将导致程序终止\n * 在join之前我们应该检查显示是否可以被join,通过使用joinable()\n*/\n#include <iostream>\n#include <thread>\n#include <chrono>\n\nusing namespace std;\n\n\nvoid run(int count) {\n    while (count-- > 0) {\n        cout << count << endl;\n    }\n    std::this_thread::sleep_for(chrono::seconds(3));\n}\n\nint main() {\n    thread t1(run, 10);\n    cout << \"main()\" << endl;\n    t1.join();\n    if (t1.joinable()) {\n        t1.join();\n    }\n    cout << \"main() after\" << endl;\n    return 0;\n}"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/4.mutex/BUILD",
    "content": "# please run `bazel run //concurrency/Threading_In_CPlusPlus/4.mutex:critical_section`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"critical_section\",\n    srcs = [\"critical_section.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/4.mutex/an_example_of_bank_account.cpp",
    "content": "#include <iostream>\n#include <thread>\n#include <mutex>\n#include <vector>\n\nusing namespace std;\n\nclass BankAccount {\npublic:\n    BankAccount() : balance(0) {}\n\n   \n    void deposit(int amount) {\n        lock_guard<mutex> lock(mtx);  \n        balance += amount;\n    }\n\n\n    void withdraw(int amount) {\n        lock_guard<mutex> lock(mtx);  \n        if (balance >= amount) {\n            balance -= amount;\n        } else {\n            cout << \"Not enough funds to withdraw \" << amount << endl;\n        }\n    }\n\n\n    int get_balance() const {\n        return balance;\n    }\n\nprivate:\n    int balance;      \n    mutable mutex mtx;  \n};\n\nvoid deposit_to_account(BankAccount& account) {\n    for (int i = 0; i < 1000; ++i) {\n        account.deposit(1);  \n    }\n}\n\nvoid withdraw_from_account(BankAccount& account) {\n    for (int i = 0; i < 500; ++i) {\n        account.withdraw(1); \n    }\n}\n\nint main() {\n    BankAccount account;\n\n    thread t1(deposit_to_account, ref(account));\n    thread t2(withdraw_from_account, ref(account));\n    thread t3(deposit_to_account, ref(account));\n    thread t4(withdraw_from_account, ref(account));\n\n    t1.join();\n    t2.join();\n    t3.join();\n    t4.join();\n\n    cout << \"Final balance: \" << account.get_balance() << endl;\n\n    return 0;\n}\n"
  },
  {
    "path": "concurrency/Threading_In_CPlusPlus/4.mutex/critical_section.cpp",
    "content": "//\n// Created by light on 20-2-1.\n//\n#include <iostream>\n#include <mutex>\n#include <thread>\n\nusing namespace std;\n\nint sum = 0; //shared\n\nmutex m;\n\nvoid *countgold() {\n    int i; //local to each thread\n    for (i = 0; i < 10000000; i++) {\n        m.lock();\n        sum += 1;\n        m.unlock();\n    }\n    return NULL;\n}\n\nint main() {\n    thread t1(countgold);\n    thread t2(countgold);\n\n    //Wait for both threads to finish\n    t1.join();\n    t2.join();\n\n    cout << \"sum = \" << sum << endl;\n    return 0;\n}\n"
  },
  {
    "path": "concurrency/concurrency_v1/chapter1/1_helloworld.cpp",
    "content": "//\n// Created by light on 19-11-5.\n//\n\n#include <iostream>\n#include <thread>\n#include <unistd.h>\n\nusing namespace std;\n\nvoid hello() {\n    cout << \"hello world\" << endl;\n}\n\nint main() {\n    thread t(hello);\n    t.join();       // must add this line otherwise will failed!\n    // 需要注意的是线程对象执行了join后就不再joinable了，所以只能调用join一次。\n    return 0;\n}"
  },
  {
    "path": "concurrency/concurrency_v1/chapter1/BUILD",
    "content": "# please run `bazel run //concurrency/concurrency_v1/chapter1:1_helloworld`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"1_helloworld\",\n    srcs = [\"1_helloworld.cpp\"],\n)\n"
  },
  {
    "path": "concurrency/concurrency_v1/chapter2/2.1_basic.cpp",
    "content": "//\n// Created by light on 19-11-5.\n//\n\n#include <iostream>\n#include <thread>\n#include <unistd.h>\n#include <cassert>\n\nusing namespace std;\n\nclass background_task {\npublic:\n    void operator()() const {\n        cout << \"ok\" << endl;\n    }\n};\n\nvoid do_something(int &i) {\n    cout << \"do_something\" << endl;\n}\n\nstruct func {\n    int &i;\n\n    func(int &i_) : i(i_) {}\n\n    void operator()() {\n        for (unsigned j = 0; j < 1000000; ++j) {\n            do_something(i);           // 1. 潜在访问隐患：悬空引用\n        }\n    }\n};\n\n// 特殊情况下的等待\nvoid f() {\n    int some_local_state = 0;\n    func my_func(some_local_state);\n    std::thread t(my_func);\n    try {\n//        do_something_in_current_thread();\n    }\n    catch (...) {\n        t.join();  // 1\n        throw;\n    }\n    t.join();  // 2\n}\n\n// try catch 只能捕获轻量级错误,所以如需确保线程在函数之前结束——查看是否因为线程函数使用了局部变量的引用，\n// 以及其他原因——而后再确定一下程序可能会退出的途径，无论正常与否，可以提供一个简洁的机制，来做解决这个问题。\n\n// 一种方式是使用“资源获取即初始化方式”(RAII，Resource Acquisition Is Initialization)，并且提供一个类，在析构函数中使用join()，\n// std::thread支持移动的好处是可以创建thread_guard类的实例，并且拥有其线程的所有权。\nclass thread_guard {\n    std::thread &t;\npublic:\n    explicit thread_guard(std::thread &t_) :\n            t(t_) {}\n\n    ~thread_guard() {\n        if (t.joinable()) // 1\n        {\n            t.join();      // 2\n        }\n    }\n\n    thread_guard(thread_guard const &) = delete;   // 3\n    thread_guard &operator=(thread_guard const &) = delete;\n};\nvoid f1()\n{\n    int some_local_state=0;\n    func my_func(some_local_state);\n    std::thread t(my_func);\n    thread_guard g(t);\n//    do_something_in_current_thread();\n}    // 4\n// 当线程执行到4处时，局部对象就要被逆序销毁了。因此，thread_guard对象g是第一个被销毁的，\n// 这时线程在析构函数中被加入2到原始线程中。\n// 即使do_something_in_current_thread抛出一个异常，这个销毁依旧会发生。\n\n\nint main() {\n\n    background_task f;\n//    thread t(f);       // ok\n//    t.join();\n    //声明一个名为my_threadx的函数,这个函数带有一个参数(函数指针指向没有参数并返回background_task对象的函数)，返回一个std::thread对象的函数\n//    thread my_thread1(background_task());\n\n    // 针对Most Vexing Parse问题解决如下：\n//    thread my_thread1((background_task())); // 多组括号\n//    my_thread1.join();\n//    thread my_thread2{background_task()};   // 新的初始化语法\n//    my_thread2.join();\n\n//    thread myThread([](){\n//        cout<<\"ok\"<<endl;\n//    });\n//    myThread.join();\n    // 后台运行线程\n    std::thread t(f);\n    t.detach();\n    assert(!t.joinable());\n\n    return 0;\n}"
  },
  {
    "path": "concurrency/concurrency_v1/chapter2/2.2_transfer.cpp",
    "content": "//\n// Created by light on 19-11-5.\n//\n#include <iostream>\n#include <thread>\n\nusing namespace std;\n\nclass X {\npublic:\n    void do_length_work() {};\n};\n\nvoid process_big_object(std::unique_ptr<X>)\n{\n    // TODO\n}\n\nint main() {\n    X my_x;\n    thread t(&X::do_length_work, &my_x); // 1\n\n\n    std::unique_ptr<X> p(new X);\n    p->do_length_work();\n    std::thread tt(process_big_object,std::move(p));\n    //std::thread实例的可移动且不可复制性。不可复制保性证了在同一时间点，\n    // 一个std::thread实例只能关联一个执行线程；可移动性使得程序员可以自己决定，哪个实例拥有实际执行线程的所有权。\n    return 0;\n}"
  },
  {
    "path": "concurrency/concurrency_v1/chapter2/2.3_ownership.cpp",
    "content": "//\n// Created by light on 19-11-5.\n//\n#include <iostream>\n#include <thread>\n#include <unistd.h>\n#include <vector>\n#include <algorithm>\n#include <functional>\n\nusing namespace std;\n\nvoid some_function() {}\n\nvoid some_other_function() {}\n\n// std::thread不支持拷贝语义。\n// std::thread支持移动语义。\n\n\n\n// scoped_thread实例\nvoid do_something(int i) {\n    cout << i << endl;\n}\n\nstruct func {\n    int &i;\n\n    func(int &i_) : i(i_) {}\n\n    void operator()() {\n        for (unsigned j = 0; j < 1000000; ++j) {\n            do_something(i);\n        }\n    }\n};\n\nclass scoped_thread {\n    std::thread t;\npublic:\n    explicit scoped_thread(std::thread t_) :                 // 1\n            t(std::move(t_)) {\n        if (!t.joinable())                                     // 2\n            throw std::logic_error(\"No thread\");\n    }\n\n    ~scoped_thread() {\n        t.join();                                            // 3\n    }\n\n    scoped_thread(scoped_thread const &) = delete;\n\n    scoped_thread &operator=(scoped_thread const &) = delete;\n};\n\nvoid do_work(unsigned id) {}\n\nvoid f() {\n    std::vector<std::thread> threads;\n    for (unsigned i = 0; i < 20; ++i) {\n        threads.push_back(std::thread(do_work, i)); // 产生线程\n    }\n    std::for_each(threads.begin(), threads.end(),\n                  std::mem_fn(&std::thread::join)); // 对每个线程调用join()\n}\n\nint main() {\n//    std::thread t1(some_function); // 构造一个thread对象t1\n//    std::thread t2 = std::move(t1); // 把t1 move给另外一个thread对象t2，t1不再管理之前的线程了。\n//    // 这句不需要std::move()，从临时变量进行移动是自动和隐式的。调用的是operator=(std::thread&&)\n//    t1 = std::thread(some_other_function);\n//    std::thread t3;\n//    t3 = std::move(t2); // 把t2 move给t3\n//    // 把t3 move给t1，非法。因为`t1`已经有了一个相关的线程，会调用`std::terminate()`来终止程序。\n//    t1 = std::move(t3);\n    f();\n    return 0;\n}"
  },
  {
    "path": "concurrency/concurrency_v1/chapter2/2.4_runtime.cpp",
    "content": "//\n// Created by light on 19-11-5.\n//\n#include <iostream>\n#include <algorithm>\n#include <thread>\n#include <vector>\n#include <numeric>\n#include <functional>\n\nusing namespace std;\n\n//使得每个线程具有最小数目的元素以避免过多的线程开销\ntemplate<typename Iterator, typename T>\nstruct accumulate_block {\n    void operator()(Iterator first, Iterator last, T &result) {\n        result = std::accumulate(first, last, result);\n    }\n};\n\ntemplate<typename Iterator, typename T>\nT parallel_accumlate(Iterator first, Iterator last, T init) {\n    unsigned long const length = std::distance(first, last);\n\n    if (!length)\n        return init;\n\n    unsigned long const min_per_thread = 25;\n    unsigned long const max_threads = (length + min_per_thread - 1) / min_per_thread;\n    cout<<max_threads<<endl;\n    unsigned long const hardware_threads = std::thread::hardware_concurrency();\n    cout<<hardware_threads<<endl;\n    unsigned long const num_threads = std::min(hardware_threads != 0 ? hardware_threads : 2, max_threads);\n    cout<<num_threads<<endl;\n    unsigned long const block_size = length / num_threads;\n    cout<<block_size<<endl;\n\n    std::vector<T> results(num_threads);\n    std::vector<std::thread> threads(num_threads - 1);\n\n    Iterator block_start = first;\n    for (unsigned long i = 0; i < (num_threads - 1); ++i) {\n        Iterator block_end = block_start;\n        std::advance(block_end, block_size);\n        threads[i] = std::thread(accumulate_block<Iterator, T>(), block_start, block_end, std::ref(results[i]));\n        block_start = block_end;\n    }\n    accumulate_block<Iterator, T>()(block_start, last, results[num_threads - 1]);\n    std::for_each(threads.begin(), threads.end(), std::mem_fn(&std::thread::join));\n\n    return std::accumulate(results.begin(), results.end(), init);\n}\n\n\nint main() {\n\n    vector<int> v{3,4,5,6};\n    int res=0;\n    cout<<parallel_accumlate(v.begin(),v.end(),res);\n    return 0;\n}"
  },
  {
    "path": "concurrency/concurrency_v1/chapter2/2_5_id.cpp",
    "content": "//\n// Created by light on 19-11-5.\n//\n\n#include <iostream>\n#include <thread>\n\nusing namespace std;\n\n\n// 线程的通用标识符\nstd::thread::id master_thread;\n\nvoid do_master_thread_work() {\n    cout << \"master\" << endl;\n}\n\nvoid do_common_work() {\n    cout << \"common\" << endl;\n}\n\nvoid some_core_part_of_algorithm() {\n    if (std::this_thread::get_id() == master_thread) {\n        do_master_thread_work();\n    }\n    do_common_work();\n}\n\nint main() {\n    master_thread = std::this_thread::get_id();\n    std::cout << \"master_thread: \" << master_thread << endl;\n    cout << \"master_thread 中运行:\" << endl;\n    some_core_part_of_algorithm();\n    cout << \"thread 中运行:\" << endl;\n    thread t(some_core_part_of_algorithm);\n    t.join();\n    return 0;\n}\n"
  },
  {
    "path": "concurrency/concurrency_v1/chapter2/BUILD",
    "content": "# please run `bazel run //concurrency/concurrency_v1/chapter2:2.4_runtime`\n# please run `bazel run //concurrency/concurrency_v1/chapter2:2.1_basic`\n# please run `bazel run //concurrency/concurrency_v1/chapter2:2.3_ownership`\n# please run `bazel run //concurrency/concurrency_v1/chapter2:2_5_id`\n# please run `bazel run //concurrency/concurrency_v1/chapter2:2.2_transfer`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"2.4_runtime\",\n    srcs = [\"2.4_runtime.cpp\"],\n)\ncc_binary(\n    name = \"2.1_basic\",\n    srcs = [\"2.1_basic.cpp\"],\n)\ncc_binary(\n    name = \"2.3_ownership\",\n    srcs = [\"2.3_ownership.cpp\"],\n)\ncc_binary(\n    name = \"2_5_id\",\n    srcs = [\"2_5_id.cpp\"],\n    copts = [\"-std=c++11\"]\n)\ncc_binary(\n    name = \"2.2_transfer\",\n    srcs = [\"2.2_transfer.cpp\"],\n    copts = [\"-std=c++11\"]\n)\n"
  },
  {
    "path": "cpp2.0/cpp11/BUILD",
    "content": "#please run `bazel run //cpp2.0/cpp11:decltype`\n#please run `bazel run //cpp2.0/cpp11:auto`\n# please run `bazel run //cpp2.0/cpp11:override`\n# please run `bazel run //cpp2.0/cpp11:tuple`\n# please run `bazel run //cpp2.0/cpp11:noexcept`\n# please run `bazel run //cpp2.0/cpp11:constexpr`\n# please run `bazel run //cpp2.0/cpp11:nullptr`\n# please run `bazel run //cpp2.0/cpp11:initializer`\n# please run `bazel run //cpp2.0/cpp11:type_alias`\n# please run `bazel run //cpp2.0/cpp11:hash`\n# please run `bazel run //cpp2.0/cpp11:lambda`\n# please run `bazel run //cpp2.0/cpp11:uniform_initialization`\n# please run `bazel run //cpp2.0/cpp11:final`\n# please run `bazel run //cpp2.0/cpp11:default_delete`\n# please run `bazel run //cpp2.0/cpp11:move`\n# please run `bazel run //cpp2.0/cpp11:alias`\n# please run `bazel run //cpp2.0/cpp11:rvalue`\n# please run `bazel run //cpp2.0/cpp11:template_template`\n# please run `bazel run //cpp2.0/cpp11:explicit`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"decltype\",\n    srcs = [\"decltype.cpp\"],\n)\ncc_binary(\n    name = \"auto\",\n    srcs = [\"auto.cpp\"],\n)\ncc_binary(\n    name = \"override\",\n    srcs = [\"override.cpp\"],\n)\ncc_binary(\n    name = \"tuple\",\n    srcs = [\"tuple.cpp\"],\n)\ncc_binary(\n    name = \"noexcept\",\n    srcs = [\"noexcept.cpp\"],\n)\ncc_binary(\n    name = \"constexpr\",\n    srcs = [\"constexpr.cpp\"],\n)\ncc_binary(\n    name = \"nullptr\",\n    srcs = [\"nullptr.cpp\"],\n)\ncc_binary(\n    name = \"initializer\",\n    srcs = [\"initializer.cpp\"],\n)\ncc_binary(\n    name = \"type_alias\",\n    srcs = [\"type_alias.cpp\"],\n)\ncc_binary(\n    name = \"hash\",\n    srcs = [\"hash.cpp\"],\n)\ncc_binary(\n    name = \"lambda\",\n    srcs = [\"lambda.cpp\"],\n)\ncc_binary(\n    name = \"uniform_initialization\",\n    srcs = [\"uniform_initialization.cpp\"],\n)\ncc_binary(\n    name = \"final\",\n    srcs = [\"final.cpp\"],\n)\ncc_binary(\n    name = \"default_delete\",\n    srcs = [\"default_delete.cpp\"],\n)\ncc_binary(\n    name = \"move\",\n    srcs = [\"move.cpp\"],\n)\ncc_binary(\n    name = \"alias\",\n    srcs = [\"alias.cpp\"],\n)\ncc_binary(\n    name = \"rvalue\",\n    srcs = [\"rvalue.cpp\"],\n)\ncc_binary(\n    name = \"template_template\",\n    srcs = [\"template_template.cpp\"],\n)\ncc_binary(\n    name = \"explicit\",\n    srcs = [\"explicit.cpp\"],\n)\n"
  },
  {
    "path": "cpp2.0/cpp11/README.md",
    "content": "# C++新特性\n\n## 1.C++11新特性\n\n- [Variadic Templates](./variadic)\n\n- Spaces in Template Expressions\n\n  ```cpp\n  vector<list<int> > //ok in each C++ version\n  vector<list<int>> // before c++ 11 error error: ‘>>’ should be ‘> >’ within a nested template argument list,c++11后可以正常通过\n  ```\n\n- [nullptr and nullptr_t](./nullptr.cpp)\n\n- [Automatic Type Deduction with auto](./auto.cpp)\n\n- [Uniform Initialization ](./uniform_initialization.cpp)\n\n- [initializer_list](./initializer.cpp)\n\n- [explicit for ctors taking more than one argument](./explicit.cpp)\n\n- [range-based for statement](./auto.cpp)\n\n  ```cpp\n  for(decl:col) {\n      statement\n  }\n  ```\n\n- [=default,=delete](./default_delete.cpp)\n\n  如果你自行定义了一个ctor,那么编译器就不会给你一个default ctor\n  如果强制加上=default,就可以重新获得并使用default ctor.\n\n- Alias(化名)Template(template typedef)\n\n  - [alias.cpp](./alias.cpp) \n  - [template_template.cpp](./template_template.cpp)\n\n- [template template parameter](./template_template.cpp)\n\n- [type alias](./type_alias.cpp)\n\n- [noexcept](./noexcept.cpp)\n\n- [override](./override.cpp)\n\n- [final](./final.cpp)\n\n- [decltype](./decltype.cpp)\n\n- [lambda](./lambda.cpp)\n\n- [Rvalue reference](./rvalue.cpp)\n\n- [move aware class](./move.cpp)\n\n- 容器-结构与分类\n\n  - (1) 序列式容器包括：array(C++2.0新引入),vector,deque,list,forward_list(C++2.0新引入)\n  - (2) 关联式容器包括：set/multiset,map/multimap\n  - (3) 无序容器(C++2.0新引入,更换原先hash_xxx为unordered_xxx)包括：unordered_map/unordered_multimap,unordered_set/unordered_multiset\n\n- [Hash Function](./hash.cpp)\n\n- [tuple](./tuple.cpp)\n\n  学习资料：https://www.bilibili.com/video/av51863195?from=search&seid=3610634846288253061"
  },
  {
    "path": "cpp2.0/cpp11/alias.cpp",
    "content": "//\n// Created by light on 19-11-3.\n//\n\n#include <vector>\n\nusing namespace std;\n\n// 不可以对alias template做偏特化或全特化\ntemplate<typename T>\nusing Vec=vector<T, allocator<T>>;  // alias template using新用法\n//# define Vec<T> template<typename T> vector<T, allocator<T>> // 使用宏不行\n\nint main() {\n    Vec<int> col;\n// 如果使用宏上述会被替换为template<typename int> vector<int, allocator<int>>  error 不是我们想要的\n// typedef vector<int, allocator<int>> Vec;   // typedef也无法做到,没法指定模板参数\n\n    return 0;\n}"
  },
  {
    "path": "cpp2.0/cpp11/auto.cpp",
    "content": "//\n// Created by light on 19-11-2.\n//\n\n#include <iostream>\n#include <list>\n#include <algorithm>\n#include <vector>\n#include <functional>\n#include <iterator>\n\nusing namespace std;\n\n// iterator  stl_iterator.h里面的源码 使用auto\ntemplate<typename _Iterator>\n#if __cplusplus < 201103L\ninline typename reverse_iterator<_Iterator>::difference_type\n    operator-(const reverse_iterator<_Iterator>& __x,\n          const reverse_iterator<_Iterator>& __y)\n#else\ninline auto\noperator-(const reverse_iterator<_Iterator> &__x,\n          const reverse_iterator<_Iterator> &__y)\n-> decltype(__x.base() - __y.base())\n#endif\n{ return __y.base() - __x.base(); }\n\n\n// auto 关键字(c++11)  语法糖\n// for 循环  ranged-base for\n\n\n\nclass C {\npublic:\n    explicit C(const string &s) : mystr(s) {\n\n    }\n\n    friend ostream &operator<<(ostream out, const C &c) {\n        out << c.mystr << endl;\n    }\n\nprivate:\n    string mystr;\n};\n\nint main() {\n\n    auto i = 42;  // 编译器具备实参推导\n\n    auto ll1 = [](int x) -> int {\n        return x + 10;\n    };\n    // lambda 匿名函数\n    function<int(int x)> ll = [](int x) -> int {\n        return x + 10;\n    };\n\n    cout << ll(10) << endl;\n    list<int> l{1, 2, 3};\n    list<int>::iterator iterator;\n    iterator = find(l.begin(), l.end(), 10);\n\n    auto ite = find(l.begin(), l.end(), 11);    // auto 关键字\n\n    vector<int> vec;\n    //=====================range-based for statement=========================\n    // (1)\n    for (auto elem:vec) // pass  by value\n        cout << elem << endl;\n    // c++2.0 之前 (2)   编译器会把(1)转换为(2)或(3)\n    for (auto _pos = l.begin(), _end = l.end(); _pos != _end; ++_pos)\n        cout << *_pos << \" \";\n    cout << endl;\n    // (3)\n    // c++2.0之后 全局函数begin(container)与end(container) 可以接受容器\n    for (auto _pos = begin(l), _end = end(l); _pos != _end; ++_pos)\n        cout << *_pos << \" \";\n    cout << endl;\n\n    // (4)\n    for (auto &elem:vec) // pass by reference\n        elem *= 3;\n\n    // (5) 编译器会把(4)转换为(5)\n    for (auto _pos = l.begin(), _end = l.end(); _pos != _end; ++_pos) {\n        auto &elem = *_pos;\n        elem *= 3;\n    }\n    cout << endl;\n\n    vector<string> vs = {\"hello\", \"world\"};\n    // 加了explicit就不能转了 error\n//    for (const C &elem:vs) {\n//    }\n    // auto 不利：降低代码可读性、可能得不到你预想的类型、配合decltype有意想不到的结果\n    // auto可能得不到你预想的类型，如vector<bool>[]的返回类型。\n    vector<bool> v(true);\n    auto var = v.at(0);\n    cout<< typeid(var).name()<<endl;\n    return 0;\n}\n\n\n"
  },
  {
    "path": "cpp2.0/cpp11/constexpr.cpp",
    "content": "//\n// Created by light on 19-11-4.\n//\n\n#include <iostream>\n#include <array>\nusing namespace std;\n\n// 对于修饰Object来说:\n// const并未区分出编译期常量和运行期常量\n// constexpr限定在了编译期常量\n// constexpr作用：优化!效率!\n// constexpr修饰的函数，返回值不一定是编译期常量。It is not a bug, it is a feature.\n// const修饰的是类型，constexpr修饰的是用来算出值的那段代码。\n/**\n * constexpr修饰的函数，简单的来说，如果其传入的参数可以在编译时期计算出来，那么这个函数就会产生编译时期的值。\n * 但是，传入的参数如果不能在编译时期计算出来，那么constexpr修饰的函数就和普通函数一样了。不\n * @param i\n * @return\n */\nconstexpr int foo(int i)\n{\n    return i + 10;\n}\n\nint main()\n{\n    int i = 10;\n    std::array<int, foo(2)> arr; // OK\n\n    foo(i); // Call is Ok\n\n    std::array<int, foo(i)> arr1; // error: the value of ‘i’ is not usable in a constant expression\n}\n"
  },
  {
    "path": "cpp2.0/cpp11/decltype.cpp",
    "content": "//\n// Created by light on 19-11-3.\n//\n\n#include <iostream>\n#include <map>\n#include <set>\n\nusing namespace std;\n\n// 1.used to declare return tyoes  返回类型\n\n// c++2.0之前编译不出来\n// template<typename T1, typename T2>\n// decltype(x + y) add(T1 x, T2 y);\n\n// 编译器编不出来在2.0之后变成下面这个,先用auto暂定,后面返回类型为decltype(x+y)\ntemplate <typename T1, typename T2>\nauto add(T1 x, T2 y) -> decltype(x + y)\n{ // -> 也用在lambda\n    return x + y;\n}\n\nclass Person\n{\npublic:\n    string firstname;\n    string lastname;\n};\nint main()\n{\n\n    // 1.used to declare return tyoes\n    cout << add(1, 2) << endl;\n\n    // 2.模板元编程 例如在一个模板函数或类获取容器的value_type,这里就不封装了,直接写在main函数里面\n    // 获得表达式的type 有点像typeof()特点\n    map<string, float> coll;\n    // 获取上述类型\n    decltype(coll)::value_type m{\"as\", 1}; // value_type为pair<string,int> m\n    cout << m.first << \" \" << m.second << endl;\n    pair<string, int> p{\"a\", 2};\n    cout << p.first << \" \" << p.second << endl;\n    // 3.used to pass the type of a lambda\n\n    // 比大小\n    auto cmp = [](const Person &p1, const Person &p2)\n    {\n        return p1.lastname < p2.lastname;\n    };\n\n    // 对于lambda,我们往往只有object,很少有人能够写出它的类型，而有时就需要知道它的类型,要获得其type,就要借助其decltype\n    set<Person, decltype(cmp)> col(cmp);\n\n    return 0;\n}"
  },
  {
    "path": "cpp2.0/cpp11/default_delete.cpp",
    "content": "//\n// Created by light on 19-11-3.\n//\n\n#include <iostream>\n#include <complex>\n\nusing namespace std;\n\nclass Zoo {\n\npublic:\n    Zoo(int a, int b) : a(a), b(b) {\n        cout << \"with param ctor \\n\";\n    }\n\n    Zoo() = default;\n\n    Zoo(const Zoo &) = delete;  // copy ctor\n\n    Zoo(Zoo &&) = default;      // move ctor\n\n    Zoo &operator=(const Zoo &) = default;  // copy assignment\n\n    Zoo &operator=(const Zoo &&) = delete;  // move assignment\n\n    // 一般函数可以使用=default或=delete?\n//    void fun1()= default; // error: ‘void Zoo::fun1()’ cannot be defaulted\n    void fun1() = delete; // compile ok\n    // 析构函数可以使用=default或=delete?\n//    ~Zoo()= delete;   // ok,但是造成使用Zoo object出错\n    ~Zoo() = default;\n\n    // 上述得出=delete可以使用在任何函数身上,而=default不可以使用在普通函数上\n    // 上述会联想到=0,=0只能用在virtual funciton上\n\nprivate:\n    int a, b;\n};\n\nclass Empty {\n};\n\n// 等价于  编译器给出的默认版本函数都是public且inline\nclass Empty1 {\npublic:\n    Empty1() {}\n\n    Empty1(const Empty1 &rhs) {}\n\n    ~Empty1() {}\n};\n\n// no-copy\n// copy ctor与copy assignment都delete掉\nstruct NoCopy {\n    NoCopy() = default;\n\n    NoCopy(const NoCopy &) = delete;\n\n    NoCopy &operator=(const NoCopy &) = delete;\n\n    ~NoCopy() = default;\n};\n\n// no-dtor\nstruct NoDtor {\n    NoDtor() = default;\n\n    ~NoDtor() = delete;\n};\n\n// 对no-copy的改进 将copy ctor与copy assignment 放到private ,但是可以对member与friend调用\n// boost::noncopyable 就用到了PrivateCopy\nclass PrivateCopy {\nprivate:\n    PrivateCopy(const PrivateCopy &) {};\n\n    PrivateCopy &operator=(const PrivateCopy &) {};\n\npublic:\n    PrivateCopy() = default;\n\n    ~PrivateCopy() {};\n};\n\n// 继承了的都拥有PrivateCopy性质\nclass Foo : public PrivateCopy {\n\n};\n\nint main() {\n    Zoo z;\n    Zoo z1(1, 2);\n//    Zoo z2=z;   // error copy ctor delete\n    z = z1;\n    complex<int> a;     // 内部不带指针的可以不用写 big three 没有析构 有copy ctor与copy assignmetn 都是使用=default,没有自己写\n    string s;       // 内部带指针的有big five\n\n//    NoDtor n;  //error no-dtor 不能够自动delete\n//    NoDtor *p=new NoDtor;   // ok\n//    delete p;   // error no-dtor 不能够delete\n    Foo foo;\n    Foo foo1;\n//    foo = foo1;   // 继承了的都拥有PrivateCopy性质\n    return 0;\n}"
  },
  {
    "path": "cpp2.0/cpp11/explicit.cpp",
    "content": "//\n// Created by light on 19-11-3.\n//\n\n#include <iostream>\n\nusing namespace std;\n\n// non-explicit-one-argument ctor\nclass Fraction\n{\npublic:\n    // non-explicit-one-argument ctor 可以把别的东西转换为Fraction这种\n    Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}\n\n    Fraction operator+(const Fraction &f)\n    {\n        return Fraction(this->m_numerator + f.m_numerator);\n    }\n\n    void getNumAndDen()\n    {\n        cout << m_numerator << \" \" << m_denominator << endl;\n    }\n\nprivate:\n    int m_numerator;   // 分子\n    int m_denominator; // 分母\n};\n\n// explicit-one-argument ctor\nclass Fraction1\n{\npublic:\n    // explicit-one-argument ctor 可以把别的东西转换为Fraction这种\n    explicit Fraction1(int num, int den = 1) : m_numerator(num), m_denominator(den) {}\n\n    Fraction1 operator+(const Fraction1 &f)\n    {\n        return Fraction1(this->m_numerator + f.m_numerator);\n    }\n\n    void getNumAndDen()\n    {\n        cout << m_numerator << \" \" << m_denominator << endl;\n    }\n\nprivate:\n    int m_numerator;   // 分子\n    int m_denominator; // 分母\n};\n\nclass P\n{\npublic:\n    P(int a, int b)\n    {\n        cout << \"P(int a,int b)\\n\";\n    }\n\n    P(int a, int b, int c)\n    {\n        cout << \"non-explicit P(int a,int b,int c)\\n\";\n    }\n    explicit P(int a, int b, int c, int d)\n    {\n        cout << \"explicit P(int a,int b,int c,int d)\\n\";\n    }\n};\n\nvoid fp(const P &)\n{\n}\n\nint main()\n{\n    Fraction f(3, 5);\n    Fraction ff = f + 3; // 会将3转换为(3,1)   隐式转换\n    ff.getNumAndDen();\n\n    // 如果不想编译器进行隐式转换,可以在前面添加explicit关键字\n\n    // c++2.0之前explicit只禁止\"存在单一实参\"转换\n    // c++2.0之后explicit可以进制\"多个实参\"转换\n    //    Fraction1 f1(3,5);\n    //    Fraction1 f2 = f1+3; // 会将3转换为(3,1)   不会隐式转换    error: no match for ‘operator+’ (operand types are ‘Fraction1’ and ‘int’)\n    //    f2.getNumAndDen();\n    P p1 = {77, 5};\n    P p2 = {77, 5, 89};\n    //    P p3 = {77, 5, 89,99}; //error: converting to ‘P’ from initializer list would use explicit constructor ‘P::P(int, int, int, int)’\n    return 0;\n}\n"
  },
  {
    "path": "cpp2.0/cpp11/final.cpp",
    "content": "//\n// Created by light on 19-11-3.\n//\n\n#include <iostream>\n\nusing namespace std;\n\n// final关键字用于两个地方\n// 第一个用在类，用于说明该类是继承体系下最后的一个类，不要其他类继承我，当继承时就会报错。\nclass Base final {\npublic:\n    Base() {}\n\n    virtual void func() {}\n};\n\nclass Derivered : public Base {     // error: cannot derive from ‘final’ base ‘Base’ in derived type ‘Derivered’\n};\n// 第二个用在虚函数，表示这个虚函数不能再被override了，再override就会报错。\nclass Base1 {\npublic:\n    Base1() {}\n\n    virtual void func() final {}\n};\n\nclass Derivered1 : public Base1 {\n    virtual void func() {}      // error: overriding final function ‘virtual void Base1::func()’\n};\nint main() {\n\n}"
  },
  {
    "path": "cpp2.0/cpp11/hash.cpp",
    "content": "//\n// Created by light on 19-11-4.\n//\n\n#include <iostream>\n#include <unordered_set>\n#include <complex>\n\n\nusing namespace std;\n\nclass Customer {\n\n};\n\n// 形式一 通过一个类，重载了操作符()的方式，形成了一个仿函数（函数对象）\nclass CustomerHash {\npublic:\n    size_t operator()(const Customer &c) const {\n//        return ...;\n    }\n};\n\n// 形式二 这个方法实际是通过指定了一个函数指针的方式来通知容器，对于这个自定义类到底应该使用哪个hash function\nsize_t customer_hash_func(const Customer &t) {\n//    return ...;\n}\n// 形式三 以struct hash 偏特化形式实现hash function\nnamespace std {     // 必须放在std里面\n    template<>\n    struct hash<Customer> : public __hash_base<size_t, Customer> {      // 继承不继承都可以!\n        size_t\n        operator()(const Customer&s) const noexcept {\n//            return ...;\n        }\n    };\n}\n\n\n// 万能的HashFunction 使用variadic templates\n\n// (4)\ntemplate<typename T>\ninline void hash_combine(size_t& seed, const T& val){\n    seed = std::hash<T>()(val) + 0x9e3779b9\n           + (seed << 6) + (seed >> 2);\n}\n\n// (3)\ntemplate<typename T>\ninline void hash_val(size_t& seed, const T& val){\n    hash_combine(seed, val);\n}\n\n// (2)\ntemplate<typename T, typename... Types>\ninline void hash_val(size_t& seed, const T& val, const Types&... args){\n    hash_combine(seed, val);\n    hash_val(seed, args...);\n}\n// (1)\ntemplate<typename... Types>\ninline size_t hash_val(const Types&... args){\n    size_t seed = 0;\n    hash_val(seed, args...);\n    return seed;\n}\n\n\nint main() {\n    unordered_set<Customer, CustomerHash> unorderedSet;\n    unordered_set<Customer,size_t(*)(const Customer&)>custset(20,customer_hash_func);\n    unordered_set<Customer> unset;\n    int *p= reinterpret_cast<int *>(new Customer());\n    cout<<hash_val(p)<<endl;\n\n    return 0;\n}\n"
  },
  {
    "path": "cpp2.0/cpp11/initializer.cpp",
    "content": "//\n// Created by light on 19-11-3.\n//\n\n#include <iostream>\n#include <algorithm>\nusing namespace std;\n\nint main() {\n    int i;\n    int j();        // 0\n    int *p;\n    int *q();       // nullptr\n\n//    int x1{5.0};    //  error:narrowing conversion\n    // 编译器调用initializer_list私有构造之前,编译器准备好array(array头)与len,传递给该构造,并没有内含这个array,\n    // 指针指向array,copy只是浅copy.\n    // 如今所有容器都接受任意数量的值  insert()或assign() max() min()\n\n    cout<<max({1,2,3})<<endl;   // algorithm里面的max/min\n    return 0;\n}"
  },
  {
    "path": "cpp2.0/cpp11/lambda.cpp",
    "content": "//\n// Created by light on 19-11-3.\n//\n\n#include <iostream>\n#include <set>\n#include <vector>\n#include <algorithm>\n#include <functional>\n\nusing namespace std;\n\n// [introducer](optional)mutable throwSpec->retType {}\n// mutable决定[]能够被改写  mutable throwSpec retType都是选择的,只要有一个存在就得写()\n// retType 返回类型\n// ()放参数\n// []放外面变量 passed by value or reference\n\n\nclass UnNamedLocalFunction {\nprivate:\n    int localVar;\npublic:\n    UnNamedLocalFunction(int var) : localVar(var) {}\n\n    bool operator()(int val) {\n        return val == localVar;\n    }\n};\n\nclass Person {\npublic:\n    string firstname;\n    string lastname;\n};\n\nclass LambdaFunctor {\npublic:\n    LambdaFunctor(int a, int b) : m_a(a), m_b(b) {}\n\n    bool operator()(int n) const {\n        return m_a < n && n < m_b;\n    }\n\nprivate:\n    int m_a;\n    int m_b;\n};\n\nclass X {\nprivate:\n    int __x, __y;\npublic:\n    X(int x, int y) : __x(x), __y(y) {}\n\n    int operator()(int a) { return a; }\n\n    int f() {\n        // 下列 lambda 的语境是成员函数 X::f\n        // 对于[=]或[&]的形式，lambda 表达式可以直接使用 this 指针\n        return [&]() -> int {\n            return operator()(this->__x + __y); // X::operator()(this->x + (*this).y)\n            // 拥有类型 X*\n        }();\n    }\n\n\n    int ff() {\n        return [this]() {\n            return this->__x;\n        }();\n    }\n};\n\nint main() {\n\n    [] {\n        cout << \"hello\" << endl;\n    }();\n\n    auto I = [] {\n        cout << \"hello\" << endl;\n    };\n    I();\n    int id = 0;\n    // 先看前面的id 如果没有mutable error: increment of read-only variable ‘id’\n    auto f = [id]()mutable {\n        cout << \"id=\" << id << endl;\n        ++id;\n    };\n    id = 42;\n    f();    // 0\n    f();    // 1\n    f();    // 2\n    cout << id << endl;\n\n    // 上述lambda就相当于\n//    class Functor {\n//    private:\n//        int id;\n//    public:\n//        void operator() {\n//            cout << \"id=\" << id << endl;\n//            ++id;\n//        }\n//    };\n//    Functor f;\n\n    int id1 = 0;\n    // 加不加mutable没影响,且传引用只要后面id1被修改了,就会使用修改后的值进行操作\n    auto f1 = [&id1]() {\n        cout << \"id1=\" << id1 << endl;\n        ++id1;\n    };\n    id1 = 42;\n    f1();    // 42\n    f1();    // 43\n    f1();    // 44\n    cout << id1 << endl;\n\n\n    // 传参与返回\n    int id2 = 0;\n    auto f2 = [&id2](int &param) {\n        cout << \"id2=\" << id2 << endl;\n        ++id2;\n        ++param;\n        cout << \"param=\" << param << endl;\n        static int x = 5;\n        return id2;\n    };\n    int param = 1;\n    f2(param);\n    cout << \"param=\" << param << endl;\n\n    // [=] =表示默认以by value传递外部所有变量\n    // [&] &表示默认以by reference传递外部所有变量\n    auto f3 = [&]() {\n        cout << \"id=\" << id << endl;\n        cout << \"id1=\" << id1 << endl;\n        cout << \"id2=\" << id2 << endl;\n        cout << \"param=\" << param << endl;\n    };\n    f3();\n\n    // 一部分传引用,其余传值\n    cout << \"id=\" << id << endl;\n    auto f4 = [=, &id]() {       // =不可以放在&id后面\n        cout << \"id=\" << id << endl;\n        id++;\n        cout << \"id1=\" << id1 << endl;\n        cout << \"id2=\" << id2 << endl;\n        cout << \"param=\" << param << endl;\n    };\n    f4();\n\n    // 一部分传值,其余传引用\n    cout << \"id=\" << id << endl;\n    auto f5 = [&, id]() {       // &不可以放在id后面\n        cout << \"id=\" << id << endl;\n        cout << \"id1=\" << id1 << endl;\n        cout << \"id2=\" << id2 << endl;\n        cout << \"param=\" << param << endl;\n    };\n    f5();\n    // this 指针\n    X x_(1, 2);\n    cout << \"x_.f()=\" << x_.f() << endl;   // 1+2=3\n    cout << \"x_.ff()=\" << x_.ff() << endl; // 1\n\n\n\n    // 下面lambda函数等价于上述的UnNamedLocalFunction\n    int tobefound = 5;\n    auto lambda1 = [tobefound](int val) {\n        return val == tobefound;\n    };\n    bool b1 = lambda1(5);\n    UnNamedLocalFunction lambda2(tobefound);\n    bool b2 = lambda2(5);\n    cout << b1 << \" \" << b2 << endl;\n\n    auto ll1 = [](int x) -> int {\n        return x + 10;\n    };\n    // lambda 匿名函数\n    function<int(int x)> ll = [](int x) -> float {\n        return x + 10.0;\n    };\n    cout<<ll(1)<<endl;\n\n    // decltype+lambda\n    // 比大小\n    function<bool(const Person&p1,const Person&p2)> cmp = [](const Person &p1, const Person &p2) {\n        return p1.lastname < p2.lastname;\n    };\n\n\n    // 对于lambda,我们往往只有object,很少有人能够写出它的类型，而有时就需要知道它的类型,要获得其type,就要借助其decltype\n    set<Person, decltype(cmp)> col(cmp);\n\n    // 要申明lambda对象的类型，可以使用template或者auto进行自动推导。\n    // 如果需要知道类型，可以使用decltype，比如，让lambda函数作为关联容器或者无序容器的排序函数或者哈希函数。\n    // 上面代码给出了事例（decltype的第三种用法中的事例），定义了一个lambda函数用cmp表示，用来比较Person对象的大小，传入到Set容器中去，\n    // 但根据右边的set容器的定义，我们传入的不仅是cmp（构造函数），还要传入模板的cmp类型（Set内部需要声明cmp类型），\n    // 所以必须使用decltype来推导出类型。\n    // （如果没有向构造函数传入cmp，调用的是默认的构造函数，也就是set() : t(Compare()), 这里会报错, 现在不会出问题了!\n    // 因为Compare()指的是调用默认的lambda构造函数，而lambda函数没有默认构造函数和赋值函数）\n\n\n    vector<int> vec{5, 28, 50, 83, 70, 590, 245, 59, 24};\n    int x = 30, y = 100;\n    // 函数对象是很强大的，封装代码和数据来自定义标准库的行为，但需要写出函数对象需要写出整个class，这是不方便的，而且是非本地的，\n    // 用起来也麻烦，需要去看怎样使用，另外编译出错的信息也不友好，而且它们不是inline的，效率会低一些（算法效率还是最重要的）。\n//    vec.erase(remove_if(vec.begin(), vec.end(), LambdaFunctor(x, y)), vec.end());\n//    for(auto i:vec) cout<<i<<\" \";\n//    cout<<endl;\n    // 而lambda函数的提出解决了这个问题，简短有效清晰，上面的事例很好的说明了这个问题，用lambda要简短许多，功能一样，很直观。\n    vec.erase(remove_if(vec.begin(), vec.end(), [x, y](int n) { return x < n && n < y; }), vec.end());\n    for_each(vec.begin(), vec.end(), [](int i) { cout << i << \" \"; });\n    cout << endl;\n    return 0;\n\n}\n"
  },
  {
    "path": "cpp2.0/cpp11/move.cpp",
    "content": "//\n// Created by light on 19-11-4.\n//\n\n#include <iostream>\n#include <cstring>\n#include <vector>\n#include <list>\n#include <typeinfo>\n#include <deque>\n#include <set>\n#include <map>\n#include <unordered_set>\n#include <unordered_map>\n\nusing namespace std;\n\nclass MyStringNoMove {\npublic:\n    static size_t DCtor;    // default-ctor\n    static size_t Ctor;     // ctor\n    static size_t CCtor;    // copy-ctor\n    static size_t CAsgn;    // copy-assignment\n    static size_t MCtor;    // move-ctor\n    static size_t MAsgn;    // move-assignment\n    static size_t Dtor;     // dtor\nprivate:\n    char *_data;\n    size_t _len;\n\n    void _init_data(const char *s) {\n        _data = new char[_len];\n        memcpy(_data, s, _len);\n        _data[_len] = '\\0';\n    };\npublic:\n    MyStringNoMove() : _data(NULL), _len(0) { ++DCtor; }\n\n    MyStringNoMove(const char *p) : _len(strlen(p)) {\n        ++Ctor;\n        _init_data(p);\n    }\n\n    // copy ctor\n    MyStringNoMove(const MyStringNoMove &str) : _len(str._len) {\n        ++CCtor;\n        _init_data(str._data);\n    }\n\n\n    // copy assignment\n    MyStringNoMove &operator=(const MyStringNoMove &str) {\n        ++CAsgn;\n        if (this != &str) {\n            if (_data) delete _data;\n            _len = str._len;\n            _init_data(str._data);\n        } else {\n\n        }\n        return *this;\n    }\n\n    ~MyStringNoMove() {       // dtor 默认是noexcept\n        ++Dtor;\n        if (_data)\n            delete _data;\n    }\n\n    bool\n    operator<(const MyStringNoMove &rhs) const {\n        return string(this->_data) < string(rhs._data);\n    }\n\n    bool\n    operator==(const MyStringNoMove &rhs) const {\n        return string(this->_data) == string(rhs._data);\n    }\n\n    char *get() const {\n        return _data;\n    }\n};\n\nsize_t MyStringNoMove::DCtor = 0;    // default-ctor\nsize_t MyStringNoMove::Ctor = 0;     // ctor\nsize_t MyStringNoMove::CCtor = 0;    // copy-ctor\nsize_t MyStringNoMove::CAsgn = 0;    // copy-assignment\nsize_t MyStringNoMove::MAsgn = 0;    // move-assignment\nsize_t MyStringNoMove::MCtor = 0;    // move-ctor\nsize_t MyStringNoMove::Dtor = 0;     // dtor\n// move aware class\n\nclass MyString {\npublic:\n    static size_t DCtor;    // default-ctor\n    static size_t Ctor;     // ctor\n    static size_t CCtor;    // copy-ctor\n    static size_t CAsgn;    // copy-assignment\n    static size_t MCtor;    // move-ctor\n    static size_t MAsgn;    // move-assignment\n    static size_t Dtor;     // dtor\nprivate:\n    char *_data;\n    size_t _len;\n\n    void _init_data(const char *s) {\n        _data = new char[_len];\n        memcpy(_data, s, _len);\n        _data[_len] = '\\0';\n    };\npublic:\n    MyString() : _data(NULL), _len(0) { ++DCtor; }\n\n    MyString(const char *p) : _len(strlen(p)) {\n        ++Ctor;\n        _init_data(p);\n    }\n\n    // copy ctor\n    MyString(const MyString &str) : _len(str._len) {\n        ++CCtor;\n        _init_data(str._data);\n    }\n\n    // move ctor\n    MyString(MyString &&str) noexcept: _data(str._data), _len(str._len) {\n        ++MCtor;\n        str._len = 0;\n        str._data = NULL;   // 重要\n    }\n\n    // copy assignment\n    MyString &operator=(const MyString &str) {\n        ++CAsgn;\n        if (this != &str) {\n            if (_data) delete _data;\n            _len = str._len;\n            _init_data(str._data);\n        } else {\n\n        }\n        return *this;\n    }\n\n    // move assignment\n    MyString &operator=(MyString &&str) {\n        ++MAsgn;\n        if (this != &str) {\n            if (_data) delete _data;\n            _data = str._data;\n            _len = str._len;\n            str._len = 0;\n            str._data = NULL;\n        } else {\n\n        }\n        return *this;\n    }\n\n    ~MyString() {       // dtor 默认是noexcept\n        ++Dtor;\n        if (_data)\n            delete _data;\n    }\n\n    bool\n    operator<(const MyString &rhs) const {\n        return string(this->_data) < string(rhs._data);\n    }\n\n    bool\n    operator==(const MyString &rhs) const {\n        return string(this->_data) == string(rhs._data);\n    }\n\n    char *get() const {\n        return _data;\n    }\n};\n\nsize_t MyString::DCtor = 0;    // default-ctor\nsize_t MyString::Ctor = 0;     // ctor\nsize_t MyString::CCtor = 0;    // copy-ctor\nsize_t MyString::CAsgn = 0;    // copy-assignment\nsize_t MyString::MAsgn = 0;    // move-assignment\nsize_t MyString::MCtor = 0;    // move-ctor\nsize_t MyString::Dtor = 0;     // dtor\nnamespace std {\n    template<>\n    struct hash<MyStringNoMove> : public __hash_base<size_t, MyStringNoMove> {\n        size_t\n        operator()(const MyStringNoMove &s) const noexcept {\n            return hash<string>()(string(s.get()));\n        }\n    };\n\n    template<>\n    struct hash<MyString> : public __hash_base<size_t, MyString> {\n        size_t\n        operator()(const MyString &s) const noexcept {\n            return hash<string>()(string(s.get()));\n        }\n    };\n}\n\ntemplate<typename T>\nvoid output_static_data(const T &myStr) {\n    cout << typeid(myStr).name() << \"==\" << endl;\n    cout << \"CCtor=\" << T::CCtor\n         << \",MCtor=\" << T::MCtor\n         << \",CAsgn=\" << T::CAsgn\n         << \",MAsgn=\" << T::MAsgn\n         << \",Dtor=\" << T::Dtor\n         << \",Ctor=\" << T::Ctor\n         << \",DCtor=\" << T::DCtor\n         << endl;\n}\n\n\ntemplate<typename M, typename NM>\nvoid test_moveable(M c1, NM c2, long &value) {\n    cout << \"\\n\\ntest, with moveable elements\" << endl;\n    typedef typename iterator_traits<typename M::iterator>::value_type V1type;\n    clock_t timeStart = clock();\n    for (long i = 0; i < value; i++) {\n        string buf = to_string(i);\n        auto ite = c1.end();\n        c1.insert(ite, V1type(buf.c_str()));\n    }\n    cout << \"construction,milli-seconds:\" << clock() - timeStart << endl;\n    cout << \"size()=\" << c1.size() << endl;\n    output_static_data(*(c1.begin()));\n\n    // 容器本身操作\n    timeStart = clock();\n    M c11(c1);\n    cout << \"copy, milli-seconds: \" << (clock() - timeStart) << endl;\n    timeStart = clock();\n    M c12(move(c1));\n    cout << \"move copy, milli-seconds: \" << (clock() - timeStart) << endl;\n    timeStart = clock();\n    c11.swap(c12);\n    cout << \"swap, milli-seconds: \" << (clock() - timeStart) << endl;\n\n    //測試 non-moveable\n    cout << \"\\n\\ntest, with non-moveable elements\" << endl;\n    typedef typename iterator_traits<typename NM::iterator>::value_type V2type;\n    timeStart = clock();\n    for (long i = 0; i < value; ++i) {\n        string buf = to_string(i);\n        auto ite = c2.end();\n        c2.insert(ite, V2type(buf.c_str()));\n    }\n\n    cout << \"construction, milli-seconds : \" << (clock() - timeStart) << endl;\n    cout << \"size()= \" << c2.size() << endl;\n    output_static_data(*(c2.begin()));\n\n    // 容器本身操作\n    timeStart = clock();\n    NM c21(c2);\n    cout << \"copy, milli-seconds : \" << (clock() - timeStart) << endl;\n\n    timeStart = clock();\n    NM c22(std::move(c2));\n    cout << \"move copy, milli-seconds : \" << (clock() - timeStart) << endl;\n\n    timeStart = clock();\n    c21.swap(c22);\n    cout << \"swap, milli-seconds: \" << (clock() - timeStart) << endl;\n}\n\nint main() {\n    long value = 3000;           // vector 测试结果的MCtor与CCtor结果大于3000000,是因为vector的动态扩容,当容量不够的时候,会动态分配并进行拷贝。\n//    test_moveable(vector<MyString>(), vector<MyStringNoMove>(), value);\n//    test_moveable(list<MyString>(), list<MyStringNoMove>(), value);\n//    test_moveable(deque<MyString>(), deque<MyStringNoMove>(), value);\n//    test_moveable(multiset<MyString>(), multiset<MyStringNoMove>(), value);\n//    test_moveable(unordered_multiset<MyString>(), unordered_multiset<MyStringNoMove>(), value);\n    return 0;\n}\n"
  },
  {
    "path": "cpp2.0/cpp11/noexcept.cpp",
    "content": "//\n// Created by light on 19-11-3.\n//\n\n#include <iostream>\n#include <vector>\n\nusing namespace std;\n\n// noexcepte ()中可以加条件\n// 异常可传递  a调用b b抛出异常,a没处理,就会抛出异常\n// 1.异常的回传机制:调用foo如果抛出异常，foo会接着往上层抛出异常，\n// 如果最上层没有处理，则会调用terminate函数，terminate函数内部调用abort，使程序退出\n\n\n// 2.在使用vector deque等容器的移动构造和移动赋值的时候，如果移动构造和移动赋值没有加上noexcept，\n// 则容器增长的时候不会调用move constructor，效率就会偏低，所以后面需要加上noexcept，安心使用。\n\nvoid foo() noexcept(true) {\n}\n\nint main() {\n    foo();\n    vector<int> vec;\n    return 0;\n}\n"
  },
  {
    "path": "cpp2.0/cpp11/nullptr.cpp",
    "content": "//\n// Created by light on 19-11-2.\n//\n\n#include <iostream>\nusing namespace std;\n\nvoid f(int i) {\n   cout<<\"void f(int i)\" <<endl;\n}\n\nvoid f(void *p) {\n    cout<<\"void f(void *p)\" <<endl;\n}\nint main() {\n    f(0);\n\n    // #ifndef __cplusplus\n    // #define NULL ((void *)0)\n    // #else   /* C++ */\n    // #define NULL 0\n\n    //  c语言中将NULL定义为空指针，而在c++中直接定义为0，这是因为C++是强类型的，void *是不能隐式转换成其他指针类型的。\n\n    if(NULL==0)  cout<<\"NULL==0\"<<endl;\n    // f(NULL); // ambiguous,因为NULL==0所以存在二义性 指针也可以是个int的地址\n    // c++11 空指针nullptr\n    f(nullptr); //typedef decltype(nullptr)\tnullptr_t;\n    // nullptr_t为nullptr的类型\n    nullptr_t  nl;  // 使用nullptr_t定义的任何变量都具有nullptr一样的行为\n    f(nl);\n\n    return 0;\n}\n"
  },
  {
    "path": "cpp2.0/cpp11/override.cpp",
    "content": "//\n// Created by light on 19-11-3.\n//\n\n#include <iostream>\n\nusing namespace std;\n\nclass Base\n{\npublic:\n    Base() {}\n    virtual void func() {}\n};\nclass Derivered : public Base\n{\n    virtual void func(int) override {} // error: ‘virtual void Derivered::func(int)’ marked ‘override’, but does not override\n};\n// override用于虚函数，上面的virtual void func(int)实际上不是重写父类的虚函数，而是定义一个新的虚函数，\n// 我们的本意是重写虚函数，当不加overrride的时候,这样写编译器不会报错，\n// 那如果像下面加上override的话，则会报错，表示告诉了编译器，我确实要重写，但写错了，没有重写，于是就报错了,\n// 这样就能给我们对虚函数的重写做检查!\n\nint main()\n{\n}"
  },
  {
    "path": "cpp2.0/cpp11/rvalue.cpp",
    "content": "//\n// Created by light on 19-11-4.\n//\n\n#include <iostream>\n#include <complex>\n#include <vector>\n\nusing namespace std;\n\n// Rvalue references 解决非必要的拷贝  当赋值右手边是一个\"右值\",左手边对象可以偷右手边对象,而不需要重新分配内存。\n// 浅copy\nint foo() { return 5; }\n\nclass Foo {\npublic:\n    Foo() = default;\n\n    Foo(const Foo &foo) = default;\n\n    Foo(Foo &&foo) noexcept {}\n};\n\n\n// 判断调用哪一个函数\nvoid process(int &i) {\n    cout << \"左值传入\" << endl;\n}\n\nvoid process(int &&i) {\n    cout << \"右值传入\" << endl;\n}\n\nvoid UnPerfectForward(int &&i) {\n    cout << \"forward(int&& i)\" << endl;\n    process(i);\n}\n// std::forward()实现就是下面这样\nvoid PerfectForward(int &&i) {\n    cout << \"forward(int&& i)\" << endl;\n    process(static_cast<int&&>(i));\n}\n// Lvalue: 变量\n// Rvalue: 临时对象就是个右值,右值不可以放在左边\nint main() {\n    int a = 9, b = 4;\n\n    a = b;\n    b = a;\n//    a+b=42;// error Rvalue\n\n    string s1(\"hello\");\n    string s2(\"world\");\n    s1 + s2 = s2;   // ok\n    string() = \"ok\";  // ok\n\n    cout << \"s2:\" << s1 + s2 << endl;\n    cout << \"s1:\" << s1 << endl;\n    cout << \"s2:\" << s2 << endl;\n\n    complex<int> c1(3, 8), c2(1, 0);\n    c1 + c2 = complex<int>(3, 4);   // ok\n    complex<int>() = complex<int>(1, 2); // ok\n\n    int x = foo();\n//    int *p=&foo(); //error!  Rvalue不可以取地址\n//    foo()=7;  // error\n    // Rvalue references\n//    vector<Foo> vec;\n//    vec.insert(vec.begin(), Foo());  // Rvalue references and Move Semantics\n    //  原先是下面这个\n    //  iterator insert(const_iterator __position, const value_type& __x);\n    // 调用下面这个Move Semantics\n    //  iterator insert(const_iterator __position, value_type&& __x)        // 然后转交给Foo(Foo&& foo)\n    //  { return emplace(__position, std::move(__x)); }\n    // Foo()这个临时对象为右值交给insert的move assignment然后再交给Foo的move ctor。中间有可能会丢失一些信息。\n\n    int aa = 1;\n    process(aa);            // L\n    process(1);             // R\n    process(move(aa));      // R\n\n    UnPerfectForward(2); // 希望通过转交调用的是右值传入函数,可是调用的是左值传入 这就是个Unperfect Forwarding\n    UnPerfectForward(move(aa));  // 同上\n    // 那如何设计Perfect Forwarding?\n    // 为传递加上static_cast<int &&>强转或者直接使用std::forward()\n    PerfectForward(2);\n    PerfectForward(move(aa));\n\n    return 0;\n}"
  },
  {
    "path": "cpp2.0/cpp11/template_template.cpp",
    "content": "//\n// Created by light on 19-11-3.\n//\n#include <iostream>\n#include <list>\n\nusing namespace std;\n// template template parameter 模板模板参数\ntemplate<typename T,\n        template<typename U> class Container>\nclass XCls {\nprivate:\n    Container<T> c;\n};\n\n// alias template\ntemplate<typename T>\nusing Lst = list<T>;\n\nint main() {\n//    XCls<string, list> mylist1;   // error\n    XCls<string, Lst> mylist2;\n\n    return 0;\n}\n"
  },
  {
    "path": "cpp2.0/cpp11/tuple.cpp",
    "content": "//\n// Created by light on 19-11-4.\n//\n\n#include <iostream>\n#include <tuple>\n\nusing namespace std;\n\n// tuple使用\nint main() {\n    tuple<int, int, string> t, t1, t2;\n    t = make_tuple(1, 2, \"qwe\");\n    t1 = make_tuple(3, 2, \"qwe\");\n    t2 = make_tuple(3, 2, \"qwe\");\n    int a = get<0>(t);\n    get<0>(t) = get<1>(t);   // 可以修改\n    cout << a << endl;\n    cout << (get<0>(t) > get<1>(t) ? \"true\" : \"false\") << endl; // 比较大小\n    cout << (t1 == t2 ? \"true\" : \"false\") << endl;\n    typedef tuple<int, int, int, string> T;\n    cout << tuple_size<T>::value << endl;   // meta programming 处理类型\n    cout << tuple_size<T>::value << endl;\n    tuple_element<1, T>::type a1 = 10;\n    cout << a1 << endl;\n    return 0;\n}"
  },
  {
    "path": "cpp2.0/cpp11/type_alias.cpp",
    "content": "//\n// Created by light on 19-11-3.\n//\n\n#include <iostream>\n#include <vector>\n\nusing namespace std;\n\n// 第一种使用\n// type alias\n// 等价于typedef void(*func)(int, int);\nusing fun=void (*)(int, int);\n\nvoid example(int, int) {}\n\n// 第二种使用\ntemplate<typename T>\nstruct Container {\n    using value_type=T;  // 等价于typedef T value_type;\n};\n\ntemplate<typename Cn>\nvoid func2(const Cn &cn) {\n    typename Cn::value_type n;\n}\n\n// 第三种使用\n// alias template\ntemplate<typename CharT>\nusing string=basic_string<CharT, char_traits<CharT>>;\n// 等价于 typedef basic_string<char>    string;\n\n// 综上:type alias等价于typedef,没有什么不同\n// using 的所有用法拓展如下:\n// 1.using-directives\n// using namespace std; or using std::cout;\n// 2. using-declaration\n// protected:\n//    using _Base::_M_alloc;\n// 3.c++2.0特性 type alias and alias template\nint main() {\n    fun fn = example;   // 函数指针\n    return 0;\n}\n"
  },
  {
    "path": "cpp2.0/cpp11/uniform_initialization.cpp",
    "content": "//\n// Created by light on 19-11-2.\n//\n\n#include <iostream>\n#include <vector>\n\nusing namespace std;\n\nint main() {\n    int value[]{1, 2, 3};       // initializer_list<T> 会关联一个array<T,n> 里面元素被编译器逐一分解传给函数\n    vector<int> v{2, 3, 5};\n    return 0;\n}"
  },
  {
    "path": "cpp2.0/cpp11/variadic/BUILD",
    "content": "# please run `bazel run //cpp2.0/cpp11/variadic:variadic7`\n# please run `bazel run //cpp2.0/cpp11/variadic:variadic3_4`\n# please run `bazel run //cpp2.0/cpp11/variadic:variadic2`\n# please run `bazel run //cpp2.0/cpp11/variadic:variadic6`\n# please run `bazel run //cpp2.0/cpp11/variadic:variadic5`\n# please run `bazel run //cpp2.0/cpp11/variadic:variadic`\n# please run `bazel run //cpp2.0/cpp11/variadic:variadic1`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"variadic7\",\n    srcs = [\"variadic7.cpp\"],\n)\ncc_binary(\n    name = \"variadic3_4\",\n    srcs = [\"variadic3_4.cpp\"],\n)\ncc_binary(\n    name = \"variadic2\",\n    srcs = [\"variadic2.cpp\"],\n)\ncc_binary(\n    name = \"variadic6\",\n    srcs = [\"variadic6.cpp\"],\n)\ncc_binary(\n    name = \"variadic5\",\n    srcs = [\"variadic5.cpp\"],\n)\ncc_binary(\n    name = \"variadic\",\n    srcs = [\"variadic.cpp\"],\n)\ncc_binary(\n    name = \"variadic1\",\n    srcs = [\"variadic1.cpp\"],\n)\n"
  },
  {
    "path": "cpp2.0/cpp11/variadic/variadic.cpp",
    "content": "//\n// Created by light on 19-11-2.\n//\n\n// variadic template(c++11) 数量不定的模板参数\n#include <iostream>\n#include <vector>\n#include <tuple>\n\nusing namespace std;\n// 参数个数  参数个数逐一递减\n// 参数类型  参数类型也逐一递减\n\n// (3)\n// 需要重载一个终止函数\ntemplate<typename T>\nvoid _hash(size_t &seed, const T &val) {\n    cout << \"hash over \" << val << endl;\n}\n\n// (2)\n// 展开函数, 把参数分为一个普通参数和一个参数包，调用一次，参数包大小就减一\ntemplate<typename T,typename... Args>\nvoid _hash(size_t &seed, const T& val,const Args &... args) {\n    cout << \"parameter \" << val<< endl;\n    // 递归调用自己\n    _hash(seed, args...);       // 上面如果不给T参数,会自己调用自己成死循环\n}\n\n\n// 泛化版 (1)\ntemplate<typename ...Args>\nsize_t _hash(const Args &... args) {\n    cout << \"hash start \" << endl;\n    size_t seed = 10;\n    // 递归调用自己\n    _hash(seed, args...);\n    return seed;\n}\n\n\ntemplate<typename ...Args>\nclass A {\nprivate:\n    int size = 0;    // c++11 支持类内初始化\npublic:\n    A() {\n        size = sizeof...(Args);\n        cout << size << endl;\n    }\n};\n\nint main(void) {\n    size_t f = 10;\n    _hash(\"asdas\", 2, 3, 4);\n    A<int, string, vector<int>> a;    // 类型任意\n\n    // Tuple就是利用这个特性(变长参数模板)\n    tuple<int, string> t = make_tuple(1, \"hha\");\n    int firstArg = get<0>(t);\n    string secondArg = get<1>(t);\n    cout << firstArg << \" \" << secondArg << endl;\n    return 0;\n}\n\n\n"
  },
  {
    "path": "cpp2.0/cpp11/variadic/variadic1.cpp",
    "content": "//\n// Created by light on 19-11-4.\n//\n\n#include <iostream>\n#include <bitset>\n\nusing namespace std;\n\n// example 1\nvoid printX() {\n\n}\n\n// 特化\ntemplate<typename T, typename... Type>\nvoid printX(const T &firstArg, const Type &...args) {\n    cout << firstArg << endl;       // 先减少一个做操作\n    printX(args...);    // 其余的继续分为一个+一包进行递归知道没有了 调用printX()\n}\n\n// 如果写了下面接收任意参数,下面这个跟上面可以共存 泛化版  永远不会被调用,前面特化把它取代了\ntemplate<typename T, typename... Type>\nvoid printX(const Type &...args) {\n    printX(args...);\n}\n\n\nint main() {\n    printX(1, \"hello\", bitset<16>(377), 42);\n    return 0;\n}"
  },
  {
    "path": "cpp2.0/cpp11/variadic/variadic2.cpp",
    "content": "//\n// Created by light on 19-11-4.\n//\n\n#include <iostream>\n\nusing namespace std;\n\nvoid print(const char *s) {\n    while (*s) {\n        if (*s == '%' && *(++s) != '%')\n            throw std::runtime_error(\"invalid format string: missing arguments\");\n        std::cout << *s++;\n    }\n}\n\ntemplate<typename T, typename... Args>\nvoid print(const char *s, T value, Args... args) {\n    while (*s) {\n        if (*s == '%' && *(++s) != '%') {\n            std::cout << value;\n            print(++s, args...); // 即便当 *s == 0 也会产生调用，以检测更多的类型参数。\n            return;\n        }\n        std::cout << *s++;\n    }\n    throw std::logic_error(\"extra arguments provided to printf\");\n}\n\nint main() {\n    int *pi = new int;\n    print(\"%d %s %p %f\\n\", 15, \"Acc\", pi, 3.14);\n    return 0;\n}"
  },
  {
    "path": "cpp2.0/cpp11/variadic/variadic3_4.cpp",
    "content": "//\n// Created by light on 19-11-4.\n//\n\n#include <iostream>\n\nusing namespace std;\n\n// 参数type相同的max\nint max(initializer_list<int> initializerList) {\n    int res = *initializerList.begin();\n    for (auto elem:initializerList)\n        res = max(res, elem);\n    return res;\n}\n\n\n// 参数type相同的maximum\nint maximum(int n) {\n    return n;\n}\n\ntemplate<typename ...Args>\nint maximum(int n, Args...args) {\n    return std::max(n, maximum(args...));\n}\n\nint main() {\n    cout << max({10, 8, 100, 1}) << endl;\n    cout << maximum(57, 48, 60, 100, 20, 18) << endl;\n}"
  },
  {
    "path": "cpp2.0/cpp11/variadic/variadic5.cpp",
    "content": "//\n// Created by light on 19-11-4.\n//\n\n#include <iostream>\n#include <tuple>\n#include <bitset>\n\nusing namespace std;\n\n// tuple递归调用\n\n// 得出这种打印[7,5....,42]\n// 需要知道有几个以及现在操作的是第几个   sizeof...()\n\n// cout<< make_tuple(7.5,string(\"hello\"),bitset<16>(377),47);\n\n\ntemplate<int IDX, int MAX, typename... Args>\nstruct print_tuple {\n    static void print(ostream &os, const tuple<Args...> &t) {\n        os << get<IDX>(t) << (IDX + 1 == MAX ? \"\" : \",\");\n        print_tuple<IDX + 1, MAX, Args...>::print(os, t);\n    }\n};\n\n// 偏特化\ntemplate<int MAX, typename... Args>\nstruct print_tuple<MAX, MAX, Args...> {\n    static void print(ostream &os, const tuple<Args...> &t) {\n    }\n};\n\ntemplate<typename ... Args>\nostream &operator<<(ostream &os, const tuple<Args...> &t) {\n    os << \"[\";\n    print_tuple<0, sizeof...(Args), Args...>::print(os, t);\n    return os << \"]\";\n}\n\n\nint main() {\n    cout << make_tuple(7.5, string(\"hello\"), bitset<16>(377), 47) << endl;\n}"
  },
  {
    "path": "cpp2.0/cpp11/variadic/variadic6.cpp",
    "content": "//\n// Created by light on 19-11-4.\n//\n\n#include <iostream>\n\n\nusing namespace std;\n// 上一个例子:variadic5.cpp为递归调用\n// 当前这个例子为递归继承\nnamespace light {\n    template<typename...Values>\n    class tuple;\n\n    template<>\n    class tuple<> {\n    };\n\n    template<typename Head, typename...Tail>\n    class tuple<Head, Tail...> : private tuple<Tail...> {\n        typedef tuple<Tail...> inherited;\n    protected:\n        Head m_head;\n    public:\n        tuple() {}\n\n        tuple(Head h, Tail...tail) : m_head(h), inherited(tail...) {}\n\n        // decltype()中的m_head必须放到前面,否则编译器找不到\n        auto head() -> decltype(m_head) { return m_head; }\n        // 或者 Head head()  { return m_head; }\n\n        inherited &tail() { return *this; }\n    };\n}\n/**\n * string 32   8字节对齐\n * float  4\n * int    4\n * 4+4+32=40 自底向上\n */\n\nint main() {\n    using light::tuple;\n    tuple<int, float, string> t(41, 6.3, \"nico\");\n    cout << sizeof(t) << endl;\n    cout << t.head() << endl;\n    cout << t.tail().head() << endl;\n    cout << t.tail().tail().head() << endl;\n\n    return 0;\n}\n"
  },
  {
    "path": "cpp2.0/cpp11/variadic/variadic7.cpp",
    "content": "//\n// Created by light on 19-11-4.\n//\n\n#include <iostream>\n\nusing namespace std;\n\nnamespace light {\n    template<typename...Values>\n    class tuple;\n\n    template<>\n    class tuple<> {\n    };\n\n    template<typename Head, typename...Tail>\n    class tuple<Head, Tail...> {\n        typedef tuple<Tail...> composited;\n    protected:\n        Head m_head;\n        composited m_tai;\n    public:\n        tuple() {}\n        int get() { return sizeof(composited);}\n        tuple(Head h, Tail...tail) : m_head(h), m_tai(tail...) {}\n\n        // decltype()中的m_head必须放到前面,否则编译器找不到\n        auto head() -> decltype(m_head) { return m_head; }\n        // 或者 Head head()  { return m_head; }\n\n        composited &tail() { return m_tai; }\n    };\n}\n\n// 根据string为8字节对齐,我们得出8字节对齐\n// 第一次 int 4字节Head调整到边界8 那composite呢?继续往下分析\n// 第二次 把第一次的composited拆分为float与一包, float也占4字节,调整到8边界为8字节,那这一步的composited呢?继续往下分析\n// 第三次 把第二次的compoisted拆分成sting与一包, string占32字节,已经为8倍数,无需调整,而这一步的composited呢?由于到这里一包已经没了,就会调用它的全特化版本,全特环版本为空,sizeof为1,\n// 调整到8的倍数为8.\n// 因此最后的内存结构(自底向上)为：8 + 8 + 32 + 8 = 56     64位机器结果\n\nstruct A {\n    string a;\n    float b;\n};\nint main() {\n    using light::tuple;\n    tuple<int, float, string> t(41, 6.3, \"nico\");\n    cout<<sizeof(A)<<endl;\n    cout<<t.get()<<endl;\n    cout << sizeof(t) << endl;\n    cout << t.head() << endl;\n    cout << t.tail().head() << endl;\n    cout << t.tail().tail().head() << endl;\n    return 0;\n}"
  },
  {
    "path": "design_pattern/producer_consumer/BUILD",
    "content": "# please run `bazel run //design_pattern/producer_consumer:producer_consumer`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"producer_consumer\",\n    srcs = [\"producer_consumer.cpp\"],\n)\n"
  },
  {
    "path": "design_pattern/producer_consumer/producer_consumer.cpp",
    "content": "#include <condition_variable>\n#include <iostream>\n#include <mutex>\n#include <thread>\n#include <unistd.h>\n#include <vector>\n\nusing namespace std;\n\nconst int MAX_NUM = 10000;\n\nclass BoundedBuffer {\npublic:\n  BoundedBuffer(size_t n) {\n    array_.resize(n);\n    start_pos_ = 0;\n    end_pos_ = 0;\n    pos_ = 0;\n  }\n  void Produce(int n) {\n    {\n      std::unique_lock<std::mutex> lock(mtx_);\n      // wait for not full\n      not_full_.wait(lock, [=] { return pos_ < array_.size(); });\n\n      usleep(1000 * 400);\n      array_[end_pos_] = n;\n      end_pos_ = (end_pos_ + 1) % array_.size();\n      ++pos_;\n      cout << \"Produce pos:\" << pos_ << endl;\n    } // auto unlock\n\n    not_empty_.notify_one();\n  }\n\n  int Consume() {\n    std::unique_lock<std::mutex> lock(mtx_);\n    // wait for not empty\n    not_empty_.wait(lock, [=] { return pos_ > 0; });\n\n    usleep(1000 * 400);\n    int n = array_[start_pos_];\n    start_pos_ = (start_pos_ + 1) % array_.size();\n    --pos_;\n    cout << \"Consume pos:\" << pos_ << endl;\n    lock.unlock();\n\n    not_full_.notify_one();\n\n    return n;\n  }\n\nprivate:\n  std::vector<int> array_;\n  size_t start_pos_;\n  size_t end_pos_;\n  size_t pos_;\n  std::mutex mtx_;\n  std::condition_variable not_full_;\n  std::condition_variable not_empty_;\n};\n\nBoundedBuffer bb(10);\nstd::mutex g_mtx;\n\nvoid Producer() {\n  int n = 0;\n  while (n < 100) {\n    bb.Produce(n);\n    cout << \"Producer:\" << n << endl;\n    n++;\n  }\n\n  bb.Produce(-1);\n}\n\nvoid Consumer() {\n  std::thread::id thread_id = std::this_thread::get_id();\n  int n = 0;\n  do {\n    n = bb.Consume();\n    cout << \"Consumer thread:\" << thread_id << \"=====> \" << n << endl;\n  } while (-1 != n);\n\n  bb.Produce(-1);\n}\n\nint main() {\n  std::vector<std::thread> t;\n  t.push_back(std::thread(&Producer));\n  t.push_back(std::thread(&Consumer));\n  t.push_back(std::thread(&Consumer));\n  t.push_back(std::thread(&Consumer));\n\n  for (auto &one : t) {\n    one.join();\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "design_pattern/singleton/README.md",
    "content": "## C++设计模式之单例模式\n\n## 0.导语\n\n相信大家面试都逃不开设计模式话题，本节将阐述面试中的最常用的设计模式(单例模式)，从分类，线程安全，不基于C++11标准的角度与基于C++11标准的角度，有哪些解决线程安全的单例模式方案，相信认真看完本篇文章，在以后面试中就不用担忧了。\n\n## 1.众所周知的单例\n\n在一般书籍中或者大家比较是熟知的单例模式是下面这样：\n\n```cpp\nclass singleton {\nprivate:\n    singleton() {}\n    static singleton *p;\npublic:\n    static singleton *instance();\n};\n\nsingleton *singleton::p = nullptr;\n\nsingleton* singleton::instance() {\n    if (p == nullptr)\n        p = new singleton();\n    return p;\n}\n```\n\n这是一个非常简单的实现，将构造函数声明为private或protect防止被外部函数实例化，内部有一个静态的类指针保存唯一的实例，实例的实现由一个public方法来实现，该方法返回该类的唯一实例。\n\n当然这个代码只适合在单线程下，当多线程时，是不安全的。考虑两个线程同时首次调用instance方法且同时检测到p是nullptr，则两个线程会同时构造一个实例给p，这将违反了单例的准则。\n\n## 2.懒汉与饿汉\n\n单例分为两种实现方法：\n\n- 懒汉\n  - 第一次用到类实例的时候才会去实例化，上述就是懒汉实现。\n- 饿汉\n  - 单例类定义的时候就进行了实例化。\n\n这里也给出饿汉的实现：\n\n```cpp\nclass singleton {\nprivate:\n    singleton() {}\n    static singleton *p;\npublic:\n    static singleton *instance();\n};\n\nsingleton *singleton::p = new singleton();\nsingleton* singleton::instance() {\n    return p;\n}\n```\n\n当然这个是线程安全的，对于我们通常阐述的线程不安全，为懒汉模式，下面会阐述懒汉模式的线程安全代码优化。\n\n## 3.多线程加锁\n\n在C++中加锁有个类实现原理采用RAII，不用手动管理unlock，那就是lock_guard，这里采用其进行加锁。\n\n```cpp\nclass singleton {\nprivate:\n    singleton() {}\n    static singleton *p;\n    static mutex lock_;\npublic:\n    static singleton *instance();\n};\n\nsingleton *singleton::p = nullptr;\n\nsingleton* singleton::instance() {\n    lock_guard<mutex> guard(lock_);\n    if (p == nullptr)\n        p = new singleton();\n    return p;\n}\n```\n\n这种写法不会出现上面两个线程都执行到p=nullptr里面的情况，当线程A在执行`p = new Singleton()`的时候，线程B如果调用了`instance()`，一定会被阻塞在加锁处，等待线程A执行结束后释放这个锁。从而是线程安全的。\n\n但是这种写法性能非常低下，因为每次调用`instance()`都会加锁释放锁，而这个步骤只有在第一次`new Singleton()`才是有必要的，只要`p`被创建出来了，不管多少线程同时访问，使用`if (p == nullptr) `进行判断都是足够的（只是读操作，不需要加锁），没有线程安全问题，加了锁之后反而存在性能问题。\n\n因此引出DCL。\n\n## 4.双重检查锁模式\n\n上面写法是不管任何情况都会去加锁，然后释放锁，而对于读操作是不存在线程安全的，故只需要在第一次实例创建的时候加锁，以后不需要。下面先看一下DCLP的实现：\n\n```cpp\nsingleton* singleton::instance() {\n\tif(p == nullptr) {  // 第一次检查\n\t\tLock lock;\n\t\tif(p == nullptr){ // 第二次检查\n\t\t\tp = new singleton;\n\t\t}\n\t}\n\treturn p;\n}\n```\n\n基于上述，我们可以写出双重检查锁+自动回收\n\n```cpp\nclass singleton {\nprivate:\n    singleton() {}\n\n    static singleton *p;\n    static mutex lock_;\npublic:\n    static singleton *instance();\n\n    // 实现一个内嵌垃圾回收类\n    class CGarbo\n    {\n    public:\n        ~CGarbo()\n        {\n            if(singleton::p)\n                delete singleton::p;\n        }\n    };\n    static CGarbo Garbo; // 定义一个静态成员变量，程序结束时，系统会自动调用它的析构函数从而释放单例对象\n};\n\nsingleton *singleton::p = nullptr;\nsingleton::CGarbo Garbo;\nstd::mutex singleton::lock_;\n\nsingleton* singleton::instance() {\n    if (p == nullptr) {\n        lock_guard<mutex> guard(lock_);\n        if (p == nullptr)\n            p = new singleton();\n    }\n    return p;\n}\n\n```\n\nDCLP的关键在于，大多数对instance的调用会看到p是非空的，因此甚至不用尝试去初始化它。因此，DCLP在尝试获取锁之前检查p是否为空。只有当检查成功（也就是p还没有被初始化）时才会去获得锁，然后再次检查p是否仍然为空（因此命名为双重检查锁）。**第二次检查是必要，因为就像我们刚刚看到的，很有可能另一个线程偶然在第一次检查之后，获得锁成功之前初始化p。**\n\n看起来上述代码非常美好，可是过了相当一段时间后，才发现这个漏洞，原因是：**内存读写的乱序执行(编译器问题)**。\n\n再次考虑初始化`p`的那一行：\n\n```cpp\np = new singleton;\n```\n\n这条语句会导致三个事情的发生：\n\n1. 分配能够存储`singleton`对象的内存；\n2. 在被分配的内存中构造一个`singleton`对象；\n3. 让`p`指向这块被分配的内存。\n\n可能会认为这三个步骤是按顺序执行的，但实际上只能确定步骤`1`是最先执行的，步骤`2`，`3`却不一定。问题就出现在这。\n\n- 线程A调用instance，执行第一次p的测试，获得锁，按照1,3,执行，然后被挂起。此时p是非空的，但是p指向的内存中还没有Singleton对象被构造。\n- 线程B调用instance，判定p非空， 将其返回给instance的调用者。调用者对指针解引用以获得singleton，噢，一个还没有被构造出的对象。bug就出现了。\n\nDCLP能够良好的工作仅当步骤一和二在步骤三之前被执行，但是并没有方法在C或C++中表达这种限制。这就像是插在DCLP心脏上的一把匕首：我们需要在相对指令顺序上定义限制，但是我们的语言没有给出表达这种限制的方法。\n\n## 5.memory barrier指令\n\nDCLP问题在C++11中，这个问题得到了解决。\n\n因为新的C++11规定了新的内存模型，保证了执行上述3个步骤的时候不会发生线程切换，相当这个初始化过程是“原子性”的的操作，DCL又可以正确使用了，不过在C++11下却有更简洁的多线程singleton写法了，这个留在后面再介绍。\n\nC++11之前解决方法是barrier指令。要使其正确执行的话，就得在步骤2、3直接加上一道memory barrier。强迫CPU执行的时候按照1、2、3的步骤来运行。\n\n**第一种实现：**\n\n基于operator new+placement new，遵循1,2,3执行顺序依次编写代码。\n\n```\n// method 1 operator new + placement new\nsingleton *instance() {\n    if (p == nullptr) {\n        lock_guard<mutex> guard(lock_);\n        if (p == nullptr) {\n            singleton *tmp = static_cast<singleton *>(operator new(sizeof(singleton)));\n            new(tmp)singleton();\n            p = tmp;\n        }\n    }\n    return p;\n}\n```\n\n**第二种实现：**\n\n基于直接嵌入ASM汇编指令mfence，uninx的barrier宏也是通过该指令实现的。\n\n```cpp\n#define barrier() __asm__ volatile (\"lwsync\")\nsingleton *singleton::instance() {\n    if (p == nullptr) {\n        lock_guard<mutex> guard(lock_);\n        barrier();\n        if (p == nullptr) {\n            p = new singleton();\n        }\n    }\n    return p;\n}\n```\n\n通常情况下是调用cpu提供的一条指令，这条指令的作用是会阻止cpu将该指令之前的指令交换到该指令之后，这条指令也通常被叫做barrier。 上面代码中的**asm**表示这个是一条汇编指令，volatile是可选的，如果用了它，则表示向编译器声明不允许对该汇编指令进行优化。lwsync是POWERPC提供的barrier指令。\n\n## 6.静态局部变量\n\nScott Meyer在《Effective C++》中提出了一种简洁的singleton写法\n\n```cpp\nsingleton &singleton::instance() {\n    static singleton p;\n    return p;\n}\n```\n\n- 单线程下，正确。\n- C++11及以后的版本（如C++14）的多线程下，正确。\n- C++11之前的多线程下，不一定正确。\n\n原因在于在C++11之前的标准中并没有规定local static变量的内存模型。于是乎它就是不是线程安全的了。但是在C++11却是线程安全的，这是因为新的C++标准规定了当一个线程正在初始化一个变量的时候，其他线程必须得等到该初始化完成以后才能访问它。\n\n上述使用的内存序：\n\n- memory_order_relaxed：松散内存序，只用来保证对原子对象的操作是原子的\n- memory_order_acquire：获得操作，在读取某原子对象时，当前线程的任何后面的读写操作都不允许重排到这个操作的前面去，并且其他线程在对同一个原子对象释放之前的所有内存写入都在当前线程可见\n- memory_order_release：释放操作，在写入某原子对象时，当前线程的任何前面的读写操作都不允许重排到这个操作的后面去，并且当前线程的所有内存写入都在对同一个原子对象进行获取的其他线程可见\n\n## 7.Atomic\n\n在C++11之前的版本下，除了通过锁实现线程安全的Singleton外，还可以利用各个编译器内置的atomic operation来实现。\n\n`java`和`c#`发现乱序问题后，就加了一个关键字`volatile`，在声明`p`变量的时候，要加上`volatile`修饰，编译器看到之后，就知道这个地方不能够reorder（一定要先分配内存，在执行构造器，都完成之后再赋值）。\n\n而对于`c++`标准却一直没有改正，所以`VC++`在`2005`版本也加入了这个关键字，但是这并不能够跨平台（只支持微软平台）。\n\n而到了`c++ 11`版本，为了从根本上消除这些漏洞，引入了适合多线程的内存模型。终于有了这样的机制帮助我们实现跨平台的方案。\n\n```cpp\nmutex singleton::lock_;\natomic<singleton *> singleton::p;\n\n/*\n* std::atomic_thread_fence(std::memory_order_acquire); \n* std::atomic_thread_fence(std::memory_order_release);\n* 这两句话可以保证他们之间的语句不会发生乱序执行。\n*/\nsingleton *singleton::instance() {\n    singleton *tmp = p.load(memory_order_relaxed);\n    atomic_thread_fence(memory_order_acquire);\n    if (tmp == nullptr) {\n        lock_guard<mutex> guard(lock_);\n        tmp = p.load(memory_order_relaxed);\n        if (tmp == nullptr) {\n            tmp = new singleton();\n            atomic_thread_fence(memory_order_release);\n            p.store(tmp, memory_order_relaxed);\n        }\n    }\n    return p;\n}\n```\n\n值得注意的是，上述代码使用两个比较关键的术语，获得与释放：\n\n- 获得是一个对内存的读操作，当前线程的任何后面的读写操作都不允许重排到这个操作的前面去。\n- 释放是一个对内存的写操作，当前线程的任何前面的读写操作都不允许重排到这个操作的后面去。\n\nacquire 和 release 通常都是配对出现的，目的是保证如果对同一个原子对象的 release 发生在 acquire 之前的话，release 之前发生的内存修改能够被 acquire 之后的内存读取全部看到。\n\n## 8.pthread_once\n\n如果是在unix平台的话，除了使用atomic operation外，在不适用C++11的情况下，还可以通过pthread_once来实现Singleton。\n\n原型如下：\n\n```\nint pthread_once(pthread_once_t once_control, void (init_routine) (void))；\n```\n\n实现：\n\n```cpp\nclass singleton {\nprivate:\n    singleton(); //私有构造函数，不允许使用者自己生成对象\n    singleton(const singleton &other);\n\n    //要写成静态方法的原因：类成员函数隐含传递this指针（第一个参数）\n    static void init() {\n        p = new singleton();\n    }\n\n    static pthread_once_t ponce_;\n    static singleton *p; //静态成员变量 \npublic:\n    singleton *instance() {\n        // init函数只会执行一次\n        pthread_once(&ponce_, &singleton::init);\n        return p;\n    }\n};\n```\n\n## 9.总结\n\n本文讲解了几种单例模式，并讲解了线程安全的单例模式，以及不用C++11实现的几种线程安全的单例模式：memory barrier，静态局部变量，pthread_once方式，C++11的atomic实现等。\n\n最后值得注意的是，针对上述单例类的析构函数请参考双重锁检查模式+自动回收实现，必须在类中声明一个静态局部变量，静态局部变量可以理解为全局变量，在程序结束时，自动调用该静态局部变量的析构函数，这就是为什么要在类中声明与定义嵌套类，而不是直接编写单例的析构函数。\n\n## 10.参考资料\n\n> https://thorns.cn/2019/03/09/DCLP.html\n\n> https://www.cnblogs.com/liyuan989/p/4264889.html\n\n> https://segmentfault.com/a/1190000015950693\n"
  },
  {
    "path": "design_pattern/singleton/barrier_singleton.cpp",
    "content": "//\n// Created by light on 20-2-7.\n//\n#include <iostream>\n\nusing namespace std;\n\n#include <mutex>\n\n#define barrier() __asm__ volatile(\"lwsync\")\n\n// method 1 operator new + placement new\n// singleton *instance() {\n//    if (p == nullptr) {\n//        lock_guard<mutex> guard(lock_);\n//        if (p == nullptr) {\n//            singleton *tmp = static_cast<singleton *>(operator\n//            new(sizeof(singleton))); new(p)singleton(); p = tmp;\n//        }\n//    }\n//    return p;\n//}\nclass singleton {\nprivate:\n  singleton() {}\n\n  static singleton *p;\n  static mutex lock_;\n\npublic:\n  static singleton *instance();\n};\n\nsingleton *singleton::p = nullptr;\n\nsingleton *singleton::instance() {\n  if (p == nullptr) {\n    lock_guard<mutex> guard(lock_);\n    barrier();\n    if (p == nullptr) {\n      p = new singleton();\n    }\n  }\n  return p;\n}\n"
  },
  {
    "path": "design_pattern/singleton/cpulpuls11_singleton.cpp",
    "content": "//\n// Created by light on 20-2-7.\n//\n\n#include <iostream>\n\nusing namespace std;\n\n#include <atomic>\n#include <mutex>\n\n// C++ 11版本之后的跨平台实现\nclass singleton {\nprivate:\n  singleton() {}\n\n  static mutex lock_;\n  static atomic<singleton *> p;\n\npublic:\n  singleton *instance();\n};\n\nmutex singleton::lock_;\natomic<singleton *> singleton::p;\n\n/*\n * std::atomic_thread_fence(std::memory_order_acquire);\n * std::atomic_thread_fence(std::memory_order_release);\n * 这两句话可以保证他们之间的语句不会发生乱序执行。\n */\nsingleton *singleton::instance() {\n  singleton *tmp = p.load(memory_order_relaxed);\n  atomic_thread_fence(memory_order_acquire);\n  if (tmp == nullptr) {\n    lock_guard<mutex> guard(lock_);\n    tmp = p.load(memory_order_relaxed);\n    if (tmp == nullptr) {\n      tmp = new singleton();\n      atomic_thread_fence(memory_order_release);\n      p.store(tmp, memory_order_relaxed);\n    }\n  }\n  return p;\n}\n"
  },
  {
    "path": "design_pattern/singleton/dcl_singleton.cpp",
    "content": "//\n// Created by light on 20-2-7.\n//\n#include <iostream>\n\nusing namespace std;\n\n#include <mutex>\n\nclass singleton {\nprivate:\n  singleton() {}\n\n  static singleton *p;\n  static mutex lock_;\n\npublic:\n  static singleton *instance();\n\n  // 实现一个内嵌垃圾回收类\n  class CGarbo {\n  public:\n    ~CGarbo() {\n      if (singleton::p)\n        delete singleton::p;\n    }\n  };\n  static CGarbo\n      Garbo; // 定义一个静态成员变量，程序结束时，系统会自动调用它的析构函数从而释放单例对象\n};\n\nsingleton *singleton::p = nullptr;\nsingleton::CGarbo Garbo;\nstd::mutex singleton::lock_;\n\nsingleton *singleton::instance() {\n  if (p == nullptr) {\n    lock_guard<mutex> guard(lock_);\n    if (p == nullptr)\n      p = new singleton();\n  }\n  return p;\n}\n"
  },
  {
    "path": "design_pattern/singleton/hungrysingleton.cpp",
    "content": "//\n// Created by light on 20-2-6.\n//\n\nclass singleton {\nprivate:\n  singleton() {}\n  static singleton *p;\n\npublic:\n  static singleton *instance();\n};\n\nsingleton *singleton::p = new singleton();\nsingleton *singleton::instance() { return p; }\n"
  },
  {
    "path": "design_pattern/singleton/iazysingleton.cpp",
    "content": "//\n// Created by light on 20-2-6.\n//\n\nclass singleton {\nprivate:\n  singleton() {}\n  static singleton *p;\n\npublic:\n  static singleton *instance();\n};\n\nsingleton *singleton::p = nullptr;\n\nsingleton *singleton::instance() {\n  if (p == nullptr)\n    p = new singleton();\n  return p;\n}\n"
  },
  {
    "path": "design_pattern/singleton/lock_singleton.cpp",
    "content": "//\n// Created by light on 20-2-7.\n//\n#include <iostream>\nusing namespace std;\n\n#include <mutex>\n\nclass singleton {\nprivate:\n  singleton() {}\n  static singleton *p;\n  static mutex lock_;\n\npublic:\n  static singleton *instance();\n};\n\nsingleton *singleton::p = nullptr;\n\nsingleton *singleton::instance() {\n  lock_guard<mutex> guard(lock_);\n  if (p == nullptr)\n    p = new singleton();\n  return p;\n}\n"
  },
  {
    "path": "design_pattern/singleton/pthreadoncesingleton.cpp",
    "content": "//\n// Created by light on 20-2-6.\n//\n\n#include <pthread.h>\n#include <sys/param.h>\n\nclass singleton {\nprivate:\n  singleton(); //私有构造函数，不允许使用者自己生成对象\n  singleton(const singleton &other);\n\n  //要写成静态方法的原因：类成员函数隐含传递this指针（第一个参数）\n  static void init() { p = new singleton(); }\n\n  static pthread_once_t ponce_;\n  static singleton *p; //静态成员变量\npublic:\n  singleton *instance() {\n    // init函数只会执行一次\n    pthread_once(&ponce_, &singleton::init);\n    return p;\n  }\n};\n"
  },
  {
    "path": "design_pattern/singleton/static_local_singleton.cpp",
    "content": "//\n// Created by light on 20-2-7.\n// 在C++11标准下，《Effective C++》提出了一种更优雅的单例模式实现，使用函数内的\n// local static 对象。 这种方法也被称为Meyers' Singleton。\n//\n\n#include <iostream>\n\nusing namespace std;\n\nclass singleton {\nprivate:\n  singleton() {}\n\npublic:\n  static singleton &instance();\n};\n\nsingleton &singleton::instance() {\n  static singleton p;\n  return p;\n}\n"
  },
  {
    "path": "effective_cpp/2.cpp",
    "content": "//\n// Created by light on 19-9-11.\n//\n\n#include <iostream>\n\nusing namespace std;\nstruct A {\n#define p \"hello\"\n};\n\nclass C {\npublic:\n    // static const 可以直接在类内部初始化\n    // no-const static 只能在外面初始化\n    static const int NUM = 3; //声明\n    enum con {\n        NUM1 = 3\n    };\n};\n\n#define MAX(a,b) ((a) > (b) ? (a) : (b))\ntemplate<typename T>\ninline int Max(const T& a, const T& b){\n    return (a>b ? a:b);\n}\nconst int C::NUM;   // 定义\nint main() {\n    cout << p << endl; // macro is global\n    C c;\n    cout << &c.NUM << endl;     // 未定义的引用,需要定义\n\n    cout << C::NUM1 << endl;\n//    cout << &C:NUM1 << endl;  //error enum no address\n\n    int a=5, b=0;\n    cout<<MAX(++a, b)<<endl;              // a被增加两次\n    cout<<MAX(++a, b+10)<<endl;           // a被累加一次\n    a=5,b=0;\n    cout<<Max(++a,b)<<endl;\n}\n\n\n/**\nconst 有地址,enum与#define没有地址\n 1.const 定义的实际是一个变量,const只限定它不能被修改,所有变量都可在程序运行时获取其地址\n 2.enum类型中的枚举项只是enum类型声明的一部分,它不是定义出来的变量,所以不能取地址\n 3.#define出来的是宏,它是预处理的东西,预处理后的编译阶段已经不存在,所以也不可能获取宏的地址\n\n */"
  },
  {
    "path": "effective_cpp/BUILD",
    "content": "# please run `bazel run //effective_cpp:2`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"2\",\n    srcs = [\"2.cpp\"],\n    copts = [\"-std=c++17\"],\n)\n"
  },
  {
    "path": "english/basic_content/abstract/README.md",
    "content": "# Pure virtual functions and abstract classes\n\n## About Author：\n\n\n\n![](../img/wechat.jpg)\n\n## 1.Pure virtual function and abstract class\n\n\nPure virtual functions (or abstract functions) in C + + are virtual functions that we have not implemented!We just need to state it! \nexample:\n```cpp\n// abstract class\nClass A {\npublic: \n    virtual void show() = 0; // pure virtual function\n    /* Other members */\n}; \n```\n\n * Pure virtual function: virtual function without function body\n * Abstract classes: classes containing pure virtual functions\n\nAbstract classes can only be used as base classes to derive new classes. Objects, pointers and references of abstract classes cannot be created->An object of a class derived from an abstract class!\n\n> Code example：[test.cpp](./test.cpp)、[pure_virtual.cpp](./pure_virtual.cpp)\n\n## 2.Implement abstract classes\n\nAbstract class：Pure virtual functions can be called within member functions.Pure virtual functions cannot be used inside constructors / destructors.\n\nIf a class derives from an abstract class, it must implement all pure virtual functions in the base class to become a non abstract class.\n```cpp\n// A is abstract class\nclass A {\npublic:\n    virtual void f() = 0;  // pure virtual function\n    void g(){ this->f(); }\n    A(){}  // 构造函数\n};\n\nclass B : public A{\npublic:\n    void f(){ cout<<\"B:f()\"<<endl;}  // 实现了抽象类的纯虚函数\n};\n```\n\n> Code Example：[abstract.cpp](./abstract.cpp)\n\n## 3.Key point\n\n- [Pure virtual functions make a class abstract](./interesting_facts1.cpp)\n```cpp\n// Abstract class contains at least one pure virtual function\nclass Base{\npublic: \n    virtual void show() = 0; // 纯虚函数\n    int getX() { return x; } // 普通成员函数\n\nprivate:\n     int x; \n}; \n```\n\n- [Pointers and references to abstract class types](./interesting_facts2.cpp)\n```cpp\nclass Derived : public Base { \npublic: \n    void show() { cout << \"In Derived \\n\"; } // 实现抽象类的纯虚函数\n    Derived(){} // 构造函数\n}; \n\nint main(void) \n{ \n    //Base b;  // error! 不能创建抽象类的对象\n    //Base *b = new Base(); error!\n    \n    Base *bp = new Derived(); // 抽象类的指针和引用 -> 由抽象类派生出来的类的对象\n    bp->show();\n    return 0; \n}\n```\n\n- [If we do not override pure virtual functions in the derived class, the derived class will also become an abstract class](./interesting_facts3.cpp)\n```cpp\n// Derived为抽象类\nclass Derived: public Base \n{ \npublic: \n//    void show() {}\n}; \n```\n\n- [Abstract classes can have constructors](./interesting_facts4.cpp)\n```cpp\n// abstract class\nclass Base { \n    protected: \n        int x; \n    public: \n        virtual void fun() = 0; \n        Base(int i) { x = i; }  // constructor function\n}; \n// 派生类\nclass Derived: public Base \n{ \n    int y; \npublic: \n    Derived(int i, int j) : Base(i) { y = j; } // constructor function\n    void fun() { cout << \"x = \" << x << \", y = \" << y; }\n}; \n```\n\n- [A constructor cannot be a virtual function, and a destructor can be a virtual destructor](./interesting_facts5.cpp)\n```cpp\n// 抽象类\nclass Base  {\npublic:\n    Base(){ cout << \"Constructor: Base\" << endl; }\n    virtual ~Base(){ cout << \"Destructor : Base\" << endl; }\n    \n    virtual void func() = 0;\n};\n\nclass Derived: public Base {\npublic:\n    Derived(){ cout << \"Constructor: Derived\" << endl; }\n    ~Derived(){ cout << \"Destructor : Derived\" << endl;}\n    \n    void func(){cout << \"In Derived.func().\" << endl;}\n};\n```\n>When the base class pointer points to a derived class object and removes the object, we may want to call the appropriate destructor.\n>The destructor can only be called if it is not a virtual destructor.\n\n## 4.Complete example\n\nAbstract classes are inherited and implemented by derived classes!\n\n> Code Example：[derived_full.cpp](./derived_full.cpp)\n"
  },
  {
    "path": "english/basic_content/abstract/abstract.cpp",
    "content": "/**\n * @file abstract.cpp\n * @brief 抽象类中：在成员函数内可以调用纯虚函数，在构造函数/析构函数内部不能使用纯虚函数\n * 如果一个类从抽象类派生而来，它必须实现了基类中的所有纯虚函数，才能成为非抽象类\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include<iostream>\nusing namespace std;\n\nclass A {\npublic:\n    virtual void f() = 0;  // 纯虚函数\n    void g(){ this->f(); }\n    A(){}\n};\nclass B:public A{\npublic:\n    void f(){ cout<<\"B:f()\"<<endl;}\n};\nint main(){\n    B b;\n    b.g();\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/abstract/derived_full.cpp",
    "content": "/**\n * @file derived_full.cpp\n * @brief 完整示例！抽象类由派生类继承实现！\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include<iostream> \nusing namespace std; \n\nclass Base \n{ \n    int x; \n    public: \n    virtual void fun() = 0; \n    int getX() { return x; } \n}; \n\nclass Derived: public Base \n{ \n    int y; \n    public: \n    void fun() { cout << \"fun() called\"; }  // 实现了fun()函数\n}; \n\nint main(void) \n{ \n    Derived d; \n    d.fun(); \n    return 0; \n} \n"
  },
  {
    "path": "english/basic_content/abstract/interesting_facts1.cpp",
    "content": "/**\n * @file interesting_facts1.cpp\n * @brief 纯虚函数使一个类变成抽象类\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include<iostream> \nusing namespace std; \n\n/**\n * @brief 抽象类至少包含一个纯虚函数\n */\nclass Test \n{ \n    int x; \npublic: \n    virtual void show() = 0; \n    int getX() { return x; } \n}; \n\nint main(void) \n{ \n    Test t;  //error! 不能创建抽象类的对象\n    return 0; \n} \n\n"
  },
  {
    "path": "english/basic_content/abstract/interesting_facts2.cpp",
    "content": "/**\n * @file interesting_facts2.cpp\n * @brief 抽象类类型的指针和引用\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include<iostream> \nusing namespace std; \n\n\n/**\n * @brief 抽象类至少包含一个纯虚函数\n */\nclass Base\n{ \n    int x; \npublic: \n    virtual void show() = 0; \n    int getX() { return x; } \n    \n}; \nclass Derived: public Base \n{ \npublic: \n    void show() { cout << \"In Derived \\n\"; } \n    Derived(){}\n}; \nint main(void) \n{ \n    //Base b;  //error! 不能创建抽象类的对象\n    //Base *b = new Base(); error!\n    Base *bp = new Derived(); // 抽象类的指针和引用 -> 由抽象类派生出来的类的对象\n    bp->show();\n    return 0; \n} \n\n"
  },
  {
    "path": "english/basic_content/abstract/interesting_facts3.cpp",
    "content": "/**\n * @file interesting_facts3.cpp\n * @brief 如果我们不在派生类中覆盖纯虚函数，那么派生类也会变成抽象类。\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include<iostream> \nusing namespace std; \n\nclass Base\n{ \n    int x; \npublic: \n    virtual void show() = 0; \n    int getX() { return x; } \n}; \nclass Derived: public Base \n{ \npublic: \n//    void show() { } \n}; \nint main(void) \n{ \n    Derived d;  //error! 派生类没有实现纯虚函数，那么派生类也会变为抽象类，不能创建抽象类的对象\n    return 0; \n} \n\n"
  },
  {
    "path": "english/basic_content/abstract/interesting_facts4.cpp",
    "content": "/**\n * @file interesting_facts4.cpp\n * @brief 抽象类可以有构造函数\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include<iostream> \nusing namespace std; \n\n// An abstract class with constructor \nclass Base \n{ \n    protected: \n        int x; \n    public: \n        virtual void fun() = 0; \n        Base(int i) { x = i; } \n}; \n\nclass Derived: public Base \n{ \n    int y; \n    public: \n    Derived(int i, int j):Base(i) { y = j; } \n    void fun() { cout << \"x = \" << x << \", y = \" << y; } \n}; \n\nint main(void) \n{ \n    Derived d(4, 5); \n    d.fun(); \n    return 0; \n} \n"
  },
  {
    "path": "english/basic_content/abstract/interesting_facts5.cpp",
    "content": "\n/**\n * @file interesting_facts5.cpp\n * @brief 构造函数不能是虚函数，而析构函数可以是虚析构函数。\n * 例如：当基类指针指向派生类对象并删除对象时，我们可能希望调用适当的析构函数。如果析构函数不是虚拟的，则只能调用基类析构函数。\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n#include<iostream>\nusing namespace std;\n\nclass Base  {\n    public:\n        Base()    { cout << \"Constructor: Base\" << endl; }\n        virtual ~Base()   { cout << \"Destructor : Base\" << endl; }\n};\n\nclass Derived: public Base {\n    public:\n        Derived()   { cout << \"Constructor: Derived\" << endl; }\n        ~Derived()   { cout << \"Destructor : Derived\" << endl; }\n};\n\nint main()  {\n    Base *Var = new Derived();\n    delete Var;\n    return 0;\n}\n\n"
  },
  {
    "path": "english/basic_content/abstract/pure_virtual.cpp",
    "content": "/**\n * @file pure_virtual.cpp\n * @brief 纯虚函数：没有函数体的虚函数\n * 抽象类：包含纯虚函数的类\n * \n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include<iostream>\n\nusing namespace std;\nclass A\n{\nprivate:\n    int a;\npublic:\n    virtual void show()=0; // 纯虚函数\n};\n\n\nint main()\n{\n    /*\n     * 1. 抽象类只能作为基类来派生新类使用\n     * 2. 抽象类的指针和引用->由抽象类派生出来的类的对象！\n     */\n    A a; // error 抽象类，不能创建对象\n\n    A *a1; // ok 可以定义抽象类的指针\n\n    A *a2 = new A(); // error, A是抽象类，不能创建对象\n}\n"
  },
  {
    "path": "english/basic_content/abstract/test.cpp",
    "content": "/**\n * @file test.cpp\n * @brief C++中的纯虚函数(或抽象函数)是我们没有实现的虚函数！我们只需声明它!通过声明中赋值0来声明纯虚函数！\n * 纯虚函数：没有函数体的虚函数\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n\n\n/**\n * @brief 抽象类\n */\nclass Test \n{    \n    // Data members of class \npublic: \n    // Pure Virtual Function \n    virtual void show() = 0; \n\n    /* Other members */\n}; \n"
  },
  {
    "path": "english/basic_content/assert/README.md",
    "content": "# Things about assert\n\n## About Author：\n\n\n![](../img/wechat.jpg)\n\n\n\n## 1.First assertion case\n\nAssert, **is macro，rather than function**.\n\nAssert the prototype of a macro is defined in <assert.h>(C) or <cassert>(C++). If its condition is false or returns an error, program execution will terminate.\n\nYou can disable assert by defining macro `NDEBUG`, **But it needs to be at the beginning of the source code，before include <assert.h>.**\n\n```c\nvoid assert(int expression);\n```\n\n> Code Example：[assert.c](./assert.c)\n```c\n#include <stdio.h> \n#include <assert.h> \n\nint main() \n{ \n    int x = 7; \n\n    /*  Some big code in between and let's say x  \n    is accidentally changed to 9  */\n    x = 9; \n\n    // Programmer assumes x to be 7 in rest of the code \n    assert(x==7); \n\n    /* Rest of the code */\n\n    return 0; \n} \n```\nOutput：\n```c\nassert: assert.c:13: main: Assertion 'x==7' failed.\n```\n\n## 2.Assertion and normal error handling\n\n+ Assertions are mainly used to check for logically impossible situations.\n\n> For example, they can be used to check the state that code expects before it starts to run, or after the run is complete. Unlike normal error handling, assertions are usually disabled at run time.\n\n+ Ignore the assertion and add at the beginning of the code：\n```c++\n#define NDEBUG          // Adding this line，then assert will be disable\n```\n\n> Code Example：[ignore_assert.c](./ignore_assert.c)\n"
  },
  {
    "path": "english/basic_content/assert/assert.c",
    "content": "#include <stdio.h> \n#include <assert.h> \n\nint main() \n{ \n    int x = 7; \n\n    /*  Some big code in between and let's say x  \n     *         is accidentally changed to 9  */\n    x = 9; \n\n    // Programmer assumes x to be 7 in rest of the code \n    assert(x==7); \n\n    /* Rest of the code */\n\n    return 0; \n} \n"
  },
  {
    "path": "english/basic_content/assert/ignore_assert.c",
    "content": "/**\n * @file ignore_assert.c\n * @brief Ignore assertion\n * @author Light-City\n * @version v1\n * @date 2019-07-25\n */\n\n#define NDEBUG // ignore assertion\n\n#include<assert.h>\n\nint main(){\n    int x=7;\n    assert(x==5);\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/bit/README.md",
    "content": "## About Author：\n\n\n![](../img/wechat.jpg)\n\n## What is Bit field ？\n\n“ Bit field is a kind of data structure.Data can be stored compactly in bits, And allows the programmer to operate on the bits of this structure. One of the advantages of this data structure is that it can save storage space in data units, which is particularly important when programs need thousands of data units. The second advantage is that bit segments can easily access part of the contents of an integer value, which can simplify the program source code. The disadvantage of this data structure is that bit segment implementation depends on specific machines and systems, and different results may be obtained in different platforms, which leads to the fact that bit segments are not portable in nature\n\n- The layout of bit fields in memory is machine dependent\n- The type of bit field must be integer or enumeration type. The behavior of bit field in signed type depends on the implementation\n- The addressing operator (&) cannot be applied to a bit field, and no pointer can point to a bit field of a class\n\n## Bit field usage\n\nBit fields usually use struct declarations, which set the name of each bit field member and determine its width:\n\n```\nstruct bit_field_name\n{\n\ttype member_name : width;\n};\n```\n\n\n\n| Elements       | Description                                                  |\n| -------------- | ------------------------------------------------------------ |\n| bit_field_name | Bit field structure name                                     |\n| type           | must be int、signed int or unsigned int type                 |\n| member_name    |                                                              |\n| width          |                                                              |\n\nFor example, declare the following bit field:\n\n```\nstruct _PRCODE\n{\n\tunsigned int code1: 2;\n\tunsigned int cdde2: 2;\n\tunsigned int code3: 8;\n};\nstruct _PRCODE prcode;\n```\n\nThis definition makes' prcode 'contain two 2-bit fields and one 8-bit field. We can use the member operator of the structure to assign values to it\n\n```\nprcode.code1 = 0;\nprcode.code2 = 3;\nprocde.code3 = 102;\n```\n\nWhen assigning a value, it should be noted that the size of the value should not exceed the capacity of the bit field member，For example prcode.code3 Is a bit domain member of 8 bits. Its capacity is 2^8 = 256，Assignment range should be [0,255]。\n\n## Size and alignment of bit fields\n\n### Size of bit field\n\nFor example, the following bit field：\n\n```\nstruct box \n{\n\tunsigned int a: 1;\n\tunsigned int  : 3;\n\tunsigned int b: 4;\n};\n```\nThere is an unnamed bit field in the middle of the bit field structure, which occupies 3 bits and only plays a filling role and has no practical significance. The padding makes the structure use 8 bits in total. But C language uses unsigned int as the basic unit of bit field，Even if the only member of a structure is a bit field of 1 bit, the size of the structure is the same as that of an unsigned int.In some systems, the unsigned int is 16 bits, and in x86 systems it is 32 bits.For the following articles, the default value of unsigned int is 32 bits.\n\n### Alignment of bit fields\n\nA bit field member is not allowed to cross the boundary of two unsigned ints. If the total number of bits declared by a member exceeds the size of an unsigned int, the editor will automatically shift the bit field members to align them according to the boundary of the unsigned int\n\nFor example：\n\n```\nstruct stuff \n{\n\tunsigned int field1: 30;\n\tunsigned int field2: 4;\n\tunsigned int field3: 3;\n};\n```\n\n\n\n`field1` + `field2` = 34 Bits，beyond 32 Bits, Complier`field2` move to the next unsigned int unit. stuff.field1 and stuff.field2 will leave the 2 Bits space， stuff.field3 follow closely stuff.field2.The structure is now of size 2 * 32 = 64 Bits。\n\nThis hole can be filled with the unnamed bit field member mentioned earlier, or we can use an unnamed bit field member with a width of 0 to align the next field member with the next integer.\nFor example:\n\n```\nstruct stuff \n{\n\tunsigned int field1: 30;\n\tunsigned int       : 2;\n\tunsigned int field2: 4;\n\tunsigned int       : 0;\n\tunsigned int field3: 3; \n};\n```\n\n\n\nBetween stuff.field1 and stuff.field2 there is a 2 Bits space. Stuff.field3 will be stored in the next unsigned in. The size of this structure is 3 * 32 = 96 Bits。\n\nCode：[learn.cpp](learn.cpp)\n\n## Initialization of bit field and remapping of bit\n\n### Initialization\n\n\nThe initialization method of bit field is the same as that of ordinary structure. Here, two methods are listed as follows:\n\n```\nstruct stuff s1= {20,8,6};\n```\n\nOr directly assign values to the bit field members:\n\n```\nstruct stuff s1;\ns1.field1 = 20;\ns1.field2 = 8;\ns1.field3 = 4;\n```\n\n### Re-mapping of bit field\n\nDeclare a bit field with a size of 32 bits\n\n```\nstruct box {\n\tunsigned int ready:     2;\n\tunsigned int error:     2;\n\tunsigned int command:   4;\n\tunsigned int sector_no: 24;\n}b1;\n```\n\n#### Zeroing bit field by remapping\n\n```\nint* p = (int *) &b1;  // 将 \"位域结构体的地址\" 映射至 \"整形（int*) 的地址\" \n*p = 0;                // clear s1, set all members to zero\n```\n\n#### The 32 bits bit field is remapped to the unsigned int type by union\n\nLet's briefly introduce the alliance\n\n> \"Union\" is a special class and a data structure of construction type.Many different data types can be defined in a \"union\". Any kind of data defined by the \"union\" can be loaded into a variable described as the \"union\" type. These data share the same memory to save space\n>\n> There are some similarities between \"union\" and \"structure\". But there are essential differences between them.In a structure, each member has its own memory space. The total length of a structure variable is the sum of the length of each member (except for empty structure, and boundary adjustment is not considered).In Union, members share a certain amount of memory space, and the length of a union variable is equal to the longest length of each member.It should be noted that the so-called sharing does not mean that multiple members are loaded into a joint variable at the same time, but that the joint variable can be assigned any member value, but only one value can be assigned at a time.\n\nWe can declare the following Union:\n\n```\nunion u_box {\n  struct box st_box;\n  unsigned int ui_box;\n};\n```\n\n\n\nIn x86 system, unsigned int and box are 32 Bits, Through this combination, St_ Box and UI_ Box shares a piece of memory.Which bit in the specific bit field corresponds to which bit of the unsigned int depends on the compiler and hardware.Use union to return the bit field to zero. The code is as follows:\n\n```\nunion u_box u;\nu.ui_box = 0;\n```\n\n> From：<http://www.yuan-ji.me/C-C-%E4%BD%8D%E5%9F%9F-Bit-fields-%E5%AD%A6%E4%B9%A0%E5%BF%83%E5%BE%97/>\n"
  },
  {
    "path": "english/basic_content/bit/learn.cpp",
    "content": "#include<iostream>\n\nusing namespace std;\nstruct stuff \n{\n    unsigned int field1: 30;\n    unsigned int       : 2;\n    unsigned int field2: 4;\n    unsigned int       : 0;\n    unsigned int field3: 3; \n};\nint main(){\n    struct stuff s={1,3,5};\n    cout<<s.field1<<endl;\n    cout<<s.field2<<endl;\n    cout<<s.field3<<endl;\n    cout<<sizeof(s)<<endl;\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/c_poly/README.md",
    "content": "# Object oriented features of C + + implemented by C\n\n\n## About Author：\n\n\n![](../img/wechat.jpg)\n\n## 1.C++ \n\nPolymorphism in C ++ :In C++, we usually maintain a virtual function table. According to the assignment compatibility rule, we know that the pointer or reference of the parent class can point to the child class object\n\n\n\nIf the pointer or reference of a parent class calls the virtual function of the parent class, the pointer of the parent class will look up its function address in its own virtual function table.If the pointer or reference of the parent object points to an object of a subclass, and the subclass has overridden the virtual function of the parent class, the pointer will call the overridden virtual function of the subclass.\n\nCode Example:[c++_examp.cpp](./c++_examp.cpp)\n\n\n\n## 2.C Implement\n\n- Encapsulation\n\nThere is no concept of class class in C language. But with struct structure, we can use struct to simulate；\n\nEncapsulating properties and methods into structures using function pointers.\n\n\n\n- Inherit\n\nStructure nesting\n\n\n- Polymorphic\n\nClass and subclass methods have different function pointers\n\nThere is no member function in the structure of C language. How to realize the common function of parent structure and child structure? We can consider using function pointers to simulate. But there is a flaw in this approach：The function pointer between the parent and child does not point to the virtual function table maintained in C ++, but a piece of physical memory. If there are too many simulated functions, it will not be easy to maintain.\n\nIn order to simulate polymorphism, function pointer variables must be aligned(They are completely consistent in content and alignment of variables).Otherwise, the parent class pointer points to the child class object, and the operation crashes!\n\n\n\nCode example :[c_examp.c](./c_examp.c)\n"
  },
  {
    "path": "english/basic_content/c_poly/c++_examp.cpp",
    "content": "/**\n * @file c++_examp.cpp\n * @brief c++中的多态\n * @author 光城\n * @version v1\n * @date 2019-08-06\n */\n\n#include <iostream>\n\nusing namespace std;\nclass A\n{\n    public:\n        virtual void f()//Implement a virtual function\n        {\n            cout << \"Base A::f() \" << endl;\n        }\n};\n\nclass B:public A    // 必须为共有继承，否则后面调用不到，class默认为私有继承！\n{\n    public:\n        virtual void f()//Virtual function implementation, virtual keyword in subclass can not be appearence\n        {\n            cout << \"Derived B::f() \" << endl;\n        }\n};\n\n\nint main() \n{\n    A a;//Base class object\n    B b;//an object of derived type\n\n    A* pa = &a;//The parent class pointer points to the parent object\n    pa->f();//Call the function of the parent class\n    pa = &b; //The parent class pointer points to the subclass object, which is implemented in polymorphism\n\n\n    pa->f();//Call the function with the same name of the derived class\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/c_poly/c_examp.c",
    "content": "/**\n * @file c_examp.c\n * @brief C实现多态\n * @author 光城\n * @version v1\n * @date 2019-08-06\n */\n\n#include <stdio.h>\n\n/// Define a pointer of function\ntypedef void (*pf) ();\n\n/**\n * @brief parent class\n */ \ntypedef struct _A\n{\n    pf _f;\n}A;\n\n\n/**\n * @brief child class\n */\ntypedef struct _B\n{ \n    A _b; ///< The inheritance of the parent class can be realized by defining an object of the base class in the subclass \n}B;\n\nvoid FunA() \n{\n    printf(\"%s\\n\",\"Base A::fun()\");\n}\n\nvoid FunB() \n{\n    printf(\"%s\\n\",\"Derived B::fun()\");\n}\n\n\nint main() \n{\n    A a;\n    B b;\n\n    a._f = FunA;\n    b._b._f = FunB;\n\n    A *pa = &a;\n    pa->_f();\n    pa = (A *)&b;   /// The parent class pointer points to the object of the subclass. Because of the type mismatch, it needs to be forced\n    pa->_f();\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/const/README.md",
    "content": "\n\n\n## 1.The Definition Of const\n\nConst type is that people use type specifier **const** signiture type to demonstrate，const variables or objects can not be updated。\n\n## 2. Effect Of Const\n\n（1）define variable\n\n```\nconst int a=100;\n```\n\n(2) check type\n\nThe difference between const variable and define variable: ~~<u>**Constants have types, which can be checked by the compiler; the define macro definition has no data type, it is just a simple string replacement, and cannot be checked for security\n**</u>~~ Thanks for point this bug out.\n> https://github.com/Light-City/CPlusPlusThings/issues/5\n\nOnly variables of type integer or enumeration are defined by `const`. In other cases, it is only a 'const' qualified variable and should not be confused with constants.\n\n（3）prevent modification, protect and increase program robustness\n\n\n```\nvoid f(const int i){\n    i++; //error!\n}\n```\n（4）Save space and avoid unnecessary memory allocation\n\nFrom the compile point of view, the variables can be defined by const only give the corresponding memory address. Meanwhile the #define gives an immediate number.\n\n## 3.const object is the file local variable by default\n\n\n<p><font style=\"color:red\">Attention：not const will be set as extern by default.For the const variable to be accessible in other files, it must be explicitly specified as extern in the file\n\n</font></p>\n> Access of variables not modified by const in different files\n\n\n```\n// file1.cpp\nint ext\n// file2.cpp\n#include<iostream>\n/**\n * by 光城\n * compile: g++ -o file file2.cpp file1.cpp\n * execute: ./file\n */\nextern int ext;\nint main(){\n    std::cout<<(ext+10)<<std::endl;\n}\n\n```\n\nYou can find that variables that are not modified by const do not need to be explicitly declared by extern! Const constants need to be explicitly declared extern and need to be initialized! Because constants cannot be modified after they are defined, they must be initialized\n\n## 4.Define Variable\n\n```\nconst int b = 10;\nb = 0; // error: assignment of read-only variable ‘b’\nconst string s = \"helloworld\";\nconst int i,j=0 // error: uninitialized const ‘i’\n```\n\nThere are two errors above. First, B is a constant and cannot be changed! Second: I is a constant and must be initialized! (since constants cannot be modified after definition, they must be initialized. )\n\n## 5.Pointer and Const\nThere are four kinds of pointer related consts\n\n```\nconst char * a; //A pointer to a const object, or a pointer to a constant.\nchar const * a; //same as above\nchar * const a; //Const pointer to the type object. Or constant pointer, const pointer.\nconst char * const a; //Const pointer to const object\n\n```\nConclusion: If *const* is on the left of `*`, const is used to modify the variable that the pointer points to, that is, the pointer points to a constant. If *const* is on the right of `*`, then the const is used to modify the pointer itself, that is, the pointer itself is a constant.\n\nThe specific method is as follows:\n(1) pointer to const:\n```\nconst int *ptr;\n*ptr = 10; //error\n```\nptr is a pointer to a const object of type int. Const defines the int type, that is, the type of object PTR points to, not the PTR itself.Therefore, ptr do not need to be assigned an initial value. However, the value of the object in question cannot be modified by ptr. Besides, we can not use void `*` pointer to save the address of const object\n\n```\nconst int p = 10;\nconst void * vp = &p;\nvoid *vp = &p; //error\n\n```\n\nAnother key point is: we allow the address of a non const object to be assigned to a pointer to a const object.\n\nExample:\n```\nconst int *ptr;\nint val = 3;\nptr = &val; //ok\n```\nWe can't change the val value through the ptr pointer, even if it points to a non const object\n\n```\nint *ptr1 = &val;\n*ptr1=4;\ncout<<*ptr<<endl;\n```\nWe cannot modify the underlying object with a pointer to the const object\n\nHowever, if the pointer points to a non const object, the object it refers to can be modified in other ways.\n\n```\nint *ptr1 = &val;\n*ptr1=4;\ncout<<*ptr<<endl;\n```\nConclusion: For a pointer to a constant, the value of the object cannot be modified by the pointer. And You cannot use the void `*` pointer to save the address of the const object. You must use a pointer of type const void `*` to hold the address of the const object.\n\n(2) Constant pointer\n\nThe const pointer must be initialized and the value of the const pointer cannot be modified.\n\n```\n#include<iostream>\nusing namespace std;\nint main(){\n\n    int num=0;\n    int * const ptr=&num; //const指针必须初始化！且const指针的值不能修改\n    int * t = &num;\n    *t = 1;\n    cout<<*ptr<<endl;\n}\n\n```\n\nModify the value pointed by ptr pointer, which can be modified by non const pointer\n\nFinally, when the address of a const constant is assigned to ptr, an error will be reported because ptr points to a variable rather than const constant.\n\n```\n#include<iostream>\nusing namespace std;\nint main(){\n    const int num=0;\n    int * const ptr=&num; //error! const int* -> int*\n    cout<<*ptr<<endl;\n}\n\n```\nIf changed to const int `*`ptr or const int `*`const ptr, no errors have occurred.\n\n\n(3) Constant pointer to a constant\n\n```\nconst int p = 3;\nconst int * const ptr = &p; \n```\n\nPTR is a const pointer and then points to a const object of type int\n\n## 6. Using const in function\nReturn value of const decorated function\n\nThe meaning of const modifying ordinary variables and pointers is basically the same\n\n（1）const int\n\n```\nconst int func1();\n```\nIt doesn't make sense, because the parameter return itself is assigned to other variables\n\n（2）const int*\n\n```\nconst int* func2();\n\n```\nThe pointer points to the same content\n\n（3）int *const\n\n```\nint *const func2();\n```\nThe pointer itself is immutable\n\nConst modifier function parameters.\n\nThe passed parameters and pointers themselves are immutable and meaningless in the function.\n\n\n```\nvoid func(const int var); // parameters are immutable\nvoid func(int *const var); // point itself is immutable\n\n```\n\n(2) Parameter pointer refers to a constant and is immutable\n\n\n```\nvoid StringCopy(char *dst, const char *src);\n\n```\nSRC is the input parameter and DST is the output parameter. After adding const modifier to SRC, if the statement in the function body tries to change the contents of SRC, the compiler will point out the error\n\nThis is one of the effects of const.\n\n(3) The parameter is a reference. In order to increase efficiency and prevent modification at the same time\n\n```\nvoid func(const A &a)\n```\n\nFor parameters of Non internal data types, Functions such as void func (A, a) are bound to be inefficient. Because a temporary object of type A will be generated in the function body to copy parameter a, and the process of constructing, copying and destructing the temporary object will take time\n\nTo improve efficiency, you can change the function declaration to void func(A &a)，Because reference passing only uses aliases for parameters, there is no need to generate temporary objects\n\nvoid func(A &a) drawback:\nIt is possible to change the parameter a, which we do not expect.\nIt's easy to solve this problem by adding const modifier\n\nFinally, it will be : void func(const A &a).\n\n\nShould void func (int x) be rewritten to void func(const int &x),in order to improve efficiency? That operation was so needless.\nBecause the internal data type parameters do not exist in the process of construction and deconstruction, and replication is very fast.\n\nConclusion:For the input parameters of non internal data type, the mode of \"value passing\" should be changed to \"const reference passing\", in order to improve the efficiency. \nFor example, change void func (a a a) to void func (const A & A). It can not improve the efficiency and reduce the comprehensibility of the functionVoid func (int x) should not be changed to void func (const int & x).\n\nSo Two interview problems were solved:\n\n\n(1)If a function needs to pass in a pointer, do you need to add const to the pointer? What is the difference between adding const at different positions of the pointer\n\n(2)If the function to be written needs to pass in an instance of a complex type, what is the difference between the passed in value parameter or reference parameter, and when to add const to the passed reference parameter\n## 7.Using const in Class\nIn a class, any function that does not modify data members should be declared as const type. When writing const member functions, if you accidentally modify data members or call other non const member functions, the compiler will point out errors, which will undoubtedly improve the robustness of the program.The member function described by const is called constant member function\n\nOnly constant member functions are qualified to operate constant or constant objects. Member functions without const keyword cannot be used to operate constant objects\n\nFor const member variables in a class, they must be initialized through the initialization list:\n\n```\nclass Apple\n{\nprivate:\n    int people[100];\npublic:\n    Apple(int i); \n    const int apple_number;\n};\n\nApple::Apple(int i):apple_number(i)\n{\n\n}\n```\nConst objects can only access const member functions, while non const objects can access any member functions, including const member functions\n\nSuch as:\n```\n//apple.cpp\nclass Apple\n{\nprivate:\n    int people[100];\npublic:\n    Apple(int i); \n    const int apple_number;\n    void take(int num) const;\n    int add(int num);\n    int add(int num) const;\n    int getCount() const;\n\n};\n//main.cpp\n#include<iostream>\n#include\"apple.cpp\"\nusing namespace std;\n\nApple::Apple(int i):apple_number(i)\n{\n\n}\nint Apple::add(int num){\n    take(num);\n}\nint Apple::add(int num) const{\n    take(num);\n}\nvoid Apple::take(int num) const\n{\n    cout<<\"take func \"<<num<<endl;\n}\nint Apple::getCount() const\n{\n    take(1);\n//    add(); //error\n    return apple_number;\n}\nint main(){\n    Apple a(2);\n    cout<<a.getCount()<<endl;\n    a.add(10);\n    const Apple b(3);\n    b.add(100);\n    return 0;\n}\n//Compile： g++ -o main main.cpp apple.cpp\n//Result\ntake func 1\n2\ntake func 10\ntake func 100\n```\nThe getCount () method invokes a add method, and the add method is not const modifier, so run the wrong report. In other words, const objects can only access const member functions. Add method also calls const modified take method, which proves that non const objects can access any member function, including const member function.\nIn addition, we also see an overloaded function of add, which also outputs two results, indicating that const objects call const member functions by default\nIn addition to the above initialization const constant, we can also use the following methods:\n(1)Combining the definition of constants with static, i.e:\n```\nstatic const int apple_number\n```\n(2)Initialize outside:\n\n```\nconst int Apple::apple_number=10;\n```\nOf course, if you use C + + 11 to compile, you can directly define the initialization, you can write:\n```\nstatic const int apple_number=10;\nor\nconst int apple_number=10;\n```\nBoth are supported in C + + 11.\nWhen compiling, just add '- STD = C + + 11'.\nLet's talk about static briefly:\nIn C + +, static static member variables cannot be initialized inside a class.It is only declared inside the class, and the definition must be outside the class definition body, usually initialized in the class's implementation file\n\nDeclared in the class:\n\n```\nstatic int ap;\n```\nUse in class implementation file\n```\nint Apple::ap=666\n```\nFor this implement, C + + 11 cannot be declared and initialized.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "english/basic_content/const/class_const/c++11_example/apple.cpp",
    "content": "class Apple\n{\nprivate:\n    int people[100];\npublic:\n    Apple(int i);\n    //使用c++11标准编译\n    static const int apple_number=10;\n    //const int apple_number=10;\n    void take(int num) const;\n    int add(int num);\n    int add(int num) const;\n    int getCount() const;\n};\n\n"
  },
  {
    "path": "english/basic_content/const/class_const/c++11_example/main.cpp",
    "content": "#include<iostream>\n#include\"apple.cpp\"\nusing namespace std;\nApple::Apple(int i)\n{\n\n}\nint Apple::add(int num){\n    take(num);\n}\nint Apple::add(int num) const{\n    take(num);\n}\nvoid Apple::take(int num) const\n{\n    cout<<\"take func \"<<num<<endl;\n}\nint Apple::getCount() const\n{\n    take(1);\n//    add(); //error\n    return apple_number;\n}\nint main(){\n    Apple a(2);\n    cout<<a.getCount()<<endl;\n    a.add(10);\n    const Apple b(3);\n    b.add(100);\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/const/class_const/first_example/apple.cpp",
    "content": "class Apple\n{\nprivate:\n    int people[100];\npublic:\n    Apple(int i);\n    const int apple_number;\n    void take(int num) const;\n    int add(int num);\n    int add(int num) const;\n    int getCount() const;\n};\n\n"
  },
  {
    "path": "english/basic_content/const/class_const/first_example/main.cpp",
    "content": "#include<iostream>\n#include\"apple.cpp\"\nusing namespace std;\n\nApple::Apple(int i):apple_number(i)\n{\n\n}\nint Apple::add(int num){\n    take(num);\n}\nint Apple::add(int num) const{\n    take(num);\n}\nvoid Apple::take(int num) const\n{\n    cout<<\"take func \"<<num<<endl;\n}\nint Apple::getCount() const\n{\n    take(1);\n//    add(); //error\n    return apple_number;\n}\nint main(){\n    Apple a(2);\n    cout<<a.getCount()<<endl;\n    a.add(10);\n    const Apple b(3);\n    b.add(100);\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/const/class_const/overload_example/apple.cpp",
    "content": "class Apple\n{\nprivate:\n    int people[100];\npublic:\n    Apple(int i);\n    static const int apple_number;\n    void take(int num) const;\n    int add(int num);\n    int add(int num) const;\n    int getCount() const;\n};\n\n"
  },
  {
    "path": "english/basic_content/const/class_const/overload_example/main.cpp",
    "content": "#include<iostream>\n#include\"apple.cpp\"\nusing namespace std;\nconst int Apple::apple_number=10;\nApple::Apple(int i)\n{\n\n}\nint Apple::add(int num){\n    take(num);\n}\nint Apple::add(int num) const{\n    take(num);\n}\nvoid Apple::take(int num) const\n{\n    cout<<\"take func \"<<num<<endl;\n}\nint Apple::getCount() const\n{\n    take(1);\n//    add(); //error\n    return apple_number;\n}\nint main(){\n    Apple a(2);\n    cout<<a.getCount()<<endl;\n    a.add(10);\n    const Apple b(3);\n    b.add(100);\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/const/class_const/static_example/apple.cpp",
    "content": "class Apple\n{\nprivate:\n    int people[100];\npublic:\n    Apple(int i);\n    static int ap; //在类实现文件中定义并初始化\n    static const int apple_number;\n    void take(int num) const;\n    int add(int num);\n    int add(int num) const;\n    int getCount() const;\n};\n\n"
  },
  {
    "path": "english/basic_content/const/class_const/static_example/main.cpp",
    "content": "#include<iostream>\n#include\"apple.cpp\"\nusing namespace std;\nconst int Apple::apple_number=10;\nint Apple::ap=666;\nApple::Apple(int i)\n{\n\n}\nint Apple::add(int num){\n    take(num);\n}\nint Apple::add(int num) const{\n    take(num);\n}\nvoid Apple::take(int num) const\n{\n    cout<<\"take func \"<<num<<endl;\n}\nint Apple::getCount() const\n{\n    take(1);\n//    add(); //error\n    return apple_number;\n}\nint main(){\n    Apple a(2);\n    cout<<a.getCount()<<endl;\n    cout<<a.ap<<endl;\n    a.add(10);\n    const Apple b(3);\n    b.add(100);\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/const/const_function.cpp",
    "content": "#include<iostream>\nusing namespace std;\n\nvoid f(const int i){\n    i=10; // error: assignment of read-only parameter ‘i’\n    cout<<i<<endl;\n}\n\nint main(){\n    f(1);\n}\n"
  },
  {
    "path": "english/basic_content/const/const_num.cpp",
    "content": "#include<iostream>\nusing namespace std;\nint main(){\n    const int b = 10;\n    b = 0; //error\n    const string s = \"helloworld\";\n    const int i,j=0;\n}\n"
  },
  {
    "path": "english/basic_content/const/extern_const/const_file1.cpp",
    "content": "extern const int ext=12;\n"
  },
  {
    "path": "english/basic_content/const/extern_const/const_file2.cpp",
    "content": "#include<iostream>\n/**\n * by 光城\n * compile: g++ -o file const_file2.cpp const_file1.cpp\n * execute: ./file\n */\nextern const int ext;\nint main(){\n\n    std::cout<<ext<<std::endl;\n}\n"
  },
  {
    "path": "english/basic_content/const/extern_const/file1.cpp",
    "content": "int ext;\n"
  },
  {
    "path": "english/basic_content/const/extern_const/file2.cpp",
    "content": "#include<iostream>\n/**\n * by 光城\n * compile: g++ -o file file2.cpp file1.cpp\n * execute: ./file\n */\nextern int ext;\nint main(){\n\n    std::cout<<(ext+10)<<std::endl;\n}\n"
  },
  {
    "path": "english/basic_content/const/funciton_const/condition1/condition1.cpp",
    "content": "#include<iostream>\nusing namespace std;\n\n\nint main(){\n    const int *ptr;\n    *ptr=10; //error\n}\n"
  },
  {
    "path": "english/basic_content/const/funciton_const/condition1/condition2.cpp",
    "content": "#include<iostream>\nusing namespace std;\n\n\nint main(){\n    const int p = 10;\n    // const void *vp = &p;  // const pointer to const data\n    void *vp = (void*)&p; // ✅ C-style cast\n}\n"
  },
  {
    "path": "english/basic_content/const/funciton_const/condition1/condition3.cpp",
    "content": "#include<iostream>\nusing namespace std;\n\nint main(){\n    const int *ptr;\n    int val = 3;\n    ptr = &val; //ok\n    int *ptr1 = &val;\n    *ptr1=4;\n    cout<<*ptr<<endl;\n\n}\n"
  },
  {
    "path": "english/basic_content/const/funciton_const/condition2/condition1.cpp",
    "content": "#include<iostream>\nusing namespace std;\nint main(){\n\n    int num=0;\n    int * const ptr=&num; //const指针必须初始化！且const指针的值不能修改\n    int * t = &num;\n    *t = 1;\n    cout<<*ptr<<endl;\n}\n"
  },
  {
    "path": "english/basic_content/const/funciton_const/condition2/condition2.cpp",
    "content": "#include<iostream>\nusing namespace std;\nint main(){\n\n    const int num=0;\n    int * const ptr=&num; //error! const int* -> int*\n    cout<<*ptr<<endl;\n}\n"
  },
  {
    "path": "english/basic_content/const/funciton_const/condition2/condition3.cpp",
    "content": "#include<iostream>\nusing namespace std;\nint main(){\n\n    const int num=10;\n    const int * const ptr=&num; //error! const int* -> int*\n    cout<<*ptr<<endl;\n}\n"
  },
  {
    "path": "english/basic_content/const/funciton_const/condition3/condition1.cpp",
    "content": "#include<iostream>\nusing namespace std;\n\nint main(){\n\n    const int p = 3;\n    const int * const ptr = &p;\n    cout<<*ptr<<endl;\n\n}\n"
  },
  {
    "path": "english/basic_content/decltype/README.md",
    "content": "# Things about decltype\n\n## About Author：\n\n\n![](../img/wechat.jpg)\n\n## 1.Basic\nThe syntax of decltype is:\n\n```\ndecltype (expression)\n```\n\nBrackets are essential here.The function of decltype is \"the type of query expression\".Therefore, the effect of the above statement is to return the type of the expression expression.Note that decltype only \"queries\" the type of the expression and does not \"evaluate\" the expression.\n\n\n### 1.1 Deduce expression type\n\n```\nint i = 4;\ndecltype(i) a; //The result is int. The type of a is int\n```\n\n### 1.2 Used with using / typedef to define types.\n\n```c++\nusing size_t = decltype(sizeof(0));//sizeof(a)的返回值为size_t类型\nusing ptrdiff_t = decltype((int*)0 - (int*)0);\nusing nullptr_t = decltype(nullptr);\nvector<int >vec;\ntypedef decltype(vec.begin()) vectype;\nfor (vectype i = vec.begin; i != vec.end(); i++)\n{\n//...\n}\n```\n\nlike auto, improves the readability of the code.\n\n\n### 1.3 Reuse anonymous types\n\nIn C + +, we sometimes encounter some anonymous types, such as:\n\n```c++\nstruct \n{\n    int d ;\n    doubel b;\n}anon_s;\n```\n\nWith decltype, we can reuse this anonymous structure:\n```c++\ndecltype(anon_s) as ;//Defines an anonymous structure above\n```\n\n### 1.4 In generic programming, auto is used to trace the return value type of function\n\n\n```c++\ntemplate <typename T>\nauto multiply(T x, T y)->decltype(x*y)\n{\n\treturn x*y;\n}\n```\n\nCode：[decltype.cpp](decltype.cpp)\n\n## 2.Discriminant rules\n\n\n\nFor decltype (E), the results are affected by the following conditions:\nIf e is a marker expression or class member access expression without parentheses, decltype (E) of is the type of entity named by E.In addition, if e is an overloaded function, it will result in compilation errors.\nOtherwise, assume that the type of E is t, and if e is a dying value, then decltype (E) is t&&\nOtherwise, assume that the type of E is t, and if e is a dying value, then decltype (E) is t&&\nOtherwise, assuming that the type of E is t, decltype (E) is t.\n\nMarkers are defined by programmers except for keywords, literals and other tags that the compiler needs to use. The expression corresponding to a single marker is a marker expression. For example:\n\n```c++\nint arr[4]\n```\nThen arr is a marker expression，ranther than arr[3]+0\n\nExample：\n\n```c++\nint i = 4;\nint arr[5] = { 0 };\nint *ptr = arr;\nstruct S{ double d; }s ;\nvoid Overloaded(int);\nvoid Overloaded(char);// reload function\nint && RvalRef();\nconst bool Func(int);\n\n//Rule 1: derivation is its type\ndecltype (arr) var1; //int 标记符表达式\n\ndecltype (ptr) var2;//int *  标记符表达式\n\ndecltype(s.d) var3;//doubel 成员访问表达式\n\n//decltype(Overloaded) var4;//重载函数。编译错误。\n\n//规则二：将亡值。推导为类型的右值引用。\n\ndecltype (RvalRef()) var5 = 1;\n\n//规则三：Lvalue, derived as a reference to the type\n\ndecltype ((i))var6 = i;     //int&\n\ndecltype (true ? i : i) var7 = i; //int&  A conditional expression returns an lvalue\n\ndecltype (++i) var8 = i; //int&  ++i返回i的左值。\n\ndecltype(arr[5]) var9 = i;//int&. []操作返回左值\n\ndecltype(*ptr)var10 = i;//int& *操作返回左值\n\ndecltype(\"hello\")var11 = \"hello\"; //const char(&)[9]  字符串字面常量为左值，且为const左值。\n\n\n//Rule 4: if none of the above is true, this type is derived\n\ndecltype(1) var12;//const int\n\ndecltype(Func(1)) var13=true;//const bool\n\ndecltype(i++) var14 = i;//int i++返回右值\n```\n\nFrom：https://www.cnblogs.com/QG-whz/p/4952980.html\n"
  },
  {
    "path": "english/basic_content/decltype/decltype.cpp",
    "content": "/**\n * @file decltype.cpp\n * @brief g++ -o decltype decltype.cpp -std=c++11\n * @author 光城\n * @version v1\n * @date 2019-08-08\n */\n\n#include <iostream>\n#include <vector>\nusing namespace std;\n/**\n * 泛型编程中结合auto，用于追踪函数的返回值类型\n */\ntemplate <typename T>\n\nauto multiply(T x, T y)->decltype(x*y)\n{\n    return x*y;\n}\n\nint main()\n{\n    int nums[] = {1,2,3,4};\n    vector<int> vec(nums,nums+4);\n    vector<int>::iterator it;\n\n    for(it=vec.begin();it!=vec.end();it++)\n        cout<<*it<<\" \";\n    cout<<endl;\n\n\n    using nullptr_t = decltype(nullptr);\n    nullptr_t  nu;\n    int * p =NULL;\n    if(p==nu)\n        cout<<\"NULL\"<<endl;\n\n\n    typedef decltype(vec.begin()) vectype;\n\n    for(vectype i=vec.begin();i!=vec.end();i++)\n        cout<<*i<<\" \";\n    cout<<endl;\n\n    /**\n     * 匿名结构体\n     */\n    struct\n    {\n        int d ;\n        double b;\n    }anon_s;\n\n    decltype(anon_s) as; // 定义了一个上面匿名的结构体\n\n    cout<<multiply(11,2)<<endl;\n\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/enum/README.md",
    "content": "# From beginner to Master\n\n## About author：\n\n\n![](../img/wechat.jpg)\n\n\n## Traditional\n\n\nEnum has the following problems:\n\n- The scope is not limited, and it is easy to cause naming conflicts. For example：\n\n```c++\n#include <iostream>\nusing namespace std;\n\nenum Color {RED,BLUE};\nenum Feeling {EXCITED,BLUE};\n\nint main() \n{\n    return 0;\n}\n```\n- Implicit conversion to int\n\n- The actual type used to represent an enumerated variable cannot be explicitly specified, Therefore, forward declaration of enumeration types cannot be supported.\nImplementation：[tradition_color.cpp](tradition_color.cpp)\n\n## Classic Method\n\nA simple way to solve the problem of naming conflicts caused by unlimited scope is to prefix the enumeration variables. Change the above example to COLOR_BLUE and FEELING_BLUE。\n\nGenerally speaking, we usually prefix all constants for uniformity.But the code for defining enumeration variables is cumbersome.This may have to be done in the C program. But C++ coder do not like this method。Alternatives is namespace:\n```c++\nnamespace Color \n{\n    enum Type\n    {\n        RED=15,\n        YELLOW,\n        BLUE\n    };\n};\n```\n\nThen you can use `Color::Type c = Color::RED;` to define the new enumeration。If after`using namespace Color` ，the prefix can also be omitted to simplify the code.However, the scope closure provided by a namespace is not high because it can be subsequently extended.In large projects, it is still possible for different people to give different things the same enumeration type names.\n\nA more \"effective\" approach is to limit its scope with a class or struct.For example：The new variable is defined in the same way as in the namespace above. This way, you don't have to worry about the class being modified elsewhere.We use structs instead of classes because we want these constants to be publicly accessible.\n\n```c++\nstruct Color1\n{\n    enum Type\n    {\n        RED=102,\n        YELLOW,\n        BLUE\n    };\n};\n```\n\nImplementation：[classic_practice.cpp](classic_practice.cpp)\n\n## C++11 Enum class\n\nThe above approach solves the first problem, but it can not do anything for the latter two.Fortunately，C ++ 11 standard introduces enum class. It can solve the above problems.\n\n- The scope of the new enum is no longer global\n\n- Cannot be implicitly converted to another type\n\n\n```c++\n/**\n * @brief C++11 enum class\n * Equals to enum class Color2:int\n */\nenum class Color2\n{\n    RED=2,\n    YELLOW,\n    BLUE\n};\nColor2 c2 = Color2::RED;\ncout << static_cast<int>(c2) << endl; //！\n```\n\n- You can specify a specific type to store enum\n\n```c++\nenum class Color3:char;  // Forward statement\n\n// Definition\nenum class Color3:char \n{\n    RED='r',\n    BLUE\n};\nchar c3 = static_cast<char>(Color3::RED);\n```\n\nImplementation：[classic_practice.cpp](classic_practice.cpp)\n\n## Enum types in class\n\n\n\nSometimes we want certain constants to work only in classes. Because the macro constant defined by a is global, it can not achieve the purpose, so we want to use const to modify data members.The const data member does exist, but its meaning is not what we expected. \nData members are constants only for the lifetime of an objec. However, it is variable for the whole class, because the class can create multiple objects, and the values of const data members of different objects can be different.\n\n\n\nCannot be initialized in a class declaration const data memeber。The following usage is incorrect，Because the compiler does not know what the value of size is when the object of the class is not created.(c++11)\n\n```c++\nclass A \n{\n  const int SIZE = 100;   // Error，Attempt to initialize const data member in class declaration \n  int array[SIZE];  // Error，Unknown size \n}; \n```\n\nThis should be done in the initialization list of the class's constructor:\n\n```c++\nclass A \n{\n  A(int size);  // Constructor \n  const int SIZE ;\n}; \nA::A(int size) : SIZE(size)  // The definition of Struct\n{ \n\n} \nA  a(100); // The size of Object A is 100 \nA  b(200); // The size of Object B is 200 \n```\n\nHow can I establish constants that are constant throughout a class?\n\nIt should be implemented with enumeration constants in the class. Such as:\n\n```c++\nclass Person{\npublic:\n    typedef enum {\n        BOY = 0,\n        GIRL\n    }SexType;\n};\n//Access via Person::BOY or Person::GIRL.\n```\n\nEnum constants do not take up the storage space of the object . They are all evaluated at compile time\n\n\nDrawback of Enum：Its implied data type is an integer, the maximum is limited, and it cannot represent floating point.\n"
  },
  {
    "path": "english/basic_content/enum/classic_practice.cpp",
    "content": "/**\n * @file classic_practice.cpp\n * @brief g++ -o classic_practice classic_practice.cpp -std=c++11\n * @author 光城\n * @version v1\n * @date 2019-08-07\n */\n\n#include <iostream>\nusing namespace std;\n/**\n * @brief namespace解决作用域不受限\n */\nnamespace Color \n{\n    enum Type\n    {\n        RED=15,\n        YELLOW,\n        BLUE\n    };\n};\n\n/**\n * @brief 上述如果 using namespace Color 后，前缀还可以省去，使得代码简化。\n * 不过，因为命名空间是可以随后被扩充内容的，所以它提供的作用域封闭性不高。\n * 在大项目中，还是有可能不同人给不同的东西起同样的枚举类型名。\n * 更“有效”的办法是用一个类或结构体来限定其作用域。\n *\n * 定义新变量的方法和上面命名空间的相同。\n * 不过这样就不用担心类在别处被修改内容。\n * 这里用结构体而非类，一是因为本身希望这些常量可以公开访问，\n * 二是因为它只包含数据没有成员函数。\n */\nstruct Color1\n{\n    enum Type\n    {\n        RED=102,\n        YELLOW,\n        BLUE\n    };\n};\n\n/**\n * @brief C++11的枚举类\n * 下面等价于enum class Color2:int\n */\nenum class Color2\n{\n    RED=2,\n    YELLOW,\n    BLUE\n};\n\nenum class Color3:char;  // 前向声明\n\n// 定义\nenum class Color3:char \n{\n    RED='r',\n    BLUE\n};\n\nint main() \n{\n    // 定义新的枚举变量\n    Color::Type c = Color::RED;\n    cout<<c<<endl;  \n    /**\n     * 上述的另一种方法：\n     * using namespace Color; // 定义新的枚举变量\n     * Type c = RED;\n     */\n    Color1 c1;\n    cout<<c1.RED<<endl;\n\n    Color1::Type c11 = Color1::BLUE;\n    cout<<c11<<endl;\n    \n    Color2 c2 = Color2::RED;\n    cout << static_cast<int>(c2) << endl;\n    \n    char c3 = static_cast<char>(Color3::RED);\n    cout<<c3<<endl;\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/enum/tradition_color.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nenum Color {RED,BLUE};\nenum Feeling {EXCITED,BLUE};\n\nint main() \n{\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/explicit/README.md",
    "content": "# Things about explicit\n\n## About Author：\n\n\n![](../img/wechat.jpg)\n\n- explicit When decorating a constructor, you can prevent implicit conversion and copy initialization\n- explicit When modifying a conversion function, you can prevent implicit conversion, except for context conversion\n\n\nCode :[.explicit.cpp](./explicit.cpp)\n\nReference Link：\n> https://stackoverflow.com/questions/4600295/what-is-the-meaning-of-operator-bool-const\n"
  },
  {
    "path": "english/basic_content/explicit/explicit.cpp",
    "content": "#include <iostream>\n\nusing namespace std;\n\nstruct A\n{\n    A(int) { }\n    operator bool() const { return true; }\n};\n\nstruct B\n{\n    explicit B(int) {}\n    explicit operator bool() const { return true; }\n};\n\nvoid doA(A a) {}\n\nvoid doB(B b) {}\n\nint main()\n{\n    A a1(1);        // OK：直接初始化\n    A a2 = 1;        // OK：复制初始化\n    A a3{ 1 };        // OK：直接列表初始化\n    A a4 = { 1 };        // OK：复制列表初始化\n    A a5 = (A)1;        // OK：允许 static_cast 的显式转换 \n    doA(1);            // OK：允许从 int 到 A 的隐式转换\n    if (a1);        // OK：使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换\n    bool a6(a1);        // OK：使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换\n    bool a7 = a1;        // OK：使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换\n    bool a8 = static_cast<bool>(a1);  // OK ：static_cast 进行直接初始化\n\n    B b1(1);        // OK：直接初始化\n//    B b2 = 1;        // 错误：被 explicit 修饰构造函数的对象不可以复制初始化\n    B b3{ 1 };        // OK：直接列表初始化\n//    B b4 = { 1 };        // 错误：被 explicit 修饰构造函数的对象不可以复制列表初始化\n    B b5 = (B)1;        // OK：允许 static_cast 的显式转换\n//    doB(1);            // 错误：被 explicit 修饰构造函数的对象不可以从 int 到 B 的隐式转换\n    if (b1);        // OK：被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换\n    bool b6(b1);        // OK：被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换\n//    bool b7 = b1;        // 错误：被 explicit 修饰转换函数 B::operator bool() 的对象不可以隐式转换\n    bool b8 = static_cast<bool>(b1);  // OK：static_cast 进行直接初始化\n\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/extern/README.md",
    "content": "# extern \"C\"\n\n## About Author：\n\n\n![](../img/wechat.jpg)\n\n## 1. Compiler difference between C and C ++\n\nIn C + +, we often see extern \"C\" modifier function in the header file. What's the effect. Is a function defined in C language module for C + + link.\n\nAlthough C + + is compatible with C, the symbols generated by function compilation in C + + files are different from those generated by C language.Because C + + supports function overloading, the symbols generated by C + + function compilation have the information of function parameter type, while C does not.\n\n\nTake `int add(int a, int b)` for example. The C + + compiler generates the. O file, `add` becomes `add_int_int` and so on, while C would be like this `_add`, that is：For the same function, in C and C + +, the symbols generated after compilation are different.\n\nThis leads to a problem: if the function implemented in C + + is implemented in C language, an error will occur when compiling the link, indicating that the corresponding symbol cannot be found. At this time`extern \"C\"` works：Tell the linker to find its `_ C `language symbols such as `add` are not modified by C ++.\n\n## 2.C ++ calls C functions\n\nWhen referring to the header file of C, you need to add `extern \"C\"`\n\n```c++\n//add.h\n#ifndef ADD_H\n#define ADD_H\nint add(int x,int y);\n#endif\n\n//add.c\n#include \"add.h\"\n\nint add(int x,int y) {\n    return x+y;\n}\n\n//add.cpp\n#include <iostream>\n#include \"add.h\"\nusing namespace std;\nint main() {\n    add(2,3);\n    return 0;\n}\n```\n\nCompile：\n\n```\n//Generate add.o file\ngcc -c add.c\n```\n\nLink：\n\n```\ng++ add.cpp add.o -o main\n```\n\nWithout extern \"C\"：\n\n```c++\n> g++ add.cpp add.o -o main                                   \nadd.o：在函数‘main’中：\nadd.cpp:(.text+0x0): `main'被多次定义\n/tmp/ccH65yQF.o:add.cpp:(.text+0x0)：第一次在此定义\n/tmp/ccH65yQF.o：在函数‘main’中：\nadd.cpp:(.text+0xf)：对‘add(int, int)’未定义的引用\nadd.o：在函数‘main’中：\nadd.cpp:(.text+0xf)：对‘add(int, int)’未定义的引用\ncollect2: error: ld returned 1 exit status\n```\n\nWith extern \"C\"：\n\n`add.cpp`\n\n```c++\n#include <iostream>\nusing namespace std;\nextern \"C\" {\n    #include \"add.h\"\n}\nint main() {\n    add(2,3);\n    return 0;\n}\n```\nWhen compiling, you must pay attention to generating intermediate files add.o through GCC\n\n\n```\ngcc -c add.c \n```\n\nCompile：\n\n```\ng++ add.cpp add.o -o main\n```\n\nCode：\n\n- [add.h](extern_c++/add.h)\n\n- [add.c](extern_c++/add.c)\n\n- [add.cpp](extern_c++/add.cpp)\n\n## 2.Calling C++ function in C\n\n\n`extern \"C\"` It is a syntax error in C, which needs to be put in the C + + header file.\n\n```c\n// add.h\n#ifndef ADD_H\n#define ADD_H\nextern \"C\" {\n    int add(int x,int y);\n}\n#endif\n\n// add.cpp\n#include \"add.h\"\n\nint add(int x,int y) {\n    return x+y;\n}\n\n// add.c\nextern int add(int x,int y);\nint main() {\n    add(2,3);\n    return 0;\n}\n```\n\nCompile：\n\n```c\ng++ -c add.cpp\n```\n\nLink：\n\n```\ngcc add.c add.o -o main\n```\n\nCode：\n\n- [add.h](extern_c/add.h)\n\n- [add.c](extern_c/add.c)\n\n- [add.cpp](extern_c/add.cpp)\n\nIn the header file of C language, the external function can only be specified as extern type. The declaration of extern \"C\" is not supported in C language. There will be compiler syntax error when the. C file contains extern \"C\". Therefore, the use of external \"C\" is all placed in CPP program related files or its header files.\n\n\nThe following forms are summarized:\n\n（1）C + + calls C functions:\n\n```c++\n//xx.h\nextern int add(...)\n\n//xx.c\nint add(){\n    \n}\n\n//xx.cpp\nextern \"C\" {\n    #include \"xx.h\"\n}\n```\n\n（2）C calls C + + functions\n\n```c\n//xx.h\nextern \"C\"{\n    int add();\n}\n//xx.cpp\nint add(){\n    \n}\n//xx.c\nextern int add();\n```\n\n"
  },
  {
    "path": "english/basic_content/extern/extern_c/add.c",
    "content": "extern int add(int x,int y);\nint main() {\n    add(2,3);\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/extern/extern_c/add.cpp",
    "content": "#include \"add.h\"\n\nint add(int x,int y) {\n    return x+y;\n}\n"
  },
  {
    "path": "english/basic_content/extern/extern_c/add.h",
    "content": "#ifndef ADD_H\n#define ADD_H\nextern \"C\" {\n    int add(int x,int y);\n}\n#endif\n"
  },
  {
    "path": "english/basic_content/extern/extern_c++/add.c",
    "content": "#include \"add.h\"\n\nint add(int x,int y) {\n    return x+y;\n}\n"
  },
  {
    "path": "english/basic_content/extern/extern_c++/add.cpp",
    "content": "#include <iostream>\nusing namespace std;\nextern \"C\" {\n    #include \"add.h\"\n}\nint main() {\n    add(2,3);\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/extern/extern_c++/add.h",
    "content": "#ifndef ADD_H\n#define ADD_H\nextern int add(int x,int y);\n#endif\n"
  },
  {
    "path": "english/basic_content/friend/README.md",
    "content": "# Friend and Friend Class\n\n## About Author：\n\n\n![](../img/wechat.jpg)\n\n## 0.Summary\n\nFriends provide a mechanism for ordinary functions or class member functions to access private or protected members in another class.In other words, there are two forms of friends:\n\n（1）Friend Function：Ordinary functions access a private or protected member of a class.\n\n（2）Friend Class：Member functions in class a access private or protected members in class B\n\nAdvantages: improve the efficiency of the program.\n\nDisadvantages: it destroys the encapsulation of classes and the transparency of data.\nConclusion：\n- Access to private members\n- Breaking encapsulation\n- Friendship is not transitive\n- The unidirectionality of friend relationship\n- There are no restrictions on the form and number of friend declarations\n\n## 1.Friend function\n\nIt is declared in any region of the class declaration, and the definition is outside the class.\n\n```\nfriend <type><name>(<Parameter table>);\n```\n\nNote that the friend function is only a common function, not a class member function of this class. It can be called anywhere. In the friend function, private or protected members of the class can be accessed through the object name.\n\nCode：[friend_func.cpp](friend_func.cpp)\n\n```c++\n#include <iostream>\n\nusing namespace std;\n\nclass A\n{\npublic:\n    A(int _a):a(_a){};\n    friend int geta(A &ca);  ///< Friend function\nprivate:\n    int a;\n};\n\nint geta(A &ca) \n{\n    return ca.a;\n}\n\nint main()\n{\n    A a(3);    \n    cout<<geta(a)<<endl;\n\n    return 0;\n}\n```\n\n## 2.Friend Class\nThe declaration of a friend class is in the declaration of the class, and the implementation is outside the class.\n\n```\nfriend class <friend class name>;\n```\n\nClass B is a friend of class A, so class B can directly access private members of A.\nCode：[friend_class.cpp](friend_class.cpp)\n```c++\n#include <iostream>\n\nusing namespace std;\n\nclass A\n{\npublic:\n    A(int _a):a(_a){};\n    friend class B;\nprivate:\n    int a;\n};\n\nclass B\n{\npublic:\n    int getb(A ca) {\n        return  ca.a; \n    };\n};\n\nint main() \n{\n    A a(3);\n    B b;\n    cout<<b.getb(a)<<endl;\n    return 0;\n}\n```\n\n\n## 3.Attention\n- Friendship has no inheritance\nIf class B is a friend of class A and class C inherits from Class A, then friend class B cannot directly access private or protected members of class C.\n- Friendship is not transitive\nIf class B is a friend of class A and class C is a friend of class B, then friend class C cannot directly access private or protected members of class A, that is, there is no such relationship as \"friend of friend\".\n"
  },
  {
    "path": "english/basic_content/friend/friend_class.cpp",
    "content": "#include <iostream>\n\nusing namespace std;\n\nclass A\n{\npublic:\n    A(int _a):a(_a){};\n    friend class B;\nprivate:\n    int a;\n};\n\nclass B\n{\npublic:\n    int getb(A ca) {\n        return  ca.a; \n    };\n};\n\nint main() \n{\n    A a(3);\n    B b;\n    cout<<b.getb(a)<<endl;\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/friend/friend_func.cpp",
    "content": "/**\n * @file friend_func.cpp\n * @brief 友元函数\n * @author 光城\n * @version v1\n * @date 2019-08-06\n */\n\n#include <iostream>\n\nusing namespace std;\n\nclass A\n{\npublic:\n    A(int _a):a(_a){};\n    friend int geta(A &ca);  ///< 友元函数\nprivate:\n    int a;\n};\n\nint geta(A &ca) \n{\n    return ca.a;\n}\n\nint main()\n{\n    A a(3);    \n    cout<<geta(a)<<endl;\n\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/func_pointer/func_pointer.cpp",
    "content": "/**\n * @file func_pointer.cpp\n * @brief 函数指针的使用！\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include<iostream>\nusing namespace std;\n\n/**\n * @brief 定义了一个变量pFun，这个变量是个指针，指向返回值和参数都是空的函数的指针！\n */\nvoid (*pFun)(int); \n\n/**\n * @brief 代表一种新类型，不是变量！所以与上述的pFun不一样！\n */\ntypedef void (*func)(void);  \n\nvoid myfunc(void)\n{\n    cout<<\"asda\"<<endl;\n}\n\nvoid glFun(int a){ cout<<a<<endl;} \nint main(){\n    func pfun = myfunc;/*赋值*/  \n    pfun();/*调用*/  \n    pFun = glFun; \n    (*pFun)(2); \n}\n"
  },
  {
    "path": "english/basic_content/inline/README.md",
    "content": "# Story About Inline\n\n## About Author：\n\n\n\n![](../img/wechat.jpg)\n\n## 1.Inline in Class\n\nDeclaration method in header file\n\n\n```c++\n\nclass A\n{\npublic:\n    void f1(int x); \n\n    /**\n     * @brief The function defined in the class is an implicit inline function. If you want to be an inline function, you must add the inline keyword at the implementation (definition)\n     *\n     * @param x\n     * @param y\n     */\n    void Foo(int x,int y) ///< The definition is implicit inline function\n    {\n    \n    };\n    void f1(int x); ///< To be an inline function after declaration, you must add the inline keyword to the definition  \n};\n```\n\nThe inline function is defined in the implementation file:\n\n\n```c++\n#include <iostream>\n#include \"inline.h\"\n\nusing namespace std;\n\n/**\n * @brief To work, inline should be placed with function definition. Inline is a kind of \"keyword for implementation, not for declaration\"\n *\n * @param x\n * @param y\n *\n * @return \n */\nint Foo(int x,int y);  // Function declaration\n\n\ninline int Foo(int x,int y) // Function definition\n{\n    return x+y;\n}\n\n// It is recommended to add the keyword \"inline\" to the definition！\ninline void A::f1(int x){\n\n}\n\nint main()\n{\n\n    \n    cout<<Foo(1,2)<<endl;\n\n}\n/**\n * Compiler's processing steps for inline function\n * Copy the body of the inline function to the call point of the inline function;\n * Allocate memory space for local variables in the inline function used\n * The input parameters and return values of the inline function are mapped to the local variable space of the calling method\n * If the inline function has multiple return points, convert it to a branch at the end of the inline function code block (using goto)\n */\n\n```\n\nInline can improve the efficiency of functions, but not all functions are defined as inline functions！\n\n-If the execution time of the code in the function body is higher than the cost of function call, the efficiency of receiving goods will be less！\n\n- On the other hand, every call of inline function will copy the code, which will increase the total code of the program and consume more memory space\n\nInline is not suitable for the following situations：\n\n（1）If the code in the function body is long, it will lead to high memory consumption\n\n（2）If there is a loop in the function body, it takes more time to execute the code in the function body than the cost of the function call。\n\n## 2.Could virtual be inline function?\n\n- Virtual functions can be inline functions, which can modify virtual functions, but cannot be inlined when they are polymorphic\n- Inline means that the compiler is advised to inline at compile time. However, due to the polymorphism of virtual functions, the compiler cannot know which code to call at runtime. Therefore, when the virtual function is polymorphic (runtime), it cannot be inlined.\n- `inline virtual` The only time you can inline is when the compiler knows which class the object is calling（such as: `Base::who()`），This only happens if the compiler has an actual object instead of a pointer or reference to the object.\n\n```c++\n#include <iostream>  \nusing namespace std;\nclass Base\n{\npublic:\n    inline virtual void who()\n    {\n        cout << \"I am Base\\n\";\n    }\n    virtual ~Base() {}\n};\nclass Derived : public Base\n{\npublic:\n    inline void who()  //\n    {\n        cout << \"I am Derived\\n\";\n    }\n};\n\nint main()\n{\n    // \n    Base b;\n    b.who();\n\n    // \n    Base *ptr = new Derived();\n    ptr->who();\n\n    // \n    delete ptr;\n    ptr = nullptr;\n\n    \n    return 0;\n} \n```\n\n\n\n"
  },
  {
    "path": "english/basic_content/inline/inline.cpp",
    "content": "#include <iostream>\n#include \"inline.h\"\n\n\n\nusing namespace std;\n\n/**\n * @brief inline要起作用,inline要与函数定义放在一起,inline是一种“用于实现的关键字,而不是用于声明的关键字”\n *\n * @param x\n * @param y\n *\n * @return \n */\nint Foo(int x,int y);  // 函数声明\ninline int Foo(int x,int y) // 函数定义\n{\n    return x+y;\n}\n\n\n\n// 定义处加inline关键字，推荐这种写法！\ninline void A::f1(int x){\n\n\n}\n\n\n\n/**\n * @brief 内联能提高函数效率，但并不是所有的函数都定义成内联函数！内联是以代码膨胀(复制)为代价，仅仅省去了函数调用的开销，从而提高函数的执行效率。\n * 如果执行函数体内代码的时间相比于函数调用的开销较大，那么效率的收货会更少！另一方面，每一处内联函数的调用都要复制代码，将使程序的总代码量增大，消耗更多的内存空间。\n * 以下情况不宜用内联：\n *  （1） 如果函数体内的代码比较长，使得内联将导致内存消耗代价比较高。\n *   (2)  如果函数体内出现循环，那么执行函数体内代码的时间要比函数调用的开销大。\n *\n * @return \n */\nint main()\n{\n\n    \n    cout<<Foo(1,2)<<endl;\n\n}\n/**\n * 编译器对 inline 函数的处理步骤\n * 将 inline 函数体复制到 inline 函数调用点处；\n * 为所用 inline 函数中的局部变量分配内存空间；\n * 将 inline 函数的的输入参数和返回值映射到调用方法的局部变量空间中；\n * 如果 inline 函数有多个返回点，将其转变为 inline 函数代码块末尾的分支（使用 GOTO）。\n */\n\n\n\n"
  },
  {
    "path": "english/basic_content/inline/inline.h",
    "content": "\nclass A\n{\npublic:\n    void f1(int x); \n\n    /**\n     * @brief 类中定义了的函数是隐式内联函数,声明要想成为内联函数，必须在实现处(定义处)加inline关键字。\n     *\n     * @param x\n     * @param y\n     */\n    void Foo(int x,int y) ///< 定义即隐式内联函数！\n    {\n    \n    };\n    void f1(int x); ///< 声明后，要想成为内联函数，必须在定义处加inline关键字。\n    \n};\n"
  },
  {
    "path": "english/basic_content/inline/inline_virtual.cpp",
    "content": "#include <iostream>  \nusing namespace std;\nclass Base\n{\n    public:\n        inline virtual void who()\n        {\n            cout << \"I am Base\\n\";\n        }\n        virtual ~Base() {}\n};\nclass Derived : public Base\n{\n    public:\n        inline void who()  // 不写inline时隐式内联\n        {\n            cout << \"I am Derived\\n\";\n        }\n};\n\nint main()\n{\n    // 此处的虚函数 who()，是通过类（Base）的具体对象（b）来调用的，编译期间就能确定了，所以它可以是内联的，但最终是否内联取决于编译器。 \n    Base b;\n    b.who();\n\n    // 此处的虚函数是通过指针调用的，呈现多态性，需要在运行时期间才能确定，所以不能为内联。  \n    Base *ptr = new Derived();\n    ptr->who();\n\n    // 因为Base有虚析构函数（virtual ~Base() {}），所以 delete 时，会先调用派生类（Derived）析构函数，再调用基类（Base）析构函数，防止内存泄漏。\n    delete ptr;\n\n    return 0;\n} \n"
  },
  {
    "path": "english/basic_content/macro/README.md",
    "content": "# Story about Macro\n\n\n\n![](../img/wechat.jpg)\n\n## 1.The macro contains special symbols\n\nSeveral type：`#`，`##`，`\\`\n\n### 1.1 String operator（#）\n\n**Using a # before macro parameter,The preprocessor converts this parameter into an array of characters**，In other words：**# is “stringlize”，The#, which appears in the macro definition, is to convert the following parameter into a string\n\n**。\n\n**Attention：It can only be used in macro definitions that have passed in parameters, and must be placed before the parameter name in the macro definition body.**\n\nFor example：\n\n```c++\n#define exp(s) printf(\"test s is:%s\\n\",s)\n#define exp1(s) printf(\"test s is:%s\\n\",#s)\n#define exp2(s) #s \nint main() {\n    exp(\"hello\");\n    exp1(hello);\n\n    string str = exp2(   bac );\n    cout<<str<<\" \"<<str.size()<<endl;\n    /**\n     * Ignore spaces before and after the passed in parameter name\n     */\n    string str1 = exp2( asda  bac );\n    /**\n     * 当传入参数名间存在空格时，编译器将会自动连接各个子字符串，\n     * 用每个子字符串之间以一个空格连接，忽略剩余空格。\n     */\n    cout<<str1<<\" \"<<str1.size()<<endl;\n    return 0;\n}\n```\n\nThe above code gives the basic use and space handling rules，The space handling rules are as follows：\n\n- Ignore spaces before and after the passed in parameter name\n\n```c++\nstring str = exp2(   bac );\ncout<<str<<\" \"<<str.size()<<endl;\n```\n\nOutput：\n\n```\nbac 3\n```\n\n- When there are spaces between the input parameter names, the compiler will automatically connect each substring with a space between each substring, ignoring the remaining spaces.\n\n```c++\nstring str1 = exp2( asda  bac );\ncout<<str1<<\" \"<<str1.size()<<endl;\n```\n\nOutput：\n\n```\nasda bac 8\n```\n\n### 1.2 Symbolic join operator（##）\n\n**“##” It's a separate connection. Its function is to separate and then force the connection.Converts multiple parameters defined by a macro to an actual parameter name.**\n\nAttention：\n\n**（1）When use ## connecting parameters，##The space before and after is optional**\n\n**（2）Actual parameter name after connection，Must be an actual parameter name or a macro definition known to the compiler**\n\n**（3）If ## the parameter itself is a macro，## will prevent the macro from expanding.**\n\nex：\n\n```c++\n\n#define expA(s) printf(\"前缀加上后的字符串为:%s\\n\",gc_##s)  //gc_s必须存在\n// 注意事项2\n#define expB(s) printf(\"前缀加上后的字符串为:%s\\n\",gc_  ##  s)  //gc_s必须存在\n// 注意事项1\n#define gc_hello1 \"I am gc_hello1\"\nint main() {\n    // 注意事项1\n    const char * gc_hello = \"I am gc_hello\";\n    expA(hello);\n    expB(hello1);\n}\n```\n\n### 1.3 Continuation operator（\\） \n\n**When the defined macro cannot be expressed completely in one line, you can use \"\\\" to indicate the next line to continue the macro definition **\n\n**Leave a space before \\ **\n\n```c++\n#define MAX(a,b) ((a)>(b) ? (a) \\\n   :(b))  \nint main() {\n    int max_val = MAX(3,6);\n    cout<<max_val<<endl;\n}\n```\n\nFrom：[sig_examp.cpp](sig_examp.cpp)\n\n## 2.do{...}while(0)\n\n### 2.1 Avoid semantic misinterpretation\n\nSuch as ：\n\n```\n#define fun() f1();f2();\nif(a>0)\n\tfun()\n```\n\nWhen this macro is expanded, it will be：\n\n```\nif(a>0)\n\tf1();\n\tf2();\n```\n\n\nIn order to solve this problem, when writing code, usually can adopt\n\n`{}`。\n\nex：\n\n```c++\n#define fun() {f1();f2();}\nif(a>0)\n\tfun();\n// 宏展开\nif(a>0)\n{\n    f1();\n    f2();\n};\n```\n\nHowever, you will find that there is a semicolon after the macro is expanded, so the actual syntax is not correct.(Although the compiler runs well, there is no semicolon).\n\n### 2.2 Avoid using goto to control flow\n\nIn some functions, we may need to do some cleaning before the return statement, such as releasing the memory space requested by malloc at the beginning of the function. Using goto is always a simple method:\n\n\n```c++\nint f() {\n    int *p = (int *)malloc(sizeof(int));\n    *p = 10; \n    cout<<*p<<endl;\n#ifndef DEBUG\n    int error=1;\n#endif\n    if(error)\n        goto END;\n    // dosomething\nEND:\n    cout<<\"free\"<<endl;\n    free(p);\n    return 0;\n}\n```\n\nHowever, because go to does not conform to the structure of software engineering and may make the code difficult to understand, many people do not advocate using goto. At this time, we can use do {...} while (0) to do the same thing\n\n\n```c++\nint ff() {\n    int *p = (int *)malloc(sizeof(int));\n    *p = 10; \n    cout<<*p<<endl;\n    do{ \n#ifndef DEBUG\n        int error=1;\n#endif\n        if(error)\n            break;\n        //dosomething\n    }while(0);\n    cout<<\"free\"<<endl;\n    free(p);\n    return 0;\n}\n```\n\nThe main part of function using do{...}while(0),using break instead of goto.The subsequent cleaning is after while. Now we can achieve the same effect, and the readability and maintainability of the code are better than the goto code above\n\n### 2.3 Avoid warnings caused by macros\n\n\nDue to the limitation of different architectures in the kernel，Empty macros are often used. While compiling，These empty macros give warning. In order to avoid warning，we could use do{...}while(0) to define empty macro：\n\n```\n#define EMPTYMICRO do{}while(0)\n```\n\n### 2.4 **Define a single function block to perform complex operations**\n\nIf you have a complex function. There are many variables，And you don't want to add new functions。You could use do {...}while(0)，Write your code in it. You can define variables without considering the repetition of variable names and functions.\nThis should mean that a variable is used in multiple place(But the meaning of each is different)，We can narrow down the scope in each do while，for example：\n\n```c++\nint fc()\n{\n    int k1 = 10;\n    cout<<k1<<endl;\n    do{\n        int k1 = 100;\n        cout<<k1<<endl;\n    }while(0);\n    cout<<k1<<endl;\n}\n```\n\nFrom：[do_while.cpp](do_while.cpp)\n\nArticle：<https://www.cnblogs.com/lizhenghn/p/3674430.html>\n\n"
  },
  {
    "path": "english/basic_content/macro/do_while.cpp",
    "content": "#include <iostream>\n#include <malloc.h>\n\nusing namespace std;\n#define f1() cout<<\"f1()\"<<endl;\n#define f2() cout<<\"f2()\"<<endl;\n\n#define fun() {f1();f2();}\n#define fun1() \\\n    do{ \\\n        f1();\\\n        f2();\\\n    }while(0)\n\nint f() {\n    int *p = (int *)malloc(sizeof(int));\n    *p = 10;\n    cout<<*p<<endl;\n#ifndef DEBUG\n    int error=1;\n#endif\n    if(error)\n        goto END;\n\n    // dosomething\nEND:\n    cout<<\"free\"<<endl;\n    free(p);\n    return 0;\n}\n\nint ff() {\n\n    int *p = (int *)malloc(sizeof(int));\n    *p = 10;\n    cout<<*p<<endl;\n\n    do{\n#ifndef DEBUG\n        int error=1;\n#endif\n        if(error)\n            break;\n        //dosomething\n    }while(0);\n\n    cout<<\"free\"<<endl;\n    free(p);\n    return 0;\n}\n\n\nint fc()\n{\n    int k1 = 10;\n    cout<<k1<<endl;\n    do{\n        int k1 = 100;\n        cout<<k1<<endl;\n    }while(0);    \n    cout<<k1<<endl;\n}\n\nint main() {\n\n    if(1>0)\n        fun();\n\n    if(2>0)\n        fun1();\n\n    f();\n    ff();\n    fc();\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/macro/sig_examp.cpp",
    "content": "#include <iostream>\n#include <string>\n#include <cstring>\n#include <stdio.h>\n\nusing namespace std;\n\n///===========================================\n/**\n * (#)字符串操作符\n */\n///===========================================\n#define exp(s) printf(\"test s is:%s\\n\",s)\n#define exp1(s) printf(\"test s is:%s\\n\",#s)\n\n#define exp2(s) #s \n\n\n///===========================================\n/**\n *（##）符号连接操作符\n */\n///===========================================\n#define expA(s) printf(\"前缀加上后的字符串为:%s\\n\",gc_##s)  //gc_s必须存在\n\n#define expB(s) printf(\"前缀加上后的字符串为:%s\\n\",gc_  ##  s)  //gc_s必须存在\n\n#define gc_hello1 \"I am gc_hello1\"\n\n\n///===========================================\n/**\n * (\\)续行操作符\n */\n///===========================================\n#define MAX(a,b) ((a)>(b) ? (a) \\\n   :(b))  \n\nint main() { \n    ///===========================================\n    /**\n     * (#)字符串操作符\n     */\n    ///===========================================\n    exp(\"hello\");\n    exp1(hello);\n\n    string str = exp2(   bac );\n    cout<<str<<\" \"<<str.size()<<endl;\n    /**\n     * 忽略传入参数名前面和后面的空格。\n     */\n    string str1 = exp2( asda  bac );\n    /**\n     * 当传入参数名间存在空格时，编译器将会自动连接各个子字符串，\n     * 用每个子字符串之间以一个空格连接，忽略剩余空格。\n     */\n    cout<<str1<<\" \"<<str1.size()<<endl;\n\n    ///===========================================\n    /**\n     * (#)字符串操作符\n     */\n    ///===========================================\n\n    const char * gc_hello = \"I am gc_hello\";\n    expA(hello);\n    expB(hello1);\n\n    char var1_p[20];\n    char var2_p[20];\n\n    // 连接后的实际参数名赋值\n    strcpy(var1_p, \"aaaa\");\n    strcpy(var2_p, \"bbbb\");\n\n\n    ///===========================================\n    /**\n     * (\\)续行操作符\n     */\n    ///===========================================\n    int max_val = MAX(3,6);\n    cout<<max_val<<endl;\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/maohao/README.md",
    "content": "# :: Story about range resolution operators\n\n## Abput Author：\n\n\n![](../img/wechat.jpg)\n\n- Global scope（::name）：Used before a type name (class, class member, member function, variable, etc.), indicates that the scope is a global namespace\n- Class scope（class::name）：The scope used to represent the specified type is class specific\n- Namespace scope（namespace::name）: The scope used to represent the specified type is a namespace specific\n\nCode ：[maohao.cpp](maohao.cpp)\n\n"
  },
  {
    "path": "english/basic_content/maohao/README.md~",
    "content": "# :: 范围解析运算符那些事\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n- 全局作用域符（::name）：用于类型名称（类、类成员、成员函数、变量等）前，表示作用域为全局命名空间\n- 类作用域符（class::name）：用于表示指定类型的作用域范围是具体某个类的\n- 命名空间作用域符（namespace::name）:用于表示指定类型的作用域范围是具体某个命名空间的\n\n具体代码见：[::.cpp](::.cpp)\n\n"
  },
  {
    "path": "english/basic_content/maohao/maohao.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nint count=0;    // 全局(::)的count\n\nclass A {\npublic:\n    static int count;  // 类A的count (A::count)\n\n};\n// 静态变量必须在此处定义\nint A::count;\nint main() {\n    ::count=1; // 设置全局的count为1\n    A::count=5; // 设置类A的count为2\n    cout<<A::count<<endl;\n//    int count=3; // 局部count\n//    count=4;     // 设置局部的count为4\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/pointer_refer/README.md",
    "content": "# Reference and Pointer\n\n## About Author：\n\n\n![](../img/wechat.jpg)\n\n## 1.References and pointers\n\nConclusion：\n\n| Reference         | Pointer              |\n| ------------ | ------------              |\n| Must be initialized   | No initialization is allowed    |\n| cannot be empty     | could be empty     |\n| Target cannot be changed | could change target   |\n\n> References must be initialized, and pointers can be uninitialized\n\nWhen we define a reference, we must specify an initial value for it, but the pointer does not\n\n```c++\nint &r;    //Illegal, no initialization reference\nint *p;    //It is legal, but p is a wild pointer. You should be careful when using it\n```\n\n> Reference cannot be null and pointer can be null\n\nSince the reference cannot be null, we do not need to test its validity when using a reference. When using a pointer, we need to first judge whether the pointer is a null pointer, otherwise it may cause the program to crash.\n```c++\nvoid test_p(int* p)\n{\n  \tif(p != null_ptr)    //\n    \t*p = 3;\n    return;\n}\nvoid test_r(int& r)\n{\n    r = 3;    //\n    return;\n}\n```\n\n> References cannot change targets\n\n\n\nThe pointer can be changed at any time, but the reference can only point to the object pointed to during initialization, and cannot be changed.\n\n\n```\nint a = 1;\nint b = 2;\n\nint &r = a;    //\nint *p = &a;   //\n\np = &b;        //\nr = b;         //引\n```\n\n## 2.Reference\n\n#### lvalue reference\n\nGeneral reference, which generally represents the identity of an object.\n\n\n#### rvalue reference\n\n\n\nAn R-value reference is a reference that must be bound to an R-value (a temporary object, an object to be destroyed), and generally represents the value of an object.\n\n\nAn R-value reference is a reference that must be bound to an R-value (a temporary object, an object to be destroyed), and generally represents the value of an object.\n\n- It eliminates unnecessary copy of objects when two objects interact, saves operation storage resources and improves efficiency\n- It can define generic functions more concisely and clearly\n\n#### Reference collapse\n\n- `X& &`、`X& &&`、`X&& &` can be folded into `X&`\n- `X&& &&` can be floded into `X&&`\n\nThe reference of C ++ **At the same time, it improves the security of memory operation and the beauty of semantics**。For example, the mandatory requirement of reference must be initialized, so that we don't have to judge whether the reference is empty when using the reference, which makes the code more concise and elegant, and avoids the situation of pointer flying all over the sky. In addition to this scenario, references are used for the following two scenarios：\n\n> Reference type parameter\n\nIn general, we use const reference parameter as a read-only formal parameter. In this case, we can not only avoid parameter copy, but also get the same call method as value passing parameter.\n\n```c++\nvoid test(const vector<int> &data)\n{\n    //...\n}\nint main()\n{\n  \tvector<int> data{1,2,3,4,5,6,7,8};\n    test(data);\n}\n```\n\n> Reference type return value\n\nC++ Provides the ability to overload operators.The syntax of the overloaded operator is the same as that of the original operator.An example is the operator [] operator, which generally needs to return a reference object in order to be modified correctly.\n\n```c++\nvector<int> v(10);\nv[5] = 10;    //[]Operator returns the reference, and then the corresponding element of vector can be modified\n              //If[] operator do not return a reference but a pointer, the assignment statement needs to be written like this\n*v[5] = 10;   // This way of writing is totally inconsistent with our understanding of the call of [], which is easy to be misunderstood\n```\n\n## 3.Performance gap between pointer and reference\n\nIs there a performance gap between pointers and references？This kind of problem needs to enter the assembly level to have a look. Let's first write a test1 function, which uses pointers for parameter passing：\n\n```c++\nvoid test1(int* p)\n{\n    *p = 3;    //此处应该首先判断p是否为空，为了测试的需要，此处我们没加。\n    return;\n}\n```\n\nThe assembly code corresponding to this code segment is as follows：\n\n```c++\n(gdb) disassemble \nDump of assembler code for function test1(int*):\n   0x0000000000400886 <+0>:  push   %rbp\n   0x0000000000400887 <+1>:  mov    %rsp,%rbp\n   0x000000000040088a <+4>:  mov    %rdi,-0x8(%rbp)\n=> 0x000000000040088e <+8>:  mov    -0x8(%rbp),%rax\n   0x0000000000400892 <+12>: movl   $0x3,(%rax)\n   0x0000000000400898 <+18>: nop\n   0x0000000000400899 <+19>: pop    %rbp\n   0x000000000040089a <+20>: retq   \nEnd of assembler dump.\n\n```\n\nThe above code lines 1 and 2 are the field operation of parameter calling and saving；The third line is parameter passing. The first parameter of the function call is usually placed in the RDI register. This line of code writes the RDI register value (the value of pointer P) to the stack；Line 4 writes the value of P in the stack to the rax register；Line 5 is to write the immediate number 3 to the**Memory pointed to by the value of the rax register**.\n\nLet's write out the reference C + + code segment test2 for parameter passing：\n\n```c++\nvoid test2(int& r)\n{\n    r = 3;    // do not need to judge whether reference is null.\n    return;\n}\n```\n\nThis code corresponds to the following assembly code：\n\n```c++\n(gdb) disassemble \nDump of assembler code for function test2(int&):\n   0x000000000040089b <+0>:  push   %rbp\n   0x000000000040089c <+1>:  mov    %rsp,%rbp\n   0x000000000040089f <+4>:  mov    %rdi,-0x8(%rbp)\n=> 0x00000000004008a3 <+8>:  mov    -0x8(%rbp),%rax\n   0x00000000004008a7 <+12>: movl   $0x3,(%rax)\n   0x00000000004008ad <+18>: nop\n   0x00000000004008ae <+19>: pop    %rbp\n   0x00000000004008af <+20>: retq   \nEnd of assembler dump.\n\n```\n\nWe find that the assembly code corresponding to test2 is exactly the same as that of test1.This shows that the C + + compiler compiles the pointer and reference into exactly the same machine code when compiling the program. Therefore, the reference in C + + is just a \"syntax sugar\" of pointer operation in C ++，In the underlying implementation, the C + + compiler implements these two operations in the same way.\n\n## 3. Conclusion\n\nThe introduction of reference operation in C + + ensures the security and convenience of reference use and maintains the elegance of code under the condition of adding more restrictions on the use of reference. The use of reference can avoid the situation of \"pointer flying all over the sky\" to a certain extent, and it has a positive significance to improve the stability of the program. Finally, the underlying implementations of pointers and references are the same, and there is no need to worry about the performance gap between them.\n\nFrom：<http://irootlee.com/juicer_pointer_reference/#>\n"
  },
  {
    "path": "english/basic_content/pointer_refer/copy_construct.cpp",
    "content": "/**\n * @file copy_construct.cpp\n * @brief g++ -o copy_construct copy_construct.cpp -fno-elide-constructors\n * -fno-elide-constructors选项(关闭返回值优化)\n * @author 光城\n * @version v1\n * @date 2019-08-09\n */\n\n#include <iostream>\nusing namespace std;\n\nclass Copyable {\npublic:\n    Copyable(){}\n    Copyable(const Copyable &o) {\n        cout << \"Copied\" << endl;\n    }\n};\nCopyable ReturnRvalue() {\n    return Copyable(); //返回一个临时对象\n}\nvoid AcceptVal(Copyable a) {\n\n}\nvoid AcceptRef(const Copyable& a) {\n\n}\n\nint main() {\n    cout << \"pass by value: \" << endl;\n    AcceptVal(ReturnRvalue()); // 应该调用两次拷贝构造函数\n    cout << \"pass by reference: \" << endl;\n    AcceptRef(ReturnRvalue()); //应该只调用一次拷贝构造函数\n}\n"
  },
  {
    "path": "english/basic_content/pointer_refer/effec.cpp",
    "content": "#include<iostream>\nusing namespace std;\nvoid test1(int* p)\n{\n    *p = 3;    //此处应该首先判断p是否为空，为了测试的需要，此处我们没加。\n    return;\n}\n\nvoid test2(int& p)\n{\n    p = 3;    //此处应该首先判断p是否为空，为了测试的需要，此处我们没加。\n    return;\n}\n\nint main() {\n    int a=10;\n    int *p=&a;\n    test1(p);\n    test2(a);\n    cout<<a<<endl;\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/sizeof/README.md",
    "content": "# Class size calculation\n\nFirst of all, let's make a summary, and then we will give an actual example\n\n- The size of the empty class is 1 byte\n- In a class, the virtual function itself, member functions (including static and non-static) and static data members do not occupy the storage space of class objects\n- For classes containing virtual functions, no matter how many virtual functions there are, there is only one virtual pointer, the size of VPTR\n- Ordinary inheritance, derived class inherits all the functions and members of the base class, and the size is calculated according to byte alignment\n- Virtual function inheritance, whether single inheritance or multi inheritance, inherits the VPTR of the base class(32 bit operating system 4 bytes, 64 bit operating system 8 bytes)！\n- Virtual inheritance inherits the VPTR of the base class\n\n## 1.Rule 1\n\n```c++\n/**\n * @file blackclass.cpp\n * @brief The size of the empty class is 1 byte\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n#include<iostream>\nusing namespace std;\nclass A{};\nint main()\n{\n    cout<<sizeof(A)<<endl;\n    return 0;\n}\n```\n\n## 2.Rule 2\n\n```c++\n/**\n * @file static.cpp\n * @brief\n * \n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n#include<iostream>\nusing namespace std;\nclass A\n{\n    public:\n        char b;\n        virtual void fun() {};\n        static int c;\n        static int d;\n        static int f;\n};\n\nint main()\n{\n    /**\n     * @brief 16 \n     */\n    cout<<sizeof(A)<<endl; \n    return 0;\n}\n```\n\n## 3.Rule 3\n\n```c++\n/**\n * @file morevir.cpp\n * @brief \n * @author \n * @version v1\n * @date 2019-07-21\n */\n#include<iostream>\nusing namespace std;\nclass A{\n    virtual void fun();\n    virtual void fun1();\n    virtual void fun2();\n    virtual void fun3();\n};\nint main()\n{\n    cout<<sizeof(A)<<endl; // 8\n    return 0;\n}\n```\n\n## 4.Rule 4 and 5\n\n```c++\n/**\n * @file geninhe.cpp\n * @brief \n * \n * \n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include<iostream>\n\nusing namespace std;\n\nclass A\n{\n    public:\n        char a;\n        int b;\n};\n\n/**\n * @brief \n * char a\n * int b\n * short a\n * long b\n * 根据字节对齐4+4=8+8+8=24\n */\nclass B:A\n{\n    public:\n        short a;\n        long b;\n};\nclass C\n{\n    A a;\n    char c;\n};\nclass A1\n{\n    virtual void fun(){}\n};\nclass C1:public A1\n{\n};\n\nint main()\n{\n    cout<<sizeof(A)<<endl; // 8\n    cout<<sizeof(B)<<endl; // 24\n    cout<<sizeof(C)<<endl; // 12\n    /**\n     * @brief \n     */\n    cout<<sizeof(C1)<<endl; // 8 \n    return 0;\n}\n```\n\n## 5.Rule 6\n\n```c++\n/**\n * @file virnhe.cpp\n * @brief \n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include<iostream>\nusing namespace std;\nclass A\n{\n    virtual void fun() {}\n};\nclass B\n{\n    virtual void fun2() {}\n};\nclass C : virtual public  A, virtual public B\n{\n    public:\n        virtual void fun3() {}\n};\n\nint main()\n{\n    /**\n     * @brief 8 8 16  The derived class inherits multiple virtual functions and inherits the VPTR of all virtual functions\n     */\n    cout<<sizeof(A)<<\" \"<<sizeof(B)<<\" \"<<sizeof(C);\n\n    return 0;\n}\n```\n\n"
  },
  {
    "path": "english/basic_content/sizeof/blackclass.cpp",
    "content": "/**\n * @file blackclass.cpp\n * @brief 空类的大小为1字节\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include<iostream>\n\nusing namespace std;\n\nclass A{};\nint main()\n{\n    cout<<sizeof(A)<<endl;\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/sizeof/genA.cpp",
    "content": "/**\n * @file genA.cpp\n * @brief 普通成员函数，大小为1,一个类中，虚函数本身、成员函数（包括静态与非静态）和静态数据成员都是不占用类对象的存储空间。\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include<iostream>\n\n\nusing namespace std;\n\nclass A\n{\n    public:\n        A();\n        ~A();\n        static int a;\n        static void fun3();\n        void fun();\n        void fun1();\n};\n\nint main()\n{\n    cout<<sizeof(A)<<endl; // 1\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/sizeof/geninhe.cpp",
    "content": "/**\n * @file geninhe.cpp\n * @brief 1.普通单继承,继承就是基类+派生类自身的大小(注意字节对齐)\n * 注意：类的数据成员按其声明顺序加入内存，无访问权限无关，只看声明顺序。\n * 2.虚单继承，派生类继承基类vptr\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include<iostream>\n\nusing namespace std;\n\nclass A\n{\n    public:\n        char a;\n        int b;\n};\n\n/**\n * @brief 此时B按照顺序：\n * char a\n * int b\n * short a\n * long b\n * 根据字节对齐4+4=8+8+8=24\n */\nclass B:A\n{\n    public:\n        short a;\n        long b;\n};\nclass C\n{\n    A a;\n    char c;\n};\n\nclass A1\n{\n    virtual void fun(){}\n};\nclass C1:public A\n{\n};\n\n\nint main()\n{\n    cout<<sizeof(A)<<endl; // 8\n    cout<<sizeof(B)<<endl; // 24\n    cout<<sizeof(C)<<endl; // 12\n\n    /**\n     * @brief 对于虚单函数继承，派生类也继承了基类的vptr，所以是8字节\n     */\n    cout<<sizeof(C1)<<endl; // 8 \n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/sizeof/moreinhe.cpp",
    "content": "/**\n * @file moreinhe.cpp\n * @brief 普通多继承与虚函数多继承\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include<iostream>\n\nusing namespace std;\n\nclass A\n{\n    public:\n        char a;\n        int b;\n};\n\nclass B\n{\n    public:\n        short a;\n        long b;\n};\n\n/**\n * @brief 8+16+8=32\n */\nclass C:A,B\n{\n    char c;\n};\n\n\nint main()\n{\n    cout<<sizeof(A)<<endl; // 8\n    cout<<sizeof(B)<<endl; // 16\n    cout<<sizeof(C)<<endl; // 32\n\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/sizeof/morevir.cpp",
    "content": "/**\n * @file morevir.cpp\n * @brief 对于包含虚函数的类，不管有多少个虚函数，只有一个虚指针,vptr的大小。\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include<iostream>\n\nusing namespace std;\n\nclass A{\n\n    virtual void fun();\n    virtual void fun1();\n    virtual void fun2();\n    virtual void fun3();\n};\nint main()\n{\n    cout<<sizeof(A)<<endl;\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/sizeof/static.cpp",
    "content": "/**\n * @file static.cpp\n * @brief 静态数据成员\n * 静态数据成员被编译器放在程序的一个global data members中，它是类的一个数据成员，但不影响类的大小。不管这个类产生了多少个实例，还是派生了多少新的类，静态数据成员只有一个实例。静态数据成员，一旦被声明，就已经存在。 \n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include<iostream>\nusing namespace std;\nclass A\n{\n    public:\n        char b;\n        virtual void fun() {};\n        static int c;\n        static int d;\n        static int f;\n};\n\n\n\nint main()\n{\n\n    /**\n     * @brief 16  字节对齐、静态变量不影响类的大小、vptr指针=8\n     */\n    cout<<sizeof(A)<<endl; \n\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/sizeof/virinhe.cpp",
    "content": "/**\n * @file virnhe.cpp\n * @brief 虚继承\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include<iostream>\n\nusing namespace std;\n\nclass A\n{\n    virtual void fun() {}\n};\n\nclass B\n{\n    virtual void fun2() {}\n};\nclass C : virtual public  A, virtual public B\n{\n    public:\n        virtual void fun3() {}\n};\n\n\nint main()\n{\n\n    /**\n     * @brief 8 8 16  派生类虚继承多个虚函数，会继承所有虚函数的vptr\n     */\n    cout<<sizeof(A)<<\" \"<<sizeof(B)<<\" \"<<sizeof(C);\n\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/sizeof/virmoreinhe.cpp",
    "content": "/**\n * @file virmoreinhe.cpp\n * @brief 虚函数多继承\n * @author 光城\n * @version v1\n * @date 2019-07-21\n */\n\n#include<iostream>\n\nusing namespace std;\n\nclass A\n{\n    virtual void fun() {}\n};\n\nclass B\n{\n    virtual void fun2() {}\n};\nclass C : public  A, public B\n{\n    public:\n        virtual void fun3() {}\n};\n\n\nint main()\n{\n\n    /**\n     * @brief 8 8 16  派生类继承多个虚函数，会继承所有虚函数的vptr\n     */\n    cout<<sizeof(A)<<\" \"<<sizeof(B)<<\" \"<<sizeof(C);\n\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/static/README.md",
    "content": "# Story about static\n\nThe static keyword has different meanings when used with different types. We can use the static keyword：\n\n**Static variable：** Variables in functions, variables in classes\n\n**Members of a static class：** Class objects and functions in classes\n\nNow let's take a closer look at these static usages：\n\n**Static variable**\n\n- Static variables in functions\n\nWhen the variable is declared static，space **Will be allocated over the life cycle of the program**。Even if the function is called multiple times, the space for the static variable is allocated**only once**，The value of the variable in the previous call is passed through the next function call. This is useful in C / C + + or any other application that needs to store the state of previous functions. \n\n```c++\n#include <iostream> \n#include <string> \nusing namespace std; \n\nvoid demo() \n{ \n\t// static variable \n\tstatic int count = 0; \n\tcout << count << \" \"; \n\t\n\t// value is updated and \n\t// will be carried to next \n\t// function calls \n\tcount++; \n} \n\nint main() \n{ \n\tfor (int i=0; i<5; i++)\t \n\t\tdemo(); \n\treturn 0; \n} \n```\n\nOutput：\n\n```\n0 1 2 3 4 \n```\n\nYou can see in the above program that the variable count is declared static.So, Its value is passed through a function call. The variable count is not initialized each time the function is called.\n\n- Static variables in class\n\nBecause variables declared static are initialized only once，Because they allocate space in separate static storage。Therefore, static variables in a class are **shared by objects。**You cannot have multiple copies of the same static variable for different objects. For this reason, static variables cannot be initialized with constructors.\n\n```c++\n\n#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \npublic: \n\tstatic int i; \n\t\n\tApple() \n\t{ \n\t\t// Do nothing \n\t}; \n}; \n\nint main() \n{ \nApple obj1; \nApple obj2; \nobj1.i =2; \nobj2.i = 3; \n\t\n// prints value of i \ncout << obj1.i<<\" \"<<obj2.i; \n} \n```\n\nYou can see in the above program that we have tried to create multiple copies of static variable i for multiple objects. But it didn't happen.\nTherefore, static variables in a class should be explicitly initialized by the user using class names and range resolution operators outside the class, as shown below\n\n```c++\n\n#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \npublic: \n\tstatic int i; \n\t\n\tApple() \n\t{ \n\t\t// Do nothing \n\t}; \n}; \n\nint Apple::i = 1; \n\nint main() \n{ \n\tApple obj; \n\t// prints value of i \n\tcout << obj.i; \n} \n```\n\nOutput：\n\n```\n1\n```\n\n**Static member**\n\n- Class objects are static\n\nJust like variables, objects have scope when declared static until the life cycle of the program\n\nConsider the following program, where the object is non static\n\n```c++\n#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \n\tint i; \n\tpublic: \n\t\tApple() \n\t\t{ \n\t\t\ti = 0; \n\t\t\tcout << \"Inside Constructor\\n\"; \n\t\t} \n\t\t~Apple() \n\t\t{ \n\t\t\tcout << \"Inside Destructor\\n\"; \n\t\t} \n}; \n\nint main() \n{ \n\tint x = 0; \n\tif (x==0) \n\t{ \n\t\tApple obj; \n\t} \n\tcout << \"End of main\\n\"; \n} \n\n```\n\n\nOutput：\n\n```c++\nInside Constructor\nInside Destructor\nEnd of main\n```\n\nIn the above program, the object is declared non static within the if block. Therefore, the scope of the variable is only within the if block. Therefore, when an object is created, the constructor is called, and is called when the control of the if block passes over the destructor, because the scope of the object is only within the if block in which it is declared.\n\nIf we declare the object static, now let's look at the changes in the output.\n\n```c++\n#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \n\tint i; \n\tpublic: \n\t\tApple() \n\t\t{ \n\t\t\ti = 0; \n\t\t\tcout << \"Inside Constructor\\n\"; \n\t\t} \n\t\t~Apple() \n\t\t{ \n\t\t\tcout << \"Inside Destructor\\n\"; \n\t\t} \n}; \n\nint main() \n{ \n\tint x = 0; \n\tif (x==0) \n\t{ \n\t\tstatic Apple obj; \n\t} \n\tcout << \"End of main\\n\"; \n} \n\n```\n\n\nOutput：\n\n```\nInside Constructor\nEnd of main\nInside Destructor\n```\n\nYou can clearly see the output change. Now, call the destructor function after the end of main.This is because the scope of static objects is throughout the life cycle of the program.\n\n- Static functions in class\n\nJust like static data members or static variables in a class, Static member functions are also independent of class objects.We are allowed to use objects and \".\" to call static member functions. However, it is recommended to call static members using class names and range resolution operators.\n\nAllows static member functions to access only static data members or other static member functions that cannot access non static data members or member functions of a class.\n\n```c++\n#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \n    public: \n        // static member function \n        static void printMsg() \n        {\n            cout<<\"Welcome to Apple!\"; \n        }\n}; \n\n// main function \nint main() \n{ \n    // invoking a static member function \n    Apple::printMsg(); \n} \n```\n\nOutput：\n\n```\nWelcome to Apple!\n```\n\n"
  },
  {
    "path": "english/basic_content/static/nostatic_class.cpp",
    "content": "#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \n    int i; \n    public: \n    Apple() \n    { \n        i = 0; \n        cout << \"Inside Constructor\\n\"; \n    } \n    ~Apple() \n    { \n        cout << \"Inside Destructor\\n\"; \n    } \n}; \n\nint main() \n{ \n    int x = 0; \n    if (x==0) \n    { \n        Apple obj; \n    } \n    cout << \"End of main\\n\"; \n} \n\n"
  },
  {
    "path": "english/basic_content/static/static_class.cpp",
    "content": "#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \n    int i; \n    public: \n    Apple() \n    { \n        i = 0; \n        cout << \"Inside Constructor\\n\"; \n    } \n    ~Apple() \n    { \n        cout << \"Inside Destructor\\n\"; \n    } \n}; \n\nint main() \n{ \n    int x = 0; \n    if (x==0) \n    { \n        static Apple obj; \n    } \n    cout << \"End of main\\n\"; \n} \n\n"
  },
  {
    "path": "english/basic_content/static/static_demo.cpp",
    "content": "// the use of static Static \n// variables in a Function \n#include <iostream> \n#include <string> \nusing namespace std; \n\nvoid demo() \n{ \n    // static variable \n    static int count = 0; \n    cout << count << \" \"; \n\n    // value is updated and \n    // will be carried to next \n    // function calls \n    count++; \n} \n\nint main() \n{ \n    for (int i=0; i<5; i++)  \n        demo(); \n    return 0; \n} \n"
  },
  {
    "path": "english/basic_content/static/static_error_variable.cpp",
    "content": "// variables inside a class \n\n#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \n    public: \n        static int i; \n\n        Apple() \n        { \n            // Do nothing \n        }; \n}; \n\nint main() \n{ \n    Apple obj1; \n    Apple obj2; \n    obj1.i =2; \n    obj2.i = 3; \n\n    // prints value of i \n    cout << obj1.i<<\" \"<<obj2.i; \n} \n"
  },
  {
    "path": "english/basic_content/static/static_funciton.cpp",
    "content": "#include<iostream> \nusing namespace std; \n\nclass Apple \n{ \n    public: \n        // static member function \n        static void printMsg() \n        { \n            cout<<\"Welcome to Apple!\"; \n        } \n}; \n\n// main function \nint main() \n{ \n    // invoking a static member function \n    Apple::printMsg(); \n} \n\n"
  },
  {
    "path": "english/basic_content/static/static_variable.cpp",
    "content": "// variables inside a class \n\n#include<iostream> \nusing namespace std; \n\nclass GfG \n{ \n    public: \n        static int i; \n\n        GfG() \n        { \n            // Do nothing \n        }; \n}; \n\nint GfG::i = 1; \n\nint main() \n{ \n    GfG obj; \n    // prints value of i \n    cout << obj.i; \n} \n"
  },
  {
    "path": "english/basic_content/struct/README.md",
    "content": "# Understand C and C + + struct\n\n\n\n![](../img/wechat.jpg)\n\n## 1.Struct In C\n\n- In C, struct is only used as the composite type of data, that is, in the structure declaration, only data members can be placed inside, but not functions \n- C + + access modifiers cannot be used in C structure declarations,such as：public、protected、private. But they can be used in C + +\n- Define the structure variable in C. If you use the following definition, you must add struct\n- The structure of C cannot be inherited (there is no such concept)\n- If the structure name is the same as the function name, it can run normally and call normally! For example, you can define void base() that does not conflict with struct base{}。\n\nExample：\n\n```c\n#include<stdio.h>\n\nstruct Base {            // public\n    int v1; \n//    public:      //error\n        int v2; \n    //private:\n        int v3; \n    //void print(){       // c中不能在结构体中嵌入函数\n    //    printf(\"%s\\n\",\"hello world\");\n    //};    //error!\n};\n\nvoid Base(){\n    printf(\"%s\\n\",\"I am Base func\");\n}\n//struct Base base1;  //ok\n//Base base2; //error\nint main() {\n    struct Base base;\n    base.v1=1;\n    //base.print();\n    printf(\"%d\\n\",base.v1);\n    Base();\n    return 0;\n}\n```\n\nFinally Output：\n\n```\n1\nI am Base func\n```\n\nCode：[struct_func.c](./struct_func.c)\n\n## 2.C++ struct\n\nThe comparison with C is as follows：\n\n- In C + + structure, not only data can be defined, but also functions can be defined.\n- Access modifiers can be used in C + + structures, such as ：public、protected、private 。\n- C + + structure can be used directly without struct.\n- C + + inheritance\n- If the structure name is the same as the function name, it can run normally and call normally！However, when defining the structure variable, we only use the structure with struct！\n\nExample：\n\n> Case 1：\n\nBefore adding a function with the same name：\n\n```c++\nstruct Student {\n    \n};\nStudent(){}\nStruct Student s; //ok\nStudent s;  //ok\n```\n\nAfter adding a function with the same name：\n\n```c++\nstruct Student {\n    \n};\nStudent(){}\nStruct Student s; //ok\nStudent s;  //error\n```\n\n> Case 2：\n\n```c++\ntypedef struct Base1 {         \n    int v1;\n//    private:   //error!\n        int v3;\n    public:     //显示声明public\n        int v2;\n    void print(){       \n        printf(\"%s\\n\",\"hello world\");\n    };    \n}B;\n//void B() {}  //error! 符号 \"B\" 已经被定义为一个 \"struct Base1\" 的别名\n```\n\n> The first three cases\n\n```c++\n#include<iostream>\n#include<stdio.h>\n\nstruct Base {         \n    int v1;\n//    private:   //error!\n        int v3;\n    public:     //显示声明public\n        int v2;\n    void print(){       \n        printf(\"%s\\n\",\"hello world\");\n    };    \n};\n\nint main() {\n    struct Base base1;  //ok\n    Base base2; //ok\n    Base base;\n    base.v1=1;\n    base.v3=2;\n    base.print();\n    printf(\"%d\\n\",base.v1);\n    printf(\"%d\\n\",base.v3);\n    return 0;\n}\n```\n\nCode ：[struct_func.cpp](struct_func.cpp)\n\n> Inheritance cases\n\n```c++\n#include<iostream>\n#include<stdio.h>\nstruct Base {         \n    int v1;\n//    private:   //error!\n        int v3;\n    public:   //显示声明public\n        int v2;\n    virtual void print(){       \n        printf(\"%s\\n\",\"Base\");\n    };    \n};\nstruct Derived:Base {         \n\n    public:\n        int v2;\n    void print(){       \n        printf(\"%s\\n\",\"Derived\");\n    };    \n};\nint main() {\n    Base *b=new Derived();\n    b->print();\n    return 0;\n}\n```\n\nCode：[ext_struct_func.cpp](./ext_struct_func.cpp)\n\n> Functions with the same name\n\n```c++\n#include<iostream>\n#include<stdio.h>\n\nstruct Base {         \n    int v1;\n//    private:   //error!\n        int v3;\n    public:     //显示声明public\n        int v2;\n    void print(){       \n        printf(\"%s\\n\",\"hello world\");\n    };    \n};\n\ntypedef struct Base1 {         \n    int v1;\n//    private:   //error!\n        int v3;\n    public:     //显示声明public\n        int v2;\n    void print(){       \n        printf(\"%s\\n\",\"hello world\");\n    };    \n}B;\nvoid Base(){\n    printf(\"%s\\n\",\"I am Base func\");\n}\n//void B() {}  //error! 符号 \"B\" 已经被定义为一个 \"struct Base1\" 的别名\nint main() {\n    struct Base base;  //ok\n    //Base base1;  // error!\n    base.v1=1;\n    base.v3=2;\n    base.print();\n    printf(\"%d\\n\",base.v1);\n    printf(\"%d\\n\",base.v3);\n    Base();\n    return 0;\n}\n```\nCode：[struct_func_func.cpp](./struct_func_func.cpp)\n\n## 3.Conclusion\n\n### The difference of structure between C and C + +\n\n| C                                                      | C++                                                          |\n| ------------------------------------------------------ | ------------------------------------------------------------ |\n| You cannot place a function in a structure declaration | Function can be declared in structure                                       |\n| C + + access modifiers cannot be used in C structure declarations.            | public、protected、private They can be used in C + +                 |\n| Define the structure variable in C. If you use the following definition, you must add struct  | You can do without struct                            |\n|Not inheritance                       | you could use inheritance                                                     |\n| If the structure name is the same as the function name, it can run normally and call normally | If the structure name is the same as the function name, the structure can only be defined with struct |\n"
  },
  {
    "path": "english/basic_content/struct/ext_struct_func.cpp",
    "content": "#include<iostream>\n#include<stdio.h>\nusing namespace std;\nstruct Base {         \n    int v1;\n//    private:   //error!\n        int v3;\n    public:   //显示声明public\n        int v2;\n    virtual void print(){       \n        printf(\"%s\\n\",\"Base\");\n    };    \n    Base(){cout<<\"Base construct\"<<endl;};\n    virtual ~Base(){cout<<\"Base deconstruct\"<<endl;};\n};\n\nstruct Derived:Base {         \n\n    Derived(){cout<<\"Derived construct\"<<endl;};\n    virtual ~Derived(){cout<<\"Derived deconstruct\"<<endl;};\n    public:\n        int v2;\n    void print(){       \n        printf(\"%s\\n\",\"Derived\");\n    };    \n};\n\nint main() {\n    Base *b=new Derived();\n    b->print();\n    delete b;\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/struct/struct_func.c",
    "content": "#include<stdio.h>\n\nstruct Base {            // public\n    int v1;\n//    public:      //error\n        int v2;\n    //private:\n        int v3;\n    //void print(){       // c中不能在结构体中嵌入函数\n    //    printf(\"%s\\n\",\"hello world\");\n    //};    //error!\n};\n\nvoid Base(){\n    printf(\"%s\\n\",\"I am Base func\");\n}\n//struct Base base1;  //ok\n//Base base2; //error\n\nint main() {\n    struct Base base;\n    base.v1=1;\n    //base.print();\n    printf(\"%d\\n\",base.v1);\n    Base();\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/struct/struct_func.cpp",
    "content": "#include<iostream>\n#include<stdio.h>\n\nstruct Base {         \n    int v1;\n//    private:   //error!\n        int v3;\n    public:     //显示声明public\n        int v2;\n    void print(){       \n        printf(\"%s\\n\",\"hello world\");\n    };    \n};\n\nint main() {\n    struct Base base1;  //ok\n    Base base2; //ok\n    Base base;\n    base.v1=1;\n    base.v3=2;\n    base.print();\n    printf(\"%d\\n\",base.v1);\n    printf(\"%d\\n\",base.v3);\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/struct/struct_func_func.cpp",
    "content": "#include<iostream>\n#include<stdio.h>\n\nstruct Base {         \n    int v1;\n//    private:   //error!\n        int v3;\n    public:     //显示声明public\n        int v2;\n    void print(){       \n        printf(\"%s\\n\",\"hello world\");\n    };    \n};\n\ntypedef struct Base1 {         \n    int v1;\n//    private:   //error!\n        int v3;\n    public:     //显示声明public\n        int v2;\n    void print(){       \n        printf(\"%s\\n\",\"hello world\");\n    };    \n}B;\nvoid Base(){\n    printf(\"%s\\n\",\"I am Base func\");\n}\n//void B() {}  //error! 符号 \"B\" 已经被定义为一个 \"struct Base1\" 的别名\nint main() {\n    struct Base base;  //ok\n    //Base base1;  // error!\n    base.v1=1;\n    base.v3=2;\n    base.print();\n    printf(\"%d\\n\",base.v1);\n    printf(\"%d\\n\",base.v3);\n    Base();\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/struct_class/README.md",
    "content": "# The difference struct and class\n\n\n\n![](../img/wechat.jpg)\n\nMore detail about struct [Story about struct](../struct)\n\nGenerally speaking, struct is more suitable to be regarded as an implementation body of data structure, and class is more suitable to be regarded as the implementation body of an object. \n\nDif:\n\nOne of the essential differences is the default access control\n\n\n\nDefault inherited access.Struct is public, while class is private.\n\nStructure as the implementation body of data structure.Its default data access control is public, while class is the implementation body of the object, and its default member variable access control is private.\n"
  },
  {
    "path": "english/basic_content/this/README.md",
    "content": "# Story about this\n\n## About Author\n\n\n![](../img/wechat.jpg)\n\n## 1.This pointer\n\nI believe that many people sitting here are learning python. For Python, there is self. Analogy to C + + is this pointer. Let's analyze the use of this pointer in class.\n\nLet's first talk about the use of this pointer：\n\n（1）This pointer to an object is not part of the object itself and does not affect the result of sizeof.\n\n（2）This scope is within the class. When accessing the non static members of the class in the non static member function of the class, the compiler will automatically pass the address of the object itself to the function as an implicit parameter. That is to say, even if you don't write this pointer, the compiler will add this when compiling. As the implicit formal parameter of non static member function, the access to each member is carried out through this.\n\nSecond, the use of this pointer:\n\n（1）When the class object itself is returned in a class's non static member function, directly use return *this。\n\n（2）When the parameter is the same as the member variable name, 如this->n = n （不能写成n = n)。\n\n\nThe following examples are available:\n\n```c++\n#include<iostream>\n#include<cstring>\n\n\nusing namespace std;\nclass Person{\npublic:\n    typedef enum {\n        BOY = 0, \n        GIRL \n    }SexType;\n    Person(char *n, int a,SexType s){\n        name=new char[strlen(n)+1];\n        strcpy(name,n);\n        age=a;\n        sex=s;\n    }\n    int get_age() const{\n    \n        return this->age; \n    }\n    Person& add_age(int a){\n        age+=a;\n        return *this; \n    }\n    ~Person(){\n        delete [] name;\n    }\nprivate:\n    char * name;\n    int age;\n    SexType sex;\n};\n\n\nint main(){\n    Person p(\"zhangsan\",20,Person::BOY); \n    cout<<p.get_age()<<endl;\n\tcout<<p.add_age(10).get_age()<<endl;\n    return 0;\n}\n```\n\n\nIn order to verify which this pointer is, now in 'Add'_ Add a breakpoint at \"age\". After running, it is as follows ：\n\n![thiscontrust](./img/thiscontrust.png)\n\n![genthis](./img/genthis.png)\n\nYou'll find that the compiler adds it to us automatically `A* const`，rather than `A const *this`！\n\nClosely.There is also a constant function above，so we try to add a breakpoint at`get_age` as following：\n\n![constthis](./img/constthis.png)\n\nYou'll find that the compiler takes the this，then it changed to`const A* const`. Because this is the const function，It can only access const variables and const functions\n\n，You cannot modify the values of other variables. So you need a this to point to a variable that cannot be modified，that is `const A*`,and because this is`const` pointer.\nConclusion：This is constructed before the start of member function execution. Clear after member execution. Get above_ The age function is resolved to `get_age(const A * const this)`,`add_age`function will be paresed as `add_age(A* const this,int a)`. In C + +, there is only one difference between a class and a structure: the member of a class is private by default, while the structure is public. This is the pointer of the class. If it is replaced by a structure, this is the pointer of the structure.\n"
  },
  {
    "path": "english/basic_content/this/person.cpp",
    "content": "#include<iostream>\n#include<cstring>\n\n\nusing namespace std;\nclass Person{\npublic:\n    typedef enum {\n        BOY = 0, \n        GIRL \n    }SexType;\n    Person(char *n, int a,SexType s){\n        name=new char[strlen(n)+1];\n        strcpy(name,n);\n        age=a;\n        sex=s;\n    }\n    int get_age() const{\n    \n        return this->age; \n    }\n    Person& add_age(int a){\n        age+=a;\n        return *this; \n    }\n    ~Person(){\n        delete [] name;\n    }\nprivate:\n    char * name;\n    int age;\n    SexType sex;\n};\n\n\nint main(){\n    Person p(\"zhangsan\",20,Person::BOY); \n    cout<<p.get_age()<<endl;\n\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/union/README.md",
    "content": "# Story About Union\n\n\n\n\n![](../img/wechat.jpg)\n\n（Union）is a space saving special class.A union can have multiple data members.However, only one data member can have a value at any time. When a member is assigned a value, other members become undefined.\n\n- The default access control character is public\n- It can contain constructors and destructors\n- Cannot contain a member of a reference type\n- Cannot inherit from other classes, cannot be a base class\n- Cannot contain virtual functions\n- Anonymous union can directly access union members in the scope of definition\n- Anonymous union cannot contain protected or private members\n- Global anonymous union must be static\n\n\n"
  },
  {
    "path": "english/basic_content/union/union.cpp",
    "content": "/**\n * @file union.cpp\n * @brief UNION\n * @author 光城\n * @version v1\n * @date 2019-08-06\n */\n\n#include<iostream>\n/**\n * 默认访问控制符为public\n */\nunion UnionTest {\n    /**\n     * 可以含有构造函数、析构函数\n     */\n    UnionTest() : i(10) {print(i);};\n    ~UnionTest(){};\n    int i; \nprivate:\n    void print(int i){std::cout<<i<<std::endl;};\n};\n/**\n * 全局匿名联合必须是静态的 \n */\nstatic union {\n    int i;\n    double d;\n};\n\nint main() {\n    UnionTest u;\n\n    union {\n        int i;\n        double d;\n    };\n\n    std::cout << u.i << std::endl;  // 输出 UnionTest 联合的 10\n\n    ::i = 20;\n    std::cout << ::i << std::endl;  // 输出全局静态匿名联合的 20\n    /**\n     * 匿名union在定义所在作用域可直接访问union成员\n     */\n    i = 30;\n    std::cout << i << std::endl;    // 输出局部匿名联合的 30\n\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/using/README.md",
    "content": "# Story About using\n## About Author：\n\n\n![](../img/wechat.jpg)\n\n## Basic use\n\nLocal and global using. See the following cases for specific operation and use：\n\n```c++\n#include <iostream>\n#define isNs1 1\n//#define isGlobal 2\nusing namespace std;\nvoid func() \n{\n    cout<<\"::func\"<<endl;\n}\n\nnamespace ns1 {\n    void func()\n    {\n        cout<<\"ns1::func\"<<endl; \n    }\n}\n\nnamespace ns2 {\n#ifdef isNs1 \n    using ns1::func;    /// function in ns1\n#elif isGlobal\n    using ::func; /// global function\n#else\n    void func() \n    {\n        cout<<\"other::func\"<<endl; \n    }\n#endif\n}\n\nint main() \n{\n    /**\n     * 这就是为什么在c++中使用了cmath而不是math.h头文件\n     */\n    ns2::func(); // 会根据当前环境定义宏的不同来调用不同命名空间下的func()函数\n    return 0;\n}\n```\nCode：[using_global.cpp](using_global.cpp)\n## Changing accessibility\n\n```\nclass Base{\npublic:\n std::size_t size() const { return n;  }\nprotected:\n std::size_t n;\n};\nclass Derived : private Base {\npublic:\n using Base::size;\nprotected:\n using Base::n;\n};\n```\n\n类Derived私有继承了Base，对于它来说成员变量n和成员函数size都是私有的，如果使用了using语句，可以改变他们的可访问性，如上述例子中，size可以按public的权限访问，n可以按protected的权限访问。\nCode：[derived_base.cpp](derived_base.cpp)\n## 函数重载\n\n在继承过程中，派生类可以覆盖重载函数的0个或多个实例，一旦定义了一个重载版本，那么其他的重载版本都会变为不可见。\n\n如果对于基类的重载函数，我们需要在派生类中修改一个，又要让其他的保持可见，必须要重载所有版本，这样十分的繁琐。\n\n```c++\n#include <iostream>\nusing namespace std;\n\nclass Base{\n    public:\n        void f(){ cout<<\"f()\"<<endl;\n        }\n        void f(int n){\n            cout<<\"Base::f(int)\"<<endl;\n        }\n};\n\nclass Derived : private Base {\n    public:\n        using Base::f;\n        void f(int n){\n            cout<<\"Derived::f(int)\"<<endl;\n        }\n};\n\nint main()\n{\n    Base b;\n    Derived d;\n    d.f();\n    d.f(1);\n    return 0;\n}\n```\n如上代码中，在派生类中使用using声明语句指定一个名字而不指定形参列表，所以一条基类成员函数的using声明语句就可以把该函数的所有重载实例添加到派生类的作用域中。此时，派生类只需要定义其特有的函数就行了，而无需为继承而来的其他函数重新定义。\n\n完整代码见：[using_derived.cpp](using_derived.cpp)\n## Replace typedef\n\nThe syntax of typedef a B is often used in C, which defines B as type A, that is, giving type a an alias B\n\n\n对应typedef A B，使用using B=A可以进行同样的操作。\n\n```c++\ntypedef vector<int> V1; \nusing V2 = vector<int>;\n```\nCode：[using_typedef.cpp](using_typedef.cpp)\n"
  },
  {
    "path": "english/basic_content/using/derived_base.cpp",
    "content": "#include <iostream>\n\nusing namespace std;\n\nclass Base1 {\n    public:\n        Base1():value(10) {}\n        virtual ~Base1() {}\n        void test1() { cout << \"Base test1...\" << endl; }\n    protected: // 保护\n        int value;\n};\n// 默认为私有继承\nclass Derived1 : Base1 {\n    public:\n        void test2() { cout << \"value is \" << value << endl; }\n};\n\nclass Base {\n    public:\n        Base():value(20) {}\n        virtual ~Base() {}\n        void test1() { cout << \"Base test1...\" << endl; }\n    private:  //私有\n        int value;\n};\n\n/**\n * 子类对父类成员的访问权限跟如何继承没有任何关系，\n * “子类可以访问父类的public和protected成员，不可以访问父类的private成员”——这句话对任何一种继承都是成立的。\n *\n */\nclass Derived : Base {\n    public:\n        using Base::value;\n        void test2() { cout << \"value is \" << value << endl; }\n};\n\n\nint main() \n{\n    Derived1 d1;\n    d1.test2();\n\n    Derived d;\n    d.test2();\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/using/using_derived.cpp",
    "content": "/**\n * @file using_derived.cpp\n * @brief 函数重装\n * @author 光城\n * @version v1\n * @date 2019-08-07\n */\n\n#include <iostream>\nusing namespace std;\n\nclass Base{\n    public:\n        void f(){ cout<<\"f()\"<<endl;\n        }\n        void f(int n){\n            cout<<\"Base::f(int)\"<<endl;\n        }\n};\n\nclass Derived : private Base {\n    public:\n        using Base::f;\n        void f(int n){\n            cout<<\"Derived::f(int)\"<<endl;\n        }\n};\n\nint main()\n{\n    Base b;\n    Derived d;\n    d.f();\n    d.f(1);\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/using/using_global.cpp",
    "content": "/**\n * @file using_global.cpp\n * @brief using各种使用\n * @author 光城\n * @version v1\n * @date 2019-08-07\n */\n\n#include <iostream>\n#define isNs1 1\n//#define isGlobal 2\nusing namespace std;\nvoid func() \n{\n    cout<<\"::func\"<<endl;\n}\n\nnamespace ns1 {\n    void func()\n    {\n        cout<<\"ns1::func\"<<endl; \n    }\n}\n\nnamespace ns2 {\n#ifdef isNs1 \n    using ns1::func;    /// ns1中的函数\n#elif isGlobal\n    using ::func; /// 全局中的函数\n#else\n    void func() \n    {\n        cout<<\"other::func\"<<endl; \n    }\n#endif\n}\n\nint main() \n{\n    /**\n     * 这就是为什么在c++中使用了cmath而不是math.h头文件\n     */\n    ns2::func(); // 会根据当前环境定义宏的不同来调用不同命名空间下的func()函数\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/using/using_typedef.cpp",
    "content": "/**\n * @file using_typedef.cpp\n * @brief g++ -o using_typedef using_typedef.cpp -std=c++11\n * 取代typedef，使用using来定义别名\n * @author 光城\n * @version v1\n * @date 2019-08-07\n */\n\n#include <iostream>\n#include <vector>\nusing namespace std;\n\ntypedef vector<int> V1;\nusing V2 = vector<int>;\n\n\nint main() \n{\n    int nums1[] = {1,2,3,4,5,6};\n    V1 vec1(nums1,nums1+sizeof(nums1)/sizeof(int));\n    int nums2[] = {5,7,6};\n    V2 vec2(nums2,nums2+sizeof(nums2)/sizeof(int));\n    \n    for(auto i:vec1)\n        cout<<i<<\" \";\n    cout<<endl;\n\n    for(auto i:vec2)\n        cout<<i<<\" \";\n    cout<<endl;\n\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/virtual/README.md",
    "content": "# virtual那些事\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 1.虚函数与运行多态\n\n对应的代码：[emp.cpp](./set1/emp.cpp)\n\n**虚函数的调用取决于指向或者引用的对象的类型，而不是指针或者引用自身的类型。**\n\n## 2.vptr与vtable\n\n见[vptr_vtable那些事](../vptr_vtable)\n\n## 3.虚函数中默认参数\n\n对应的代码：[default_arg.cpp](./set2/default_arg.cpp)\n\n**默认参数是静态绑定的，虚函数是动态绑定的。 默认参数的使用需要看指针或者引用本身的类型，而不是对象的类型**。\n\n## 4.可以不可以\n\n（1） **静态函数可以声明为虚函数吗？**\n\n原因主要有两方面：\n\n**静态函数不可以声明为虚函数，同时也不能被const 和 volatile关键字修饰**\n\nstatic成员函数不属于任何类对象或类实例，所以即使给此函数加上virutal也是没有任何意义\n\n虚函数依靠vptr和vtable来处理。vptr是一个指针，在类的构造函数中创建生成，并且只能用this指针来访问它，静态成员函数没有this指针，所以无法访问vptr。\n\n代码学习：[static_error.cpp  ](./set3/static_error.cpp  )\n\n（2）**构造函数可以为虚函数吗？**\n\n构造函数不可以声明为虚函数。同时除了inline|explicit之外，构造函数不允许使用其它任何关键字。\n\n为什么构造函数不可以为虚函数？\n\n尽管虚函数表vtable是在编译阶段就已经建立的，但指向虚函数表的指针vptr是在运行阶段实例化对象时才产生的。 如果类含有虚函数，编译器会在构造函数中添加代码来创建vptr。 问题来了，如果构造函数是虚的，那么它需要vptr来访问vtable，可这个时候vptr还没产生。 因此，构造函数不可以为虚函数。\n\n我们之所以使用虚函数，是因为需要在信息不全的情况下进行多态运行。而构造函数是用来初始化实例的，实例的类型必须是明确的。 因此，构造函数没有必要被声明为虚函数。\n\n代码学习：\n\n- [copy_consrtuct.cpp](./set3/copy_construct.cpp) \n\n- [vir_con.cpp](./set3/vir_con.cpp) \n\n（3）**析构函数可以为虚函数吗？**\n\n**析构函数可以声明为虚函数。如果我们需要删除一个指向派生类的基类指针时，应该把析构函数声明为虚函数。 事实上，只要一个类有可能会被其它类所继承， 就应该声明虚析构函数(哪怕该析构函数不执行任何操作)。**\n\n代码学习：\n\n- [full_virde.cpp](./set3/full_virde.cpp)\n\n- [vir_de.cpp ](./set3/vir_de.cpp)      \n\n（4）**虚函数可以为私有函数吗？**\n\n- 基类指针指向继承类对象，则调用继承类对象的函数；\n- int main()必须声明为Base类的友元，否则编译失败。 编译器报错： ptr无法访问私有函数。 当然，把基类声明为public， 继承类为private，该问题就不存在了。\n\n代码学习：\n\n- [virtual_function.cpp](./set3/virtual_function.cpp)\n- [virtual_function1.cpp](./set3/virtual_function1.cpp)\n\n（5）**虚函数可以被内联吗？**\n\n**通常类成员函数都会被编译器考虑是否进行内联。 但通过基类指针或者引用调用的虚函数必定不能被内联。 当然，实体对象调用虚函数或者静态调用时可以被内联，虚析构函数的静态调用也一定会被内联展开。**\n\n- 虚函数可以是内联函数，内联是可以修饰虚函数的，但是当虚函数表现多态性的时候不能内联。\n- 内联是在编译器建议编译器内联，而虚函数的多态性在运行期，编译器无法知道运行期调用哪个代码，因此虚函数表现为多态性时（运行期）不可以内联。\n- `inline virtual` 唯一可以内联的时候是：编译器知道所调用的对象是哪个类（如 `Base::who()`），这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。\n\n代码学习：\n\n- [virtual_inline.cpp](./set3/virtual_inline.cpp)\n\n- [inline_virtual.cpp](./set3/inline_virtual.cpp)\n\n## 5.RTTI与dynamic_cast\n\nRTTI（Run-Time Type Identification)，通过运行时类型信息程序能够使用[基类](https://baike.baidu.com/item/%E5%9F%BA%E7%B1%BB/9589663)的[指针](https://baike.baidu.com/item/%E6%8C%87%E9%92%88/2878304)或引用来检查这些指针或引用所指的对象的实际[派生类](https://baike.baidu.com/item/%E6%B4%BE%E7%94%9F%E7%B1%BB)型。\n\n在面向对象程序设计中，有时我们需要在运行时查询一个对象是否能作为某种多态类型使用。与Java的instanceof，以及C#的as、is运算符类似，C++提供了dynamic_cast函数用于动态转型。相比C风格的强制类型转换和C++ reinterpret_cast，dynamic_cast提供了类型安全检查，是一种基于能力查询(Capability Query)的转换，所以在多态类型间进行转换更提倡采用dynamic_cast。\n\n代码学习：\n\n- [rtti.cpp](./set4/rtti.cpp)\n- [warn_rtti.cpp](./set4/warn_rtti.cpp)\n\n## 6.纯虚函数和抽象类\n\n见[纯虚函数和抽象类那些事](../abstract)\n"
  },
  {
    "path": "english/basic_content/virtual/set1/emp.cpp",
    "content": "#include<iostream>\nusing namespace std;\n\n\nclass Employee \n{ \n    public: \n        virtual void raiseSalary() \n        {\n            cout<<0<<endl; \n        }\n\n        virtual void promote() \n        { /* common promote code */ } \n}; \n\nclass Manager: public Employee { \n    virtual void raiseSalary() \n    {   \n        cout<<100<<endl;    \n    } \n\n    virtual void promote() \n    { /* Manager specific promote */ } \n}; \nclass Engineer: public Employee { \n    virtual void raiseSalary() \n    {\n        cout<<200<<endl;    \n    }\n\n    virtual void promote() \n    { /* Manager specific promote */ } \n}; \n\n// Similarly, there may be other types of employees \n// We need a very simple function to increment salary of all employees \n// Note that emp[] is an array of pointers and actual pointed objects can \n// be any type of employees. This function should ideally be in a class  \n// like Organization, we have made it global to keep things simple \nvoid globalRaiseSalary(Employee *emp[], int n) \n{ \n    for (int i = 0; i < n; i++) \n        emp[i]->raiseSalary(); // Polymorphic Call: Calls raiseSalary()  \n    // according to the actual object, not  \n    // according to the type of pointer                                  \n} \nint main(){\n    Employee *emp[]={new Manager(),new Engineer};\n    globalRaiseSalary(emp,2); \n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/virtual/set2/default_arg.cpp",
    "content": "/**\n * @file first_example.cpp\n * @brief 虚函数中默认参数\n * 规则：虚函数是动态绑定的，默认参数是静态绑定的。默认参数的使用需要看指针或者应用本身的类型，而不是对象的类型！\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\n#include <iostream> \nusing namespace std; \n\nclass Base \n{ \n    public: \n        virtual void fun ( int x = 10 ) \n        { \n            cout << \"Base::fun(), x = \" << x << endl; \n        } \n}; \n\nclass Derived : public Base \n{ \n    public: \n        virtual void fun ( int x=20 ) \n        { \n            cout << \"Derived::fun(), x = \" << x << endl; \n        } \n}; \n\n\nint main() \n{ \n    Derived d1; \n    Base *bp = &d1; \n    bp->fun();  // 10\n    return 0; \n} \n\n"
  },
  {
    "path": "english/basic_content/virtual/set3/copy_consrtuct.cpp",
    "content": "#include <iostream> \nusing namespace std; \n\nclass Base \n{ \n    public: \n\n}; \n\nclass Derived : public Base \n{ \n    public: \n        Derived() \n        { \n            cout << \"Derived created\" << endl; \n        } \n\n        Derived(const Derived &rhs) \n        { \n            cout << \"Derived created by deep copy\" << endl; \n        } \n\n        ~Derived() \n        { \n            cout << \"Derived destroyed\" << endl; \n        } \n}; \n\nint main() \n{ \n    Derived s1; \n\n    Derived s2 = s1; // Compiler invokes \"copy constructor\" \n    // Type of s1 and s2 are concrete to compiler \n\n    // How can we create Derived1 or Derived2 object \n    // from pointer (reference) to Base class pointing Derived object? \n\n    return 0; \n} \n\n"
  },
  {
    "path": "english/basic_content/virtual/set3/full_virde.cpp",
    "content": "/**\n * @file full_virde.cpp\n * @brief 将基类的析构函数声明为虚函数\n * 输出结果：\n *      Constructing base\n *      Constructing derived\n *      Destructing derived\n *      Destructing base\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n#include<iostream> \n\nusing namespace std; \n\nclass base { \n    public: \n        base()      \n        { cout<<\"Constructing base \\n\"; } \n        virtual ~base() \n        { cout<<\"Destructing base \\n\"; }      \n}; \n\nclass derived: public base { \n    public: \n        derived()      \n        { cout<<\"Constructing derived \\n\"; } \n        ~derived() \n        { cout<<\"Destructing derived \\n\"; } \n}; \n\nint main(void) \n{ \n    derived *d = new derived();   \n    base *b = d; \n    delete b; \n    return 0; \n} \n"
  },
  {
    "path": "english/basic_content/virtual/set3/inline_virtual.cpp",
    "content": "#include <iostream>  \nusing namespace std;\nclass Base\n{\n    public:\n        inline virtual void who()\n        {\n            cout << \"I am Base\\n\";\n        }\n        virtual ~Base() {}\n};\nclass Derived : public Base\n{\n    public:\n        inline void who()  // 不写inline时隐式内联\n        {\n            cout << \"I am Derived\\n\";\n        }\n};\n\nint main()\n{\n    // 此处的虚函数 who()，是通过类（Base）的具体对象（b）来调用的，编译期间就能确定了，所以它可以是内联的，但最终是否内联取决于编译器。 \n    Base b;\n    b.who();\n\n    // 此处的虚函数是通过指针调用的，呈现多态性，需要在运行时期间才能确定，所以不能为内联。  \n    Base *ptr = new Derived();\n    ptr->who();\n\n    // 因为Base有虚析构函数（virtual ~Base() {}），所以 delete 时，会先调用派生类（Derived）析构函数，再调用基类（Base）析构函数，防止内存泄漏。\n    delete ptr;\n\n    return 0;\n} \n"
  },
  {
    "path": "english/basic_content/virtual/set3/static_error.cpp",
    "content": "/**\n * @file static_error.cpp\n * @brief 静态函数不可以声明为虚函数，同时也不能被const和volatile关键字修饰!\n * 原因如下：\n * static成员函数不属于任何类对象或类实例，所以即使给此函数加上virutal也是没有任何意义\n * 虚函数依靠vptr和vtable来处理。vptr是一个指针，在类的构造函数中创建生成，并且只能用this指针来访问它，静态成员函数没有this指针，所以无法访问vptr。\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\nvirtual static void fun()  { }\nstatic void fun() const { }\n"
  },
  {
    "path": "english/basic_content/virtual/set3/vir_con.cpp",
    "content": "/**\n * @file vir_con.cpp\n * @brief 构造函数不可以声明为虚函数。同时除了inline之外，构造函数不允许使用其它任何关键字。\n *\n * 为什么构造函数不可以为虚函数？\n *\n * 尽管虚函数表vtable是在编译阶段就已经建立的，但指向虚函数表的指针vptr是在运行阶段实例化对象时才产生的。 如果类含有虚函数，编译器会在构造函数中添加代码来创建vptr。 问题来了，如果构造函数是虚的，那么它需要vptr来访问vtable，可这个时候vptr还没产生。 因此，构造函数不可以为虚函数。\n * 我们之所以使用虚函数，是因为需要在信息不全的情况下进行多态运行。而构造函数是用来初始化实例的，实例的类型必须是明确的。\n * 因此，构造函数没有必要被声明为虚函数。\n * 尽管构造函数不可以为虚函数，但是有些场景下我们确实需要 “Virtual Copy Constructor”。 “虚复制构造函数”的说法并不严谨，其只是一个实现了对象复制的功能的类内函数。 举一个应用场景，比如剪切板功能。 复制内容作为基类，但派生类可能包含文字、图片、视频等等。 我们只有在程序运行的时候才知道我们需要复制的具体是什么类型的数据。\n *\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\n#include <iostream> \nusing namespace std; \n\n//// LIBRARY SRART \nclass Base \n{ \n    public: \n        Base() { } \n\n        virtual // Ensures to invoke actual object destructor \n            ~Base() { } \n\n        virtual void ChangeAttributes() = 0; \n\n        // The \"Virtual Constructor\" \n        static Base *Create(int id); \n\n        // The \"Virtual Copy Constructor\" \n        virtual Base *Clone() = 0; \n}; \n\nclass Derived1 : public Base \n{ \n    public: \n        Derived1() \n        { \n            cout << \"Derived1 created\" << endl; \n        } \n\n        Derived1(const Derived1& rhs) \n        { \n            cout << \"Derived1 created by deep copy\" << endl; \n        } \n\n        ~Derived1() \n        { \n            cout << \"~Derived1 destroyed\" << endl; \n        } \n\n        void ChangeAttributes() \n        { \n            cout << \"Derived1 Attributes Changed\" << endl; \n        } \n\n        Base *Clone() \n        { \n            return new Derived1(*this); \n        } \n}; \n\nclass Derived2 : public Base \n{ \n    public: \n        Derived2() \n        { \n            cout << \"Derived2 created\" << endl; \n        } \n\n        Derived2(const Derived2& rhs) \n        { \n            cout << \"Derived2 created by deep copy\" << endl; \n        } \n\n        ~Derived2() \n        { \n            cout << \"~Derived2 destroyed\" << endl; \n        } \n\n        void ChangeAttributes() \n        { \n            cout << \"Derived2 Attributes Changed\" << endl; \n        } \n\n        Base *Clone() \n        { \n            return new Derived2(*this); \n        } \n}; \n\nclass Derived3 : public Base \n{ \n    public: \n        Derived3() \n        { \n            cout << \"Derived3 created\" << endl; \n        } \n\n        Derived3(const Derived3& rhs) \n        { \n            cout << \"Derived3 created by deep copy\" << endl; \n        } \n\n        ~Derived3() \n        { \n            cout << \"~Derived3 destroyed\" << endl; \n        } \n\n        void ChangeAttributes() \n        { \n            cout << \"Derived3 Attributes Changed\" << endl; \n        } \n\n        Base *Clone() \n        { \n            return new Derived3(*this); \n        } \n}; \n\n// We can also declare \"Create\" outside Base. \n// But is more relevant to limit it's scope to Base \nBase *Base::Create(int id) \n{ \n    // Just expand the if-else ladder, if new Derived class is created \n    // User need not be recompiled to create newly added class objects \n\n    if( id == 1 ) \n    { \n        return new Derived1; \n    } \n    else if( id == 2 ) \n    { \n        return new Derived2; \n    } \n    else\n    { \n        return new Derived3; \n    } \n} \n//// LIBRARY END \n\n//// UTILITY SRART \nclass User \n{ \n    public: \n        User() : pBase(0) \n    { \n        // Creates any object of Base heirarchey at runtime \n\n        int input; \n\n        cout << \"Enter ID (1, 2 or 3): \"; \n        cin >> input; \n\n        while( (input != 1) && (input != 2) && (input != 3) ) \n        { \n            cout << \"Enter ID (1, 2 or 3 only): \"; \n            cin >> input; \n        } \n\n        // Create objects via the \"Virtual Constructor\" \n        pBase = Base::Create(input); \n    } \n\n        ~User() \n        { \n            if( pBase ) \n            { \n                delete pBase; \n                pBase = 0; \n            } \n        } \n\n        void Action() \n        { \n            // Duplicate current object \n            Base *pNewBase = pBase->Clone(); \n\n            // Change its attributes \n            pNewBase->ChangeAttributes(); \n\n            // Dispose the created object \n            delete pNewBase; \n        } \n\n    private: \n        Base *pBase; \n}; \n\n//// UTILITY END \n\n//// Consumer of User (UTILITY) class \nint main() \n{ \n    User *user = new User(); \n\n    user->Action(); \n\n    delete user; \n} \n\n"
  },
  {
    "path": "english/basic_content/virtual/set3/vir_de.cpp",
    "content": "/**\n * @file vir_de.cpp\n * @brief 派生类的析构函数没有被调用!\n * 输出结果：\n *      Constructing base\n *      Constructing derived\n *      Destructing base\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\n// CPP program without virtual destructor  \n// causing undefined behavior \n#include<iostream> \n\nusing namespace std; \n\nclass base { \n    public: \n        base()      \n        { cout<<\"Constructing base \\n\"; } \n        ~base() \n        { cout<<\"Destructing base \\n\"; }      \n}; \n\nclass derived: public base { \n    public: \n        derived()      \n        { cout<<\"Constructing derived \\n\"; } \n        ~derived() \n        { cout<<\"Destructing derived \\n\"; } \n}; \n\nint main(void) \n{ \n    derived *d = new derived();   \n    base *b = d; \n    delete b; \n    return 0; \n} \n"
  },
  {
    "path": "english/basic_content/virtual/set3/virtual_function.cpp",
    "content": "/**\n * @file virtual_function.cpp\n * @brief 虚函数可以被私有化，但有一些细节需要注意。\n * 基类指针指向继承类对象，则调用继承类对象的函数；\n * int main()必须声明为Base类的友元，否则编译失败。 编译器报错： ptr无法访问私有函数。 \n * 当然，把基类声明为public， 继承类为private，该问题就不存在了。----> 见另外一个例子！\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\n#include<iostream> \nusing namespace std; \n\nclass Derived; \n\nclass Base { \n    private: \n        virtual void fun() { cout << \"Base Fun\"; } \n        friend int main(); \n}; \n\nclass Derived: public Base { \n    public: \n        void fun() { cout << \"Derived Fun\"; } \n}; \n\nint main() \n{ \n    Base *ptr = new Derived; \n    ptr->fun(); \n    return 0; \n}\n"
  },
  {
    "path": "english/basic_content/virtual/set3/virtual_function1.cpp",
    "content": "#include<iostream> \nusing namespace std; \n\nclass Derived; \n\nclass Base { \n    public: \n        virtual void fun() { cout << \"Base Fun\"; } \n     //   friend int main(); \n}; \n\nclass Derived: public Base { \n    private: \n        void fun() { cout << \"Derived Fun\"; } \n}; \n\nint main() \n{ \n    Base *ptr = new Derived; \n    ptr->fun(); \n    return 0; \n}\n"
  },
  {
    "path": "english/basic_content/virtual/set3/virtual_inline.cpp",
    "content": "/**\n * @file virtual_inline.cpp\n * @brief 通常类成员函数都会被编译器考虑是否进行内联。 \n * 但通过基类指针或者引用调用的虚函数必定不能被内联。\n * 当然，实体对象调用虚函数或者静态调用时可以被内联，虚析构函数的静态调用也一定会被内联展开。\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\n#include <iostream> \nusing namespace std; \nclass Base \n{ \n    public: \n        virtual void who() \n        { \n            cout << \"I am Base\\n\"; \n        } \n}; \nclass Derived: public Base \n{ \n    public: \n        void who() \n        {  \n            cout << \"I am Derived\\n\"; \n        } \n}; \n\nint main() \n{ \n    // note here virtual function who() is called through \n    // object of the class (it will be resolved at compile \n    // time) so it can be inlined. \n    Base b; \n    b.who(); \n\n    // Here virtual function is called through pointer, \n    // so it cannot be inlined \n    Base *ptr = new Derived(); \n    ptr->who(); \n\n    return 0; \n} \n"
  },
  {
    "path": "english/basic_content/virtual/set4/rtti.cpp",
    "content": "/**\n * @file rtti.cpp\n * @brief 在面向对象程序设计中，有时我们需要在运行时查询一个对象是否能作为某种多态类型使用。与Java的instanceof，以及C#的as、is运算符类似，C++提供了dynamic_cast函数用于动态转型。相比C风格的强制类型转换和C++ reinterpret_cast，dynamic_cast提供了类型安全检查，是一种基于能力查询(Capability Query)的转换，所以在多态类型间进行转换更提倡采用dynamic_cast\n * @author 光城\n * @version v1\n * @date 2019-07-24\n */\n\n// CPP program to illustrate  \n// // Run Time Type Identification  \n#include<iostream>\n#include<typeinfo>\nusing namespace std; \nclass B { virtual void fun() {} }; \nclass D: public B { }; \n\nint main() \n{ \n    B *b = new D;  // 向上转型\n    B &obj = *b;\n    D *d = dynamic_cast<D*>(b);   // 向下转型\n    if(d != NULL) \n        cout << \"works\"<<endl; \n    else\n        cout << \"cannot cast B* to D*\"; \n \n    try {\n        D& dobj = dynamic_cast<D&>(obj);  \n        cout << \"works\"<<endl; \n    } catch (bad_cast bc) { // ERROR\n        cout<<bc.what()<<endl;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "english/basic_content/virtual/set4/warn_rtti.cpp",
    "content": "// 在使用时需要注意：被转换对象obj的类型T1必须是多态类型，即T1必须公有继承自其它类，或者T1拥有虚函数（继承或自定义）。若T1为非多态类型，使用dynamic_cast会报编译错误。\n\n// A为非多态类型 \n\nclass A{\n\n};\n\n//B为多态类型\n\nclass B{ \n\n    public: virtual ~B(){}\n\n};\n\n//D为多态类型\n\nclass D: public A{\n\n};\n\n//E为非多态类型\n\nclass E : private A{\n\n};\n\n//F为多态类型\n\nclass F : private B{\n\n}\n\n\n"
  },
  {
    "path": "english/basic_content/volatile/README.md",
    "content": "## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n## 1.volatile\n\n被 `volatile` 修饰的变量，在对其进行读写操作时，会引发一些**可观测的副作用**。而这些可观测的副作用，是由**程序之外的因素决定的**。\n\n## 2.volatile应用\n（1）并行设备的硬件寄存器（如状态寄存器）。\n假设要对一个设备进行初始化，此设备的某一个寄存器为0xff800000。\n\n```c++\nint  *output = (unsigned  int *)0xff800000; //定义一个IO端口；  \nint   init(void)  \n{  \n    int i;  \n    for(i=0;i< 10;i++)\n    {  \n    *output = i;  \n    }  \n}\n```\n经过编译器优化后，编译器认为前面循环半天都是废话，对最后的结果毫无影响，因为最终只是将output这个指针赋值为 9，所以编译器最后给你编译编译的代码结果相当于：\n```c++\nint  init(void)  \n{  \n    *output = 9;  \n}\n```\n如果你对此外部设备进行初始化的过程是必须是像上面代码一样顺序的对其赋值，显然优化过程并不能达到目的。反之如果你不是对此端口反复写操作，而是反复读操作，其结果是一样的，编译器在优化后，也许你的代码对此地址的读操作只做了一次。然而从代码角度看是没有任何问题的。这时候就该使用volatile通知编译器这个变量是一个不稳定的，在遇到此变量时候不要优化。\n\n（2）一个中断服务子程序中访问到的变量；\n\n```c++\nstatic int i=0;\n\nint main()\n{\n    while(1)\n    {\n    if(i) dosomething();\n    }\n}\n\n/* Interrupt service routine */\nvoid IRS()\n{\n\ti=1;\n}\n```\n上面示例程序的本意是产生中断时，由中断服务子程序IRS响应中断，变更程序变量i，使在main函数中调用dosomething函数，但是，由于编译器判断在main函数里面没有修改过i，因此可能只执行一次对从i到某寄存器的读操作，然后每次if判断都只使用这个寄存器里面的“i副本”，导致dosomething永远不会被调用。如果将变量i加上volatile修饰，则编译器保证对变量i的读写操作都不会被优化，从而保证了变量i被外部程序更改后能及时在原程序中得到感知。\n\n（3）多线程应用中被多个任务共享的变量。\n当多个线程共享某一个变量时，该变量的值会被某一个线程更改，应该用 volatile 声明。作用是防止编译器优化把变量从内存装入CPU寄存器中，当一个线程更改变量后，未及时同步到其它线程中导致程序出错。volatile的意思是让编译器每次操作该变量时一定要从内存中真正取出，而不是使用已经存在寄存器中的值。示例如下：\n```c++\nvolatile  bool bStop=false;  //bStop 为共享全局变量  \n//第一个线程\nvoid threadFunc1()\n{\n    ...\n    while(!bStop){...}\n}\n//第二个线程终止上面的线程循环\nvoid threadFunc2()\n{\n    ...\n    bStop = true;\n}\n```\n要想通过第二个线程终止第一个线程循环，如果bStop不使用volatile定义，那么这个循环将是一个死循环，因为bStop已经读取到了寄存器中，寄存器中bStop的值永远不会变成FALSE，加上volatile，程序在执行时，每次均从内存中读出bStop的值，就不会死循环了。\n\n是否了解volatile的应用场景是区分C/C++程序员和嵌入式开发程序员的有效办法，搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道，这些都要求用到volatile变量，不懂得volatile将会带来程序设计的灾难。\n\n## 3.volatile常见问题\n下面的问题可以看一下面试者是不是直正了解volatile。\n（1）一个参数既可以是const还可以是volatile吗？为什么？\n可以。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。\n\n（2）一个指针可以是volatile吗？为什么？\n可以。尽管这并不常见。一个例子是当一个中断服务子程序修该一个指向一个buffer的指针时。\n\n（3）下面的函数有什么错误？\n```c++\nint square(volatile int *ptr) \n{ \nreturn *ptr * *ptr; \n} \n```\n这段代码有点变态，其目的是用来返回指针ptr指向值的平方，但是，由于ptr指向一个volatile型参数，编译器将产生类似下面的代码：\n```c++\nint square(volatile int *ptr) \n{ \nint a,b; \na = *ptr; \nb = *ptr; \nreturn a * b; \n} \n```\n由于*ptr的值可能被意想不到地改变，因此a和b可能是不同的。结果，这段代码可能返回的不是你所期望的平方值！正确的代码如下：\n```c++\nlong square(volatile int *ptr) \n{ \nint a=*ptr; \nreturn a * a; \n} \n```\n## 4.volatile使用\n\n- volatile 关键字是一种类型修饰符，用它声明的类型变量表示可以被某些编译器未知的因素（操作系统、硬件、其它线程等）更改。所以使用 volatile 告诉编译器不应对这样的对象进行优化。\n\n- volatile 关键字声明的变量，每次访问时都必须从内存中取出值（没有被 volatile 修饰的变量，可能由于编译器的优化，从 CPU 寄存器中取值）\n- const 可以是 volatile （如只读的状态寄存器）\n- 指针可以是 volatile\n\n代码学习：\n\n- [noopt_vola.cpp](./noopt_vola.cpp)\n- [volatile.cpp](./volatile.cpp)\n\n"
  },
  {
    "path": "english/basic_content/volatile/noopt_vola.cpp",
    "content": "/* Compile code without optimization option */\n#include <stdio.h> \nint main(void) \n{ \n    const int local = 10; \n    int *ptr = (int*) &local; \n\n    printf(\"Initial value of local : %d \\n\", local); \n\n    *ptr = 100; \n\n    printf(\"Modified value of local: %d \\n\", local); \n\n    return 0; \n} \n"
  },
  {
    "path": "english/basic_content/volatile/volatile.cpp",
    "content": "/* Compile code with optimization option */\n#include <stdio.h> \n\nint main(void) \n{ \n    const volatile int local = 10; \n    int *ptr = (int*) &local; \n\n    printf(\"Initial value of local : %d \\n\", local); \n\n    *ptr = 100; \n\n    printf(\"Modified value of local: %d \\n\", local); \n\n    return 0; \n} \n"
  },
  {
    "path": "english/basic_content/vptr_vtable/README.md",
    "content": "# 深入浅出C++虚函数的vptr与vtable\n\n## 关于作者：\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n\n\n## 1.基础理论\n\n为了实现虚函数，C ++使用一种称为虚拟表的特殊形式的后期绑定。该虚拟表是用于解决在动态/后期绑定方式的函数调用函数的查找表。虚拟表有时会使用其他名称，例如“vtable”，“虚函数表”，“虚方法表”或“调度表”。\n\n虚拟表实际上非常简单，虽然用文字描述有点复杂。首先，**每个使用虚函数的类（或者从使用虚函数的类派生）都有自己的虚拟表**。该表只是编译器在编译时设置的静态数组。虚拟表包含可由类的对象调用的每个虚函数的一个条目。此表中的每个条目只是一个函数指针，指向该类可访问的派生函数。\n\n其次，编译器还会添加一个隐藏指向基类的指针，我们称之为vptr。vptr在创建类实例时自动设置，以便指向该类的虚拟表。与this指针不同，this指针实际上是编译器用来解析自引用的函数参数，vptr是一个真正的指针。\n\n因此，它使每个类对象的分配大一个指针的大小。这也意味着vptr由派生类继承，这很重要。\n\n## 2.实现与内部结构\n\n下面我们来看自动与手动操纵vptr来获取地址与调用虚函数！\n\n开始看代码之前，为了方便大家理解，这里给出调用图：\n\n![base](./img/base.jpg)\n\n代码全部遵循标准的注释风格，相信大家看了就会明白，不明白的话，可以留言！\n\n```c++\n/**\n * @file vptr1.cpp\n * @brief C++虚函数vptr和vtable\n * 编译：g++ -g -o vptr vptr1.cpp -std=c++11\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include <iostream>\n#include <stdio.h>\nusing namespace std;\n\n/**\n * @brief 函数指针\n */\ntypedef void (*Fun)();\n\n/**\n * @brief 基类\n */\nclass Base\n{\n    public:\n        Base(){};\n        virtual void fun1()\n        {\n            cout << \"Base::fun1()\" << endl;\n        }\n        virtual void fun2()\n        {\n            cout << \"Base::fun2()\" << endl;\n        }\n        virtual void fun3(){}\n        ~Base(){};\n};\n\n/**\n * @brief 派生类\n */\nclass Derived: public Base\n{\n    public:\n        Derived(){};\n        void fun1()\n        {\n            cout << \"Derived::fun1()\" << endl;\n        }\n        void fun2()\n        {\n            cout << \"DerivedClass::fun2()\" << endl;\n        }\n        ~Derived(){};\n};\n/**\n * @brief 获取vptr地址与func地址,vptr指向的是一块内存，这块内存存放的是虚函数地址，这块内存就是我们所说的虚表\n *\n * @param obj\n * @param offset\n *\n * @return \n */\nFun getAddr(void* obj,unsigned int offset)\n{\n    cout<<\"=======================\"<<endl;\n    void* vptr_addr = (void *)*(unsigned long *)obj;  //64位操作系统，占8字节，通过*(unsigned long *)obj取出前8字节，即vptr指针\n    printf(\"vptr_addr:%p\\n\",vptr_addr);\n    \n    /**\n     * @brief 通过vptr指针访问virtual table，因为虚表中每个元素(虚函数指针)在64位编译器下是8个字节，因此通过*(unsigned long *)vptr_addr取出前8字节，\n     * 后面加上偏移量就是每个函数的地址！\n     */\n    void* func_addr = (void *)*((unsigned long *)vptr_addr+offset);\n    printf(\"func_addr:%p\\n\",func_addr);\n    return (Fun)func_addr;\n}\nint main(void)\n{\n    Base ptr;\n    Derived d;\n    Base *pt = new Derived(); // 基类指针指向派生类实例\n    Base &pp = ptr; // 基类引用指向基类实例\n    Base &p = d; // 基类引用指向派生类实例\n    cout<<\"基类对象直接调用\"<<endl;\n    ptr.fun1();\n    cout<<\"基类对象调用基类实例\"<<endl;\n    pp.fun1(); \n    cout<<\"基类指针指向基类实例并调用虚函数\"<<endl;\n    pt->fun1();\n    cout<<\"基类引用指向基类实例并调用虚函数\"<<endl;\n    p.fun1();\n    \n    // 手动查找vptr 和 vtable\n    Fun f1 = getAddr(pt, 0);\n    (*f1)();\n    Fun f2 = getAddr(pt, 1);\n    (*f2)();\n    delete pt;\n    return 0;\n}\n```\n\n运行结果：\n\n```\n基类对象直接调用\nBase::fun1()\n基类引用指向派生类实例\nBase::fun1()\n基类指针指向派生类实例并调用虚函数\nDerived::fun1()\n基类引用指向基类实例并调用虚函数\nDerived::fun1()\n=======================\nvptr_addr:0x401130\nfunc_addr:0x400ea8\nDerived::fun1()\n=======================\nvptr_addr:0x401130\nfunc_addr:0x400ed4\nDerivedClass::fun2()\n```\n\n我们发现C++的动态多态性是通过虚函数来实现的。简单的说，通过virtual函数，指向子类的基类指针可以调用子类的函数。例如，上述通过基类指针指向派生类实例，并调用虚函数，将上述代码简化为：\n\n```c++\nBase *pt = new Derived(); // 基类指针指向派生类实例\ncout<<\"基类指针指向派生类实例并调用虚函数\"<<endl;\npt->fun1();\n```\n\n其过程为：首先程序识别出fun1()是个虚函数，其次程序使用pt->vptr来获取Derived的虚拟表。第三，它查找Derived虚拟表中调用哪个版本的fun1()。这里就可以发现调用的是Derived::fun1()。因此pt->fun1()被解析为Derived::fun1()!\n\n除此之外，上述代码大家会看到，也包含了手动获取vptr地址，并调用vtable中的函数，那么我们一起来验证一下上述的地址与真正在自动调用vtable中的虚函数，比如上述`pt->fun1()`的时候，是否一致！\n\n这里采用gdb调试，在编译的时候记得加上`-g`。\n\n通过`gdb vptr`进入gdb调试页面，然后输入`b Derived::fun1`对fun1打断点，然后通过输入r运行程序到断点处，此时我们需要查看调用栈中的内存地址，通过`disassemable fun1`可以查看当前有关fun1中的相关汇编代码，我们看到了`0x0000000000400ea8`，然后再对比上述的结果会发现与手动调用的fun1一致，fun2类似，以此证明代码正确!\n\ngdb调试信息如下：\n\n```c++\n(gdb) b Derived::fun1\nBreakpoint 1 at 0x400eb4: file vptr1.cpp, line 23.\n(gdb) r\nStarting program: /home/light/Program/CPlusPlusThings/virtual/pure_virtualAndabstract_class/vptr \n基类对象直接调用\nBase::fun1()\n基类引用指向派生类实例\nBase::fun1()\n基类指针指向派生类实例并调用虚函数\n\nBreakpoint 1, Derived::fun1 (this=0x614c20) at vptr1.cpp:23\n23\t            cout << \"Derived::fun1()\" << endl;\n(gdb) disassemble fun1\nDump of assembler code for function Derived::fun1():\n   0x0000000000400ea8 <+0>:\tpush   %rbp\n   0x0000000000400ea9 <+1>:\tmov    %rsp,%rbp\n   0x0000000000400eac <+4>:\tsub    $0x10,%rsp\n   0x0000000000400eb0 <+8>:\tmov    %rdi,-0x8(%rbp)\n=> 0x0000000000400eb4 <+12>:\tmov    $0x401013,%esi\n   0x0000000000400eb9 <+17>:\tmov    $0x602100,%edi\n   0x0000000000400ebe <+22>:\tcallq  0x4009d0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>\n   0x0000000000400ec3 <+27>:\tmov    $0x400a00,%esi\n   0x0000000000400ec8 <+32>:\tmov    %rax,%rdi\n   0x0000000000400ecb <+35>:\tcallq  0x4009f0 <_ZNSolsEPFRSoS_E@plt>\n   0x0000000000400ed0 <+40>:\tnop\n   0x0000000000400ed1 <+41>:\tleaveq \n   0x0000000000400ed2 <+42>:\tretq   \nEnd of assembler dump.\n(gdb) disassemble fun2\nDump of assembler code for function Derived::fun2():\n   0x0000000000400ed4 <+0>:\tpush   %rbp\n   0x0000000000400ed5 <+1>:\tmov    %rsp,%rbp\n   0x0000000000400ed8 <+4>:\tsub    $0x10,%rsp\n   0x0000000000400edc <+8>:\tmov    %rdi,-0x8(%rbp)\n   0x0000000000400ee0 <+12>:\tmov    $0x401023,%esi\n   0x0000000000400ee5 <+17>:\tmov    $0x602100,%edi\n   0x0000000000400eea <+22>:\tcallq  0x4009d0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>\n   0x0000000000400eef <+27>:\tmov    $0x400a00,%esi\n   0x0000000000400ef4 <+32>:\tmov    %rax,%rdi\n   0x0000000000400ef7 <+35>:\tcallq  0x4009f0 <_ZNSolsEPFRSoS_E@plt>\n   0x0000000000400efc <+40>:\tnop\n   0x0000000000400efd <+41>:\tleaveq \n   0x0000000000400efe <+42>:\tretq   \nEnd of assembler dump.\n```\n\n"
  },
  {
    "path": "english/basic_content/vptr_vtable/vptr1.cpp",
    "content": "/**\n * @file vptr1.cpp\n * @brief C++虚函数vptr和vtable\n * 编译：g++ -g -o vptr vptr1.cpp -std=c++11\n * @author 光城\n * @version v1\n * @date 2019-07-20\n */\n\n#include <iostream>\n#include <stdio.h>\nusing namespace std;\n\n/**\n * @brief 函数指针\n */\ntypedef void (*Fun)();\n\n\n/**\n * @brief 基类\n */\nclass Base\n{\n    public:\n        Base(){};\n        virtual void fun1()\n        {\n            cout << \"Base::fun1()\" << endl;\n        }\n        virtual void fun2()\n        {\n            cout << \"Base::fun2()\" << endl;\n        }\n        virtual void fun3(){}\n        ~Base(){};\n};\n\n\n/**\n * @brief 派生类\n */\nclass Derived: public Base\n{\n    public:\n        Derived(){};\n        void fun1()\n        {\n            cout << \"Derived::fun1()\" << endl;\n        }\n        void fun2()\n        {\n            cout << \"DerivedClass::fun2()\" << endl;\n        }\n        ~Derived(){};\n};\n\n/**\n * @brief 获取vptr地址与func地址,vptr指向的是一块内存，这块内存存放的是虚函数地址，这块内存就是我们所说的虚表\n *\n * @param obj\n * @param offset\n *\n * @return \n */\nFun getAddr(void* obj,unsigned int offset)\n{\n    cout<<\"=======================\"<<endl;\n    void* vptr_addr = (void *)*(unsigned long *)obj;  //64位操作系统，占8字节，通过*(unsigned long *)obj取出前8字节，即vptr指针\n    printf(\"vptr_addr:%p\\n\",vptr_addr);\n\n    /**\n     * @brief 通过vptr指针访问virtual table，因为虚表中每个元素(虚函数指针)在64位编译器下是8个字节，因此通过*(unsigned long *)vptr_addr取出前8字节，\n     * 后面加上偏移量就是每个函数的地址！\n     */\n    void* func_addr = (void *)*((unsigned long *)vptr_addr+offset);\n    printf(\"func_addr:%p\\n\",func_addr);\n    return (Fun)func_addr;\n}\nint main(void)\n{\n    Base ptr;\n    Derived d;\n    Base *pt = new Derived(); // 基类指针指向派生类实例\n    Base &pp = ptr; // 基类引用指向基类实例\n    Base &p = d; // 基类引用指向派生类实例\n    cout<<\"基类对象直接调用\"<<endl;\n    ptr.fun1();\n    cout<<\"基类引用指向基类实例\"<<endl;\n    pp.fun1(); \n    cout<<\"基类指针指向派生类实例并调用虚函数\"<<endl;\n    pt->fun1();\n    cout<<\"基类引用指向基类实例并调用虚函数\"<<endl;\n    p.fun1();\n\n\n    // 手动查找vptr 和 vtable\n    Fun f1 = getAddr(pt, 0);\n    (*f1)();\n    Fun f2 = getAddr(pt, 1);\n    (*f2)();\n\n    delete pt;\n    return 0;\n}\n"
  },
  {
    "path": "extension/some_problem/string_int.md",
    "content": "# C++中如何将string类型转换为int类型？\n\n个人公众号：\n\n![](../img/wechat.jpg)\n\n首先提出解决方案：\n\n- atoi\n- strtol\n- stoi\n\n这几个有什么不同呢？下面测试对比。\n\n> C语言风格函数\n\natoi与strtol对比：\n\n```cpp\nstring str = \"16s\";\nint a = atoi(str.c_str());\nint b = strtol(str.c_str(), nullptr, 10);\n```\n\n输出：\n\n```\natoi的结果为:16\nstrtol的结果为:16\n```\n\n这两个函数都是从字符串开始寻找数字或者正负号或者小数点,遇到非法字符终止。\n\n所以到上述s字符就不输出了，提前结束，也就是说当你的字符串不是数字的时候，或者小数点等非数字，不会报异常！直接输出0.\n\n例如：\n\n```cpp\nstring str = \"asdsa\";\nint a = atoi(str.c_str());\nint b = strtol(str.c_str(), nullptr, 10);\n```\n\n输出：\n\n```\n0\n0\n```\n\nstrtol相比与atoi来说，支持多种进制转换,例如8进制等\n\n例如：\n\n```\nint b = strtol(str.c_str(), nullptr, 8);\n```\n\n> C++风格\n\n在C++中可以使用stoi来转int，这个函数相比于前两个一个最大特点是：异常！\n\n我们知道C++相比于C语言多了异常，这也是这个函数在C++中具有的最显著功能。\n\n例如：\n\n```cpp\nstring str1 = \"asq,\";\n//    int c = stoi(str1);    // 报异常\nstring str2 = \"12312\";\nint c = stoi(str2);     // ok\ncout << c << endl;\n```\n\n异常如下：\n\n```\nterminate called after throwing an instance of 'std::invalid_argument'\nwhat():  stoi\n```\n\n> 自定义\n\n也就是自己写呗，如下：\n\n```cpp\nint stringToInt(const string &s) {\n    int v;\n    stringstream ss;\n    ss << s;\n    ss >> v;\n    return v;\n}\nint main() {\n    int i = stringToInt(\"2.3\");\n    cout<<i<<endl;\n}\n```"
  },
  {
    "path": "learn_class/modern_cpp_30/RAII/BUILD",
    "content": "# please run `bazel run //learn_class/modern_cpp_30/RAII:stack`\n# please run `bazel run //learn_class/modern_cpp_30/RAII:heap`\n# please run `bazel run //learn_class/modern_cpp_30/RAII:RAII`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"stack\",\n    srcs = [\"stack.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"heap\",\n    srcs = [\"heap.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"RAII\",\n    srcs = [\"RAII.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n"
  },
  {
    "path": "learn_class/modern_cpp_30/RAII/RAII.cpp",
    "content": "#include <fstream>\n#include <iostream>\n#include <mutex>\nusing namespace std;\n\nenum class shape_type {\n  circle,\n  triangle,\n  rectangle,\n};\n\nclass shape {\npublic:\n  shape() { cout << \"shape\" << endl; }\n\n  virtual void print() { cout << \"I am shape\" << endl; }\n\n  virtual ~shape() {}\n};\n\nclass circle : public shape {\npublic:\n  circle() { cout << \"circle\" << endl; }\n\n  void print() { cout << \"I am circle\" << endl; }\n};\n\nclass triangle : public shape {\npublic:\n  triangle() { cout << \"triangle\" << endl; }\n\n  void print() { cout << \"I am triangle\" << endl; }\n};\n\nclass rectangle : public shape {\npublic:\n  rectangle() { cout << \"rectangle\" << endl; }\n\n  void print() { cout << \"I am rectangle\" << endl; }\n};\n\n// 利用多态 上转 如果返回值为shape,会存在对象切片问题。\nshape *create_shape(shape_type type) {\n  switch (type) {\n  case shape_type::circle:\n    return new circle();\n  case shape_type::triangle:\n    return new triangle();\n  case shape_type::rectangle:\n    return new rectangle();\n  }\n}\n\nclass shape_wrapper {\npublic:\n  explicit shape_wrapper(shape *ptr = nullptr) : ptr_(ptr) {}\n\n  ~shape_wrapper() { delete ptr_; }\n\n  shape *get() const { return ptr_; }\n\nprivate:\n  shape *ptr_;\n};\n\nvoid foo() {\n  shape_wrapper ptr(create_shape(shape_type::circle));\n  ptr.get()->print();\n}\n\nint main() {\n\n  // 第一种方式\n  shape *sp = create_shape(shape_type::circle);\n  sp->print();\n  delete sp;\n\n  // 第二种方式\n  foo();\n\n  return 0;\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/RAII/heap.cpp",
    "content": "#include <iostream>\n\nusing namespace std;\n\nclass bar {};\n\n// java 程序员风格\nvoid foo() {\n  cout << \"method 1\" << endl;\n  bar *ptr = new bar();\n  delete ptr;\n}\n\nbar *make_bar() {\n  bar *ptr = nullptr;\n  try {\n    ptr = new bar();\n  } catch (...) {\n    delete ptr;\n    throw;\n  }\n  return ptr;\n}\n// 独立出函数  分配和释放不在一个函数里\nvoid foo1() {\n  cout << \"method 2\" << endl;\n  bar *ptr = make_bar();\n  delete ptr;\n}\n\nint main() {\n  foo();\n  foo1();\n  return 0;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/RAII/stack.cpp",
    "content": "//\n// Created by light on 19-12-9.\n//\n#include <iostream>\nclass Obj {\npublic:\n  Obj() { puts(\"Obj()\"); }\n  ~Obj() { puts(\"~Obj()\"); }\n};\nvoid foo(int n) {\n  Obj obj;\n  if (n == 42)\n    throw \"life, the universe and everything\";\n}\n// 不管是否发生了异常，obj 的析构函数都会得到执行。\nint main() {\n  try {\n    foo(41);\n    foo(42);\n  } catch (const char *s) {\n    puts(s);\n  }\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/README.md",
    "content": "学习自：https://jguegant.github.io/blogs/tech/sfinae-introduction.html\n"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/SFINAE.cpp",
    "content": "//\n// Created by light on 20-1-5.\n//\n#include <iostream>\n\nusing namespace std;\n\ntemplate <typename T> class IsClassT {\nprivate:\n  typedef char One;\n  typedef struct {\n    char a[2];\n  } Two;\n\n  template <typename C> static One test(int C::*);\n\n  // Will be chosen if T is anything except a class.\n  template <typename C> static Two test(...);\n\npublic:\n  enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };\n  enum { No = !Yes };\n};\n\nstruct A {};\n\nint main() {\n  // 0不能转换为int int::*因为int不是类，所以它不能有成员指针。\n  cout << IsClassT<int>::Yes << endl;\n  cout << IsClassT<A>::Yes << endl;\n\n  return 0;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/auto.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n#include <iostream>\n\nbool f();\n\nauto test = f(); // Famous usage, auto deduced that test is a boolean, hurray!\n\n\n\n//                             vvv t wasn't declare at that point, it will be after as a parameter!\ntemplate<typename T>\ndecltype(t.serialize()) g(const T &t) {} // Compilation error\n\n// Less famous usage:\n//                    vvv auto delayed the return type specification!\n//                    vvv                vvv the return type is specified here and use t!\ntemplate<typename T>\nauto g(const T &t) -> decltype(t.serialize()) {} // No compilation error.\n\n\nauto myFunction() // Automagically figures out that myFunction returns ints.\n{\n    return int();\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/blending1.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n\n#include <iostream>\n#include \"structData.h\"\n\n\ntemplate<class T>\nstruct hasSerialize {\n    // We test if the type has serialize using decltype and declval.\n    // decltype将根据逗号表达式选择最后一个类型作为返回值\n    // 如果C有serialize将调用当前版本,没有serialize,则失败,此时编译时不报错,继续寻找重载函数,也就是SFINAE.\n    template<typename C>\n    static constexpr decltype(std::declval<C>().serialize(), bool()) test(int /* unused */) {\n        // We can return values, thanks to constexpr instead of playing with sizeof.\n        return true;\n    }\n\n    template<typename C>\n    static constexpr bool test(...) {\n        return false;\n    }\n\n    // int is used to give the precedence!\n    static constexpr bool value = test<T>(int());\n};\n\ntemplate<class T>\nstd::string serialize(const T &obj) {\n    // 不加constexpr 报错：error: no member named 'serialize' in 'A'.\n    if constexpr (hasSerialize<T>::value)\n        return obj.serialize();\n    else\n        return to_string(obj);\n}\n\nint main() {\n    A a;\n    B b;\n    C c;\n\n    // The following lines work like a charm!\n    std::cout << serialize(a) << std::endl;\n    std::cout << serialize(b) << std::endl;\n    std::cout << serialize(c) << std::endl;\n\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/blending2.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n\n#include <iostream>\n#include \"structData.h\"\n\n\ntemplate<typename T, typename=std::string>\nstruct hasSerialize : std::false_type {\n\n};\ntemplate<typename T>\nstruct hasSerialize<T, decltype(std::declval<T>().serialize())> : std::true_type {\n\n};\n\ntemplate<class T>\nstd::string serialize(const T &obj) {\n    // 不加constexpr 报错：error: no member named 'serialize' in 'A'.\n    if constexpr (hasSerialize<T>::value)   // c++17特性\n        return obj.serialize();\n    else\n        return to_string(obj);\n}\n\nint main() {\n    A a;\n    B b;\n    C c;\n\n    // The following lines work like a charm!\n    std::cout << serialize(a) << std::endl;\n    std::cout << serialize(b) << std::endl;\n    std::cout << serialize(c) << std::endl;\n\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/combiningAndGenius.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n\n#include <iostream>\n#include \"structData.h\"\n\n\ntemplate<class T>\nstruct hasSerialize {\n    // 编译时比较\n    typedef char yes[1];\n    typedef char no[2];\n    // 允许我们检查序列化确实是一种方法\n    // 第二个参数必须是第一个参数的类型\n    // 例如： reallyHas<int,10> 替换为 reallyHas<int,int 10> 并起作用\n    // 注意：它仅适用于整数常量和指针(因此函数指针可以使用)\n    // 例如：reallyHas<std::string (C::*)(), &C::serialize> 替换为\n    // reallyHas<std::string (C::*)(), std::string (C::*)() &C::serialize> 并起作用\n    template<typename U, U u>\n    struct reallyHas;\n\n    // std::string (C::*)() 是函数指针声明\n    template<typename C>\n    static yes &test(reallyHas<std::string (C::*)(), &C::serialize> * /*unused*/) {}\n\n    //  ()()const 函数指针 -> std::string serialize() const\n    template<typename C>\n    static yes &test(reallyHas<std::string (C::*)() const, &C::serialize> * /*unused*/) {}\n\n    // The famous C++ sink-hole.\n    // Note that sink-hole must be templated too as we are testing test<T>(0).\n    // If the method serialize isn't available, we will end up in this method.\n    template<typename>\n    static no &test(...) { /* dark matter */ }\n\n    //用作测试的返回值的常数。\n    //由于编译时评估的大小，因此实际上在这里完成了测试。\n    static const bool value = sizeof(test<T>(0)) == sizeof(yes);\n};\n\n// Using the previous A struct and hasSerialize helper.\nstruct D : A {\n    std::string serialize() const {\n        return \"I am a D!\";\n    }\n};\n\ntemplate<class T>\nbool testHasSerialize(const T & /*t*/) { return hasSerialize<T>::value; }\n\n// 不能够判断仿函数里面的serialize\nstruct E {\n    struct Functor {\n        std::string operator()() {\n            return \"I am a E!\";\n        }\n    };\n\n    Functor serialize;\n};\n\ntemplate<class T>\nstd::string serialize(const T &obj) {\n    // 不加constexpr 报错：error: no member named 'serialize' in 'A'.\n    if constexpr (hasSerialize<T>::value)\n        return obj.serialize();\n    else\n        return to_string(obj);\n}\n\nint main() {\n    // 检测结构体是否有serialize方法\n    // Using the struct A, B, C defined in the previous hasSerialize example.\n    std::cout << hasSerialize<A>::value << std::endl;\n    std::cout << hasSerialize<B>::value << std::endl;\n    std::cout << hasSerialize<C>::value << std::endl;\n\n\n    D d;\n    A &a = d; // Here we lost the type of d at compile time.\n    std::cout << testHasSerialize(d) << std::endl; // Output 1.\n    std::cout << testHasSerialize(a) << std::endl; // Output 0.\n\n    E e;\n    std::cout << e.serialize() << std::endl; // Succefully call the functor.\n    std::cout << testHasSerialize(e) << std::endl; // Output 0.\n\n\n    A a_;\n    B b_;\n    C c_;\n    std::cout << serialize(a_) << std::endl;\n    std::cout << serialize(b_) << std::endl;\n    std::cout << serialize(c_) << std::endl;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/constexpr.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n#include <iostream>\n\nconstexpr int factorial(int n) {\n    return n <= 1 ? 1 : (n * factorial(n - 1));\n}\n\nstruct testStruct : std::true_type {\n}; // Inherit from the true type.\n\nint main() {\n    int i = factorial(5); // Call to a constexpr function.\n    // Will be replace by a good compiler by:\n    // int i = 120;\n    std::cout << i << std::endl;\n\n\n    constexpr bool testVar = testStruct(); // Generate a compile-time testStruct.\n    bool test = testStruct::value; // Equivalent to: test = true;\n    std::cout << test << std::endl;\n    test = testVar; // true_type has a constexpr converter operator, equivalent to: test = true;\n    std::cout << test << std::endl;\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/decltype.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n#include <iostream>\nstruct Default {\n    int foo() const {return 1;}\n};\n\nstruct NonDefault {\n    NonDefault(const NonDefault&) {}\n    int foo() const {return 1;}\n};\n\nint main()\n{\n    decltype(Default().foo()) n1 = 1; // int n1\n//  decltype(NonDefault().foo()) n2 = n1; // error: no default constructor\n    decltype(std::declval<NonDefault>().foo()) n2 = n1; // int n2\n    std::cout << \"n2 = \" << n2 << '\\n';\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/fis_valid.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n\n#include <iostream>\n#include \"boost/hana.hpp\"\n#include \"structData.h\"\n\ntemplate<typename UnnamedType>\nstruct container {\n// Let's put the test in private.\nprivate:\n    // We use std::declval to 'recreate' an object of 'UnnamedType'.\n    // We use std::declval to also 'recreate' an object of type 'Param'.\n    // We can use both of these recreated objects to test the validity!\n    template<typename Param>\n    constexpr auto testValidity(int /* unused */)\n    -> decltype(std::declval<UnnamedType>()(std::declval<Param>()),boost::hana::true_c) {\n        // If substitution didn't fail, we can return a true_type.\n        return boost::hana::true_c;\n    }\n\n    template<typename Param>\n    constexpr std::false_type testValidity(...) {\n        // Our sink-hole returns a false_type.\n        return boost::hana::false_c;\n    }\n\npublic:\n    // A public operator() that accept the argument we wish to test onto the UnnamedType.\n    // Notice that the return type is automatic!\n    template<typename Param>\n    constexpr auto operator()(const Param &p) {\n        // The argument is forwarded to one of the two overloads.\n        // The SFINAE on the 'true_type' will come into play to dispatch.\n        // Once again, we use the int for the precedence.\n        return testValidity<Param>(int());\n    }\n};\n\ntemplate<typename UnnamedType>\nconstexpr auto is_valid(const UnnamedType &t) {\n    // We used auto for the return type: it will be deduced here.\n    return container<UnnamedType>();\n}\n\n// Check if a type has a serialize method.\nauto hasSerialize = is_valid([](auto &&x) -> decltype(x.serialize()) {});\n\n// Notice how I simply swapped the return type on the right?\ntemplate<class T>\nauto serialize(T &obj)\n-> typename std::enable_if<decltype(hasSerialize(obj))::value, std::string>::type {\n    return obj.serialize();\n}\n\ntemplate<class T>\nauto serialize(T &obj)\n-> typename std::enable_if<!decltype(hasSerialize(obj))::value, std::string>::type {\n    return to_string(obj);\n}\n\nint main() {\n    A a;\n    B b;\n    C c;\n    auto test = is_valid([](const auto &t) -> decltype(t.serialize()) {});\n\n    std::cout << test(a) << std::endl;\n    std::cout << test(b) << std::endl;\n    std::cout << test(c) << std::endl;\n\n\n    // The following lines work like a charm!\n    std::cout << serialize(a) << std::endl;\n    std::cout << serialize(b) << std::endl;\n    std::cout << serialize(c) << std::endl;\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/hana.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n// https://www.boost.org/doc/libs/1_62_0/libs/hana/doc/html/structboost_1_1hana_1_1type.html#a2d2e7e08e284f7e0bd1bd9c3ad0e0a2b\n#include <boost/hana.hpp>\n#include <vector>\n#include <string>\n\nnamespace hana = boost::hana;\n\n\nint main() {\n    // 检查成员\n    struct Person {\n        std::string name;\n    };\n    auto has_name = hana::is_valid([](auto &&p) -> decltype(p.name) {});\n\n    Person joe{\"Joe\"};\n    static_assert(has_name(joe), \"\");\n    static_assert(!has_name(1), \"\");\n    // 检查嵌套类型\n    auto has_value_type = hana::is_valid([](auto t) -> hana::type<typename decltype(t)::type::value_type> {});\n    static_assert(has_value_type(hana::type_c<std::vector<int>>), \"\");\n    static_assert(!has_value_type(hana::type_c<Person>), \"\");\n\n    return 0;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/is_valid.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n\n#include <iostream>\n#include \"structData.h\"\n\ntemplate<typename UnnamedType>\nstruct container {\n// Let's put the test in private.\nprivate:\n    // We use std::declval to 'recreate' an object of 'UnnamedType'.\n    // We use std::declval to also 'recreate' an object of type 'Param'.\n    // We can use both of these recreated objects to test the validity!\n    template<typename Param>\n    constexpr auto testValidity(int /* unused */)\n    -> decltype(std::declval<UnnamedType>()(std::declval<Param>()), std::true_type()) {\n        // If substitution didn't fail, we can return a true_type.\n        return std::true_type();\n    }\n\n    template<typename Param>\n    constexpr std::false_type testValidity(...) {\n        // Our sink-hole returns a false_type.\n        return std::false_type();\n    }\n\npublic:\n    // A public operator() that accept the argument we wish to test onto the UnnamedType.\n    // Notice that the return type is automatic!\n    template<typename Param>\n    constexpr auto operator()(const Param &p) {\n        // The argument is forwarded to one of the two overloads.\n        // The SFINAE on the 'true_type' will come into play to dispatch.\n        // Once again, we use the int for the precedence.\n        return testValidity<Param>(int());\n    }\n};\n\ntemplate<typename UnnamedType>\nconstexpr auto is_valid(const UnnamedType &t) {\n    // We used auto for the return type: it will be deduced here.\n    return container<UnnamedType>();\n}\n\n// Check if a type has a serialize method.\nauto hasSerialize = is_valid([](auto &&x) -> decltype(x.serialize()) {});\n\n// Notice how I simply swapped the return type on the right?\ntemplate<class T>\nauto serialize(T &obj)\n-> typename std::enable_if<decltype(hasSerialize(obj))::value, std::string>::type {\n    return obj.serialize();\n}\n\ntemplate<class T>\nauto serialize(T &obj)\n-> typename std::enable_if<!decltype(hasSerialize(obj))::value, std::string>::type {\n    return to_string(obj);\n}\n\nint main() {\n    A a;\n    B b;\n    C c;\n    auto test = is_valid([](const auto &t) -> decltype(t.serialize()) {});\n\n    std::cout << test(a) << std::endl;\n    std::cout << test(b) << std::endl;\n    std::cout << test(c) << std::endl;\n\n\n    // The following lines work like a charm!\n    std::cout << serialize(a) << std::endl;\n    std::cout << serialize(b) << std::endl;\n    std::cout << serialize(c) << std::endl;\n\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/lambda.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n#include <iostream>\n#include \"structData.h\"\n// Equivalent to:\nstruct l5UnamedType {\n    template<typename T>\n    auto operator()(T &t) const -> decltype(t.serialize()) // /!\\ This signature is nice for a SFINAE!\n    {\n        return t.serialize();\n    }\n};\n\n\nvoid fun(A a,B b,C c) {\n\n    // ***** Simple lambda unamed type *****\n    auto l4 = [](int a, int b) { return a + b; };\n    std::cout << l4(4, 5) << std::endl; // Output 9.\n\n    // Equivalent to:\n    struct l4UnamedType {\n        int operator()(int a, int b) const {\n            return a + b;\n        }\n    };\n\n    l4UnamedType l4Equivalent = l4UnamedType();\n    std::cout << l4Equivalent(4, 5) << std::endl; // Output 9 too.\n\n\n\n    // ***** auto parameters lambda unnamed type *****\n\n    // b's type is automagically deduced!\n    auto l5 = [](auto &t) -> decltype(t.serialize()) { return t.serialize(); };\n\n    std::cout << l5(b) << std::endl; // Output: I am a B!\n//    std::cout << l5(a) << std::endl; // Error: no member named 'serialize' in 'A'.\n\n\n    l5UnamedType l5Equivalent = l5UnamedType();\n\n    std::cout << l5Equivalent(b) << std::endl; // Output: I am a B!\n//    std::cout << l5Equivalent(a) << std::endl; // Error: no member named 'serialize' in 'A'.\n\n}\nint main() {\n    A a;\n    B b;\n    C c;\n    auto l1 = [](B &b) { return b.serialize(); }; // Return type figured-out by the return statement.\n    auto l3 = [](B &b) -> std::string { return b.serialize(); }; // Fixed return type.\n    auto l2 = [](B &b) -> decltype(b.serialize()) { return b.serialize(); }; // Return type dependant to the B type.\n\n    std::cout << l1(b) << std::endl; // Output: I am a B!\n    std::cout << l2(b) << std::endl; // Output: I am a B!\n    std::cout << l3(b) << std::endl; // Output: I am a B!\n\n    fun(a,b,c);\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/overload1.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n#include <iostream>\n\nvoid f(std::string s); // int can't be convert into a string.\nvoid f(double d); // int can be implicitly convert into a double, so this version could be selected, but...\nvoid f(int i); // ... this version using the type int directly is even more close!\n\n\n\nint main() {\n    f(1); // Call f(int i);\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/overload2.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n#include <iostream>\n\nstd::string f(...) // Variadic functions are so \"untyped\" that...\n{\n    return \"...\";\n}\n\ntemplate<typename T>\nstd::string f(const T &t)// ...this templated function got the precedence!\n{\n\n    return \"T\";\n}\n\nint main() {\n    std::cout << f<int>(1) << std::endl;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/p1SFINAE.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n\n/*\n The compiler will try this overload since it's less generic than the variadic.\n T will be replace by int which gives us void f(const int& t, int::iterator* b = nullptr);\n int doesn't have an iterator sub-type, but the compiler doesn't throw a bunch of errors.\n It simply tries the next overload.\n\n 编译器尝试此重载,因为模板化函数比可变参数函数更精确(通用)。T将被int取代，这将使我们得到\n void f(const int& t, int::iterator* b = nullptr); int 没有迭代器子类型，但是编译器不会抛出一堆错误。\n 它只是尝试下一个重载。\n*/\ntemplate<typename T>\nvoid f(const T &t, typename T::iterator *it = nullptr) {}\n\n// The sink-hole.\nvoid f(...) {}\n\nint main() {\n    f(1); // Calls void f(...) { }\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/p2SFINAE.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n\n\n// The compiler will be really unhappy when it will later discover the call to hahahaICrash.\n// 当以后发现对hahahaICrash的调用时，编译器将非常不满意。\ntemplate <typename T> void f(T t) { t.hahahaICrash(); }\nvoid f(...) { } // The sink-hole wasn't even considered.\n\nint main() {\n    f(1);\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/packis_valid.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n\n#include <iostream>\n#include \"structData.h\"\n\ntemplate <typename UnnamedType> struct container\n{\n// Let's put the test in private.\nprivate:\n    // We use std::declval to 'recreate' an object of 'UnnamedType'.\n    // We use std::declval to also 'recreate' an object of type 'Param'.\n    // We can use both of these recreated objects to test the validity!\n    template <typename... Params> constexpr auto test_validity(int /* unused */)\n    -> decltype(std::declval<UnnamedType>()(std::declval<Params>()...), std::true_type())\n    {\n        // If substitution didn't fail, we can return a true_type.\n        return std::true_type();\n    }\n\n    template <typename... Params> constexpr std::false_type test_validity(...)\n    {\n        // Our sink-hole returns a false_type.\n        return std::false_type();\n    }\n\npublic:\n    // A public operator() that accept the argument we wish to test onto the UnnamedType.\n    // Notice that the return type is automatic!\n    template <typename... Params> constexpr auto operator()(Params&& ...)\n    {\n        // The argument is forwarded to one of the two overloads.\n        // The SFINAE on the 'true_type' will come into play to dispatch.\n        return test_validity<Params...>(int());\n    }\n};\n\ntemplate <typename UnnamedType> constexpr auto is_valid(UnnamedType&& t)\n{\n    // We used auto for the return type: it will be deduced here.\n    return container<UnnamedType>();\n}\n\n// Check if a type has a serialize method.\nauto hasSerialize = is_valid([](auto &&x) -> decltype(x.serialize()) {});\n\n// Notice how I simply swapped the return type on the right?\ntemplate<class T>\nauto serialize(T &obj)\n-> typename std::enable_if<decltype(hasSerialize(obj))::value, std::string>::type {\n    return obj.serialize();\n}\n\ntemplate<class T>\nauto serialize(T &obj)\n-> typename std::enable_if<!decltype(hasSerialize(obj))::value, std::string>::type {\n    return to_string(obj);\n}\n\nint main() {\n    A a;\n    B b;\n    C c;\n    auto test = is_valid([](const auto &t) -> decltype(t.serialize()) {});\n\n    std::cout << test(a,b) << std::endl;\n    std::cout << test(b) << std::endl;\n    std::cout << test(c) << std::endl;\n\n\n    // The following lines work like a charm!\n    std::cout << serialize(a) << std::endl;\n    std::cout << serialize(b) << std::endl;\n    std::cout << serialize(c) << std::endl;\n\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/serialize.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n\n#include <boost/hana.hpp>\n#include <iostream>\n#include <string>\n#include \"structData.h\"\nusing namespace std;\n\nnamespace hana = boost::hana;\n// 检查类型是否有一个serialize方法\nauto hasSerialize = hana::is_valid([](auto &&x) -> decltype(x.serialize()) {});\n\n// 序列化任意对象\ntemplate<typename T>\nstd::string serialize(T const &obj) {\n    return hana::if_(hasSerialize(obj),\n                     [](auto &x) { return x.serialize(); },\n                     [](auto &x) { return to_string(x); }\n    )(obj);\n}\n\n\n\nint main() {\n    A a;\n    B b;\n    C c;\n    std::cout << serialize(a) << std::endl;\n    std::cout << serialize(b) << std::endl;\n    std::cout << serialize(c) << std::endl;\n}\n\n\n"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/sizeof1.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n\n#include <iostream>\n\ntypedef char type_test[42];\n\ntype_test &f() {}\n\nint main() {\n\n    // In the following lines f won't even be truly called but we can still access to the size of its return type.\n    // Thanks to the \"fake evaluation\" of the sizeof operator.\n    char arrayTest[sizeof(f())];\n    std::cout << sizeof(f()) << std::endl; // Output 42.\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/sizeof2.cpp",
    "content": "#include <iostream>\n\n//\n// Created by light on 20-1-6.\n//\n\ntypedef char yes; // Size: 1 byte.\ntypedef yes no[2]; // Size: 2 bytes.\n\n// Two functions using our type with different size.\nyes &f1() {}\nno &f2() {}\n\nint main() {\n    std::cout << sizeof(f1()) << std::endl;\n    std::cout << sizeof(f2()) << std::endl;\n    std::cout << (sizeof(f1()) == sizeof(f2())) << std::endl; // Output 0.\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/structData.h",
    "content": "//\n// Created by light on 20-1-6.\n//\n\n#ifndef MORDEN_C_STRUCTDATA_H\n#define MORDEN_C_STRUCTDATA_H\n// 类型A只有to_string 方法\nstruct A {\n};\n\nstd::string to_string(const A &) {\n    return \"I am A\";\n}\n\n// 类型B有serialize方法\nstruct B {\n    std::string serialize() const {\n        return \"I am B\";\n    }\n};\n\n// 类型C有个serialize数据成员与to_string方法\nstruct C {\n    std::string serialize;\n};\n\nstd::string to_string(const C &) {\n    return \"I am C\";\n}\n#endif //MORDEN_C_STRUCTDATA_H\n"
  },
  {
    "path": "learn_class/modern_cpp_30/SFINAE/sfinae paper/timeGenius.cpp",
    "content": "//\n// Created by light on 20-1-6.\n//\n\n#include <iostream>\n#include \"structData.h\"\n\n\ntemplate<class T>\nstruct hasSerialize {\n    // 编译时比较\n    typedef char yes[1];\n    typedef char no[2];\n    // 允许我们检查序列化确实是一种方法\n    // 第二个参数必须是第一个参数的类型\n    // 例如： reallyHas<int,10> 替换为 reallyHas<int,int 10> 并起作用\n    // 注意：它仅适用于整数常量和指针(因此函数指针可以使用)\n    // 例如：reallyHas<std::string (C::*)(), &C::serialize> 替换为\n    // reallyHas<std::string (C::*)(), std::string (C::*)() &C::serialize> 并起作用\n    template<typename U, U u>\n    struct reallyHas;\n\n    // std::string (C::*)() 是函数指针声明\n    template<typename C>\n    static yes &test(reallyHas<std::string (C::*)(), &C::serialize> * /*unused*/) {}\n\n    //  ()()const 函数指针 -> std::string serialize() const\n    template<typename C>\n    static yes &test(reallyHas<std::string (C::*)() const, &C::serialize> * /*unused*/) {}\n\n    // The famous C++ sink-hole.\n    // Note that sink-hole must be templated too as we are testing test<T>(0).\n    // If the method serialize isn't available, we will end up in this method.\n    template<typename>\n    static no &test(...) { /* dark matter */ }\n\n    //用作测试的返回值的常数。\n    //由于编译时评估的大小，因此实际上在这里完成了测试。\n    static const bool value = sizeof(test<T>(0)) == sizeof(yes);\n};\n\n\ntemplate<bool B, class T = void> // Default template version.\nstruct enable_if {\n}; // This struct doesn't define \"type\" and the substitution will fail if you try to access it.\n\ntemplate<class T> // A specialisation used if the expression is true.\nstruct enable_if<true, T> {\n    typedef T type;\n}; // This struct do have a \"type\" and won't fail on access.\n\ntemplate<class T>\ntypename enable_if<hasSerialize<T>::value, std::string>::type serialize(const T &obj) {\n    return obj.serialize();\n}\n\ntemplate<class T>\ntypename enable_if<!hasSerialize<T>::value, std::string>::type serialize(const T &obj) {\n    return to_string(obj);\n}\n\nint main() {\n\n    // Usage:\n//    enable_if<true, int>::type t1; // Compiler happy. t's type is int.\n//    enable_if<hasSerialize<B>::value, int>::type t2; // Compiler happy. t's type is int.\n//\n//    enable_if<false, int>::type t3; // Compiler unhappy. no type named 'type' in 'enable_if<false, int>';\n//    enable_if<hasSerialize<A>::value, int>::type t4; // no type named 'type' in 'enable_if<false, int>';\n\n\n    A a;\n    B b;\n    C c;\n\n    // The following lines work like a charm!\n    std::cout << serialize(a) << std::endl;\n    std::cout << serialize(b) << std::endl;\n    std::cout << serialize(c) << std::endl;\n\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/compilercompute/BUILD",
    "content": "# please run `bazel run //learn_class/modern_cpp_30/compilercompute:IF`\n# please run `bazel run //learn_class/modern_cpp_30/compilercompute:factorial`\n# please run `bazel run //learn_class/modern_cpp_30/compilercompute:fmap`\n# please run `bazel run //learn_class/modern_cpp_30/compilercompute:WhileLoop`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"IF\",\n    srcs = [\"IF.cpp\"],\n    copts = [\"-std=c++17\"],\n)\ncc_binary(\n    name = \"factorial\",\n    srcs = [\"factorial.cpp\"],\n    copts = [\"-std=c++17\"],\n)\ncc_binary(\n    name = \"fmap\",\n    srcs = [\"fmap.cpp\"],\n    copts = [\"-std=c++17\"],\n)\ncc_binary(\n    name = \"WhileLoop\",\n    srcs = [\"WhileLoop.cpp\"],\n    copts = [\"-std=c++17\"],\n)\n"
  },
  {
    "path": "learn_class/modern_cpp_30/compilercompute/IF.cpp",
    "content": "//\n// Created by light on 19-12-28.\n//\n\n#include <iostream>\n\nusing namespace std;\n\n// 使用template实现IF条件判断\n\ntemplate <bool cond, typename Then, typename Else> struct IF;\n\ntemplate <typename Then, typename Else> struct IF<true, Then, Else> {\n  typedef Then result;\n};\n\ntemplate <typename Then, typename Else> struct IF<false, Then, Else> {\n  typedef Else result;\n};\n\n// 判断奇数与偶数\ntemplate <int N> struct isEven {\n  static const auto RES = IF<(N & 1) == 0, true_type, false_type>::result::value;\n};\n\ntemplate <int nums1, int nums2> struct Add_ {\n  static const int value = nums1 + nums2;\n};\n\ntemplate <int nums1, int nums2> struct Sub_ {\n  static const int value = nums1 - nums2;\n};\n\n// 加减\ntemplate <bool cond, int nums1, int nums2> struct addSub {\n  static const auto RES =\n      IF<cond, Add_<nums1, nums2>, Sub_<nums1, nums2>>::result::value;\n};\n\nint main() {\n  cout << isEven<10>::RES << endl;\n  cout << addSub<true, 10, 2>::RES << endl;\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/compilercompute/WhileLoop.cpp",
    "content": "//\n// Created by light on 20-1-5.\n//\n\n#include <iostream>\n\nusing namespace std;\n\n// 使用template实现while循环\n\ntemplate <bool condition, typename Body> struct WhileLoop;\n\ntemplate <typename Body> struct WhileLoop<true, Body> {\n  typedef\n      typename WhileLoop<Body::cond_value, typename Body::next_type>::type type;\n};\n\ntemplate <typename Body> struct WhileLoop<false, Body> {\n  typedef typename Body::res_type type;\n};\n\ntemplate <typename Body> struct While {\n  typedef typename WhileLoop<Body::cond_value, Body>::type type;\n};\ntemplate <typename Body> using While_t = WhileLoop<Body::cond_value, Body>;\n\nnamespace my {\ntemplate <class T, T v> struct integral_constant {\n  static const T value = v;\n  typedef T value_type;\n  typedef integral_constant<T, v> type;\n};\n} // namespace my\ntemplate <int result, int n> struct SumLoop {\n\n  // 循环的条件\n  static const bool cond_value = n != 0;\n\n  // 循环后的结果\n  static const int res_value = result;\n\n  // 循环时的状态\n  typedef my::integral_constant<int, res_value> res_type;\n\n  // 循环执行一次时的状态\n  typedef SumLoop<result + n, n - 1> next_type;\n};\n\ntemplate <int n> struct Sum { typedef SumLoop<0, n> type; };\n\ntemplate <int n> using Sum_t = SumLoop<0, n>;\n\nint main() {\n  cout << While<Sum<6>::type>::type::value << endl;\n  cout << While_t<Sum_t<6>>::type::value << endl;\n  return 0;\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/compilercompute/factorial.cpp",
    "content": "//\n// Created by light on 19-12-28.\n//\n\n#include <iostream>\n\nusing namespace std;\n\ntemplate <int n> struct factorial {\n  static_assert(n >= 0, \"Arg must be non-negative\");\n  static const int value = n * factorial<n - 1>::value;\n};\n\ntemplate <> struct factorial<0> { static const int value = 1; };\n\nint main() {\n\n  printf(\"%d\\n\", factorial<10>::value);\n  return 0;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/compilercompute/fmap.cpp",
    "content": "//\n// Created by light on 20-1-5.\n//\n\n#include <iostream>\n#include <vector>\n\nusing namespace std;\n\ntemplate <template <typename, typename> class OutContainer = vector, typename F,\n          class R>\nauto fmap(F &&f, R &&inputs) {\n  typedef decay_t<decltype(f(*inputs.begin()))> result_type;\n\n  OutContainer<result_type, allocator<result_type>> result;\n\n  for (auto &&item : inputs) {\n    result.push_back(f(item));\n  }\n  return result;\n}\n\n// 对每一个数进行加1操作\nint add_1(int x) { return x + 1; }\n\nint main() {\n  vector<int> v{1, 2, 3, 4, 5};\n  auto result = fmap(add_1, v);\n\n  for (auto item : result)\n    cout << item << \" \";\n  cout << endl;\n  return 0;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/compilerpoly/BUILD",
    "content": "# please run `bazel run //learn_class/modern_cpp_30/compilerpoly:template`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"template\",\n    srcs = [\"template.cpp\"],\n    copts = [\"-std=c++17\"],\n)\n"
  },
  {
    "path": "learn_class/modern_cpp_30/compilerpoly/README.md",
    "content": "# 编译期多态：泛型编程和模板入门\n\n运行时多态通过利用接口或者虚函数实现，本节则主要阐述编译时多态。\n\n>如果一只鸟走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子，那么这只鸟就可以被当作鸭子。\n\n鸭子类型使得开发者可以不使用继承体系来灵活地实现一些“约定”，尤其是使得混合不同来源、使用不同对象继承体系的代码成为可能。唯一的要求只是，这些不同的对象有“共通”的成员函数。这些成员函数应当有相同的名字和相同结构的参数（并不要求参数类型相同）。\n\n在C++中实现鸭子类型可以通过模板或者说泛型编程。不管是类模板还是函数模板，编译器在看到其定义时只能做最基本的语法检查，真正的类型检查要在实例化（instantiation）的时候才能做。一般而言，这也是编译器会报错的时候。\n\n> “动态”多态和“静态”多态的对比\n\n“动态”多态解决的是运行时的行为变化—这个是无法在编译时确定的。“静态”多态或者“泛型”—解决的是很不同的问题，让适用于不同类型的“同构”算法可以用同一套代码来实现，实际上强调的是对代码的复用。"
  },
  {
    "path": "learn_class/modern_cpp_30/compilerpoly/template.cpp",
    "content": "//\n// Created by light on 19-12-27.\n//\n#include<iostream>\n#include <vector>\n#include <cstring>\n\n\nusing namespace std;\n\n// 函数模板\ntemplate<typename T>\nbool isEqual(T t1, T t2) {\n    cout << \"函数模板\" << endl;\n    return t1 == t2;\n}\n\n// 函数模板全特化\ntemplate<>\nbool isEqual(const char *t1, const char *t2) {\n    cout << \"函数模板全特化\" << endl;\n    return strcmp(t1, t2);\n}\n\n// 函数模板偏特化   C++不支持函数模板偏特化 可以重载解决\n//template<typename T>\n//bool isEqual<T, int>(T a, double b) {\n//    cout << \"函数模板偏特化\" << endl;\n//    return a == b;\n//}\n\n// 改为重载   下面是重载!!! 而不是偏特化!\ntemplate<typename T>\nbool isEqual(T a, double b) {\n    cout << \"函数重载\" << endl;\n    return a == b;\n}\n\n/// =============================================================\n\n// 类模板\ntemplate<class T>\nclass comp {\npublic:\n    bool isEqual(T t1, T t2) {\n        cout << \"类模板\" << endl;\n        return t1 == t2;\n    }\n};\n\n// 类模板全特化\ntemplate<>\nclass comp<const char *> {\npublic:\n    bool isEqual(const char *t1, const char *t2) {\n        cout << \"类模板全特化\" << endl;\n        return strcmp(t1, t2);\n    }\n};\n\n// 类模板偏特化\ntemplate<typename T>\nclass comp<T *> {\npublic:\n    bool isEqual(T *t1, T *t2) {\n        cout << \"类模板偏特化\" << endl;\n        return strcmp(t1, t2);\n    }\n};\n\nint main() {\n    isEqual(1, 2);       // 实例化的时候做类型检查\n    isEqual(1, 2.1);\n//    isEqual(1.1, 2.1); // 模糊的!\n    isEqual(\"qwqw\", \"asd\");\n    comp<int> c;\n    c.isEqual(10, 11);\n    comp<const char *> cc;\n    cc.isEqual(\"he\", \"lo\");\n    comp<char *> p;\n    p.isEqual(\"he\", \"ll\");\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/constexpr/BUILD",
    "content": "# please run `bazel run //learn_class/modern_cpp_30/constexpr:test3`\n# please run `bazel run //learn_class/modern_cpp_30/constexpr:newconstexpr`\n# please run `bazel run //learn_class/modern_cpp_30/constexpr:container`\n# please run `bazel run //learn_class/modern_cpp_30/constexpr:sqrt`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"test3\",\n    srcs = [\"test3.cpp\"],\n    copts = [\"-std=c++17\"],\n)\ncc_binary(\n    name = \"newconstexpr\",\n    srcs = [\"newconstexpr.cpp\"],\n    copts = [\"-std=c++17\"],\n)\ncc_binary(\n    name = \"container\",\n    srcs = [\"container.cpp\"],\n    copts = [\"-std=c++17\"],\n    deps = [\n        \"//learn_class/modern_cpp_30/container1:output_container\",\n    ],\n)\ncc_binary(\n    name = \"sqrt\",\n    srcs = [\"sqrt.cpp\"],\n    copts = [\"-std=c++17\"],\n)\n"
  },
  {
    "path": "learn_class/modern_cpp_30/constexpr/container.cpp",
    "content": "//\n// Created by light on 19-12-16.\n//\n\n#include \"../container1/output_container.h\"\n#include <iostream>\n#include <map>\n#include <set>\n#include <vector>\n\nusing namespace std;\n\nint main() {\n  map<int, int> mp{{1, 1}, {2, 4}, {3, 9}};\n  cout << mp << endl;\n  vector<vector<int>> vv{{1, 1}, {2, 4}, {3, 9}};\n  cout << vv << endl;\n\n  pair<int, int> p{1, 2};\n  cout << p << endl;\n\n  set<int> s{1, 2, 3};\n  cout << s << endl;\n\n  vector<char> v{'a', 'b'};\n  cout << v << endl;\n  set<char *> vs{\"a\", \"b\"};\n  cout << vs << endl;\n\n  map<int, char *> mm{{1, \"23\"}, {2, \"234hi\"}};\n  cout << mm << endl;\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/constexpr/newconstexpr.cpp",
    "content": "//\n// Created by light on 20-1-7.\n//\n\n#include <array>\n#include <iostream>\n#include <vector>\n\nusing namespace std;\n\n// C++17 内联变量\nstruct magic {\n  //    static const int number = 42;  // error\n  static constexpr int number = 42; // ok\n  // 类的静态constexpr成员变量默认就是内联的。\n  //    static inline const int number = 42; // ok\n  //  const常量和类外面的constexpr变量是不默认内联，需要手工加inline关键字才会变成内联\n};\n\n// 内联变量之前用这个方法,称为one definition\n// rule，有了内联变量及模板，则不受这条规则限制! const int magic::number = 42;\n// // ok\n\n// C++14 变量模板\n// 之前我们需要用类静态数据成员来表达的东西，使用变量模板可以更简洁地表达。\nnamespace my {\ntemplate <class T>\ninline constexpr bool is_trivially_destructible_v =\n    std::is_trivially_destructible<T>::value;\n}\n// 不重要析构\nclass A {};\n\n// 重要析构\nclass B {\n  ~B() {}\n};\n\nint main() {\n  std::cout << magic::number << std::endl;\n\n  std::vector<int> v;\n  v.push_back(magic::number); // undefined reference to `magic::number'\n  std::cout << v[0] << std::endl;\n  // 不重要的析构\n  std::cout << is_trivially_destructible_v<A> << std::endl;\n  std::cout << is_trivially_destructible_v<B> << std::endl;\n\n  // constexpr变量仍是const\n\n  constexpr int a = 42;\n  const int &b = a;\n  // 上述const不能去掉,否则报如下错误：\n  // binding reference of type ‘int&’ to ‘const int’ discards qualifiers\n\n  // constexpr 构造函数和字面类型\n  // C++14 放开了 constexpr函数里的循环\n  // C++20 放开了 try...catch语句 和 asm声明\n  // constexpr函数里不能使用goto语句\n\n  // 一个有意思的情况是一个类的构造函数。如果一个类的构造函数里面只包含常量表达式、\n  // 满足对 constexpr\n  // 函数的限制的话（这也意味着，里面不可以有任何动态内存分配），\n  // 并且类的析构函数是平凡的，那这个类就可以被称为是一个字面类型。\n  std::array<int, 5> ay;\n\n  constexpr string_view sv{\"hi\"};\n  constexpr pair pr{sv[0], sv[1]};\n  constexpr array aa{pr.first, pr.second};\n  constexpr int n1 = aa[0];\n  constexpr int n2 = aa[1];\n  cout << n1 << ' ' << n2 << '\\n';\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/constexpr/output_container.h",
    "content": "//\n// Created by light on 20-1-8.\n//\n\n#ifndef MORDEN_C_OUTPUT_CONTAINER_H\n#define MORDEN_C_OUTPUT_CONTAINER_H\n\n#include <ostream>     // std::ostream\n#include <type_traits> // std::false_type/true_type/decay_t/is_same_v\n#include <utility>     // std::declval/pair\n\n// 检测是否是pair\ntemplate <typename T> struct is_pair : std::false_type {};\ntemplate <typename T, typename U>\nstruct is_pair<std::pair<T, U>> : std::true_type {};\ntemplate <typename T> inline constexpr bool is_pair_v = is_pair<T>::value;\n\n// 检测是否可以直接输出\ntemplate <typename T> struct has_output_function {\n  template <class U>\n  static auto output(U *ptr)\n      -> decltype(std::declval<std::ostream &>() << *ptr, std::true_type());\n\n  template <class U> static std::false_type output(...);\n\n  static constexpr bool value = decltype(output<T>(nullptr))::value;\n};\n\ntemplate <typename T>\ninline constexpr bool has_output_function_v = has_output_function<T>::value;\n\nenum CHARS {\n  ORD,   // 其他类型\n  CHAR,  // char 类型\n  STRING // string 类型\n};\n\ntemplate <typename T> int ischarOrString(T &elem) {\n  using std::decay_t;\n  using std::is_same_v;\n  using element_type = decay_t<decltype(elem)>;\n  constexpr bool is_char_v = is_same_v<element_type, char>;\n  constexpr bool is_string_v = is_same_v<element_type, char *> ||\n                               is_same_v<element_type, const char *> ||\n                               is_same_v<element_type, std::string>;\n  if (is_char_v)\n    return CHAR;\n  else if (is_string_v)\n    return STRING;\n  else\n    return ORD;\n}\n\ntemplate <typename T> void output(T &elem, int type, std::ostream &os) {\n  switch (type) {\n  case CHAR:\n    os << '\\'' << elem << '\\'';\n    break;\n  case STRING:\n    os << '\\\"' << elem << '\\\"';\n    break;\n  case ORD:\n    os << elem;\n    break;\n  }\n}\n\ntemplate <typename T, typename Cont>\nauto output_element(std::ostream &os, const T &element, const Cont &) ->\n    typename std::enable_if<is_pair<typename Cont::value_type>::value,\n                            bool>::type {\n  int ftype = ischarOrString(element.first);\n  int stype = ischarOrString(element.second);\n\n  output(element.first, ftype, os);\n  os << \" => \";\n  output(element.second, stype, os);\n  return true;\n}\n\ntemplate <typename T, typename Cont>\nauto output_element(std::ostream &os, const T &element, const Cont &) ->\n    typename std::enable_if<!is_pair<typename Cont::value_type>::value,\n                            bool>::type {\n  int etype = ischarOrString(element);\n  output(element, etype, os);\n  return false;\n}\n\ntemplate <typename T, typename U>\nstd::ostream &operator<<(std::ostream &os, const std::pair<T, U> &pr) {\n  os << '(' << pr.first << \", \" << pr.second << ')';\n  return os;\n}\n\n// template<typename T, typename Cont>\n// auto output_element(std::ostream& os, const T& element,\n//                     const Cont&, const std::true_type)\n//-> decltype(std::declval<typename Cont::key_type>(), os)\n//{\n//     os << element.first << \" => \" << element.second;\n//     return os;\n// }\n//\n// template <typename T, typename Cont>\n// auto output_element(std::ostream& os, const T& element,\n//                     const Cont&, ...)\n//-> decltype(os)\n//{\n//     os << element;\n//     return os;\n// }\n\n// 针对没有输出函数的容器处理\ntemplate <typename T, typename = std::enable_if_t<!has_output_function_v<T>>>\nauto operator<<(std::ostream &os, const T &container)\n    -> decltype(container.begin(), container.end(), os) {\n  os << \"{ \";\n  if (!container.empty()) {\n    bool on_first_element = true;\n    for (auto elem : container) {\n      if (!on_first_element) {\n        os << \", \";\n      } else {\n        on_first_element = false;\n      }\n      output_element(os, elem, container);\n    }\n  }\n  os << \" }\";\n  return os;\n}\n\n#endif // MORDEN_C_OUTPUT_CONTAINER_H\n"
  },
  {
    "path": "learn_class/modern_cpp_30/constexpr/sqrt.cpp",
    "content": "//\n// Created by light on 20-1-7.\n//\n\n#include <array>\n#include <iostream>\n\nint sqr(int n) { return n * n; }\n\nint rand() { return std::rand(); }\n\n// constant expression\nconstexpr int sqr1(int n) { return n * n; }\n\nconstexpr int factorial(int n) {\n  // n是普通的int 不能使用static_assert\n  //    static_assert(n>=0); //error\n  // 正确方式\n  if (n < 0) {\n    throw std::invalid_argument(\"Arg must be non-negative\");\n  }\n\n  if (n == 0) {\n    return 1;\n  } else {\n    return n * factorial(n - 1);\n  }\n}\n\nint main() {\n  int a[sqr(3)];         // ok\n  const int n1 = sqr(3); // ok\n\n  constexpr int n = sqr1(3); // constexpr=constant expression 常量表达式\n  std::array<int, n> aa;     // ok\n\n  std::array<int, factorial(10)> b; // cordump\n  int cc[rand()];\n  return 0;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/constexpr/test3.cpp",
    "content": "//\n// Created by light on 20-1-10.\n//\n\n#include <iostream>\n\nclass test3 {\npublic:\n  int value;\n\n  // constexpr const method - can't chanage the values of object fields and can\n  // be evaluated at compile time.\n  constexpr int getvalue() const { return (value); }\n\n  constexpr test3(int Value) : value(Value) {}\n};\n\nint main() {\n\n  // 加不加都行\n  constexpr test3 x(100); // OK. Constructor is constexpr.\n\n  int array[x.getvalue()]; // OK. x.getvalue() is constexpr and can be evaluated\n                           // at compile time.\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/container1/BUILD",
    "content": "# please run `bazel run //learn_class/modern_cpp_30/container1:container`\n# please run `bazel run //learn_class/modern_cpp_30/container1:vector_l`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\")\n\ncc_library(\n    name = \"output_container\",\n    hdrs = [\"output_container.h\"],\n    visibility = [\"//visibility:public\"],\n)\n\ncc_binary(\n    name = \"container\",\n    srcs = [\"container.cpp\"],\n    copts = [\"-std=c++17\"],\n    deps = [\n        \":output_container\",\n    ],\n)\ncc_binary(\n    name = \"vector_l\",\n    srcs = [\"vector_l.cpp\"],\n    copts = [\"-std=c++17\"],\n    deps = [\n        \":output_container\",\n    ],\n)\n"
  },
  {
    "path": "learn_class/modern_cpp_30/container1/container.cpp",
    "content": "//\n// Created by light on 19-12-16.\n//\n\n#include \"output_container.h\"\n#include <iostream>\n#include <map>\n#include <vector>\n\nusing namespace std;\n\nint main() {\n  map<int, int> mp{{1, 1}, {2, 4}, {3, 9}};\n  cout << mp << endl;\n  vector<vector<int>> vv{{1, 1}, {2, 4}, {3, 9}};\n  cout << vv << endl;\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/container1/output_container.h",
    "content": "/*\n * Written by Wu Yongwei <wuyongwei AT gmail DOT com>.\n *\n * Using this file requires a C++17-compliant compiler.\n *\n * This is free and unencumbered software released into the public domain.\n *\n * Anyone is free to copy, modify, publish, use, compile, sell, or\n * distribute this software, either in source code form or as a compiled\n * binary, for any purpose, commercial or non-commercial, and by any\n * means.\n *\n * In jurisdictions that recognize copyright laws, the author or authors\n * of this software dedicate any and all copyright interest in the\n * software to the public domain. We make this dedication for the benefit\n * of the public at large and to the detriment of our heirs and\n * successors. We intend this dedication to be an overt act of\n * relinquishment in perpetuity of all present and future rights to this\n * software under copyright law.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n *\n * For more information, please refer to <http://unlicense.org>\n *\n */\n\n#ifndef OUTPUT_CONTAINER_H\n#define OUTPUT_CONTAINER_H\n\n#include <ostream>     // std::ostream\n#include <type_traits> // std::false_type/true_type/decay_t/is_same_v\n#include <utility>     // std::declval/pair\n\n// Type trait to detect std::pair\ntemplate <typename T> struct is_pair : std::false_type {};\ntemplate <typename T, typename U>\nstruct is_pair<std::pair<T, U>> : std::true_type {};\ntemplate <typename T> inline constexpr bool is_pair_v = is_pair<T>::value;\n\n// Type trait to detect whether an output function already exists\ntemplate <typename T> struct has_output_function {\n  template <class U>\n  static auto output(U *ptr)\n      -> decltype(std::declval<std::ostream &>() << *ptr, std::true_type());\n  template <class U> static std::false_type output(...);\n  static constexpr bool value = decltype(output<T>(nullptr))::value;\n};\ntemplate <typename T>\ninline constexpr bool has_output_function_v = has_output_function<T>::value;\n/* NB: Visual Studio 2017 (or below) may have problems with\n *     has_output_function_v<T>: you should then use\n *     has_output_function<T>::value instead, or upgrade to\n *     Visual Studio 2019. */\n\n// Output function for std::pair\ntemplate <typename T, typename U>\nstd::ostream &operator<<(std::ostream &os, const std::pair<T, U> &pr);\n\n// Element output function for containers that define a key_type and\n// have its value type as std::pair\ntemplate <typename T, typename Cont>\nauto output_element(std::ostream &os, const T &element, const Cont &,\n                    const std::true_type)\n    -> decltype(std::declval<typename Cont::key_type>(), os);\n// Element output function for other containers\ntemplate <typename T, typename Cont>\nauto output_element(std::ostream &os, const T &element, const Cont &, ...)\n    -> decltype(os);\n\n// Main output function, enabled only if no output function already exists\ntemplate <typename T, typename = std::enable_if_t<!has_output_function_v<T>>>\nauto operator<<(std::ostream &os, const T &container)\n    -> decltype(container.begin(), container.end(), os) {\n  using std::decay_t;\n  using std::is_same_v;\n\n  using element_type = decay_t<decltype(*container.begin())>;\n  constexpr bool is_char_v = is_same_v<element_type, char>;\n  if constexpr (!is_char_v) {\n    os << \"{ \";\n  }\n  if (!container.empty()) {\n    auto end = container.end();\n    bool on_first_element = true;\n    for (auto it = container.begin(); it != end; ++it) {\n      if constexpr (is_char_v) {\n        if (*it == '\\0') {\n          break;\n        }\n      }\n      if constexpr (!is_char_v) {\n        if (!on_first_element) {\n          os << \", \";\n        } else {\n          on_first_element = false;\n        }\n      }\n      output_element(os, *it, container, is_pair<element_type>());\n    }\n  }\n  if constexpr (!is_char_v) {\n    os << \" }\";\n  }\n  return os;\n}\n\ntemplate <typename T, typename Cont>\nauto output_element(std::ostream &os, const T &element, const Cont &,\n                    const std::true_type)\n    -> decltype(std::declval<typename Cont::key_type>(), os) {\n  os << element.first << \" => \" << element.second;\n  return os;\n}\n\ntemplate <typename T, typename Cont>\nauto output_element(std::ostream &os, const T &element, const Cont &, ...)\n    -> decltype(os) {\n  os << element;\n  return os;\n}\n\ntemplate <typename T, typename U>\nstd::ostream &operator<<(std::ostream &os, const std::pair<T, U> &pr) {\n  os << '(' << pr.first << \", \" << pr.second << ')';\n  return os;\n}\n\n#endif // OUTPUT_CONTAINER_H\n"
  },
  {
    "path": "learn_class/modern_cpp_30/container1/vector_l.cpp",
    "content": "//\n// Created by light on 19-12-16.\n//\n#include <iostream>\n#include <queue>\n#include <vector>\nusing namespace std;\n\nclass Obj1 {\npublic:\n  Obj1() { cout << \"Obj1()\\n\"; }\n  Obj1(const Obj1 &) { cout << \"Obj1(const Obj1&)\\n\"; }\n  Obj1(Obj1 &&) { cout << \"Obj1(Obj1&&)\\n\"; }\n};\n\nclass Obj2 {\npublic:\n  Obj2() { cout << \"Obj2()\\n\"; }\n  Obj2(const Obj2 &) { cout << \"Obj2(const Obj2&)\\n\"; }\n  Obj2(Obj2 &&) noexcept { cout << \"Obj2(Obj2&&)\\n\"; }\n};\n\nint main() {\n  vector<int> v;\n\n  int nums = 20;\n  for (int i = 0; i < nums; ++i) {\n    v.push_back(i + 1);\n    cout << \"v_size: \" << v.size() << \"\\t v_capacity: \" << v.capacity() << endl;\n  }\n  // 头两个在已有空间上成功构造。第三个时发现空间不足，系统会请求更大的空间，大小由实现决定（比如两倍）。\n  // 有了足够的空间后，就会在新空间的第三个的位置构造（第三个obj1），成功之后再把头两个拷贝或移动过来。\n  vector<Obj1> v1;\n  //    v1.reserve(2);\n  v1.emplace_back();\n  v1.emplace_back();\n  v1.emplace_back();\n  v1.emplace_back();\n\n  vector<Obj2> v2;\n  v2.reserve(2);\n  v2.emplace_back();\n  v2.emplace_back();\n  v2.emplace_back();\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/container2/BUILD",
    "content": "# please run `bazel run //learn_class/modern_cpp_30/container2:relacontainer`\n# please run `bazel run //learn_class/modern_cpp_30/container2:priority_queue`\n# please run `bazel run //learn_class/modern_cpp_30/container2:hash`\n# please run `bazel run //learn_class/modern_cpp_30/container2:array`\n# please run `bazel run //learn_class/modern_cpp_30/container2:unorder`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"relacontainer\",\n    srcs = [\"relacontainer.cpp\"],\n    copts = [\"-std=c++17\"],\n    deps = [\n        \"//learn_class/modern_cpp_30/container1:output_container\",\n    ],\n)\ncc_binary(\n    name = \"priority_queue\",\n    srcs = [\"priority_queue.cpp\"],\n    copts = [\"-std=c++17\"],\n    deps = [\n        \"//learn_class/modern_cpp_30/container1:output_container\",\n    ],\n)\ncc_binary(\n    name = \"hash\",\n    srcs = [\"hash.cpp\"],\n    copts = [\"-std=c++17\"],\n    deps = [\n        \"//learn_class/modern_cpp_30/container1:output_container\",\n    ],\n)\ncc_binary(\n    name = \"array\",\n    srcs = [\"array.cpp\"],\n    copts = [\"-std=c++17\"],\n    deps = [\n        \"//learn_class/modern_cpp_30/container1:output_container\",\n    ],\n)\ncc_binary(\n    name = \"unorder\",\n    srcs = [\"unorder.cpp\"],\n    copts = [\"-std=c++17\"],\n    deps = [\n        \"//learn_class/modern_cpp_30/container1:output_container\",\n    ],\n)\n"
  },
  {
    "path": "learn_class/modern_cpp_30/container2/array.cpp",
    "content": "//\n// Created by light on 19-12-16.\n//\n\n#include \"../container1/output_container.h\"\n#include <iostream>\n#include <map> // std::map\n\nusing namespace std;\n#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))\n\nvoid test(int a[8]) { cout << ARRAY_LEN(a) << endl; }\n\nvoid test1(int arr[]) {\n  //      不能编译\n  //     std::cout << std::size(arr)\n  //               << std::endl;\n}\n\ntypedef char mykey_t[8];\ntypedef std::array<char, 8> mykey_t1;\n\nint main() {\n  int a[8];\n  test(a);\n\n  // C++17 直接提供了一个 size 方法，可以用于提供数组长度，\n  int arr[] = {1, 2, 3, 4, 5};\n  std::cout << \"The array length is \" << std::size(arr) << std::endl;\n  // 并且在数组退化成指针的情况下会直接失败\n  test1(arr);\n\n  std::map<mykey_t, int> mp;\n  mykey_t mykey{\"hello\"};\n  //    mp[mykey] = 5;\n  //  轰，大段的编译错误\n\n  std::map<mykey_t1, int> mp1;\n  mykey_t1 mykey1{\"hello\"};\n  mp1[mykey1] = 5; // ok\n  cout << mp1 << endl;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/container2/hash.cpp",
    "content": "//\n// Created by light on 19-12-16.\n//\n\n#include \"../container1/output_container.h\"\n#include <algorithm>  // std::sort\n#include <functional> // std::less/greater/hash\n#include <iostream>   // std::cout/endl\n#include <string>     // std::string\n#include <vector>     // std::vector\n\nusing namespace std;\n\nint main() {\n  //  初始数组\n  vector<int> v{13, 6, 4, 11, 29};\n  cout << v << endl;\n\n  //  从小到大排序\n  sort(v.begin(), v.end());\n  cout << v << endl;\n\n  //  从大到小排序\n  sort(v.begin(), v.end(), greater<int>());\n  cout << v << endl;\n\n  cout << hex;\n\n  auto hp = hash<int *>();\n  cout << \"hash(nullptr)  = \" << hp(nullptr) << endl;\n  cout << \"hash(v.data()) = \" << hp(v.data()) << endl;\n  cout << \"v.data()       = \" << static_cast<void *>(v.data()) << endl;\n\n  auto hs = hash<string>();\n  cout << \"hash(\\\"hello\\\")  = \" << hs(string(\"hello\")) << endl;\n  cout << \"hash(\\\"hellp\\\")  = \" << hs(string(\"hellp\")) << endl;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/container2/priority_queue.cpp",
    "content": "//\n// Created by light on 19-12-16.\n//\n\n#include \"../container1/output_container.h\"\n#include <functional> // std::greater\n#include <iostream>   // std::cout/endl\n#include <memory>     // std::pair\n#include <queue>      // std::priority_queue\n#include <vector>     // std::vector\n\nusing namespace std;\n\nint main() {\n  priority_queue<pair<int, int>, vector<pair<int, int>>,\n                 greater<pair<int, int>>>\n      q;\n  q.push({1, 1});\n  q.push({2, 2});\n  q.push({0, 3});\n  q.push({9, 4});\n  while (!q.empty()) {\n    cout << q.top() << endl;\n    q.pop();\n  }\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/container2/relacontainer.cpp",
    "content": "//\n// Created by light on 19-12-16.\n//\n\n#include \"../container1/output_container.h\"\n#include <functional>\n#include <iostream>\n#include <map>\n#include <set>\n#include <string>\n#include <tuple>\n\nusing namespace std;\n\nint main() {\n  set<int> s{1, 1, 1, 2, 3, 4};\n  cout << s << endl;\n\n  multiset<int, greater<int>> ms{1, 1, 1, 2, 3, 4};\n  cout << ms << endl;\n\n  map<string, int> mp{{\"one\", 1}, {\"two\", 2}, {\"three\", 3}, {\"four\", 4}};\n\n  cout << mp << endl;\n\n  mp.insert({\"four\", 4});\n  cout << mp << endl;\n\n  cout << (mp.find(\"four\") == mp.end()) << endl;\n\n  cout << (mp.find(\"five\") == mp.end()) << endl;\n\n  mp[\"five\"] = 5;\n\n  cout << mp << endl;\n\n  multimap<string, int> mmp{{\"one\", 1}, {\"two\", 2}, {\"three\", 3}, {\"four\", 4}};\n\n  cout << mmp << endl;\n\n  mmp.insert({\"four\", -4});\n\n  cout << mmp << endl;\n\n  cout << (mp.find(\"four\")->second) << endl;\n  cout << (mp.lower_bound(\"four\")->second) << endl;\n\n  cout << (mp.upper_bound(\"four\")->second) << endl;\n  cout << ((--mp.upper_bound(\"four\"))->second) << endl;\n\n  multimap<string, int>::iterator lower, upper;\n  std::tie(lower, upper) = mmp.equal_range(\"four\");\n  cout << (lower != upper) << endl; // 检测区间非空\n\n  cout << lower->second << endl;\n  cout << (--upper)->second << endl;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/container2/unorder.cpp",
    "content": "//\n// Created by light on 19-12-16.\n//\n\n#include \"../container1/output_container.h\"\n#include <complex>       // std::complex\n#include <iostream>      // std::cout/endl\n#include <unordered_map> // std::unordered_map\n#include <unordered_set> // std::unordered_set\n\nusing namespace std;\n\nnamespace std {\n\ntemplate <typename T> struct hash<complex<T>> {\n  size_t operator()(const complex<T> &v) const noexcept {\n    hash<T> h;\n    return h(v.real()) + h(v.imag());\n  }\n};\n\n} // namespace std\n\nint main() {\n  unordered_set<int> s{1, 1, 2, 3, 5, 8, 13, 21};\n  cout << s << endl;\n\n  unordered_map<complex<double>, double> umc{{{1.0, 1.0}, 1.4142},\n                                             {{3.0, 4.0}, 5.0}};\n  cout << umc << endl;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/exception/BUILD",
    "content": "# please run `bazel run //learn_class/modern_cpp_30/exception:exception`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"exception\",\n    srcs = [\"exception.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n"
  },
  {
    "path": "learn_class/modern_cpp_30/exception/exception.cpp",
    "content": "//\n// Created by light on 19-12-17.\n//\n\n#include <vector>\n#include <iostream>\n\n// “首先是内存分配。如果 new 出错，按照 C++ 的规则，一般会得到异常 bad_alloc，对象的构造也就失败了。\n// 这种情况下，在 catch 捕捉到这个异常之前，所有的栈上对象会全部被析构，资源全部被自动清理。”\n// 之所以是栈上对象会全被被析构原因是堆上的东西都是由栈上的变量所引用的，栈上对象析构的过程，\n// 堆上相应的资源自然就被释放了。而且被释放的对象的范围还被栈帧限定了。\n\n\n// -fexceptions（缺省开启）\n// g++ test.cpp -std=c++17 -fno-exceptions\n// 关闭异常,可看到可执行文件的大小的变化。\nint main()\n{\n    std::vector<int> v{1, 2, 3, 4, 5};\n    v.push_back(20);\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/functionLambda/BUILD",
    "content": "# please run `bazel run //learn_class/modern_cpp_30/functionLambda:adder`\n# please run `bazel run //learn_class/modern_cpp_30/functionLambda:function`\n# please run `bazel run //learn_class/modern_cpp_30/functionLambda:autoLambda`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"adder\",\n    srcs = [\"adder.cpp\"],\n    copts = [\"-std=c++17\"],\n)\ncc_binary(\n    name = \"function\",\n    srcs = [\"function.cpp\"],\n    copts = [\"-std=c++17\"],\n)\ncc_binary(\n    name = \"autoLambda\",\n    srcs = [\"autoLambda.cpp\"],\n    copts = [\"-std=c++17\"],\n)\n"
  },
  {
    "path": "learn_class/modern_cpp_30/functionLambda/adder.cpp",
    "content": "//\n// Created by light on 20-1-11.\n//\n#include <iostream>\n\nusing namespace std;\n\nstruct adder {\n  adder(int n) : n_(n) {}\n\n  int operator()(int x) const { return x + n_; }\n\nprivate:\n  int n_;\n};\n\nint main() {\n  auto add_2 = adder(2);\n\n  // x+2\n  cout << add_2(3) << endl;\n\n  auto t = bind1st(plus<int>(), 2);\n  cout << t(1) << endl;\n  // 上述的C++98\n  binder2nd<plus<int>> a2(plus<int>(), 2);\n  cout << a2(3) << endl;\n\n  cout << [](int x) { return x * x; }(3) << endl;\n  return 0;\n  // lambda表达式默认就是constexpr函数\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/functionLambda/autoLambda.cpp",
    "content": "//\n// Created by light on 20-1-11.\n//\n\n#include <chrono>\n#include <iostream>\n#include <sstream>\n#include <thread>\n\nusing namespace std;\n\nint get_count() {\n  static int count = 0;\n  return ++count;\n}\n\nclass task {\npublic:\n  task(int data) : data_(data) {}\n\n  /**\n   * this 标明按引用捕获外围对象（针对 lambda\n   * 表达式定义出现在一个非静态类成员内的情况）； 注意默认捕获符 = 和 &\n   * 号可以自动捕获 this（并且在 C++20 之前，在 = 后写 this 会导致出错）\n   * 本例子两次都按照第二次输出(this_thread::sleep_for(100ms);\n   * this 标明按引用捕获外围对象\n   *\n   *\n   * *this 标明按值捕获外围对象（针对 lambda\n   * 表达式定义出现在一个非静态类成员内的情况；C++17 新增语法） 本例子正常输出\n   */\n  auto lazy_launch() {\n    return [*this, count = get_count()]() mutable {\n      ostringstream oss;\n      oss << \"Done work \" << data_ << \" (No. \" << count << \") in thread \"\n          << this_thread::get_id() << '\\n';\n      msg_ = oss.str();\n      calculate();\n    };\n  }\n\n  void calculate() {\n    this_thread::sleep_for(100ms);\n    cout << msg_;\n  }\n\nprivate:\n  int data_;\n  string msg_;\n};\n\nint main() {\n  auto t = task{37};\n  thread t1{t.lazy_launch()};\n  thread t2{t.lazy_launch()};\n  t1.join();\n  t2.join();\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/functionLambda/function.cpp",
    "content": "//\n// Created by light on 20-1-11.\n//\n\n#include <functional>\n#include <iostream>\n#include <map>\n\nusing namespace std;\n\nint main() {\n\n  map<string, function<int(int, int)>> op_dict{\n      {\"+\", [](int x, int y) { return x + y; }},\n      {\"-\", [](int x, int y) { return x - y; }},\n      {\"*\", [](int x, int y) { return x * y; }},\n      {\"/\", [](int x, int y) { return x / y; }},\n  };\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/literalAssert/BUILD",
    "content": "# please run `bazel run //learn_class/modern_cpp_30/literalAssert:literal`\n# please run `bazel run //learn_class/modern_cpp_30/literalAssert:assert`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"literal\",\n    srcs = [\"literal.cpp\"],\n    copts = [\"-std=c++17\"],\n)\ncc_binary(\n    name = \"assert\",\n    srcs = [\"assert.cpp\"],\n    copts = [\"-std=c++11\"],\n)"
  },
  {
    "path": "learn_class/modern_cpp_30/literalAssert/assert.cpp",
    "content": "//\n// Created by light on 19-12-25.\n//\n\n#include <cassert>\n#include <iostream>\n\nint main() {\n  const int alignment = 5;\n  assert((alignment & (alignment - 1)) == 0);\n  static_assert((alignment & (alignment - 1)) == 0,\n                \"Alignment must be power of two\");\n  return 0;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/literalAssert/default_delete.cpp",
    "content": "//\n// Created by light on 19-12-25.\n//\n\nclass myFun {\npublic:\n  myFun() = default;\n\n  myFun(const myFun &) = default;\n\n  myFun &operator=(const myFun &) = default;\n\n  myFun(myFun &&) = delete;\n\n  myFun &operator=(myFun &&) = delete;\n\n  ~myFun() = default;\n};"
  },
  {
    "path": "learn_class/modern_cpp_30/literalAssert/literal.cpp",
    "content": "//\n// Created by light on 19-12-25.\n//\n\n#include <chrono>\n\n#include <bitset>\n#include <complex>\n#include <iostream>\n#include <string>\n#include <thread>\n\nusing namespace std::literals::chrono_literals;\nusing namespace std::literals::string_literals;\nusing namespace std::literals::complex_literals;\n\nstruct length {\n  double value;\n  enum unit {\n    metre,\n    kilometre,\n    millimetre,\n    centimetre,\n    inch,\n    foot,\n    yard,\n    mile,\n  };\n  static constexpr double factors[] = {1.0,    1000.0, 1e-3,   1e-2,\n                                       0.0254, 0.3048, 0.9144, 1609.344};\n\n  explicit length(double v, unit u = metre) { value = v * factors[u]; }\n};\n\nlength operator+(length lhs, length rhs) {\n  return length(lhs.value + rhs.value);\n}\n\nlength operator\"\" _m(long double v) { return length(v, length::metre); }\n\nlength operator\"\" _cm(long double v) { return length(v, length::centimetre); }\n\n//  可能有其他运算符\nint main() {\n\n  std::cout << \"i * i = \" << 1i * 1i << std::endl;\n  std::cout << \"Waiting for 500ms\" << std::endl;\n  std::this_thread::sleep_for(500ms);\n  std::cout << \"Hello world\"s.substr(0, 5) << std::endl;\n\n  length l1 = length(1.0, length::metre);\n  length l2 = length(1.0, length::centimetre);\n  std::cout << l2.value << std::endl;\n  std::cout << (l1 + l2).value << std::endl;\n\n  // 1.0_m + 10.0_cm\n  std::cout << (1.0_m + 1.0_cm).value << std::endl;\n\n  // 二进制字面量\n  unsigned mask = 0b1101;\n  // 以十进制打印\n  std::cout << mask << std::endl;\n\n  // 打印二进制字面量\n  std::cout << std::bitset<4>(mask) << std::endl;\n\n  // 数字分隔符\n  unsigned mk = 0b111'000'000;\n  double pi = 3.141'5926;\n  return 0;\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/literalAssert/overridefinal.cpp",
    "content": "//\n// Created by light on 19-12-25.\n//\n\nclass A {\npublic:\n  virtual void foo();\n  virtual void bar();\n  void foobar();\n};\n\nclass B : public A {\npublic:\n  void foo() override;       // OK\n  void bar() override final; // OK\n                             // void foobar() override;\n  //   非虚函数不能  override\n};\n\nclass C final : public B {\npublic:\n  void foo() override; // OK\n                       // void bar() override;\n  //  final  函数不可  override\n};\n\nclass D : public C {\n  //  错误：final  类不可派生\n};"
  },
  {
    "path": "learn_class/modern_cpp_30/memorymodel_atomic/barrier_singleton.cpp",
    "content": "//\n// Created by light on 20-2-7.\n//\n#include <iostream>\n\nusing namespace std;\n\n#include <mutex>\n\n#define barrier() __asm__ volatile (\"lwsync\")\n\n// method 1 operator new + placement new\n//singleton *instance() {\n//    if (p == nullptr) {\n//        lock_guard<mutex> guard(lock_);\n//        if (p == nullptr) {\n//            singleton *tmp = static_cast<singleton *>(operator new(sizeof(singleton)));\n//            new(p)singleton();\n//            p = tmp;\n//        }\n//    }\n//    return p;\n//}\nclass singleton {\nprivate:\n    singleton() {}\n\n    static singleton *p;\n    static mutex lock_;\npublic:\n    static singleton *instance();\n};\n\nsingleton *singleton::p = nullptr;\n\nsingleton *singleton::instance() {\n    if (p == nullptr) {\n        lock_guard<mutex> guard(lock_);\n        barrier();\n        if (p == nullptr) {\n            p = new singleton();\n        }\n    }\n    return p;\n}\n\n"
  },
  {
    "path": "learn_class/modern_cpp_30/memorymodel_atomic/cpulpuls11_singleton.cpp",
    "content": "//\n// Created by light on 20-2-7.\n//\n\n#include <iostream>\n\nusing namespace std;\n\n#include <mutex>\n#include <atomic>\n\n//C++ 11版本之后的跨平台实现\nclass singleton {\nprivate:\n    singleton() {}\n\n    static mutex lock_;\n    static atomic<singleton *> p;\npublic:\n    singleton *instance();\n};\n\nmutex singleton::lock_;\natomic<singleton *> singleton::p;\n\n/*\n* std::atomic_thread_fence(std::memory_order_acquire);\n* std::atomic_thread_fence(std::memory_order_release);\n* 这两句话可以保证他们之间的语句不会发生乱序执行。\n*/\nsingleton *singleton::instance() {\n    singleton *tmp = p.load(memory_order_relaxed);\n    atomic_thread_fence(memory_order_acquire);\n    if (tmp == nullptr) {\n        lock_guard<mutex> guard(lock_);\n        tmp = p.load(memory_order_relaxed);\n        if (tmp == nullptr) {\n            tmp = new singleton();\n            atomic_thread_fence(memory_order_release);\n            p.store(tmp, memory_order_relaxed);\n        }\n    }\n    return p;\n}\n\n\n"
  },
  {
    "path": "learn_class/modern_cpp_30/memorymodel_atomic/dcl_singleton.cpp",
    "content": "//\n// Created by light on 20-2-7.\n//\n#include <iostream>\n\nusing namespace std;\n\n#include <mutex>\n\nclass singleton {\nprivate:\n    singleton() {}\n\n    static singleton *p;\n    static mutex lock_;\npublic:\n    singleton *instance();\n\n    // 实现一个内嵌垃圾回收类\n    class CGarbo\n    {\n    public:\n        ~CGarbo()\n        {\n            if(singleton::p)\n                delete singleton::p;\n        }\n    };\n    static CGarbo Garbo; // 定义一个静态成员变量，程序结束时，系统会自动调用它的析构函数从而释放单例对象\n};\n\nsingleton *singleton::p = nullptr;\nsingleton::CGarbo Garbo;\n\nsingleton* singleton::instance() {\n    if (p == nullptr) {\n        lock_guard<mutex> guard(lock_);\n        if (p == nullptr)\n            p = new singleton();\n    }\n    return p;\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/memorymodel_atomic/hungrysingleton.cpp",
    "content": "//\n// Created by light on 20-2-6.\n//\n\nclass singleton {\nprivate:\n    singleton() {}\n    static singleton *p;\npublic:\n    static singleton *instance();\n};\n\nsingleton *singleton::p = new singleton();\nsingleton* singleton::instance() {\n    return p;\n}\n\n"
  },
  {
    "path": "learn_class/modern_cpp_30/memorymodel_atomic/iazysingleton.cpp",
    "content": "//\n// Created by light on 20-2-6.\n//\n\nclass singleton {\nprivate:\n    singleton() {}\n    static singleton *p;\npublic:\n    static singleton *instance();\n};\n\nsingleton *singleton::p = nullptr;\n\nsingleton* singleton::instance() {\n    if (p == nullptr)\n        p = new singleton();\n    return p;\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/memorymodel_atomic/lock_singleton.cpp",
    "content": "//\n// Created by light on 20-2-7.\n//\n#include <iostream>\nusing namespace std;\n\n#include <mutex>\n\nclass singleton {\nprivate:\n    singleton() {}\n    static singleton *p;\n    static mutex lock_;\npublic:\n    static singleton *instance();\n};\n\nsingleton *singleton::p = nullptr;\n\nsingleton* singleton::instance() {\n    lock_guard<mutex> guard(lock_);\n    if (p == nullptr)\n        p = new singleton();\n    return p;\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/memorymodel_atomic/pthreadoncesingleton.cpp",
    "content": "//\n// Created by light on 20-2-6.\n//\n\n#include <sys/param.h>\n#include <pthread.h>\n\nclass singleton {\nprivate:\n    singleton(); //私有构造函数，不允许使用者自己生成对象\n    singleton(const singleton &other);\n\n    //要写成静态方法的原因：类成员函数隐含传递this指针（第一个参数）\n    static void init() {\n        p = new singleton();\n    }\n\n    static pthread_once_t ponce_;\n    static singleton *p; //静态成员变量\npublic:\n    singleton *instance() {\n        // init函数只会执行一次\n        pthread_once(&ponce_, &singleton::init);\n        return p;\n    }\n};\n\n\n"
  },
  {
    "path": "learn_class/modern_cpp_30/memorymodel_atomic/static_local_singleton.cpp",
    "content": "//\n// Created by light on 20-2-7.\n//\n\n#include <iostream>\n\nusing namespace std;\n\nclass singleton {\nprivate:\n    static singleton *p;\n    singleton() {}\npublic:\n    singleton *instance();\n};\n\nsingleton *singleton::instance() {\n    static singleton p;\n    return &p;\n}\n\n\n"
  },
  {
    "path": "learn_class/modern_cpp_30/obj/BUILD",
    "content": "# please run `bazel run //learn_class/modern_cpp_30/obj:obj1`\n# please run `bazel run //learn_class/modern_cpp_30/obj:obj4`\n# please run `bazel run //learn_class/modern_cpp_30/obj:obj5`\n# please run `bazel run //learn_class/modern_cpp_30/obj:obj3`\n# please run `bazel run //learn_class/modern_cpp_30/obj:all`\n# please run `bazel run //learn_class/modern_cpp_30/obj:obj2`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"obj1\",\n    srcs = [\"obj1.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"obj4\",\n    srcs = [\"obj4.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"obj5\",\n    srcs = [\"obj5.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"obj3\",\n    srcs = [\"obj3.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"all\",\n    srcs = [\"all.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"obj2\",\n    srcs = [\"obj2.cpp\"],\n    copts = [\"-std=c++11\"],\n)\n"
  },
  {
    "path": "learn_class/modern_cpp_30/obj/all.cpp",
    "content": "//\n// Created by light on 19-12-23.\n//\n\n#include <iostream>\n#include <string>\n#include <vector>\n\nstruct Test {\n  Test() {\n    // std::cout << \"construct a Test object\" << std::endl;\n  }\n\n  Test(const Test &) {\n    std::cout << \"copy construct  a Test object\" << std::endl;\n  }\n\n  Test &operator=(const Test &t) {\n    std::cout << \"copy assignment a Test object\" << std::endl;\n    return *this;\n  }\n\n  Test(Test &&t) { std::cout << \"move construct a Test object\" << std::endl; }\n\n  Test &operator=(Test &&t) {\n    std::cout << \"move assignment a Test object\" << std::endl;\n    return *this;\n  }\n\n  ~Test() {\n    // std::cout << \"destruct a Test object\" << std::endl;\n  }\n};\n\nTest getTest() {\n  // anonymous object\n  return Test();\n}\n\nTest getTestWithName() {\n  // named return value\n  Test temp;\n  return temp;\n}\n/**\n * 1.copy\n * construct本身在RVO和NRVO两种情况下被优化了，如果再加上move反而画蛇添足。\n * 2.加入了move assignment后，默认是调用move assignment而不是copy\n * assignment，可以将move assignment注释后测试。\n * 3.对于RVO和NRVO来说，construction的情况编译器优化得比较好了，加入move语义主要是对于assignment有比较大影响\n */\nint main() {\n  std::cout << \"==== common case ====\" << std::endl;\n  Test o1;\n  std::cout << \"---- Test copy construct: \" << std::endl;\n  Test o2(o1); // two ways for copy construct\n  Test o3 = o1;\n  std::cout << \"---- Test move construct: \" << std::endl;\n  Test o4(std::move(o3));\n  std::cout << \"---- Test assignment: \" << std::endl;\n  o2 = o1;\n  std::cout << \"---- Test move assignment: \" << std::endl;\n  Test o5;\n  o5 = std::move(o4);\n\n  std::cout << \"\\n==== test for rvo ===\" << std::endl;\n  std::cout << \"---- Test rvo for copy construct: \" << std::endl;\n  Test obj11(getTest());\n  Test obj1 = getTest();\n  std::cout << \"---- Test rvo for move construct: \" << std::endl;\n  Test obj12(std::move(getTest()));\n  std::cout << \"---- Test rvo for assignment: \" << std::endl;\n  Test obj2;\n  obj2 = getTest();\n  std::cout << \"---- Test rvo move assignment: \" << std::endl;\n  Test obj5;\n  obj5 = std::move(getTest());\n\n  std::cout << \"\\n==== test for nrvo ===\" << std::endl;\n  std::cout << \"---- Test nrvo for copy construct: \" << std::endl;\n  Test obj33(getTestWithName());\n  Test obj3 = getTestWithName();\n  std::cout << \"---- Test nrvo for move construct: \" << std::endl;\n  Test obj34(std::move(getTestWithName()));\n  std::cout << \"---- Test nrvo for assignment: \" << std::endl;\n  Test obj4;\n  obj4 = getTestWithName();\n  std::cout << \"---- Test nrvo move assignment: \" << std::endl;\n  Test obj6;\n  obj6 = std::move(getTestWithName());\n\n  return 0;\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/obj/obj1.cpp",
    "content": "//\n// Created by light on 19-12-22.\n//\n\n// rvo example\n\n#include <iostream>\n\nusing namespace std;\n\n// Can copy and move\nclass A {\npublic:\n  A() { cout << \"Create A\\n\"; }\n\n  ~A() { cout << \"Destroy A\\n\"; }\n\n  A(const A &) { cout << \"Copy A\\n\"; }\n\n  A(A &&) { cout << \"Move A\\n\"; }\n\n  A &operator=(const A &a) {\n    std::cout << \"copy assignment\" << std::endl;\n    return *this;\n  }\n\n  A &operator=(A &&a) {\n    cout << \"move assignment\\n\";\n    return *this;\n  }\n};\n\n// 编译器可以优化 返回值移动出去\nA getA_unnamed() { return A(); }\n\nint main() {\n  //    cout<<\"构造\"<<endl;\n  //    auto a = getA_unnamed();\n  cout << \"赋值\" << endl;\n  A aa;\n  aa = getA_unnamed();\n  //    aa=std::move(getA_unnamed());\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/obj/obj2.cpp",
    "content": "//\n// Created by light on 19-12-22.\n//\n\n// nrvo example\n\n#include <iostream>\n\nusing namespace std;\n\n// Can copy and move\nclass A {\npublic:\n  A() { cout << \"Create A\\n\"; }\n\n  ~A() { cout << \"Destroy A\\n\"; }\n\n  A(const A &) { cout << \"Copy A\\n\"; }\n\n  A(A &&) { cout << \"Move A\\n\"; }\n\n  A &operator=(const A &a) {\n    std::cout << \"copy assignment\" << std::endl;\n    return *this;\n  }\n\n  A &operator=(A &&a) {\n    cout << \"move assignment\\n\";\n    return *this;\n  }\n};\n\n// 编译器可以优化 返回值移动出去\nA getA_named() {\n  A a;\n  return a;\n}\n\nint main() {\n  //    cout<<\"拷贝\"<<endl;\n  //    auto a = getA_named();\n\n  cout << \"赋值\" << endl;\n  A aa;\n  aa = getA_named();\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/obj/obj3.cpp",
    "content": "//\n// Created by light on 19-12-22.\n//\n\n#include <iostream>\n\nusing namespace std;\n\n// Can copy and move\nclass A {\npublic:\n  A() { cout << \"Create A\\n\"; }\n\n  ~A() { cout << \"Destroy A\\n\"; }\n\n  A(const A &) { cout << \"Copy A\\n\"; }\n\n  A(A &&) { cout << \"Move A\\n\"; }\n\n  A &operator=(const A &a) {\n    std::cout << \"copy assignment\" << std::endl;\n    return *this;\n  }\n\n  A &operator=(A &&a) {\n    cout << \"move assignment\\n\";\n    return *this;\n  }\n};\n// 编译器无法优化\nA getA_duang() {\n  A a1;\n  A a2;\n  if (rand() > 42) {\n    return a1;\n  } else {\n    return a2;\n  }\n}\n\nint main() {\n  //    cout<<\"拷贝\"<<endl;\n  //    auto a = getA_duang();\n\n  cout << \"赋值\" << endl;\n  A aa;\n  aa = getA_duang();\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/obj/obj4.cpp",
    "content": "//\n// Created by light on 19-12-22.\n//\n\n#include <iostream>\n\nusing namespace std;\n\n// Can copy and move\nclass A {\npublic:\n  A() { cout << \"Create A\\n\"; }\n\n  ~A() { cout << \"Destroy A\\n\"; }\n\n  A(const A &) { cout << \"Copy A\\n\"; }\n\n  A(A &&) = delete;\n};\n\nA getA_duang() {\n  A a1;\n  A a2;\n  if (rand() > 42) {\n    return a1;\n  } else {\n    return a2;\n  }\n}\n\nint main() { auto a = getA_duang(); }"
  },
  {
    "path": "learn_class/modern_cpp_30/obj/obj5.cpp",
    "content": "//\n// Created by light on 19-12-22.\n//\n\n#include <iostream>\n\nusing namespace std;\n\n// Can copy and move\nclass A {\npublic:\n  A() { cout << \"Create A\\n\"; }\n\n  ~A() { cout << \"Destroy A\\n\"; }\n\n  //    A(const A &) { cout << \"Copy A\\n\"; }\n  //\n  //    A(A &&) { cout << \"Move A\\n\"; }\n\n  A(const A &&) = delete;\n  A(A &&) = delete;\n};\n\nA getA_unnamed() { return A(); }\n\nint main() { auto a = getA_unnamed(); }"
  },
  {
    "path": "learn_class/modern_cpp_30/reference/BUILD",
    "content": "# please run `bazel run //learn_class/modern_cpp_30/reference:reference`\n# please run `bazel run //learn_class/modern_cpp_30/reference:forward`\n# please run `bazel run //learn_class/modern_cpp_30/reference:ref`\n# please run `bazel run //learn_class/modern_cpp_30/reference:collapses`\n# please run `bazel run //learn_class/modern_cpp_30/reference:lifetime`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\")\n\ncc_library(\n    name = \"shape\",\n    hdrs = [\"shape.h\"],\n)\n\ncc_binary(\n    name = \"reference\",\n    srcs = [\"reference.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"forward\",\n    srcs = [\"forward.cpp\"],\n    copts = [\"-std=c++11\"],\n    deps = [\n        \":shape\",\n    ],\n)\ncc_binary(\n    name = \"ref\",\n    srcs = [\"ref.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"collapses\",\n    srcs = [\"collapses.cpp\"],\n    copts = [\"-std=c++11\"],\n)\ncc_binary(\n    name = \"lifetime\",\n    srcs = [\"lifetime.cpp\"],\n    copts = [\"-std=c++11\"],\n    deps = [\n        \":shape\",\n    ],\n)\n"
  },
  {
    "path": "learn_class/modern_cpp_30/reference/collapses.cpp",
    "content": "//\n// Created by light on 19-12-15.\n//\n#include <iostream>\n\nusing namespace std;\n\ntemplate<typename T>\nvoid f(T &&param) {\n    static_assert(std::is_lvalue_reference<T>::value, \"T& is lvalue reference\");\n    cout << \"T& is lvalue reference\" << endl;\n}\n\ntemplate<typename T>\nclass Widget {\n    typedef T& LvalueRefType;\n    typedef T&& RvalueRefType;\npublic:\n    void judge() {\n        static_assert(std::is_lvalue_reference<LvalueRefType>::value, \"LvalueRefType & is lvalue reference\");\n        static_assert(std::is_lvalue_reference<RvalueRefType>::value, \"RvalueRefType & is lvalue reference\");\n        cout << \"LvalueRefType and RvalueRefType is lvalue reference\" << endl;\n    }\n    void f(LvalueRefType&& param) {\n\n    }\n};\n\n\nint main() {\n    int x;\n    int &&r1 = 10;\n    int &r2 = x;\n    f(r1);\n    f(r2);\n\n    Widget<int &> w;\n    w.judge();\n\n\n    Widget<int> w1, w2;\n\n    auto&& v1 = w1;                  // v1 is an auto-based universal reference being\n    // initialized with an lvalue, so v1 becomes an\n    // lvalue reference referring to w1.\n\n    // 不能编译\n//    decltype(w1)&& v2 = w2;          // v2 is a decltype-based universal reference, and\n    // decltype(w1) is Widget, so v2 becomes an rvalue reference.\n    // w2 is an lvalue, and it’s not legal to initialize an\n    // rvalue reference with an lvalue, so\n    // this code does not compile.\n}\n"
  },
  {
    "path": "learn_class/modern_cpp_30/reference/forward.cpp",
    "content": "//\n// Created by light on 19-12-15.\n//\n#include <iostream>\n#include \"shape.h\"\n\nvoid overloaded( int const &arg ) { std::cout << \"by lvalue\\n\"; }\nvoid overloaded( int && arg ) { std::cout << \"by rvalue\\n\"; }\n\ntemplate< typename t >\n/* \"t &&\" with \"t\" being template param is special, and  adjusts \"t\" to be\n   (for example) \"int &\" or non-ref \"int\" so std::forward knows what to do. */\nvoid forwarding( t && arg ) {\n    std::cout << \"via std::forward: \";\n    overloaded( std::forward< t >( arg ) );\n    std::cout << \"via std::move: \";\n    overloaded( std::move( arg ) ); // conceptually this would invalidate arg\n    std::cout << \"by simple passing: \";\n    overloaded( arg );\n}\nvoid foo(const shape&)\n{\n    puts(\"foo(const shape&)\");\n}\nvoid foo(shape&&)\n{\n    puts(\"foo(shape&&)\");\n}\ntemplate <typename T>\nvoid bar(T&& s)\n{\n    foo(std::forward<T>(s));\n}\nint main() {\n    std::cout << \"initial caller passes rvalue:\\n\";\n    forwarding( 5 );\n    std::cout << \"initial caller passes lvalue:\\n\";\n    int x = 5;\n    forwarding( x );\n\n    circle temp;\n    bar(temp);\n    bar(circle());\n\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/reference/lifetime.cpp",
    "content": "//\n// Created by light on 19-12-15.\n//\n\n#include <iostream>\n#include \"shape.h\"\n\nusing namespace std;\n\nclass result {\npublic:\n    result() { puts(\"result()\"); }\n\n    ~result() { puts(\"~result()\"); }\n};\n\nresult process_shape(const shape &shape1, const shape &shape2) {\n    puts(\"process_shape()\");\n    return result();\n}\n\n\nclass Base {\npublic:\n    Base() {\n        cout << \"Base()\" << endl;\n    }\n\n    ~Base() {\n        cout << \"~Base()\" << endl;\n    }\n};\n\nclass Derived : public Base {\npublic:\n    Derived() {\n        cout << \"Derived()\" << endl;\n    }\n\n    ~Derived() {\n        cout << \"~Derived()\" << endl;\n    }\n};\n\nstring f() { return \"abc\"; }\n\nvoid g() {\n    const string &s = f();       // still legal?\n    cout << s << endl;\n}\n\nDerived factory() {\n    return Derived();\n}\n\nint main() {\n    process_shape(circle(), triangle());\n    cout << endl;\n    // 临时对象延迟\n//    result &&r = process_shape(circle(), triangle());\n    // 临时对象延迟只对rvalue有用，而对xvalue无用！\n//    result &&r = std::move(process_shape(circle(), triangle()));\n\n//   const Base &b1 = factory();\n\n    Base *b1 = new Derived;\n    delete b1;\n    cout<<endl;\n    Derived d;\n    Base &b2 =d;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/reference/ref.cpp",
    "content": "//\n// Created by light on 19-12-15.\n//\n\n#include <iostream> // std::cout/endl\n#include <utility> // std::move\nusing namespace std;\nclass Obj {\npublic:\n    Obj()\n    {\n        cout << \"Obj()\" << endl;\n    }\n    Obj(const Obj&)\n    {\n        cout << \"Obj(const Obj&)\"\n             << endl;\n    }\n    Obj(Obj&&)\n    {\n        cout << \"Obj(Obj&&)\" << endl;\n    }\n};\nObj simple()\n{\n    Obj obj;\n// 简单返回对象；一般有 NRVO\n    return obj;\n}\nObj simple_with_move()\n{\n    Obj obj;\n// move 会禁止 NRVO\n    return std::move(obj);\n}\nObj complicated(int n)\n{\n    Obj obj1;\n    Obj obj2;\n    // 有分支，一般无 NRVO\n    if (n % 2 == 0) {\n        return obj1;\n    } else {\n        return obj2;\n    }\n}\nint main()\n{\n    cout << \"*** 1 ***\" << endl;\n    auto obj1 = simple();\n    cout << \"*** 2 ***\" << endl;\n    auto obj2 = simple_with_move();\n    cout << \"*** 3 ***\" << endl;\n    auto obj3 = complicated(42);\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/reference/reference.cpp",
    "content": "//\n// Created by light on 19-12-14.\n//\n\n#include <iostream>\n#include <functional>\n\nusing namespace std;\n\n// xvalue\nint&& f(){\n    return 3;\n}\n\nstruct As\n{\n    int i;\n};\n\nAs&& ff(){\n    return As();\n}\n\nint main() {\n    // lvalue\n    int x = 0;\n    cout << \"(x).addr = \" << &x << endl;\n    cout << \"(x = 1).addr = \" << &(x = 1) << endl;\n    cout << \"(++x).addr = \" << &++x << endl;\n    cout << \"(cout << ' ').addr=\" << &(cout << ' ') << endl;\n    cout << \"(\\\"hello\\\").addr=\" << &(\"hello\") << endl;\n    // rvalue\n    cout<<true<<endl;\n    // xvalue\n    f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type.\n    static_cast<int&&>(7); // The expression static_cast<int&&>(7) belongs to the xvalue category, because it is a cast to an rvalue reference to object type.\n    std::move(7); // std::move(7) is equivalent to static_cast<int&&>(7).\n\n    ff().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category.\n\n\n    return 0;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/reference/shape.h",
    "content": "#pragma once\n#include <iostream>\nusing namespace std;\n\nclass shape {\npublic:\n    shape() { cout << \"shape\" << endl; }\n\n    ~shape() {\n        cout << \"~shape\" << endl;\n    }\n};\n\nclass circle : public shape {\npublic:\n    circle() { cout << \"circle\" << endl; }\n\n\n    ~circle() {\n        cout << \"~circle\" << endl;\n    }\n};\n\nclass triangle : public shape {\npublic:\n    triangle() { cout << \"triangle\" << endl; }\n\n\n    ~triangle() {\n        cout << \"~triangle\" << endl;\n    }\n};\n\nclass rectangle : public shape {\npublic:\n    rectangle() { cout << \"rectangle\" << endl; }\n\n    ~rectangle() {\n        cout << \"~rectangle\" << endl;\n    }\n};\n"
  },
  {
    "path": "learn_class/modern_cpp_30/smart_ptr/README.md",
    "content": "## 0.回顾\n\n前面一节编写了一个RAII的例子：\n\n```cpp\nclass shape_wrapper {\npublic:\n    explicit shape_wrapper(\n            shape* ptr = nullptr)\n            : ptr_(ptr) {}\n    ~shape_wrapper()\n    {\n        delete ptr_;\n    }\n    shape* get() const { return ptr_; }\nprivate:\n    shape* ptr_;\n};\n```\n\n这个类可以完成智能指针的最基本的功能：对超出作用域的对象进行释放。但它缺了点东\n西：\n\n- 这个类只适用于 shape 类\n\n- 该类对象的行为不够像指针\n\n- 拷贝该类对象会引发程序行为\n\n## 1.手写auto_ptr与scope_ptr\n\n针对**\"这个类只适用于 shape 类\"**，我们想到了模板，于是改造为：\n\n```cpp\ntemplate <typename  T>\nclass smart_ptr {\npublic:\n    explicit smart_ptr(\n            T* ptr = nullptr)\n            : ptr_(ptr) {}\n    ~smart_ptr()\n    {\n        delete ptr_;\n    }\n    T* get() const { return ptr_; }\nprivate:\n    T* ptr_;\n};\n```\n\n针对**\"该类对象的行为不够像指针\"**,我们想到了指针的基本操作有`*`,`->`,布尔表达式。\n\n于是添加三个成员函数：\n\n```cpp\ntemplate <typename  T>\nclass smart_ptr {\npublic:\n   \t...\n    T& operator*() const { return *ptr_; }\n    T* operator->() const { return ptr_; }\n    operator bool() const { return ptr_; }\n    ...\nprivate:\n    T* ptr_;\n};\n```\n\n针对**\"拷贝该类对象会引发程序行为\"**，我们想到了拷贝构造和赋值。\n\n现考虑如下调用：\n\n```cpp\nsmart_ptr<shape> ptr1{create_shape(shape_type::circle)};\nsmart_ptr<shape> ptr2{ptr1};\n```\n\n对于第二行，究竟应当让编译时发生错误，还是可以有一个更合理的行为？我们来逐一检查\n一下各种可能性。\n最简单的情况显然是禁止拷贝。我们可以使用下面的代码：\n\n```cpp\ntemplate <typename T>\nclass smart_ptr {\n    …\n    smart_ptr(const smart_ptr&)\n    = delete;\n    smart_ptr& operator=(const smart_ptr&)\n    = delete;\n    …\n};\n```\n\n当然，也可以设为private。\n\n禁用这两个函数非常简单，但却解决了一种可能出错的情况。否则，`smart_ptr<shape> ptr2{ptr1};` 在编译时不会出错，但在运行时却会有未定义行为——**由于会对同一内存释放两次，通常情况下会导致程序崩溃。**\n\n我们是不是可以考虑在拷贝智能指针时把对象拷贝一份？不行，通常人们不会这么用，因为使用智能指针的目的就是要减少对象的拷贝啊。何况，虽然我们的指针类型是 shape，但实际指向的却应该是 circle 或 triangle 之类的对象。在 C++ 里没有像 Java 的clone 方法这样的约定；**一般而言，并没有通用的方法可以通过基类的指针来构造出一个子类的对象来。**\n\n那关键点就来了，**所有权！**，我们可以拷贝时转移指针的所有权！下面实现便是`auto_ptr`的核心实现：\n\n```cpp\ntemplate<typename T>\nclass auto_ptr {\npublic:\n    explicit auto_ptr(\n            T *ptr = nullptr) noexcept\n            : ptr_(ptr) {}\n\n    ~auto_ptr() noexcept {\n        delete ptr_;\n    }\n\t// 返回值为T&，允许*ptr=10操作\n    T &operator*() const noexcept { return *ptr_; }\n\n    T *operator->() const noexcept { return ptr_; }\n\n    operator bool() const noexcept { return ptr_; }\n\n    T *get() const noexcept { return ptr_; }\n\n    // 拷贝构造,被复制放释放原来指针的所有权,交给复制方\n    auto_ptr(auto_ptr &other) noexcept {\n        ptr_ = other.release();\n    }\n\n    // copy and swap\n    auto_ptr &operator=(auto_ptr &rhs) noexcept {\n//        auto_ptr tmp(rhs.release());\n//        tmp.swap(*this);\n        // s上述两行等价于下面一行\n        auto_ptr(rhs.release()).swap(*this);\n        return *this;\n    }\n\n    // 原来的指针释放所有权\n    T *release() noexcept {\n        T *ptr = ptr_;\n        ptr_ = nullptr;\n        return ptr;\n    }\n\n    void swap(auto_ptr &rhs) noexcept {\n        using std::swap;\n        swap(ptr_, rhs.ptr_);    // 转移指针所有权\n    }\n\nprivate:\n    T *ptr_;\n};\n\ntemplate<typename T>\nvoid swap(auto_ptr<T> &lhs, auto_ptr<T> &rhs) noexcept {\n    lhs.swap(rhs);\n}\n\nint main() {\n    auto_ptr<shape> ptr1{create_shape(shape_type::circle)};\n    auto_ptr<shape> ptr2{ptr1};\n    if (ptr1.get() == nullptr && ptr2.get())\n        cout << \"拷贝构造：ptr1释放了所有权,ptr2获得了所有权\" << endl;\n    ptr1 = ptr1;\n\n    auto_ptr<shape> ptr3{create_shape(shape_type::rectangle)};\n    ptr1 = ptr3;\n\n    if (ptr3.get() == nullptr && ptr1.get())\n        cout << \"赋值操作：始终只有一个对象管理一个区块!ptr3释放了所有权,ptr1获得了所有权\" << endl;\n}\n```\n\n上述通过copy-swap技术完成了避免自我赋值与保证了强异常安全！\n\n如果你觉得这个实现还不错的话，那恭喜你，你达到了 C++ 委员会在 1998 年时的水平：上面给出的语义本质上就是 C++98 的 auto_ptr 的定义。如果你觉得这个实现很别扭的话，也恭喜你，因为 C++ 委员会也是这么觉得的：**auto_ptr 在 C++17 时已经被正式从C++ 标准里删除了**。\n\n上面会导致什么问题呢？\n\n看一下输出结果：\n\n```cpp\nshape\ncircle\n拷贝构造：ptr1释放了所有权,ptr2获得了所有权\nshape\nrectangle\n赋值操作：始终只有一个对象管理一个区块!ptr3释放了所有权,ptr1获得了所有权\n```\n\nshape与circle实在create_shape时候输出的，我们重点关注最后一句话，发现了一个很大的问题：**它的行为会让程序员非常容易犯错。一不小心把它传递给另外一个 auto_ptr，你就不再拥有这个对象了。**\n\n上述拷贝构造与拷贝赋值分别如下面两张图所示：\n\n\n\n\n\n针对这个问题，在C++11标准出来之前，C++98标准中都一直只有一个智能指针auto_ptr，我们知道，这是一个失败的设计。它的本质是**管理权的转移**，这有许多问题。而这时就有一群人开始扩展C++标准库的关于智能指针的部分，他们组成了boost社区，他们负责boost库的开发和维护。其目的是为C++程序员提供免费的、同行审查的、可移植的程序库。boost库可以和C++标准库完美的共同工作，并且为其提供扩展功能。现在的**C++11标准库**的智能指针很大程度上“借鉴”了boost库。\n\nboost::scoped_ptr 属于 boost 库，定义在 namespace boost 中，包含头文件`#include<boost/smart_ptr.hpp> `可以使用。scoped_ptr 跟 auto_ptr 一样，可以方便的管理单个堆内存对象，特别的是，scoped_ptr 独享所有权，避免了auto_ptr恼人的几个问题。\n\n<u>scoped_ptr是一种简单粗暴的设计，它本质就是**防拷贝**，避免出现管理权的转移。</u>这是它的最大特点，所以他的拷贝构造函数和赋值运算符重载函数都只是声明而不定义，而且为了防止有的人在类外定义，所以将函数声明为private。但这也是它最大的问题所在，就是不能赋值拷贝，也就是说功能不全。但是这种设计比较高效、简洁。没有 release() 函数，不会导致先前的内存泄露问题。下面我也将模拟实现scoped_ptr的管理机制(实际上就是前面提到的禁止拷贝)：\n\n```cpp\ntemplate<class T>\nclass scoped_ptr // noncopyable\n{\n\npublic:\n    explicit scoped_ptr(T *ptr = 0) noexcept : ptr_(ptr) {\n    }\n\n    ~scoped_ptr() noexcept {\n        delete ptr_;\n    }\n\n    void reset(T *p = 0) noexcept {\n        scoped_ptr(p).swap(*this);\n    }\n\n    T &operator*() const noexcept {\n        return *ptr_;\n    }\n\n    T *operator->() const noexcept {\n        return ptr_;\n    }\n\n    T *get() const noexcept {\n        return ptr_;\n    }\n\n    void swap(scoped_ptr &rhs) noexcept {\n        using std::swap;\n        swap(ptr_, rhs.ptr_);\n    }\n\nprivate:\n    T *ptr_;\n\n    scoped_ptr(scoped_ptr const &);\n    scoped_ptr &operator=(scoped_ptr const &);\n};\n\ntemplate<typename T>\nvoid swap(scoped_ptr<T> &lhs, scoped_ptr<T> &rhs) noexcept {\n    lhs.swap(rhs);\n}\n```\n\nscoped_ptr特点总结：\n\n1）与auto_ptr类似，采用栈上的指针去管理堆上的内容，从而使得堆上的对象随着栈上对象销毁时自动删除；\n\n2）scoped_ptr有着更严格的使用限制——不能拷贝，这也意味着scoped_ptr不能转换其所有权，所以它管理的对象不能作为**函数的返回值**，对象生命周期仅仅局限于一定区间（该指针所在的{}区间，而std::auto_ptr可以）；\n\n3）由于防拷贝的特性，使其管理的对象**不能共享所有权**，这与std::auto_ptr类似，这一特点使该指针简单易用，但也造成了功能的薄弱。\n\n## 2.手写unique_ptr之子类向基类转换\n\n在上述auto_ptr基础上，我们把拷贝构造与拷贝赋值，改为移动构造与移动赋值。\n\n```cpp\ntemplate<typename T>\nclass unique_ptr {\npublic:\n    explicit unique_ptr(\n            T *ptr = nullptr) noexcept\n            : ptr_(ptr) {}\n\n    ~unique_ptr() noexcept {\n        delete ptr_;\n    }\n\n    T &operator*() const noexcept { return *ptr_; }\n\n    T *operator->() const noexcept { return ptr_; }\n\n    operator bool() const noexcept { return ptr_; }\n\n    T *get() const noexcept { return ptr_; }\n\n    unique_ptr(unique_ptr &&other) noexcept {\n        ptr_ = other.release();\n    }\n\n    // copy and swap  始终只有一个对象有管理这块空间的权限\n    unique_ptr &operator=(unique_ptr rhs) noexcept {\n        rhs.swap(*this);\n        return *this;\n    }\n\n    // 原来的指针释放所有权\n    T *release() noexcept {\n        T *ptr = ptr_;\n        ptr_ = nullptr;\n        return ptr;\n    }\n\n    void swap(unique_ptr &rhs) noexcept {\n        using std::swap;\n        swap(ptr_, rhs.ptr_);    // 转移指针所有权\n    }\n\nprivate:\n    T *ptr_;\n};\ntemplate<typename T>\nvoid swap(unique_ptr<T> &lhs, unique_ptr<T> &rhs) {\n    lhs.swap(rhs);\n}\n```\n\n调用：\n\n```cpp\nint main() {\n    unique_ptr<shape> ptr1{create_shape(shape_type::circle)};\n//    unique_ptr<shape> ptr2{ptr1}; // error\n    unique_ptr<shape> ptr2{std::move(ptr1)};    // ok\n\n    unique_ptr<shape> ptr3{create_shape(shape_type::rectangle)};\n//    ptr1 = ptr3;    // error\n    ptr3 = std::move(ptr1); // ok\n}\n```\n\n把拷贝构造函数中的参数类型 unique_ptr& 改成了 unique_ptr&&；现在它成了移动构造函数。\n把赋值函数中的参数类型 unique_ptr& 改成了 unique_ptr，在构造参数时直接生成新的智能指针，从而不再需要在函数体中构造临时对象。现在赋值函数的行为是移动还是拷贝，完全依赖于构造参数时走的是移动构造还是拷贝构造。\n\n最后，一个` circle*` 是可以隐式转换成 `shape*`的，但上面的 `unique_ptr<circle>` 却无法自动转换成 `unique_ptr<shape>`。\n\n现在我们考虑两种情况：\n\n**（1）第一种：当我们只是在原先的移动构造上面添加`template <typename U>`，此时情况是移动构造变为带模板的移动构造，可以进行子类向基类转换，但是与移动构造相关的，则调用的是默认移动构造，除非是子类向基类转换，才调用带模板的移动构造。**\n\n```cpp\ntemplate <typename U>\nunique_ptr(unique_ptr<U> &&other) noexcept {\n    ptr_ = other.release();\n}\n```\n\n**六个特殊的成员函数其生成规则如下：**\n\n- **默认构造函数，生成规则和C++98一样，在用户没有声明自定义的构造函数的时候并且编译期需要的时候生成。**\n- **析构函数，生成规则和C++98一样，在C++11中有点不同的是，析构函数默认是noexcept。**\n- **拷贝构造函数，用户自定义了移动操作会导致不生成默认的拷贝构造函数，其它和C++98的行为一致。**\n- **拷贝赋值操作符，用户自定义了移动操作会导致不生成默认的拷贝赋值操作，其它和C++98的行为一致。**\n- **移动构造函数和移动赋值操作符，仅仅在没有用户自定义的拷贝操作，移动操作和析构操作的时候才会生成。**\n\n根据《Effective Modern C++》Item17 P115页提到，当类中含有特殊成员函数变为模板特殊成员函数的时候,此时不满足上述生成规则，也就是针对当前例子来说，编译器会默认生成拷贝构造，所以此时上述main调用里面为error的都可以正常运行！\n\n```cpp\nint main() {\n    unique_ptr<shape> ptr1{create_shape(shape_type::circle)};\n    unique_ptr<shape> ptr2{ptr1}; // 由于带模板的移动构造函数引发编译器会默认生成拷贝构造\n    if (ptr1.get() != nullptr)      // bitwise copy 此时ptr1不为NULL\n        ptr2.get()->print();\n\n    unique_ptr<shape> ptr2_2{std::move(ptr1)};    // 调用的是默认的移动构造,而不是带模板的移动构造 bitwise move\n    if (ptr2_2.get() != nullptr && ptr1.get() != nullptr)   // ptr1 不为空\n        ptr2_2.get()->print();\n\n    unique_ptr<shape> ptr3{create_shape(shape_type::rectangle)};\n    ptr1 = ptr3;    // ok  根据形参先调用默认拷贝,再调用拷贝赋值\n    ptr3 = std::move(ptr1); // ok 根据形参先调用默认移动构造，而不是带参数的移动构造,再调用移动赋值\n    unique_ptr<shape> ptr4(std::move(new circle));  // ok 调用带模板的移动构造\n}\n```\n\n调用与结果如上代码所示。\n\n**（2）第二种：移动构造与带模板的移动构造同时存在，可以完成子类向基类的转换，此时是满足上述生成规则，此时不会生成拷贝函数！**\n\n```cpp\nint main() {\n    unique_ptr<shape> ptr1{create_shape(shape_type::circle)};\n//    unique_ptr<shape> ptr2{ptr1}; // error\n    unique_ptr<shape> ptr2_2{std::move(ptr1)};    // ok\n    if (ptr2_2.get() != nullptr && ptr1.get() == nullptr)\n        ptr2_2.get()->print();\n\n    unique_ptr<shape> ptr3{create_shape(shape_type::rectangle)};\n//    ptr1 = ptr3;    // error\n    ptr3 = std::move(ptr1); // ok\n//    unique_ptr<circle> cl{create_shape(shape_type::circle)};  // error 因为create_shape返回的是shape 不能基类转子类\n    unique_ptr<circle> cl{new circle()};\n    unique_ptr<shape> ptr5(std::move(cl));  // ok unique<circle>转unique<circle>\n}\n```\n\n**小结：**\n\n（1）我们需要了解子类向基类的隐式转换，通过将移动构造函数变为带模板的移动构造函数，要明白两者共存情况与只有带模板的移动或者其他构造函数对编译器生成规则的影响！上述代码，此时还不能完成基类向子类的转换！例如:`unique_ptr<circle>`转`unique_ptr<shape>`。\n\n（2）auto_ptr与unique_tr都是独占所有权，每次只能被单个对象所拥有，unique_ptr与auto_ptr不同的是使用移动语义来显示的编写。auto_ptr是可以说你随便赋值,但赋值完了之后原来的对象就不知不觉的报废.搞得你莫名其妙。而unique_ptr就干脆不让你可以随便去复制,赋值.如果实在想传个值就哪里,显式的说明内存转移std:move一下。然后这样传值完了之后,之前的对象也同样报废了.只不过整个move你让明显的知道这样操作后会导致之前的unique_ptr对象失效。scope_ptr则是直接不允许拷贝。由于防拷贝的特性，使其管理的对象**不能共享所有权**。\n\n## 3.shared_ptr之引用计数\n\nunique_ptr 算是一种较为安全的智能指针了。但是，一个对象只能被单个 unique_ptr所拥有，这显然不能满足所有使用场合的需求。一种常见的情况是，多个智能指针同时拥有一个对象；当它们全部都失效时，这个对象也同时会被删除。这也就是 shared_ptr 了。\n\n两者区别如下：\n\n\n\n多个shared_ptr不仅共享一个对象，同时还得共享同一个计数。当最后一个指向对象(和共享计数)的shared_ptr析构时，它需要删除对象和共享计数。\n\n首先需要一个共享计数的实现：\n\n```cpp\nclass shared_count {\npublic:\n    shared_count() : count_(1) {\n\n    }\n\n    // 增加计数\n    void add_count() {\n        ++count_;\n    }\n\n    // 减少计数\n    long reduce_count() {\n        return --count_;\n    }\n\n    // 获取当前计数\n    long get_count() const {\n        return count_;\n    }\n\nprivate:\n    long count_;\n};\n```\n\n接下来实现引用计数智能指针：\n\n构造与析构、swap实现如下所示：\n\n```cpp\ntemplate<typename T>\nclass shared_ptr {\npublic:\n    explicit shared_ptr(\n            T *ptr = nullptr) noexcept\n            : ptr_(ptr) {\n        if (ptr) {\n            shared_count_ = new shared_count();\n        }\n    }\n\n    ~shared_ptr() noexcept {\n        // 最后一个shared_ptr再去删除对象与共享计数\n        // ptr_不为空且此时共享计数减为0的时候,再去删除\n        if(ptr_&&!shared_count_->reduce_count()) {\n            delete ptr_;\n            delete shared_count_;\n        }\n    }\n\t\n    void swap(shared_ptr &rhs) noexcept {\n        using std::swap;\n        swap(ptr_, rhs.ptr_);   \n        swap(shared_count_,rhs.shared_count_);\n    }\n\nprivate:\n    T *ptr_;\n    shared_count *shared_count_;\n};\ntemplate<typename T>\nvoid swap(shared_ptr<T> &lhs, shared_ptr<T> &rhs) noexcept {\n    lhs.swap(rhs);\n}\n```\n\n之前的赋值函数，编译器可以根据调用来决定是调拷贝构造还是移动构函数，所以不变：\n\n```cpp\n// copy and swap  始终只有一个对象有管理这块空间的权限\nshared_ptr &operator=(shared_ptr rhs) noexcept {\n    rhs.swap(*this);\n    return *this;\n}\n```\n\n拷贝构造与移动构造需要改变：\n\n除复制指针之外，对于拷贝构造的情况，我们需要在指针非空时把引用数加一，并复制共享\n计数的指针。对于移动构造的情况，我们不需要调整引用数，直接把 other.ptr_ 置为\n空，认为 other 不再指向该共享对象即可\n\n实现如下所示：\n\n```cpp\ntemplate<typename U>\nshared_ptr(const shared_ptr<T> &other) noexcept {\n    ptr_ = other.ptr_;\n    if (ptr_) {\n        other.shared_count_->add_count();\n        shared_count_ = other.shared_count_;\n    }\n}\n\ntemplate<typename U>\nshared_ptr(shared_ptr<U> &&other) noexcept {\n    ptr_ = other.ptr_;\n    if (ptr_) {\n        shared_count_ = other.shared_count_;\n        other.shared_count_ = nullptr;\n    }\n}\n```\n\n当运行的时候，报错：\n\n```\n‘circle* shared_ptr<circle>::ptr_’ is private\n```\n\n错误原因是**模板的各个实例间并不天然就有 friend 关系**，因而不能互访私有成员 `ptr_ `和`shared_count_`。我们需要在 smart_ptr 的定义中显式声明：\n\n```cpp\ntemplate<typename U>\nfriend class shared_ptr;\n```\n\n此外，在当前引用计数实现中，我们应该删除release释放所有权函数，编写一个返回引用计数值的函数。\n\n```cpp\nlong use_count() const noexcept {\n    if (ptr_) {\n        return shared_count_->get_count();\n    } else {\n        return 0;\n    }\n}\n```\n\n调用：\n\n```cpp\nshared_ptr<circle> ptr1(new circle());\ncout << \"use count of ptr1 is \" << ptr1.use_count() << endl;\nshared_ptr<shape> ptr2, ptr3;\ncout << \"use count of ptr2 was \" << ptr2.use_count() << endl;\nptr2 = ptr1;        // shared_ptr<circle>隐式转换shared_ptr<shape> 调用带模板的拷贝构造\n//    cout<<\"=======\"<<endl;\n//    ptr3 = ptr2;        // 调用的是编译器生成的默认拷贝构造 所以引用计数不会增加 ptr3=ptr2\n//    cout<<\"=======\"<<endl;\nptr3 = ptr1;\ncout << \"此时3个shared_ptr指向同一个资源\" << endl;\ncout << \"use count of ptr1 is now \" << ptr1.use_count() << endl;\ncout << \"use count of ptr2 is now \" << ptr2.use_count() << endl;\ncout << \"use count of ptr3 is now \" << ptr3.use_count() << endl;\nif (ptr1)\n    cout << \"ptr1 is not empty\" << endl;\n// 会先调用赋值函数,由编译器决定调用的是拷贝构造还是移动构造,造出一个新的临时对象出来,临时对象会在跳出作用域后被析构掉。\n// 在析构函数中,会先判断该临时对象的是否指向资源,如果没有,析构结束。否则,对引用计数减1,判断引用计数是否为0,如果为0,删除共享引用计数指针,否则不操作。\ncout << \"此时2个shared_ptr指向同一个资源\" << endl;\nptr2 = std::move(ptr1);\nif (!ptr1 && ptr2) {      // 调用的是bool重载操作符\n    cout << \"ptr1 move to ptr2\" << endl;\n    cout << \"use count of ptr1 is now \" << ptr1.use_count() << endl;\n    cout << \"use count of ptr2 is now \" << ptr2.use_count() << endl;\n    cout << \"use count of ptr3 is now \" << ptr3.use_count() << endl;\n}\n```\n\n输出：\n\n```cpp\nshape\ncircle\nuse count of ptr1 is 1\nuse count of ptr2 was 0\n此时3个shared_ptr指向同一个资源\nuse count of ptr1 is now 3\nuse count of ptr2 is now 3\nuse count of ptr3 is now 3\nptr1 is not empty\n此时2个shared_ptr指向同一个资源\nptr1 move to ptr2\nuse count of ptr1 is now 0\nuse count of ptr2 is now 2\nuse count of ptr3 is now 2\n~circle\n~shape\n```\n\n有几点注意事项：\n\n- 上述代码没有考虑线程安全性，这里只是简化版\n\n- =赋值重载函数不加`&`，编译器决定调用拷贝构造还是移动构造，来造出一个临时对象出来。\n- 根据前面提到的，当类中特殊函数变为带模板的函数，编译器仍然会生成默认拷贝构造与默认移动构造。\n\n针对第一点：例如：`ptr2 = std::move(ptr1);`\n\n会先调用赋值函数,由编译器决定调用的是拷贝构造还是移动构造,造出一个新的临时对象出来,临时对象会在跳出作用域后被析构掉。在析构函数中,会先判断该临时对象的是否指向资源,如果没有,析构结束。否则,对引用计数减1,判断引用计数是否为0,如果为0,删除共享引用计数指针,否则不操作。\n\n针对第二点：\n\n```cpp\nshared_ptr<shape> ptr2, ptr3;\nptr3 = ptr2;        // 调用的是编译器生成的默认拷贝构造 所以引用计数不会增加 \n```\n\n两者都是一种类型，所以在调用赋值操作后，不会调用带模板的拷贝构造来创建临时变量，而是调用编译器生成的默认拷贝构造，所以此时引用计数不会增加。\n\n## 4.指针类型转换\n\n对应于 C++ 里的不同的类型强制转：\n\n- dynamic_cast\n- static_cast\n- const_cast\n- reinterpret_cast\n\n### 4.1 dynamic_cast\n\n在上述`unique_ptr`处实现了子类向基类的转换，但是却没有实现基类向子类的转换，例如：:`unique_ptr<circle>`转`unique_ptr<shape>`。\n\n实现这种，需要使用`dynamic_cast`，实现如下：\n\n首先为了实现这些转换，我们需要添加构造函数，允许在对智能指针内部的指针对象赋值时，使用一个现有的智能指针的共享计数。\n\n```cpp\n// 实现强制类型转换需要的构造函数\ntemplate<typename U>\nshared_ptr(const shared_ptr<U> &other, T *ptr) noexcept {\n    ptr_ = ptr;\n    if (ptr_) {\n        other.shared_count_->add_count();\n        shared_count_ = other.shared_count_;\n    }\n}\n```\n\n其次，就是实现转换函数：\n\n```cpp\ntemplate<typename T, typename U>\nshared_ptr<T> dynamic_pointer_cast(const shared_ptr<U> &other) noexcept {\n    T *ptr = dynamic_cast<T *>(other.get());\n    return shared_ptr<T>(other, ptr);\n}\n```\n\n调用：\n\n```cpp\n// shape* -> circle* 使用dynamic_cast转换后,指针为空.此时资源还是被dptr2拥有,dptr1为0\nshared_ptr<shape> dptr2(new shape);\nshared_ptr<circle> dptr1 = dynamic_pointer_cast<circle>(dptr2);      // 基类转子类\n\ncout << \"use count of dptr1 is now \" << dptr1.use_count() << endl;    // 0\ncout << \"use count of dptr2 is now \" << dptr2.use_count() << endl;    // 1\n\n// circle* -> circle* 使用dynamic_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2\nshared_ptr<shape> dptr3(new circle);\n//    shared_ptr<circle> dptr3(new circle);     // 上面或者当前行,后面输出一样！\nshared_ptr<circle> dptr1_1 = dynamic_pointer_cast<circle>(dptr3);      // 基类转子类\n\ncout << \"use count of dptr1_1 is now \" << dptr1_1.use_count() << endl;    // 2\ncout << \"use count of dptr3 is now \" << dptr3.use_count() << endl;    // 2\n\n// circle* -> circle* 使用dynamic_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2\nshared_ptr<circle> dptr3_1(new circle);\nshared_ptr<shape> dptr2_1 = dynamic_pointer_cast<shape>(dptr3_1);      // 子类转基类 上行转换,安全！\n\ncout << \"use count of dptr2_1 is now \" << dptr2_1.use_count() << endl;    // 2\ncout << \"use count of dptr3_1 is now \" << dptr3_1.use_count() << endl;    // 2\n```\n\ndynamic_cast主要用于类层次间的上行转换和下行转换，还可以用于类之间的交叉转换。在类层次间进行上行转换时，dynamic_cast和static_cast的效果是一样的；在进行下行转换时，dynamic_cast具有类型检查的功能，比static_cast更安全。在多态类型之间的转换主要使用dynamic_cast，因为类型提供了运行时信息。\n\n（1）下行转换，基类转换为子类，例如：智能指针转换类似于`shape*` 转换为`circle*` 使用dynamic_cast转换后,指针为空.此时资源还是被dptr2拥有,dptr1为0。比static_cast安全。\n\n（2）平行转换，指向一致的相互转换，例如：智能指针转换类似于`circle*`转换为`circle*`。此时引用计数为两者共享。\n\n（3）上行转换，子类转基类，例如：智能指针转换类似于`circle*`转换为`shape*`，此时引用技术为两者共享。等价于static_cast。\n\n### 4.2 static_cast\n\n同样，编写如下：\n\n```cpp\ntemplate<typename T, typename U>\nshared_ptr<T> static_pointer_cast(const shared_ptr<U> &other) noexcept {\n    T *ptr = static_cast<T *>(other.get());\n    return shared_ptr<T>(other, ptr);\n}\n```\n\n调用：\n\n```\n// shape* -> circle* 使用static_cast转换后,指针为空  与dynamic_cast相比,不安全\nshared_ptr<shape> sptr2(new shape);\nshared_ptr<circle> sptr1 = static_pointer_cast<circle>(sptr2);      // 基类转子类\n\ncout << \"use count of sptr1 is now \" << dptr1.use_count() << endl;    // 0\ncout << \"use count of sptr2 is now \" << dptr2.use_count() << endl;    // 1\n\n// circle* -> circle* 使用dynamic_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2\nshared_ptr<shape> sptr3(new circle);\n//    shared_ptr<circle> sptr3(new circle);     // 上面或者当前行,后面输出一样！\nshared_ptr<circle> sptr1_1 = static_pointer_cast<circle>(sptr3);      // 基类转子类\n\ncout << \"use count of sptr1_1 is now \" << sptr1_1.use_count() << endl;    // 2\ncout << \"use count of sptr3 is now \" << sptr3.use_count() << endl;    // 2\n\n// circle* -> circle* 使用static_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2 等价于dynamic_cast\nshared_ptr<circle> sptr3_1(new circle);\nshared_ptr<shape> sptr2_1 = static_pointer_cast<shape>(sptr3_1);      // 子类转基类 上行转换,安全！\n\ncout << \"use count of sptr2_1 is now \" << sptr2_1.use_count() << endl;    // 2\ncout << \"use count of sptr3_1 is now \" << sptr3_1.use_count() << endl;    // 2\n```\n\n输出结果同上dynamic_cast，不同之处，在下行转换的时候(基类转子类)，是不安全的！\n\n### 4.3 const_cast\n\n去掉const属性：\n\n```cpp\ntemplate<typename T, typename U>\nshared_ptr<T> const_pointer_cast(\n        const shared_ptr<U> &other) noexcept {\n    T *ptr = const_cast<T *>(other.get());\n    return shared_ptr<T>(other, ptr);\n}\n```\n\n调用：\n\n```cpp\nshared_ptr<circle> s = const_pointer_cast<circle>(shared_ptr<const circle>(new circle));\n```\n\n### 4.4 reinterpret_cast\n\n例如：想把一个指针转为整数，就可以用reinterpret_cast。\n\n```cpp\ntemplate<typename T, typename U>\nshared_ptr<T> reinterpret_pointer_cast(\n        const shared_ptr<U> &other) noexcept {\n    T *ptr = reinterpret_cast<T *>(other.get());\n    return shared_ptr<T>(other, ptr);\n}\n```\n\n调用：\n\n```cpp\nint a = reinterpret_pointer_cast<int>(s);\n```\n\n"
  },
  {
    "path": "learn_class/modern_cpp_30/smart_ptr/auto_scope.cpp",
    "content": "//\n// Created by light on 19-12-9.\n//\n#include \"../RAII/shape.h\"\ntemplate <typename T> class auto_ptr {\npublic:\n  explicit auto_ptr(T *ptr = nullptr) noexcept : ptr_(ptr) {}\n\n  ~auto_ptr() noexcept { delete ptr_; }\n\n  T &operator*() const noexcept { return *ptr_; }\n\n  T *operator->() const noexcept { return ptr_; }\n\n  operator bool() const noexcept { return ptr_; }\n\n  T *get() const noexcept { return ptr_; }\n\n  // 拷贝构造,被复制放释放原来指针的所有权,交给复制方\n  // 始终只有一个对象管理一块空间\n  auto_ptr(auto_ptr &other) noexcept { ptr_ = other.release(); }\n\n  // copy and swap  始终只有一个对象有管理这块空间的权限\n  auto_ptr &operator=(auto_ptr &rhs) noexcept {\n    //        auto_ptr tmp(rhs.release());\n    //        tmp.swap(*this);\n    // s上述两行等价于下面一行\n    auto_ptr(rhs.release()).swap(*this);\n    return *this;\n  }\n\n  // 原来的指针释放所有权\n  T *release() noexcept {\n    T *ptr = ptr_;\n    ptr_ = nullptr;\n    return ptr;\n  }\n\n  void swap(auto_ptr &rhs) noexcept {\n    using std::swap;\n    swap(ptr_, rhs.ptr_); // 转移指针所有权\n  }\n\nprivate:\n  T *ptr_;\n};\n\ntemplate <typename T> void swap(auto_ptr<T> &lhs, auto_ptr<T> &rhs) noexcept {\n  lhs.swap(rhs);\n}\n\ntemplate <class T>\nclass scoped_ptr // noncopyable\n{\n\npublic:\n  explicit scoped_ptr(T *ptr = 0) noexcept : ptr_(ptr) {}\n\n  ~scoped_ptr() noexcept { delete ptr_; }\n\n  void reset(T *p = 0) noexcept { scoped_ptr(p).swap(*this); }\n\n  T &operator*() const noexcept { return *ptr_; }\n\n  T *operator->() const noexcept { return ptr_; }\n\n  T *get() const noexcept { return ptr_; }\n\n  void swap(scoped_ptr &rhs) noexcept {\n    using std::swap;\n    swap(ptr_, rhs.ptr_);\n  }\n\nprivate:\n  T *ptr_;\n\n  scoped_ptr(scoped_ptr const &);\n  scoped_ptr &operator=(scoped_ptr const &);\n};\n\ntemplate <typename T>\nvoid swap(scoped_ptr<T> &lhs, scoped_ptr<T> &rhs) noexcept {\n  lhs.swap(rhs);\n}\n\nint main() {\n  auto_ptr<shape> ptr1{create_shape(shape_type::circle)};\n  auto_ptr<shape> ptr2{ptr1};\n  if (ptr1.get() == nullptr && ptr2.get())\n    cout << \"拷贝构造：ptr1释放了所有权,ptr2获得了所有权\" << endl;\n  ptr1 = ptr1;\n\n  auto_ptr<shape> ptr3{create_shape(shape_type::rectangle)};\n  ptr1 = ptr3;\n  if (ptr3.get() == nullptr && ptr1.get())\n    cout << \"赋值操作：始终只有一个对象管理一个区块!ptr3释放了所有权,\"\n            \"ptr1获得了所有权\"\n         << endl;\n\n  scoped_ptr<shape> sptr1{create_shape(shape_type::circle)};\n  //    scoped_ptr<shape> sptr2{sptr1};       // error  不可拷贝\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/smart_ptr/shared_ptr.cpp",
    "content": "//\n// Created by light on 19-12-12.\n//\n\n#include \"../RAII/shape.h\"\nclass shared_count {\npublic:\n  shared_count() : count_(1) {}\n\n  // 增加计数\n  void add_count() { ++count_; }\n\n  // 减少计数\n  long reduce_count() { return --count_; }\n\n  // 获取当前计数\n  long get_count() const { return count_; }\n\nprivate:\n  long count_;\n};\n\ntemplate <typename T> class shared_ptr {\npublic:\n  explicit shared_ptr(T *ptr = nullptr) noexcept : ptr_(ptr) {\n    if (ptr) {\n      shared_count_ = new shared_count();\n    }\n  }\n\n  // 实现强制类型转换需要的构造函数\n  template <typename U>\n  shared_ptr(const shared_ptr<U> &other, T *ptr) noexcept {\n    ptr_ = ptr;\n    if (ptr_) {\n      other.shared_count_->add_count();\n      shared_count_ = other.shared_count_;\n    }\n  }\n\n  ~shared_ptr() noexcept {\n    // 最后一个shared_ptr再去删除对象与共享计数\n    // ptr_不为空且此时共享计数减为0的时候,再去删除\n    if (ptr_ && !shared_count_->reduce_count()) {\n      delete ptr_;\n      delete shared_count_;\n    }\n  }\n\n  T &operator*() const noexcept { return *ptr_; }\n\n  T *operator->() const noexcept { return ptr_; }\n\n  operator bool() const noexcept { return ptr_; }\n\n  T *get() const noexcept { return ptr_; }\n\n  // 带模板的拷贝与移动构造函数 模板的各个实例间并不天然就有 friend\n  // 关系，因而不能互访私有成员 ptr_ 和 shared_count_。 需要下面显示声明\n  template <typename U> friend class shared_ptr;\n\n  template <typename U> shared_ptr(const shared_ptr<U> &other) noexcept {\n    //        cout << \"调用了带模板的拷贝构造!\" << endl;\n    ptr_ = other.ptr_;\n    if (ptr_) {\n      other.shared_count_->add_count();\n      shared_count_ = other.shared_count_;\n    }\n  }\n\n  template <typename U> shared_ptr(shared_ptr<U> &&other) noexcept {\n    //        cout << \"调用了带模板的移动构造!\" << endl;\n    ptr_ = other.ptr_;\n    if (ptr_) {\n      shared_count_ = other.shared_count_;\n      other.ptr_ = nullptr;\n      other.shared_count_ = nullptr;\n    }\n  }\n\n  // copy and swap  始终只有一个对象有管理这块空间的权限\n  shared_ptr &operator=(shared_ptr rhs) noexcept {\n    rhs.swap(*this);\n    return *this;\n  }\n\n  void swap(shared_ptr &rhs) noexcept {\n    using std::swap;\n    swap(ptr_, rhs.ptr_);\n    swap(shared_count_, rhs.shared_count_);\n  }\n\n  long use_count() const noexcept {\n    if (ptr_) {\n      return shared_count_->get_count();\n    } else {\n      return 0;\n    }\n  }\n\nprivate:\n  T *ptr_;\n  shared_count *shared_count_;\n};\n\ntemplate <typename T>\nvoid swap(shared_ptr<T> &lhs, shared_ptr<T> &rhs) noexcept {\n  lhs.swap(rhs);\n}\n\ntemplate <typename T, typename U>\nshared_ptr<T> dynamic_pointer_cast(const shared_ptr<U> &other) noexcept {\n  T *ptr = dynamic_cast<T *>(other.get());\n  return shared_ptr<T>(other, ptr);\n}\n\ntemplate <typename T, typename U>\nshared_ptr<T> static_pointer_cast(const shared_ptr<U> &other) noexcept {\n  T *ptr = static_cast<T *>(other.get());\n  return shared_ptr<T>(other, ptr);\n}\n\ntemplate <typename T, typename U>\nshared_ptr<T> const_pointer_cast(const shared_ptr<U> &other) noexcept {\n  T *ptr = const_cast<T *>(other.get());\n  return shared_ptr<T>(other, ptr);\n}\n\ntemplate <typename T, typename U>\nshared_ptr<T> reinterpret_pointer_cast(const shared_ptr<U> &other) noexcept {\n  T *ptr = reinterpret_cast<T *>(other.get());\n  return shared_ptr<T>(other, ptr);\n}\n\nint main() {\n  shared_ptr<circle> ptr1(new circle());\n  cout << \"use count of ptr1 is \" << ptr1.use_count() << endl;\n  shared_ptr<shape> ptr2, ptr3;\n  cout << \"use count of ptr2 was \" << ptr2.use_count() << endl;\n  ptr2 =\n      ptr1; // shared_ptr<circle>隐式转换shared_ptr<shape> 调用带模板的拷贝构造\n            //    cout<<\"=======\"<<endl;\n  //    ptr3 = ptr2;        // 调用的是编译器生成的默认拷贝构造\n  //    所以引用计数不会增加 ptr3=ptr2 cout<<\"=======\"<<endl;\n  ptr3 = ptr1;\n  cout << \"此时3个shared_ptr指向同一个资源\" << endl;\n  cout << \"use count of ptr1 is now \" << ptr1.use_count() << endl;\n  cout << \"use count of ptr2 is now \" << ptr2.use_count() << endl;\n  cout << \"use count of ptr3 is now \" << ptr3.use_count() << endl;\n  if (ptr1)\n    cout << \"ptr1 is not empty\" << endl;\n  // 会先调用赋值函数,由编译器决定调用的是拷贝构造还是移动构造,造出一个新的临时对象出来,临时对象会在跳出作用域后被析构掉。\n  // 在析构函数中,会先判断该临时对象的是否指向资源,如果没有,析构结束。否则,对引用计数减1,判断引用计数是否为0,如果为0,删除共享引用计数指针,否则不操作。\n  cout << \"此时2个shared_ptr指向同一个资源\" << endl;\n  ptr2 = std::move(ptr1);\n  if (!ptr1 && ptr2) { // 调用的是bool重载操作符\n    cout << \"ptr1 move to ptr2\" << endl;\n    cout << \"use count of ptr1 is now \" << ptr1.use_count() << endl;\n    cout << \"use count of ptr2 is now \" << ptr2.use_count() << endl;\n    cout << \"use count of ptr3 is now \" << ptr3.use_count() << endl;\n  }\n  // shape* -> circle*\n  // 使用dynamic_cast转换后,指针为空.此时资源还是被dptr2拥有,dptr1为0\n  shared_ptr<shape> dptr2(new shape);\n  shared_ptr<circle> dptr1 = dynamic_pointer_cast<circle>(dptr2); // 基类转子类\n\n  cout << \"use count of dptr1 is now \" << dptr1.use_count() << endl; // 0\n  cout << \"use count of dptr2 is now \" << dptr2.use_count() << endl; // 1\n\n  // circle* -> circle*\n  // 使用dynamic_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2\n  shared_ptr<shape> dptr3(new circle);\n  //    shared_ptr<circle> dptr3(new circle);     //\n  //    上面或者当前行,后面输出一样！\n  shared_ptr<circle> dptr1_1 =\n      dynamic_pointer_cast<circle>(dptr3); // 基类转子类\n\n  cout << \"use count of dptr1_1 is now \" << dptr1_1.use_count() << endl; // 2\n  cout << \"use count of dptr3 is now \" << dptr3.use_count() << endl;     // 2\n\n  // circle* -> circle*\n  // 使用dynamic_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2\n  shared_ptr<circle> dptr3_1(new circle);\n  shared_ptr<shape> dptr2_1 =\n      dynamic_pointer_cast<shape>(dptr3_1); // 子类转基类 上行转换,安全！\n\n  cout << \"use count of dptr2_1 is now \" << dptr2_1.use_count() << endl; // 2\n  cout << \"use count of dptr3_1 is now \" << dptr3_1.use_count() << endl; // 2\n\n  // shape* -> circle* 使用static_cast转换后,指针为空  与dynamic_cast相比,不安全\n  shared_ptr<shape> sptr2(new shape);\n  shared_ptr<circle> sptr1 = static_pointer_cast<circle>(sptr2); // 基类转子类\n\n  cout << \"use count of sptr1 is now \" << dptr1.use_count() << endl; // 0\n  cout << \"use count of sptr2 is now \" << dptr2.use_count() << endl; // 1\n\n  // circle* -> circle*\n  // 使用dynamic_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2\n  shared_ptr<shape> sptr3(new circle);\n  //    shared_ptr<circle> sptr3(new circle);     //\n  //    上面或者当前行,后面输出一样！\n  shared_ptr<circle> sptr1_1 = static_pointer_cast<circle>(sptr3); // 基类转子类\n\n  cout << \"use count of sptr1_1 is now \" << sptr1_1.use_count() << endl; // 2\n  cout << \"use count of sptr3 is now \" << sptr3.use_count() << endl;     // 2\n\n  // circle* -> circle*\n  // 使用static_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2\n  // 等价于dynamic_cast\n  shared_ptr<circle> sptr3_1(new circle);\n  shared_ptr<shape> sptr2_1 =\n      static_pointer_cast<shape>(sptr3_1); // 子类转基类 上行转换,安全！\n\n  cout << \"use count of sptr2_1 is now \" << sptr2_1.use_count() << endl; // 2\n  cout << \"use count of sptr3_1 is now \" << sptr3_1.use_count() << endl; // 2\n  shared_ptr<const int> constV(new int);\n  shared_ptr<int> s = const_pointer_cast<int>(constV);\n  *s = 10;\n  int a = reinterpret_pointer_cast<int>(s);\n  cout << a << endl;\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/smart_ptr/unique_ptr.cpp",
    "content": "//\n// Created by light on 19-12-12.\n//\n\n#include \"../RAII/shape.h\"\n\ntemplate <typename T> class unique_ptr {\npublic:\n  explicit unique_ptr(T *ptr = nullptr) noexcept : ptr_(ptr) {}\n\n  ~unique_ptr() noexcept { delete ptr_; }\n\n  T &operator*() const noexcept { return *ptr_; }\n\n  T *operator->() const noexcept { return ptr_; }\n\n  operator bool() const noexcept { return ptr_; }\n\n  T *get() const noexcept { return ptr_; }\n\n  unique_ptr(unique_ptr &&other) noexcept {\n    cout << \"move ctor\" << endl;\n    ptr_ = other.release();\n  }\n\n  template <typename U> unique_ptr(unique_ptr<U> &&other) noexcept {\n    cout << \"U move ctor\" << endl;\n    ptr_ = other.release();\n  }\n\n  // copy and swap  始终只有一个对象有管理这块空间的权限\n  unique_ptr &operator=(unique_ptr rhs) noexcept {\n    rhs.swap(*this);\n    return *this;\n  }\n\n  // 原来的指针释放所有权\n  T *release() noexcept {\n    T *ptr = ptr_;\n    ptr_ = nullptr;\n    return ptr;\n  }\n\n  void swap(unique_ptr &rhs) noexcept {\n    using std::swap;\n    swap(ptr_, rhs.ptr_); // 转移指针所有权\n  }\n\nprivate:\n  T *ptr_;\n};\n\ntemplate <typename T> void swap(unique_ptr<T> &lhs, unique_ptr<T> &rhs) {\n  lhs.swap(rhs);\n}\n\nint main() {\n  unique_ptr<shape> ptr1{create_shape(shape_type::circle)};\n  //    unique_ptr<shape> ptr2{ptr1}; // error\n  unique_ptr<shape> ptr2_2{std::move(ptr1)}; // ok\n  if (ptr2_2.get() != nullptr && ptr1.get() == nullptr)\n    ptr2_2.get()->print();\n\n  unique_ptr<shape> ptr3{create_shape(shape_type::rectangle)};\n  //    ptr1 = ptr3;    // error\n  ptr3 = std::move(ptr1); // ok\n  //    unique_ptr<circle> cl{create_shape(shape_type::circle)};  // error\n  //    因为create_shape返回的是shape 不能基类转子类\n  unique_ptr<circle> cl{new circle()};\n  unique_ptr<shape> ptr5(std::move(cl)); // ok unique<circle>转unique<circle>\n}"
  },
  {
    "path": "learn_class/modern_cpp_30/smart_ptr/unique_ptr_U.cpp",
    "content": "//\n// Created by light on 19-12-12.\n//\n\n#include \"../RAII/shape.h\"\n\ntemplate <typename T> class unique_ptr {\npublic:\n  explicit unique_ptr(T *ptr = nullptr) noexcept : ptr_(ptr) {}\n\n  ~unique_ptr() noexcept { delete ptr_; }\n\n  T &operator*() const noexcept { return *ptr_; }\n\n  T *operator->() const noexcept { return ptr_; }\n\n  operator bool() const noexcept { return ptr_; }\n\n  T *get() const noexcept { return ptr_; }\n\n  template <typename U> unique_ptr(unique_ptr<U> &&other) noexcept {\n    cout << \"U move ctor\" << endl;\n    ptr_ = other.release();\n  }\n\n  // copy and swap  始终只有一个对象有管理这块空间的权限\n  unique_ptr &operator=(unique_ptr rhs) noexcept {\n    rhs.swap(*this);\n    return *this;\n  }\n\n  // 原来的指针释放所有权\n  T *release() noexcept {\n    T *ptr = ptr_;\n    ptr_ = nullptr;\n    return ptr;\n  }\n\n  void swap(unique_ptr &rhs) noexcept {\n    using std::swap;\n    swap(ptr_, rhs.ptr_); // 转移指针所有权\n  }\n\nprivate:\n  T *ptr_;\n};\n\nint main() {\n  unique_ptr<shape> ptr1{create_shape(shape_type::circle)};\n  unique_ptr<shape> ptr2{\n      ptr1}; // 由于带模板的移动构造函数引发编译器会默认生成拷贝构造\n  if (ptr1.get() != nullptr) // bitwise copy 此时ptr1不为NULL\n    ptr2.get()->print();\n\n  unique_ptr<shape> ptr2_2{std::move(\n      ptr1)}; // 调用的是默认的移动构造,而不是带模板的移动构造 bitwise move\n  if (ptr2_2.get() != nullptr && ptr1.get() != nullptr) // ptr1 不为空\n    ptr2_2.get()->print();\n\n  unique_ptr<shape> ptr3{create_shape(shape_type::rectangle)};\n  ptr1 = ptr3; // ok  根据形参先调用默认拷贝,再调用拷贝赋值\n  ptr3 = std::move(\n      ptr1); // ok\n             // 根据形参先调用默认移动构造，而不是带参数的移动构造,再调用移动赋值\n  unique_ptr<shape> ptr4(std::move(new circle)); // ok 调用带模板的移动构造\n}"
  },
  {
    "path": "practical_exercises/10_day_practice/day1/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day1:union`\n# please run `bazel run //practical_exercises/10_day_practice/day1:print`\n# please run `bazel run //practical_exercises/10_day_practice/day1:annotate`\n# please run `bazel run //practical_exercises/10_day_practice/day1:runnian`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"union\",\n    srcs = [\"union.cpp\"],\n)\ncc_binary(\n    name = \"print\",\n    srcs = [\"print.cpp\"],\n)\ncc_binary(\n    name = \"annotate\",\n    srcs = [\"annotate.cpp\"],\n)\ncc_binary(\n    name = \"runnian\",\n    srcs = [\"runnian.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day1/annotate.cpp",
    "content": "/* 注释.cpp */\n#include <iostream>\n\n//另一种注释方法\n#if 0\nasd\n#endif\n\n//打开注释\n//条件编译指令\n#if 1\n#endif\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day1/print.cpp",
    "content": "/* 打印练习.cpp */\n#include <iostream>\nusing namespace std;\nint main(int argc, char const *argv[]) {\n  int i, j, k, f;\n  for (i = 1; i <= 4; i++) {\n    for (j = 1; j <= 30; j++)\n      cout << \" \";\n    for (k = 1; k <= 8 - 2 * i; k++)\n      cout << \" \";\n    for (f = 1; f <= 2 * i; f++)\n      cout << '*';\n    cout << endl;\n  }\n  for (i = 1; i <= 3; i++) {\n    for (j = 1; j <= 30; j++)\n      cout << \" \";\n    for (f = 1; f <= 7 - 2 * i; f++)\n      cout << '*';\n    cout << endl;\n  }\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day1/runnian.cpp",
    "content": "/* 是否闰年.cpp */\n#include <iostream>\nusing namespace std;\nint main(int argc, char const *argv[]) {\n  int year;\n  bool isLeapYear;\n  cout << \"Enter the year: \";\n  cin >> year;\n  isLeapYear = (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0));\n  if (isLeapYear) {\n    cout << year << \" is a leap year\" << endl;\n  } else {\n    cout << year << \" is not a leap year\" << endl;\n  }\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day1/union.cpp",
    "content": "/* 联合体学习.cpp */\n#include <iostream>\nusing namespace std;\n//相同的内存地址\nunion myun {\n  struct {\n    int x;\n    int y;\n    int z;\n  } u;\n  int k;\n} a;\nint main() {\n  a.u.x = 4;\n  a.u.y = 5;\n  a.u.z = 6;\n  a.k = 0; //覆盖掉第一个int空间值\n  printf(\"%d %d %d %d\\n\", a.u.x, a.u.y, a.u.z, a.k);\n  \n  return 0;\n}"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/10-4.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\nint main(int argc, char const *argv[]) {\n  char c[30] = \"this is string\";\n  double d = -1231.232;\n  cout.width(30);\n  cout.fill('*');\n  cout.setf(ios::left);\n  cout << c << \"----L1\" << endl;\n  cout.width(30);\n  cout.fill('-');\n  cout.setf(ios::right);\n  cout << c << \"----L2\" << endl;\n  cout.setf(ios::dec | ios::showbase | ios::showpoint);\n  cout.width(30);\n  cout << d << \"----L3\"\n       << \"\\n\";\n  cout.setf(ios::showpoint);\n  cout.precision(10);\n  cout.width(30);\n  cout << d << \"----L4\"\n       << \"\\n\";\n  cout.width(30);\n  cout.setf(ios::oct, ios::basefield);\n  cout << 100 << \"----L5\"\n       << \"\\n\";\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/12-1.cpp",
    "content": "//用cin输入字符串数据时，如果字符串中含有空白就不能完整输入。因为遇到空白字符时，cin就认为字符串结束了。\n#include <iostream>\nusing namespace std;\nint main(int argc, char const *argv[]) {\n  char a[50];\n  cout << \"please input a string:\";\n  cin >> a;\n  cout << a << endl;\n  \n  return 0;\n}\n/*\n若a的内容是：\nthis is a string!\n就难以输入啦！\n这样的数据应用输入流类的成员函数输入\n*/"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/12-2.cpp",
    "content": "#include <iostream>\nusing namespace std;\nint main(int argc, char const *argv[]) {\n  char stu[5][10];\n  int i;\n  for (i = 0; i < 5; i++)\n    cin.getline(stu[i], 10, ',');\n  for (i = 0; i < 5; i++)\n    cout << stu[i] << endl;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/12-3.cpp",
    "content": "#include <iostream>\nusing namespace std;\n//函数原型\n// put(char c)\n// write(const char*c, int n)\nint main() {\n  char c;\n  char a[50] = \"this is a string...\";\n  cout << \"use get() input char:\";\n  while ((c = cin.get()) != '\\n') {\n    cout.put(c);\n    cout.put('\\n');\n    cout.put('t').put('h').put('i').put('s').put('\\n');\n    cout.write(a, 12).put('\\n');\n    cout << \"look\"\n         << \"\\t here!\" << endl;\n  }\n  \n}"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/12-5.cpp",
    "content": "// Eg12-5.cpp\n#include <iomanip>\n#include <iostream>\nusing namespace std;\nint main() {\n  char c[30] = \"this is string\";\n  double d = -1234.8976;\n  cout << setw(30) << left << setfill('*') << c << \"----L1\" << endl;\n  cout << setw(30) << right << setfill('*') << c << \"----L2\" << endl;\n  // showbase显示数值的基数前缀\n  cout << dec << showbase << showpoint << setw(30) << d << \"----L3\"\n       << \"\\n\";\n  // showpoint显示小数点\n  cout << setw(30) << showpoint << setprecision(10) << d << \"----L4\"\n       << \"\\n\";\n  // setbase(8)设置八进制\n  cout << setw(30) << setbase(16) << 100 << \"----L5\"\n       << \"\\n\";\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/12-6.cpp",
    "content": "// 12-6.cpp\n#include <fstream>\n#include <iostream>\nusing namespace std;\nint main(int argc, char const *argv[]) {\n  fstream ioFile;\n  ioFile.open(\"./a.dat\", ios::out);\n  ioFile << \"张三\"\n         << \"  \" << 76 << \" \" << 98 << \" \" << 67 << endl; // L3\n  ioFile << \"李四\"\n         << \"  \" << 89 << \" \" << 70 << \" \" << 60 << endl;\n  ioFile << \"王十\"\n         << \"  \" << 91 << \" \" << 88 << \" \" << 77 << endl;\n  ioFile << \"黄二\"\n         << \"  \" << 62 << \" \" << 81 << \" \" << 75 << endl;\n  ioFile << \"刘六\"\n         << \"  \" << 90 << \" \" << 78 << \" \" << 67 << endl;\n  ioFile.close();\n  ioFile.open(\"./a.dat\", ios::in | ios::binary);\n  char name[10];\n  int chinese, math, computer;\n  cout << \"姓名\\t\"\n       << \"英语\\t\"\n       << \"数学\\t\"\n       << \"计算机\\t\"\n       << \"总分\" << endl;\n  ioFile >> name;\n  while (!ioFile.eof()) {\n    ioFile >> chinese >> math >> computer;\n    cout << name << \"\\t\" << chinese << \"\\t\" << math << \"\\t\" << computer << \"\\t\"\n         << chinese + math + computer << endl;\n    ioFile >> name;\n  }\n  ioFile.close();\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/12-7.cpp",
    "content": "// Eg12-7.cpp\n#include <fstream>\n#include <iostream>\nusing namespace std;\nint main() {\n  char ch;\n  ofstream out(\"/test.dat\", ios::out | ios::binary); // L1\n  for (int i = 0; i < 90; i++) {\n    if (i > 0 && (i % 30) == 0)\n      out.put('\\n');\n    out.put(i);\n    out.put(' ');\n  }\n  out.close();\n  ifstream in(\"/test.dat\", ios::in | ios::binary);\n  while (in.get(ch))\n    cout << ch;\n  in.close();\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/12-9.cpp",
    "content": "// Eg12-12.cpp\n#include <cstring>\n#include <fstream>\n#include <iostream>\nusing namespace std;\nclass Employee {\nprivate:\n  int number, age;\n  char name[20];\n  double sal;\n\npublic:\n  Employee() {}\n  Employee(int num, char *Name, int Age, double Salary) {\n    number = num;\n    strcpy(name, Name);\n    age = Age;\n    sal = Salary;\n  }\n  void display() {\n    cout << number << \"\\t\" << name << \"\\t\" << age << \"\\t\" << sal << endl;\n  }\n};\n\nint main() {\n  ofstream out(\"D:/Employee.dat\", ios::out); //定义随机输出文件\n  Employee e1(1, \"张三\", 23, 2320);\n  Employee e2(2, \"李四\", 32, 3210);\n  Employee e3(3, \"王五\", 34, 2220);\n  Employee e4(4, \"刘六\", 27, 1220);\n  out.write((char *)&e1, sizeof(e1)); //按e1,e2,e3,e4顺序写入文件\n  out.write((char *)&e2, sizeof(e2));\n  out.write((char *)&e3, sizeof(e3));\n  out.write((char *)&e4, sizeof(e4));\n\n  //下面的代码将e3（即王五）的年龄改为40岁\n  Employee e5(3, \"王五\", 40, 2220);\n  out.seekp(\n      2 *\n      sizeof(\n          e1)); //指针定位到第3（起始为0）个数据块，这里写错了应该是2*sizeof（e1）\n  out.write((char *)&e5, sizeof(e5)); //将e5写到第3个数据块位置，覆盖e3\n  out.close();                        //关闭文件\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day10/file:12-9`\n# please run `bazel run //practical_exercises/10_day_practice/day10/file:12-6`\n# please run `bazel run //practical_exercises/10_day_practice/day10/file:10-4`\n# please run `bazel run //practical_exercises/10_day_practice/day10/file:12-2`\n# please run `bazel run //practical_exercises/10_day_practice/day10/file:practice`\n# please run `bazel run //practical_exercises/10_day_practice/day10/file:12-1`\n# please run `bazel run //practical_exercises/10_day_practice/day10/file:12-5`\n# please run `bazel run //practical_exercises/10_day_practice/day10/file:12-3`\n# please run `bazel run //practical_exercises/10_day_practice/day10/file:12-7`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"12-9\",\n    srcs = [\"12-9.cpp\"],\n)\ncc_binary(\n    name = \"12-6\",\n    srcs = [\"12-6.cpp\"],\n)\ncc_binary(\n    name = \"10-4\",\n    srcs = [\"10-4.cpp\"],\n)\ncc_binary(\n    name = \"12-2\",\n    srcs = [\"12-2.cpp\"],\n)\ncc_binary(\n    name = \"practice\",\n    srcs = [\"practice.cpp\"],\n)\ncc_binary(\n    name = \"12-1\",\n    srcs = [\"12-1.cpp\"],\n)\ncc_binary(\n    name = \"12-5\",\n    srcs = [\"12-5.cpp\"],\n)\ncc_binary(\n    name = \"12-3\",\n    srcs = [\"12-3.cpp\"],\n)\ncc_binary(\n    name = \"12-7\",\n    srcs = [\"12-7.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/input/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day10/file/input:getline`\n# please run `bazel run //practical_exercises/10_day_practice/day10/file/input:get2`\n# please run `bazel run //practical_exercises/10_day_practice/day10/file/input:get`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"getline\",\n    srcs = [\"getline.cpp\"],\n)\ncc_binary(\n    name = \"get2\",\n    srcs = [\"get2.cpp\"],\n)\ncc_binary(\n    name = \"get\",\n    srcs = [\"get.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/input/get.cpp",
    "content": "//【例12-2】  用函数get和getline读取数据。\n#include <iostream>\nusing namespace std;\nint main() {\n  char a, b, c, d;\n  cin.get(a);\n  cin.get(b);\n  c = cin.get();\n  d = cin.get();\n  cout << int(a) << ',' << int(b) << ',' << int(c) << ',' << int(d) << endl;\n  \n  return 0;\n}\n\n/*\n用法：a = cin.get() ?或者 ?cin.get(a)\n结束条件：输入字符足够后回车\n说明：这个是单字符的输入，用途是输入一个字符，把它的ASCALL码存入到a中\n处理方法：与cin不同，cin.get()在缓冲区遇到[enter]，[space]，[tab]不会作为舍弃，而是继续留在缓冲区中\n*/"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/input/get2.cpp",
    "content": "\n//【例12-2】  用函数get和getline读取数据。\n#include <iostream>\nusing namespace std;\n// cin.get(arrayname,size)  把字符输入到arrayname中，长度不超过size\nint main() {\n  // get()两个参数\n\n  // 1.输入串长<size，输入串长>arraylength，会自动扩张arrayname大小，使能保存所有数据\n  //  char a[10];\n  //  cin.get(a,20);\n  //  cout<<a<<endl;\n  // cout<<sizeof(a)<<endl;\n  // 2.输入串长<size，输入串长<arraylength，把串全部输入，后面补‘\\0’\n  // char b[10];\n  // cin.get(b,20);\n  // cout<<b<<endl;//12345，此时数组内数据为‘12345'\\0’\n  // cout<<sizeof(b)<<endl;\n  // 3.输入串长>size，先截取size个字符，若还是大于arraylength，则自动扩张arrayname大小，使能保存所有数据\n  // char c[5];\n  // cin.get(c,10);\n  // cout<<c<<endl;\n  // cout<<sizeof(c)<<endl;\n  // 4.输入串长>size，先截取size个字符，若小于arraylength，则把截取串放入数组中，最后补充‘\\0’\n  // char d[10];\n  // cin.get(d,5);\n  // cout<<d<<endl;\n  // cout<<sizeof(d)<<endl;\n\n  // get()三个参数\n  /*\n      用法：cin.get(arrayname,size,s)\n     ?把数据输入到arrayname字符数组中，当到达长度size时结束或者遇到字符s时结束\n      注释：a必须是字符数组，即char\n     a[]l类型，不可为string类型；size为最大的输入长度；s为控制，遇到s则当前输入结束缓存区里的s不会被舍弃\n\n  */\n  int i;\n  char e[10];\n  cin.get(e, 8, ',');\n  cout << e;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/input/getline.cpp",
    "content": "#include <iostream>\nusing namespace std;\n/*\n（1）cin.getline(arrayname,size)与cin.get(arrayname,size)的区别\ncin.get(arrayname,size)当遇到[enter]时会结束目前输入，他不会删除缓冲区中的[enter]\ncin.getline(arrayname,size)当遇到[enter]时会结束当前输入，但是会删除缓冲区中的[enter]\n*/\nint main() {\n  /*\n      char a[10];\n  char b;\n  cin.get(a,10);\n      cin.get(b);\n  cout<<a<<endl<<int(b);//输入：12345[enter] 输出：12345 【换行】 10*/\n  /*char c[10];\n  char d;\n  cin.getline(c,10);\n  cin.get(d);\n      cout<<c<<endl<<int(d);//输入：12345[enter]a[enter] 输出：12345【换行】97*/\n  // cin.getline(arrayname,size,s)与cin.gei(arrayname,size,s)的区别\n  /*\n  cin.getline(arrayname,size,s)当遇到s时会结束输入，并把s从缓冲区中删除\n  cin.get（arrayname,size,s）当遇到s时会结束输入，但不会删除缓冲区中的s\n  */\n  /*\n   char e[10];\n   char f;\n   cin.get(e,10,',');\n       cin.get(f);\n       cout<<e<<endl<<f;//输入：12345,[enter]\n   输出：12345【换行】，说明：cin,get不会删除缓冲区的，*/\n  char e1[10];\n  char f1;\n  cin.getline(e1, 10, ',');\n  cin.get(f1);\n  cout << e1 << endl << f1; //输入：asd,wqe 输出：asd【换行】w\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/file/practice.cpp",
    "content": "#include <fstream>\n#include <iostream>\n//向量是一个能够存放任意类型的动态数组\n#include <cstring>\n#include <vector>\nusing namespace std;\nclass Person {\nprivate:\n  char name[20];\n  char id[18];\n  int age;\n  char addr[20];\n\npublic:\n  Person() {}\n  Person(char *n, char *pid, int Age, char *Addr) {\n    strcpy(name, n);\n    strcpy(id, pid);\n    age = Age;\n    strcpy(addr, Addr);\n  }\n  void display() {\n    cout << name << \"\\t\" << id << \"\\t\" << age << \"\\t\" << addr << endl;\n  }\n};\nint main(int argc, char const *argv[]) {\n  vector<Person> v;\n  vector<Person>::iterator\n      pos; //声明一个迭代器，来访问vector容器，作用：遍历或者指向vector容器的元素\n  char ch;\n  ofstream out(\"d:/person.dat\", ios::out | ios::app | ios::binary);\n  char Name[20], ID[18], Addr[20];\n  int Age;\n  cout << \"------输入个人档案------\" << endl << endl;\n  do {\n    cout << \"姓名： \";\n    cin >> Name;\n    cout << \"身份证号： \";\n    cin >> ID;\n    cout << \"年龄： \";\n    cin >> Age;\n    cout << \"地址： \";\n    cin >> Addr;\n    Person per(Name, ID, Age, Addr);\n    out.write((char *)&per, sizeof(per));\n    cout << \"Enter another Person(y/n)?\";\n    cin >> ch;\n  } while (ch == 'y');\n  out.close();\n  ifstream in(\"d:/person.dat\", ios::in | ios::binary); // L9\n  Person s;\n  in.read((char *)&s, sizeof(s));\n  while (!in.eof()) {\n    v.push_back(s);\n    in.read((char *)&s, sizeof(s));\n  }\n  cout << \"\\n---------从文件中读出的数据--------\" << endl << endl; // L15\n  pos = v.begin();\n  for (pos = v.begin(); pos != v.end(); pos++)\n    (*pos).display();\n\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day10/readme.md",
    "content": "文件例题\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day2/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day2:rec1`\n# please run `bazel run //practical_exercises/10_day_practice/day2:rec2`\n# please run `bazel run //practical_exercises/10_day_practice/day2:hanoi`\n# please run `bazel run //practical_exercises/10_day_practice/day2:st`\n# please run `bazel run //practical_exercises/10_day_practice/day2:static`\n# please run `bazel run //practical_exercises/10_day_practice/day2:shaizi`\n# please run `bazel run //practical_exercises/10_day_practice/day2:pow`\n# please run `bazel run //practical_exercises/10_day_practice/day2:compute`\n# please run `bazel run //practical_exercises/10_day_practice/day2:enum`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"rec1\",\n    srcs = [\"rec1.cpp\"],\n)\ncc_binary(\n    name = \"rec2\",\n    srcs = [\"rec2.cpp\"],\n)\ncc_binary(\n    name = \"hanoi\",\n    srcs = [\"hanoi.cpp\"],\n)\ncc_binary(\n    name = \"st\",\n    srcs = [\"st.cpp\"],\n)\ncc_binary(\n    name = \"static\",\n    srcs = [\"static.cpp\"],\n)\ncc_binary(\n    name = \"shaizi\",\n    srcs = [\"shaizi.cpp\"],\n)\ncc_binary(\n    name = \"pow\",\n    srcs = [\"pow.cpp\"],\n)\ncc_binary(\n    name = \"compute\",\n    srcs = [\"compute.cpp\"],\n)\ncc_binary(\n    name = \"enum\",\n    srcs = [\"enum.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day2/compute.cpp",
    "content": "/* 求π.cpp */\n#include <iostream>\nusing namespace std;\ndouble arctan(double x);\nint main(int argc, char const *argv[]) {\n  double a = 16.0 * arctan(1.0 / 5.0);\n  double b = 4.0 * arctan(1.0 / 239.0);\n  double pi = a - b;\n  cout << pi << endl;\n  \n  return 0;\n}\ndouble arctan(double x) {\n  double head = x;\n  int tail = 1;\n  double art = 0;\n  while (double(head / tail) > 1e-15) {\n    art = (tail % 4 == 1) ? art + head / tail : art - head / tail;\n    head *= x * x;\n    tail += 2;\n    cout << \"---------------\" << endl;\n    cout << tail << endl;\n    cout << \"---------------\" << endl;\n  }\n  return art;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day2/enum.cpp",
    "content": "/* 枚举类型.cpp */\n#include <iostream>\nusing namespace std;\n\nenum weekday { s, m, t, w, thu, f, s1 };\n\nint main(int argc, char const *argv[]) {\n  enum weekday wek = s;\n  // weekday wek=s;\n  for (int i = wek; i != f; i++) {\n    cout << i << endl;\n    cout << wek + s << endl;\n    cout << \"-------哈哈-------\" << endl;\n  }\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day2/hanoi.cpp",
    "content": "/* 汉诺塔.cpp */\n#include <iostream>\nusing namespace std;\nvoid move(char A, char B);\nvoid hanoi(int n, char A, char B, char C);\nint main(int argc, char const *argv[]) {\n  cout << \"请输入盘子数量：\";\n  int disks;\n  cin >> disks;\n  hanoi(disks, 'A', 'B', 'C');\n  \n  return 0;\n}\n\nvoid move(char A, char B) { cout << A << \"->\" << B << endl; }\n\nvoid hanoi(int n, char A, char B, char C) {\n  if (n == 1) {\n    move(A, C);\n  } else {\n    hanoi(n - 1, A, C, B);\n    move(A, C);\n    hanoi(n - 1, B, A, C);\n  }\n}"
  },
  {
    "path": "practical_exercises/10_day_practice/day2/pow.cpp",
    "content": "/* x的n次方.cpp */\n#include <iostream>\nusing namespace std;\ndouble power(double x, int n);\n\nint main(int argc, char const *argv[]) {\n  int x;\n  cin >> x;\n  int wei = 0;\n  int sum = 0;\n  int each, chu;\n  for (int i = 0; i < 8; i++) {\n    each = x % 10;\n    chu = x / 10;\n    sum += each * power(2, wei);\n    x = chu;\n    wei++;\n  }\n  cout << sum << endl;\n  \n  return 0;\n}\ndouble power(double x, int n) {\n  double val = 1.0;\n  while (n--) {\n    val *= x;\n  }\n  return val;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day2/rec1.cpp",
    "content": "/* 递归1.cpp */\n#include <iostream>\nusing namespace std;\n\nint f(int n);\nint main(int argc, char const *argv[]) {\n  cout << \"input x:\";\n  int x;\n  cin >> x;\n  cout << f(x) << endl;\n  \n  return 0;\n}\n\nint f(int n) {\n  if (n == 0) {\n    return 1;\n  } else {\n    return n * f(n - 1);\n  }\n}"
  },
  {
    "path": "practical_exercises/10_day_practice/day2/rec2.cpp",
    "content": "/* 递归2.cpp */\n#include <iostream>\nusing namespace std;\nint f(int n, int k);\nint main(int argc, char const *argv[]) {\n  cout << \"请输入n与k\" << endl;\n  int n, k;\n  cin >> n;\n  cin >> k;\n  cout << f(n, k) << endl;\n  \n  return 0;\n}\n\nint f(int n, int k) {\n  if ((n == k) || (k == 0)) {\n    return 1;\n  } else {\n    return f(n - 1, k - 1) + f(n - 1, k);\n  }\n}"
  },
  {
    "path": "practical_exercises/10_day_practice/day2/shaizi.cpp",
    "content": "/* 掷骰子.cpp */\n#include <cstdlib>\n#include <iostream>\nusing namespace std;\nint rolldice();\nint main(int argc, char const *argv[]) {\n  int flag;\n  unsigned seed;\n  cout << \"请输入无符号整数：\" << endl;\n  cin >> seed;\n  srand(seed);\n  int sum = rolldice();\n  int selfdim;\n  switch (sum) {\n  case 7:\n  case 11:\n    flag = 1;\n    break;\n  case 2:\n  case 3:\n  case 12:\n    flag = 2;\n    break;\n  default:\n    flag = 0;\n    selfdim = sum;\n    break;\n  }\n  while (flag == 0) {\n    sum = rolldice();\n    if (sum == selfdim) {\n      flag = 1;\n    } else if (sum == 7) {\n      flag = 2;\n    }\n  }\n  if (flag == 1) {\n    cout << \"player win\\n\";\n  } else {\n    cout << \"player loses\\n\";\n  }\n\n  \n  return 0;\n}\n\nint rolldice() {\n  int sum = 0;\n  int dim1 = rand() % 6 + 1;\n  int dim2 = rand() % 6 + 1;\n  sum = dim1 + dim2;\n  cout << \"sum=\" << dim1 << \"+\" << dim2 << endl;\n  return sum;\n}"
  },
  {
    "path": "practical_exercises/10_day_practice/day2/st.cpp",
    "content": "/* 结构体.cpp */\n#include <iostream>\nusing namespace std;\nstruct student {\n  int num;\n  char name[20];\n  char gender;\n};\n\nint main(int argc, char const *argv[]) {\n  student s = {10, \"asd\", 'M'};\n  cout << s.num << endl;\n  cout << sizeof(s.num) << endl;\n  cout << sizeof(s.name) << endl;\n  cout << sizeof(s.gender) << endl;\n  cout << sizeof(s) << endl;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day2/static.cpp",
    "content": "/* 静态变量.cpp */\n#include <iostream>\nusing namespace std;\nint i = 1; // i 为全局变量，具有静态生存期。\nint main(void) {\n  static int a; // 静态局部变量，有全局寿命，局部可见。\n  int b = -10;  // b, c为局部变量，具有动态生存期。\n  int c = 0;\n  void other(void);\n  cout << \"---MAIN---\\n\";\n  cout << \" i: \" << i << \" a: \" << a << \" b: \" << b << \" c: \" << c\n       << endl; // 1 0 -10 0\n  c = c + 8;\n  other(); // 33 4 0 15\n  cout << \"---MAIN---\\n\";\n  cout << \" i: \" << i << \" a: \" << a << \" b: \" << b << \" c: \" << c\n       << endl; // 33 0 -10 8\n  i = i + 10;\n  other(); // 75 6 4 15\n  other(); // 107 8 6 15\n  \n  return 0;\n}\nvoid other(void) {\n  static int a = 2;\n  static int b;\n  // a,b为静态局部变量，具有全局寿命，局部可见。\n  //只第一次进入函数时被初始化。\n  int c = 10; // C为局部变量，具有动态生存期\n  //每次进入函数时都初始化。\n  a = a + 2;\n  i = i + 32;\n  c = c + 5;\n  cout << \"---OTHER---\\n\";\n  cout << \" i: \" << i << \" a: \" << a << \" b: \" << b << \" c: \" << c << endl;\n  b = a;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day3/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day3:static_member1`\n# please run `bazel run //practical_exercises/10_day_practice/day3:swap`\n# please run `bazel run //practical_exercises/10_day_practice/day3:pratice`\n# please run `bazel run //practical_exercises/10_day_practice/day3:static_member2`\n# please run `bazel run //practical_exercises/10_day_practice/day3:predeclare`\n# please run `bazel run //practical_exercises/10_day_practice/day3:inline`\n# please run `bazel run //practical_exercises/10_day_practice/day3:static_data`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"static_member1\",\n    srcs = [\"static_member1.cpp\"],\n)\ncc_binary(\n    name = \"swap\",\n    srcs = [\"swap.cpp\"],\n)\ncc_binary(\n    name = \"pratice\",\n    srcs = [\"pratice.cpp\"],\n)\ncc_binary(\n    name = \"static_member2\",\n    srcs = [\"static_member2.cpp\"],\n)\ncc_binary(\n    name = \"predeclare\",\n    srcs = [\"predeclare.cpp\"],\n)\ncc_binary(\n    name = \"inline\",\n    srcs = [\"inline.cpp\"],\n)\ncc_binary(\n    name = \"static_data\",\n    srcs = [\"static_data.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day3/inline.cpp",
    "content": "/* 内联函数.cpp */\n#include <iostream>\nusing namespace std;\n//函数声明\ninline double CalArea(double radius);\nint main(int argc, char const *argv[]) {\n  double r(3.0);\n  double area;\n  area = CalArea(r);\n  cout << area << endl;\n  \n  return 0;\n}\n//加关键字inline\ninline double CalArea(double radius) { return 3.14 * radius * radius; }"
  },
  {
    "path": "practical_exercises/10_day_practice/day3/pratice.cpp",
    "content": "/* 函数综合练习题.cpp */\n/*\n一圆型游泳池如图所示，现在需在其周围建一圆型过道，并在其四周围上栅栏。栅栏价格为35元/米，过道造价为20元/平方米。\n过道宽度为3米，游泳池半径由键盘输入。要求编程计算并输出过道和栅栏的造价。\n\n图形描述：大圆嵌套小圆：\n小圆在大圆中间，小圆为游泳池，大圆与小圆间隔为过道。\n*/\n#include <iostream>\nusing namespace std;\nconst float PI = 3.14159;\nconst float FencePrice = 35;\nconst float ConcretePrice = 20;\n\nclass Circle {\nprivate:\n  float radius;\n\npublic:\n  Circle(float r);\n  float Circumference() const;\n  float Area() const;\n};\nCircle::Circle(float r) { radius = r; }\n\n// 计算圆的周长\nfloat Circle::Circumference() const { return 2 * PI * radius; }\n\n// 计算圆的面积\nfloat Circle::Area() const { return PI * radius * radius; }\n\nint main(int argc, char const *argv[]) {\n  float radius;\n  float FenceCost, ConcreteCost;\n\n  // 提示用户输入半径\n  cout << \"Enter the radius of the pool: \";\n  cin >> radius;\n\n  // 声明 Circle 对象\n  Circle Pool(radius);\n  Circle PoolRim(radius + 3);\n  // 计算栅栏造价并输出\n  FenceCost = PoolRim.Circumference() * FencePrice;\n  cout << \"Fencing Cost is ￥\" << FenceCost << endl;\n\n  //  计算过道造价并输出\n  ConcreteCost = (PoolRim.Area() - Pool.Area()) * ConcretePrice;\n  cout << \"Concrete Cost is ￥\" << ConcreteCost << endl;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day3/predeclare.cpp",
    "content": "/* 类前向声明.cpp */\n/*\n使用前向引用声明虽然可以解决一些问题，但它并不是万能的。需要注意的是，\n尽管使用了前向引用声明，但是在提供一个完整的类声明之前，不能声明该类的对象，\n也不能在内联成员函数中使用该类的对象。请看下面的程序段：\n*/\n\n//第一种\n#include <iostream>\nclass Fred; //前向引用声明\nclass Barney {\n  Fred x; //错误：类Fred的声明尚不完善\n};\nclass Fred {\n  Barney y;\n};\n\n//第二种\nclass Fred; //前向引用声明\n\nclass Barney {\npublic:\n  void method() {\n    x->yabbaDabbaDo(); //错误：Fred类的对象在定义之前被使用\n  }\n\nprivate:\n  Fred *x; //正确，经过前向引用声明，可以声明Fred类的对象指针\n};\n\nclass Fred {\npublic:\n  void yabbaDabbaDo();\n\nprivate:\n  Barney *y;\n};\n\n/*\n总结：当使用前向引用声明时，只能使用被声明的符号，而不能涉及类的任何细节。\n*/"
  },
  {
    "path": "practical_exercises/10_day_practice/day3/static_data.cpp",
    "content": "/* 静态数据成员.cpp */\n/*\n学习知识：\n静态数据成员\n用关键字static声明\n该类的所有对象维护该成员的同一个拷贝\n必须在类外定义和初始化，用(::)来指明所属的类。\n*/\n#include <iostream>\nusing namespace std;\nclass Point {\npublic:\n  Point(int xx = 0, int yy = 0) {\n    X = xx;\n    Y = yy;\n    countP++;\n  }\n  Point(Point &p);\n  int GetX() { return X; }\n  int GetY() { return Y; }\n  void GetC() { cout << \" Object id=\" << countP << endl; }\n\nprivate:\n  int X, Y;\n  //静态数据成员，必须在外部定义和初始化，内部不能直接初始化！\n  static int countP;\n};\nPoint::Point(Point &p) {\n  X = p.X;\n  Y = p.Y;\n  countP++;\n}\n//必须在类外定义和初始化，用(::)来指明所属的类。\nint Point::countP = 0;\nint main() {\n  Point A(4, 5);\n  cout << \"Point A,\" << A.GetX() << \",\" << A.GetY();\n  A.GetC();\n  Point B(A);\n  cout << \"Point B,\" << B.GetX() << \",\" << B.GetY();\n  B.GetC();\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day3/static_member1.cpp",
    "content": "/* 静态成员函数1.cpp */\n/*\n知识点：\n静态成员函数\n类外代码可以使用类名和作用域操作符来调用静态成员函数。\n静态成员函数只能引用属于该类的静态数据成员或静态成员函数。\n*/\n#include <iostream>\nusing namespace std;\nclass Application {\npublic:\n  static void f();\n  static void g();\n\nprivate:\n  static int global;\n};\nint Application::global = 0;\nvoid Application::f() { global = 5; }\nvoid Application::g() { cout << global << endl; }\n\nint main() {\n  Application::f();\n  Application::g();\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day3/static_member2.cpp",
    "content": "/* 静态成员函数2.cpp */\n#include <iostream>\nusing namespace std;\nclass A {\npublic:\n  static void f(A a);\n\nprivate:\n  int x;\n};\nvoid A::f(A a) {\n\n  //静态成员函数只能引用属于该类的静态数据成员或静态成员函数。\n  // cout<<x; //对x的引用是错误的\n  cout << a.x; //正确\n}\n\nint main(int argc, char const *argv[]) {\n  A a;\n  a.f(A());\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day3/swap.cpp",
    "content": "/* 两数交换.cpp */\n#include <iostream>\nusing namespace std;\nvoid swap(int &a, int &b);\nint main(int argc, char const *argv[]) {\n  int x1(5);\n  int x2(7);\n  swap(x1, x2);\n  cout << x1 << \" \" << x2 << endl;\n  \n  return 0;\n}\n\nvoid swap(int &a, int &b) {\n  int t;\n  t = a;\n  a = b;\n  b = t;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/clock/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day4/clock:operator`\n# please run `bazel run //practical_exercises/10_day_practice/day4/clock:operator_plus`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"operator\",\n    srcs = [\"operator.cpp\"],\n)\ncc_binary(\n    name = \"operator_plus\",\n    srcs = [\"operator_plus.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/clock/operator.cpp",
    "content": "/* 重载()的时钟.cpp */\n#include <iostream>\nusing namespace std;\nclass Time {\nprivate:\n  int hh, mm, ss;\n\npublic:\n  Time(int h = 0, int m = 0, int s = 0) : hh(h), mm(m), ss(s) {}\n  void operator()(int h, int m, int s) {\n    hh = h;\n    mm = m;\n    ss = s;\n  }\n  void ShowTime() { cout << hh << \":\" << mm << \":\" << ss << endl; }\n};\nint main() {\n  Time t1(12, 10, 11);\n  t1.ShowTime();\n  t1.operator()(23, 20, 34);\n  t1.ShowTime();\n  t1(10, 10, 10);\n  t1.ShowTime();\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/clock/operator_plus.cpp",
    "content": "/* 重载++的时钟.cpp */\n/*\n设计一个时钟类，能够记录时、分、秒，重载它的++运算符，每执行一次++运算，加时1秒，但要使计时过程能够自动进位。\n*/\n#include <iostream>\nusing namespace std;\nclass Time {\npublic:\n  Time(int h = 0, int m = 0, int s = 0) {\n    hour = h;\n    minute = m;\n    second = s;\n  }\n  Time operator++();\n  Time operator++(int);\n  void showTime() {\n    cout << \"当前时间为：\" << hour << \":\" << minute << \":\" << second << endl;\n  }\n\nprivate:\n  int hour, minute, second;\n};\nTime Time::operator++(int n) {\n  Time tmp = *this;\n  ++(*this);\n  return tmp;\n}\nTime Time::operator++() {\n  ++second;\n  if (second == 60) {\n    second = 0;\n    ++minute;\n    if (minute == 60) {\n      minute = 0;\n      hour++;\n      if (hour == 24) {\n        hour = 0;\n      }\n    }\n  }\n  return *this;\n}\n\nint main(int argc, char const *argv[]) {\n  Time t(23, 59, 59);\n  ++t;\n  t.showTime();\n  (t++).showTime();\n  t.showTime();\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/const/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day4/const:obj_func`\n# please run `bazel run //practical_exercises/10_day_practice/day4/const:obj_ref`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"obj_func\",\n    srcs = [\"obj_func.cpp\"],\n)\ncc_binary(\n    name = \"obj_ref\",\n    srcs = [\"obj_ref.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/const/obj_func.cpp",
    "content": "#include <iostream>\nusing namespace std;\nclass R {\npublic:\n  R(int r1, int r2) {\n    R1 = r1;\n    R2 = r2;\n  }\n  // const区分成员重载函数\n  void print();\n  void print() const;\n\nprivate:\n  int R1, R2;\n};\n/*\n常成员函数说明格式：类型说明符  函数名（参数表）const;\n这里，const是函数类型的一个组成部分，因此在实现部分也要带const关键字。\nconst关键字可以被用于参与对重载函数的区分\n通过常对象只能调用它的常成员函数\n*/\n\nvoid R::print() {\n  cout << \"普通调用\" << endl;\n  cout << R1 << \":\" << R2 << endl;\n}\n//实例化也需要带上\nvoid R::print() const {\n  cout << \"常对象调用\" << endl;\n  cout << R1 << \";\" << R2 << endl;\n}\nint main() {\n  R a(5, 4);\n  a.print(); //调用void print()\n  //通过常对象只能调用它的常成员函数\n  const R b(20, 52);\n  b.print(); //调用void print() const\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/const/obj_ref.cpp",
    "content": "\n#include <iostream>\nusing namespace std;\nvoid display(const double &r);\n\nclass A {\npublic:\n  A(int i, int j) {\n    x = i;\n    y = j;\n  }\n\nprivate:\n  int x, y;\n};\nint main() {\n  double d(9.5);\n  display(d);\n  A const a(3, 4); // a是常对象，不能被更新\n  \n  return 0;\n}\nvoid display(const double &r)\n//常引用做形参，在函数中不能更新 r所引用的对象。\n{\n  cout << r << endl;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/const/readme.md",
    "content": "͵ĶгʼҲܱ¡\nãõĶܱ¡\nconst  ˵  &\n󣺱гʼ,ܱ¡\n  const  \n飺Ԫزܱ¡\n˵  const  [С]...\nָ룺ָָ롣\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/copy_ctor/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day4/copy_ctor:clock`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\")\n\ncc_library(\n    name = \"clock\",\n    hdrs = [\"clock.h\"],\n)\n\ncc_binary(\n    name = \"main\",\n    srcs = [\"clock.cpp\"],\n    deps = [\n        \":clock\",\n    ],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/copy_ctor/clock.cpp",
    "content": "#include \"clock.h\"\n#include <iostream>\nusing namespace std;\nClock::Clock(int NewH, int NewM, int NewS) {\n  this->Hour = NewH;\n  this->Minute = NewM;\n  this->Second = NewS;\n}\nClock::Clock(Clock &c) {\n  this->Hour = c.Hour;\n  this->Minute = c.Minute;\n  this->Second = c.Second;\n}\nvoid Clock::SetTime(int NewH, int NewM, int NewS) {\n  //加不加this指针都一样\n  this->Hour = NewH;\n  this->Minute = NewM;\n  this->Second = NewS;\n}\nvoid Clock::ShowTime() {\n  cout << this->Hour << endl;\n  cout << this->Minute << endl;\n  cout << this->Second << endl;\n}\n//析构函数\nClock::~Clock() {}\nint main(int argc, char const *argv[]) {\n  Clock c(0, 0, 0);\n\n  c.SetTime(10, 20, 30);\n  c.ShowTime();\n  //拷贝构造函数调用\n  Clock c1(c);\n  c1.ShowTime();\n  c1.SetTime(90, 98, 99);\n  c1.ShowTime();\n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/copy_ctor/clock.h",
    "content": "#ifndef CLOCK\n#define CLOCK\nclass Clock {\npublic:\n  Clock(int NewH, int NewM, int NewS);\n  Clock(\n      Clock &\n          c); //拷贝构造函数,如果不加，编译器会自动生成一个拷贝构造函数，因此加不加都可以直接使用。\n  void SetTime(int NewH, int NewM, int NewS);\n  void ShowTime();\n  ~Clock(); //析构函数，编译器会自动产生一个默认的析构函数。\nprivate:\n  int Hour, Minute, Second;\n};\n\n#endif\n\n/*\n#ifndef   标识符\n       程序段1\n#else\n       程序段2\n#endif\n如果“标识符”未被定义过，则编译程序段1，否则编译程序段2。\n\n*/"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/friend/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day4/friend:class`\n# please run `bazel run //practical_exercises/10_day_practice/day4/friend:func`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"class\",\n    srcs = [\"class.cpp\"],\n)\ncc_binary(\n    name = \"func\",\n    srcs = [\"func.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/friend/class.cpp",
    "content": "#include <iostream>\nusing namespace std;\n/*\n若一个类为另一个类的友元，则此类的所有成员都能访问对方类的私有成员。\n声明语法：将友元类名在另一个类中使用friend修饰说明。\n*/\n\n/*\n如果声明B类是A类的友元，B类的成员函数就可以访问A类的私有和保护数据，\n但A类的成员函数却不能访问B类的私有、保护数据。\n*/\nclass A {\n  friend class B;\n\npublic:\n  void Display() { cout << x << endl; }\n\nprivate:\n  int x;\n};\nclass B {\npublic:\n  void Set(int i);\n  void Display();\n\nprivate:\n  A a;\n};\nvoid B::Set(int i) { a.x = i; }\nvoid B::Display() { a.Display(); }\n\nint main(int argc, char const *argv[]) {\n  B b;\n  b.Set(10);\n  b.Display();\n\n  \n  return 0;\n}\n\n/*\n如果声明B类是A类的友元，B类的成员函数就可以访问A类的私有和保护数据，但A类的成员函数却不能访问B类的私有、保护数据\n*/"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/friend/func.cpp",
    "content": "//使用友元函数计算两点间距离\n#include <cmath>\n#include <iostream>\nusing namespace std;\nclass Point {\npublic:\n  Point(int x = 0, int y = 0) : X(x), Y(y) {}\n  int GetX() { return X; }\n  int GetY() { return Y; }\n  friend float Distance(Point &a, Point &b);\n\nprivate:\n  int X, Y; //私有数据成员\n};\n//通过将一个模块声明为另一个模块的友元，一个模块能够引用到另一个模块中本是被隐藏的信息。\nfloat Distance(Point &a, Point &b) {\n  double dx = a.X - b.X;\n  double dy = a.Y - b.Y;\n  return sqrt(dx * dx + dy * dy);\n}\n\nint main() {\n  Point p1(3.0, 5.0), p2(4.0, 6.0);\n  cout << \"两点距离为：\" << Distance(p1, p2) << endl;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day4/friend/readme.md",
    "content": "# Ԫ\nԪC++ṩһƻݷװصĻơ\nͨһģΪһģԪһģܹõһģбǱصϢ\nʹԪԪࡣ\nΪȷݵԣݷװصԭ򣬽龡ʹûʹԪ\n# ʹã\nԪɹؼfriend˵ķǳԱĺܹͨ private  protectedԱ\nãԣʹԱڷװͿԷѡ\nʶеĳԱͨ\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/ctor_dtor/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day5/ctor_dtor:param`\n# please run `bazel run //practical_exercises/10_day_practice/day5/ctor_dtor:noctor`\n# please run `bazel run //practical_exercises/10_day_practice/day5/ctor_dtor:cseq`\n# please run `bazel run //practical_exercises/10_day_practice/day5/ctor_dtor:ctor`\n# please run `bazel run //practical_exercises/10_day_practice/day5/ctor_dtor:ctor_d`\n# please run `bazel run //practical_exercises/10_day_practice/day5/ctor_dtor:seq`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"param\",\n    srcs = [\"param.cpp\"],\n)\ncc_binary(\n    name = \"noctor\",\n    srcs = [\"noctor.cpp\"],\n)\ncc_binary(\n    name = \"cseq\",\n    srcs = [\"cseq.cpp\"],\n)\ncc_binary(\n    name = \"ctor\",\n    srcs = [\"ctor.cpp\"],\n)\ncc_binary(\n    name = \"ctor_d\",\n    srcs = [\"ctor_d.cpp\"],\n)\ncc_binary(\n    name = \"seq\",\n    srcs = [\"seq.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/ctor_dtor/cseq.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\n/*\n先构造成员\n再构造自身（调用构造函数）\n*/\n\nclass A {\npublic:\n  A() { cout << \"Constructing A\" << endl; }\n  ~A() { cout << \"Destructing A\" << endl; }\n};\nclass B {\npublic:\n  B() { cout << \"Constructing B\" << endl; }\n  ~B() { cout << \"Destructing B\" << endl; }\n};\n\nclass C {\npublic:\n  C() { cout << \"Constructing C\" << endl; }\n  ~C() { cout << \"Destructing C\" << endl; }\n  B b;\n  A a;\n};\n\nint main() {\n  C c;\n  \n}\n\n/*\n执行结果：\nConstructing B\nConstructing A\nConstructing C\nDestructing C\nDestructing A\nDestructing B\n*/"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/ctor_dtor/ctor.cpp",
    "content": "#include <iostream>\nusing namespace std;\nclass Base {\nprivate:\n  int x;\n\npublic:\n  Base(int a) {\n    x = a;\n    cout << \"Base constructor x=\" << x << endl;\n  }\n  ~Base() { cout << \"Base destructor...\" << endl; }\n};\nclass Derived : public Base {\nprivate:\n  int y;\n\npublic:\n  Derived(int a, int b) : Base(a) { //派生类构造函数的初始化列表\n    y = b;\n    cout << \"Derived constructor y=\" << y << endl;\n  }\n  ~Derived() { cout << \"Derived destructor...\" << endl; }\n};\nint main() {\n  Derived d(1, 2);\n  \n  return 0;\n}"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/ctor_dtor/ctor_d.cpp",
    "content": "#include <iostream>\nusing namespace std;\nclass A {\npublic:\n  A() { cout << \"Constructing A\" << endl; }\n  ~A() { cout << \"Destructing A\" << endl; }\n};\nclass B {\npublic:\n  B() { cout << \"Constructing B\" << endl; }\n  ~B() { cout << \"Destructing B\" << endl; }\n};\nclass C {\npublic:\n  C() { cout << \"Constructing C\" << endl; }\n  ~C() { cout << \"Destructing C\" << endl; }\n};\nclass D : public C {\npublic:\n  D() { cout << \"Constructing D\" << endl; }\n  ~D() { cout << \"Destructing D\" << endl; }\n  B b;\n  A a;\n  C c;\n};\n\nint main() {\n  D d;\n  \n}\n\n/*\n执行结果：\nConstructing C\nConstructing B\nConstructing A\nConstructing C\nConstructing D\nDestructing D\nDestructing C\nDestructing A\nDestructing B\nDestructing C\n*/"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/ctor_dtor/noctor.cpp",
    "content": "#include <iostream>\nusing namespace std;\nclass A {\npublic:\n  A() { cout << \"Constructing A\" << endl; }\n  ~A() { cout << \"Destructing A\" << endl; }\n};\nclass B : public A {\npublic:\n  ~B() { cout << \"Destructing B\" << endl; }\n};\nint main() {\n  B b;\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/ctor_dtor/param.cpp",
    "content": "#include <iostream>\nusing namespace std;\nclass Point {\nprotected:\n  int x, y;\n\npublic:\n  Point(int a, int b = 0) {\n    x = a;\n    y = b;\n    cout << \"constructing point(\" << x << \",\" << y << \")\" << endl;\n  }\n};\nclass Line : public Point {\nprotected:\n  int len;\n\npublic:\n  Line(int a, int b, int l) : Point(a, b) { //构造函数初始化列表\n    len = l;\n    cout << \"Constructing Line,len ...\" << len << endl;\n  }\n};\nint main() {\n  Line L1(1, 2, 3);\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/ctor_dtor/readme.md",
    "content": "һԱĹ\nȹԱ\nٹù캯\n๹캯\nж࣬ҲܰԱڴʱĹ캯ҪԱĳʼ⣬ҪûͳԱĹ캯ǴݲɻӶͳԱĽͳʼ\n\n**ֻܲù캯ʼбķʽԱĹ캯ݲ**ʽ£\n\n๹캯():๹캯(),Ա1(),{\n    //\n}\n\n캯ô\n**Ĺ**\n\n- ȹ\n- ٹԱ\n- ù캯\n\n๹˳ξ**ԶĻȹ**\nԱ˳Ͷ˳\n˳빹෴\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/ctor_dtor/seq.cpp",
    "content": "// Eg6-12.cpp\n#include <iostream>\nusing namespace std;\nclass A {\n  int x;\n\npublic:\n  A(int i = 0) {\n    x = i;\n    cout << \"A-----\" << x << endl;\n  }\n};\nclass B {\n  int y;\n\npublic:\n  B(int i) {\n    y = i;\n    cout << \"B-----\" << y << endl;\n  }\n};\nclass C {\n  int z;\n\npublic:\n  C(int i) {\n    z = i;\n    cout << \"C-----\" << z << endl;\n  }\n};\nclass D : public B {\npublic:\n  C c1, c2;\n  A *a1 = new A(10);\n  A a0, a4;\n  D() : a4(4), c2(2), c1(1), B(1) { cout << \"D-----5\" << endl; }\n};\nint main() {\n  D d;\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/inherit_access/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day5/inherit_access:public`\n# please run `bazel run //practical_exercises/10_day_practice/day5/inherit_access:protected_inherit`\n# please run `bazel run //practical_exercises/10_day_practice/day5/inherit_access:protected`\n# please run `bazel run //practical_exercises/10_day_practice/day5/inherit_access:private`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"public\",\n    srcs = [\"public.cpp\"],\n)\ncc_binary(\n    name = \"protected_inherit\",\n    srcs = [\"protected_inherit.cpp\"],\n)\ncc_binary(\n    name = \"protected\",\n    srcs = [\"protected.cpp\"],\n)\ncc_binary(\n    name = \"private\",\n    srcs = [\"private.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/inherit_access/private.cpp",
    "content": "#include <iostream>\nusing namespace std;\nclass base {\n  int x;\n\npublic:\n  void setx(int n) { x = n; }\n  int getx() { return x; }\n  void showx() { cout << x << endl; }\n};\n//派生类\nclass derived : public base {\n  int y;\n\npublic:\n  void sety(int n) { y = n; }\n  void sety() { y = getx(); }\n  void showy() { cout << y << endl; }\n};\n//派生类不可直接访问基类的private成员，可通过基类的共有成员函数访问\nint main() {\n  derived obj;\n  obj.setx(10);\n  obj.sety(20);\n  obj.showx();\n  obj.showy();\n  obj.sety();\n  obj.showx();\n  obj.showy();\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/inherit_access/protected.cpp",
    "content": "/*\n基类中protected的成员\n类内部：可以访问\n类的使用者：不能访问\n类的派生类成员：可以访问\n*/\n#include <iostream>\nclass B {\nprivate:\n  int i;\n\nprotected:\n  int j;\n\npublic:\n  int k;\n};\nclass D : public B {\npublic:\n  void f() {\n    i = 1; // cannot access 派生类不可访问基类私有成员\n    j = 2; //派生类可以访问基类保护成员\n    k = 3;\n  }\n};\nint main() {\n  B b;\n  b.i = 1; // cannot access 私有成员，类的使用者不能访问\n  b.j = 2; // cannot access 保护成员，类的使用者不能访问\n  b.k = 3;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/inherit_access/protected_inherit.cpp",
    "content": "/*\n派生方式为protected的继承称为保护继承，在这种继承方式下，\n基类的public成员在派生类中会变成protected成员，\n基类的protected和private成员在派生类中保持原来的访问权限\n*/\n#include <iostream>\nusing namespace std;\nclass Base {\n  int x;\n\nprotected:\n  int getx() { return x; }\n\npublic:\n  void setx(int n) { x = n; }\n  void showx() { cout << x << endl; }\n};\nclass Derived : protected Base {\n  int y;\n\npublic:\n  void sety(int n) { y = n; }\n  void sety() { y = getx(); } //访问基类的保护成员\n  void showy() { cout << y << endl; }\n};\nint main() {\n  Derived obj;\n  obj.setx(10); //错误\n  obj.sety(20);\n  obj.showx(); //错误，\n  obj.showy();\n  \n}\n\n/*\n解释：\n如最上面文字所示：保护继承会将基类的public变为protected，而对于protected成员，\n外部去使用保护成员的时候，会报错，所以setx与showx访问错误，而对于派生类，则可直接访问基类的保护成员，\n在派生类中，y=getx()可正常访问！\n*/"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/inherit_access/public.cpp",
    "content": "#include <iostream>\nusing namespace std;\nclass Base {\n  int x;\n\npublic:\n  void setx(int n) { x = n; }\n  int getx() { return x; }\n  void showx() { cout << x << endl; }\n};\n//私有继承\n//基类的中的public成员在派生类中是private, private成员在派生类中不可访问。\nclass derived : private Base {\n  int y;\n\npublic:\n  void sety(int n) { y = n; }\n  void sety() { y = getx(); }\n  void showy() { cout << y << endl; }\n};\nint main() {\n  derived obj;\n  obj.setx(10); // cannot access\n  obj.sety(20);\n  obj.showx(); // cannot access\n  obj.showy();\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/inherit_access/readme.md",
    "content": "һм̳\n1.protectedĳԱ\nڲԷʣ\nʹߣܷʣ\nԱԷʣ\n2.಻ɷʻprivateԱ\n3.ɷʻprotectedԱ\n4.ɷʻpublicԱ\n\n              public̳         \n\npublic                 ->                  public\n\nprotected          ->                  protected\n\nprivate                ->                 ɷ\n\n˽м̳\nҲɷʻprivateԱ\n\n              private̳       \n\npublic                 ->                  private\n\nprotected          ->                  private\n\nprivate                ->                 ɷ\n\n̳\nʽΪprotectedļ̳гΪ̳Уּ̳зʽ£\npublicԱлprotectedԱ\nprotectedprivateԱбԭķȨ\nע㣺ñ̳еʱpublicԱΪprotectedԱʹ߲ɷʣɷʣ\n\n            protected̳     \n\npublic                 ->                  protected\n\nprotected          ->                  protected\n\nprivate                ->                 ɷ\n\n\nġԻԱķʽ\n1.ֱͨӷʻԱ \n2.ԱֱӷʻԱ \n3.ͨ޶ʱصĻԱ  \n\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/rela/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day5/rela:rela`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"rela\",\n    srcs = [\"rela.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/rela/readme.md",
    "content": "1.Ĺϵ \n\n֮ڸֵԡ¼\nֵ\nĵֵַָ롣\nʼá\n֮Уܰѻֵ󣻲ܰѻĵֵַָ룻ҲܰѻΪá\n\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/rela/rela.cpp",
    "content": "/* 基类与派生类(重要).cpp */\n#include <iostream>\nusing namespace std;\nclass A {\n  int a;\n\npublic:\n  void setA(int x) { a = x; }\n  int getA() { return a; }\n};\nclass B : public A {\n  int b;\n\npublic:\n  void setB(int x) { b = x; }\n  int getB() { return b; }\n};\nvoid f1(A a, int x) { a.setA(x); }\nvoid f2(A *pA, int x) { pA->setA(x); }\nvoid f3(A &rA, int x) { rA.setA(x); }\n\nint main() {\n  A a1, *pA;\n  B b1;\n  a1.setA(1);\n  b1.setA(2);\n  //把派生类对象赋值给基类对象。\n  a1 = b1;\n  cout << a1.getA() << endl;\n  cout << b1.getA() << endl;\n  a1.setA(10);\n  cout << a1.getA() << endl;\n  cout << b1.getA() << endl;\n  //把派生类对象的地址赋值给基类指针。\n  pA = &b1;\n  pA->setA(20);\n  cout << pA->getA() << endl;\n  cout << b1.getA() << endl;\n  //用派生类对象初始化基类对象的引用。\n  A &ra = b1;\n  ra.setA(30);\n  cout << pA->getA() << endl;\n  cout << b1.getA() << endl;\n  b1.setA(7);\n  cout << b1.getA() << endl;\n  f1(b1, 100);\n  cout << \"1111111111\" << endl;\n  cout << b1.getA() << endl; // 7\n  f2(&b1, 200);\n  cout << b1.getA() << endl;\n  f3(b1, 300);\n  cout << b1.getA() << endl;\n  \n  return 0;\n}"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/rule/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day5/rule:direct`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"direct\",\n    srcs = [\"direct.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/rule/direct.cpp",
    "content": "/* 直接基类与间接基类.cpp */\n//当同时存在直接基类和间接基类时，每个派生类只负责其直接基类的构造。\n#include <iostream>\nusing namespace std;\nclass A {\n  int x;\n\npublic:\n  A(int aa) {\n    x = aa;\n    cout << \"Constructing A\" << endl;\n  }\n  ~A() { cout << \"Destructing A\" << endl; }\n};\nclass B : public A {\npublic:\n  B(int x) : A(x) { cout << \"Constructing B\" << endl; }\n};\nclass C : public B {\npublic:\n  C(int y) : B(y) { cout << \"Constructing C\" << endl; }\n};\nint main() {\n  C c(1);\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/rule/readme.md",
    "content": "һ캯Ĺ\n\n1Բ幹캯 \n֮һʱԲ幹캯\nûжκι캯\nȱʡĹ캯\n޲ι캯\n2붨幹캯 \nԱֻдĹ캯ʱʹ౾ûݳԱҪʼҲ붨幹캯Թ캯ʼбķʽͳԱĹ캯ݲʵֻӶͳԱĳʼ \n3Ĺ캯ֱֻӻĳʼ \n\nC++Ա׼һĻͬʱҲһ࣬ÿֱֻӻĹ캯á\nֱӻֻдĹ캯ûĬϹ캯ʱȱʡ޲ι캯ڹ캯ĳʼбеֱӻĹ캯Ĺ캯ݲʵеĻӶĳʼ\nһʱ඼ฺʼ\n\nܽ᣺\n1жʱڼ̳зʽеãڹ캯ʼбеĴ޹ءAһBʱȵûBĹ캯ٵûAĹ캯\n\n2жԱʱеãڹ캯ʼбеĴ޹ء\n\n3캯ʼбеĻͶԱĹ캯֮󣬲ִ๹캯еĳ롣\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/virtual/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day5/virtual:example2`\n# please run `bazel run //practical_exercises/10_day_practice/day5/virtual:init`\n# please run `bazel run //practical_exercises/10_day_practice/day5/virtual:seq`\n# please run `bazel run //practical_exercises/10_day_practice/day5/virtual:example1`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"example2\",\n    srcs = [\"example2.cpp\"],\n)\ncc_binary(\n    name = \"init\",\n    srcs = [\"init.cpp\"],\n)\ncc_binary(\n    name = \"seq\",\n    srcs = [\"seq.cpp\"],\n)\ncc_binary(\n    name = \"example1\",\n    srcs = [\"example1.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/virtual/example1.cpp",
    "content": "/* 例1.cpp */\n#include <iostream>\nusing namespace std;\nclass A {\npublic:\n  void vf() { cout << \"I come from class A\" << endl; }\n};\nclass B : public A {};\nclass C : public A {};\nclass D : public B, public C {};\n\nint main() {\n  D d;\n  d.vf(); // error\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/virtual/example2.cpp",
    "content": "/* 例2.cpp */\n#include <iostream>\nusing namespace std;\nclass A {\npublic:\n  void vf() { cout << \"I come from class A\" << endl; }\n};\nclass B : virtual public A {};\nclass C : virtual public A {};\nclass D : public B, public C {};\n\nint main() {\n  D d;\n  d.vf(); // okay\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/virtual/init.cpp",
    "content": "/* 派生类初始化.cpp */\n#include <iostream>\nusing namespace std;\nclass A {\n  int a;\n\npublic:\n  A(int x) {\n    a = x;\n    cout << \"Virtual Bass A...\" << endl;\n  }\n};\nclass B : virtual public A {\npublic:\n  B(int i) : A(i) { cout << \"Virtual Bass B...\" << endl; }\n};\nclass C : virtual public A {\n  int x;\n\npublic:\n  C(int i) : A(i) {\n    cout << \"Constructing C...\" << endl;\n    x = i;\n  }\n};\nclass ABC : public C, public B {\npublic:\n  //虚基类由最终派生类初始化\n  ABC(int i, int j, int k)\n      : C(i), B(j), A(i) // L1，这里必须对A进行初始化\n  {\n    cout << \"Constructing ABC...\" << endl;\n  }\n};\nint main() {\n  ABC obj(1, 2, 3);\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/virtual/readme.md",
    "content": "̳µĶԣڶ̳зʽ£̳˶ĳԱͬӵͬԱʱײֳͻ⡣\n\n̳ԭظ࣬Ӽ̳ͬһʹüӻࣨPersonжݿԡ\n\n1̳virtual inheritanceĶ\n﷨\nclass derived_class : virtual [] base_class\nvirtual base class\n̳еĻ\nеУһ\n\n2̳еĹ\n  ĳʼһĶؼ̳еĳʼ﷨һģ캯ĵ˳ͬ\n  ṩԼӻĹ죨ڹ캯ʼбй࣬۴ֱӻǼӻࣩ\n  ˳Ĺ涨\nȵĹ캯ٵ÷Ĺ캯\nͬһа,ЩĹ캯ǵ˵Ĵ\nɷǻ,Ȼȵû๹캯,ٵ๹캯\n3ʼ \nû̳е£ÿĹ캯ֱֻӻĳʼ̳зʽ£Ĺ캯ʼ\n̳зʽ£Ĺ캯ûȷĹ캯ͻ᳢Ե಻ҪĹ캯ȱʡ޲κȱʡĹ캯ûҵͻ\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day5/virtual/seq.cpp",
    "content": "/* 虚基类调用次序(重要).cpp */\n//重要!!!\n#include <iostream>\nusing namespace std;\nclass A {\n  int a;\n\npublic:\n  A() { cout << \"Constructing A\" << endl; }\n};\nclass B {\npublic:\n  B() { cout << \"Constructing B\" << endl; }\n};\nclass B1 : virtual public B, virtual public A {\npublic:\n  B1(int i) { cout << \"Constructing B1\" << endl; }\n};\nclass B2 : public A, virtual public B {\npublic:\n  B2(int j) { cout << \"Constructing B2\" << endl; }\n};\nclass D : public B1, public B2 {\npublic:\n  D(int m, int n) : B1(m), B2(n) { cout << \"Constructing D\" << endl; }\n  A a;\n};\n\nint main() {\n  D d(1, 2);\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day6/abstract_class/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day6/abstract_class:main`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"main\",\n    srcs = [\"main.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day6/abstract_class/main.cpp",
    "content": "/* 抽象类.cpp */\n#include <iostream>\nusing namespace std;\nclass Figure {\nprotected:\n  double x, y;\n\npublic:\n  void set(double i, double j) {\n    x = i;\n    y = j;\n  }\n  virtual void area() = 0;\n};\n\nclass Trianle : public Figure {\npublic:\n  void area() { cout << \"三角形面积：\" << x * y * 0.5 << endl; }\n};\nclass Rectangle : public Figure {\npublic:\n  void area() { cout << \"这是矩形，它的面积是：\" << x * y << endl; }\n};\n\nint main(int argc, char const *argv[]) {\n  //定义抽象类指针\n  Figure *pF = NULL;\n  // Figure f1; 抽象类不能被实例化\n  Rectangle r;\n  Trianle t;\n  t.set(10, 20);\n  pF = &t;\n  pF->area();\n  r.set(10, 20);\n  pF = &r;\n  pF->area();\n  //定义抽象类引用\n  Figure &rF = t;\n  rF.set(20, 20);\n  rF.area();\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day6/readme.md",
    "content": "一、多态性\n多态性：多态就是在同一个类或继承体系结构的基类与派生类中，用同名函数来实现各种不同的功能。\n\n**静态绑定又称静态联编**，是指在编译程序时就根据调用函数提供的信息，把它所对应的具体函数确定下来，即在编译时就把调用函数名与具体函数绑定在一起。 \n\n**动态绑定又称动态联编**，是指在编译程序时还不能确定函数调用所对应的具体函数，只有在程序运行过程中才能够确定函数调用所对应的具体函数，即在程序运行时才把调用函数名与具体函数绑定在一起。\n\n编译时多态性： ---静态联编(连接)----系统在编译时就决定如何实现某一动作,即对某一消息如何处理.静态联编具有执行速度快的优点.在C++中的编译时多态性是通过函数重载和运算符重载实现的。\n运行时多态性： ---动态联编(连接)----系统在运行时动态实现某一动作,即对某一消息在运行过程实现其如何响应.动态联编为系统提供了灵活和高度问题抽象的优点,在C++中的运行时多态性是通过继承和虚函数实现的。\n\n二、虚函数\n\n虚函数的意义\n\n1、基类与派生类的赋值相容\n\n派生类对象可以赋值给基类对象。\n派生类对象的地址可以赋值给指向基类对象的指针。\n派生类对象可以作为基类对象的引用。\n赋值相容的问题：\n不论哪种赋值方式，都只能通过基类对象（或基类对象的指针或引用）访问到派生类对象从基类中继承到的成员， 不能借此访问派生类定义的成员。\n\n2、虚函数使得可以通过基类对象的指针或引用访问派生类定义的成员。\n\n3.Virtual关键字其实质是告知编译系统，被指定为virtual的函数采用动态联编的形式编译。\n\n4.虚函数的虚特征：基类指针指向派生类的对象时，通过该指针访问其虚函数将调用派生类的版本。\n\n- 一旦将某个成员函数声明为虚函数后，它在继承体系中就永远为虚函数了 \n- 如果基类定义了虚函数，当通过基类指针或引用调用派生类对象时，将访问到它们实际所指对象中的虚函数版本。\n- 只有通过基类对象的指针和引用访问派生类对象的虚函数时，才能体现虚函数的特性。\n- 派生类中的虚函数要保持其虚特征，必须与基类虚函数的函数原型完全相同，否则就是普通的重载函数，与基类的虚函数无关。\n- 派生类通过从基类继承的成员函数调用虚函数时，将访问到派生类中的版本。\n- 只有类的非静态成员函数才能被定义为虚函数，类的构造函数和静态成员函数不能定义为虚函数。原因是虚函数在继承层次结构中才能够发生作用，而构造函数、静态成员是不能够被继承的。\n- 内联函数也不能是虚函数。因为内联函数采用的是静态联编的方式，而虚函数是在程序运行时才与具体函数动态绑定的，采用的是动态联编的方式，即使虚函数在类体内被定义，C++编译器也将它视为非内联函数。\n5.基类析构函数几乎总是为虚析构函数。\nwhy?\n假定使用delete和一个指向派生类的基类指针来销毁派生类对象，如果基类析构函数不为虚,就如一个普通成员函数，delete函数调用的就是基类析构函数。在通过基类对象的引用或指针调用派生类对象时，将致使对象析构不彻底！\n\n三、纯虚函数和抽象类 \n\n1.纯虚函数概念？\n\n仅定义函数原型而不定义其实现的虚函数\nWhy pure function?\n实用角度：占位手段place-holder\n方法学：接口定义手段，抽象表达手段\nHow?\nclass X\n{\n\tvirtual ret_type func_name (param) = 0;\n}\n2.抽象类概念？\n\nWhat is an abstract class?\n包含一个或多个纯虚函数的类\nUsing abstract class\n**不能实例化抽象类**\n但是可以定义抽象类的指针和引用\nConverting abstract class to concrete class\n定义一个抽象类的派生类\n定义所有纯虚函数\n\n3.C++对抽象类具有以下限定\n\n- 抽象类中含有纯虚函数，由于纯虚函数没有实现代码，所以不能建立抽象类的对象。\n- 抽象类只能作为其他类的基类，可以通过抽象类对象的指针或引用访问到它的派生类对象，实现运行时的多态性。\n- 如果派生类只是简单地继承了抽象类的纯虚函数，而没有重新定义基类的纯虚函数，则派生类也是一个抽象类。\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day6/virtual_func/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day6/virtual_func:example`\n# please run `bazel run //practical_exercises/10_day_practice/day6/virtual_func:virtual_feature`\n# please run `bazel run //practical_exercises/10_day_practice/day6/virtual_func:virtual_dtor`\n# please run `bazel run //practical_exercises/10_day_practice/day6/virtual_func:virtual`\n# please run `bazel run //practical_exercises/10_day_practice/day6/virtual_func:vis`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"example\",\n    srcs = [\"example.cpp\"],\n)\ncc_binary(\n    name = \"virtual_feature\",\n    srcs = [\"virtual_feature.cpp\"],\n)\ncc_binary(\n    name = \"virtual_dtor\",\n    srcs = [\"virtual_dtor.cpp\"],\n)\ncc_binary(\n    name = \"virtual\",\n    srcs = [\"virtual.cpp\"],\n)\ncc_binary(\n    name = \"vis\",\n    srcs = [\"vis.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day6/virtual_func/example.cpp",
    "content": "/* 编程实例.cpp */\n/*\n某公司有经理、销售员、小时工等多类人员。经理按周计算薪金；销售员每月底薪800元，\n然后加销售提成，每销售1件产品提取销售利润的5%；小时工按小时计算薪金。每类人员都有姓名和身份证号等数据。\n为简化问题，把各类人员的共有信息抽象成基类Employee，其他人员则继承该类的功能。\n*/\n#include <iostream>\n#include <string>\nusing namespace std;\nclass Employee {\npublic:\n  Employee(string Name, string id) : name(Name), Id(id) {}\n  string getName() const { return name; }         //返回姓名\n  string getID() const { return Id; }             //返回身份证号\n  virtual float getSalary() const { return 0.0; } //返回薪水\n  virtual void print() const {                    //输出姓名和身份证号\n    cout << \"姓名: \" << name << \"\\t\\t 编号: \" << Id << endl;\n  }\n\nprivate:\n  string name;\n  string Id;\n};\n\nclass Manager : public Employee {\npublic:\n  Manager(string Name, string id, int week) : Employee(Name, id) {\n    WeeklySalary = week * 1000;\n  }\n  float getSalary() const { return WeeklySalary; } //获取经理的周薪\n  void print() const { //打印经理姓名、身份证、周薪\n    cout << \"经理：\" << getName() << \"\\t\\t 编号: \" << getID()\n         << \"\\t\\t 总工资: \" << getSalary() << endl;\n  }\n\nprivate:\n  float WeeklySalary; //周薪\n};\n\nclass SaleWorker : public Employee {\npublic:\n  SaleWorker(string name, string id, int profit, int x) : Employee(name, id) {\n    workerMoney = baseMoney + x * 0.05 * profit;\n  }\n  float getSalary() const { return workerMoney; }\n  void print() const {\n    cout << \"销售员：\" << getName() << \"\\t\\t 编号: \" << getID()\n         << \"\\t\\t 总工资: \" << getSalary() << endl;\n  }\n\nprivate:\n  float baseMoney = 800.0;\n  float workerMoney;\n};\n\nclass HourWorker : public Employee {\npublic:\n  HourWorker(string name, string id, int h) : Employee(name, id) {\n    TotalMoney = h * hourMoney;\n  }\n  float getSalary() const { return TotalMoney; }\n  void print() const {\n    cout << \"小时工：\" << getName() << \"\\t\\t 编号: \" << getID()\n         << \"\\t\\t 总工资: \" << getSalary() << endl;\n  }\n\nprivate:\n  float hourMoney = 100.0;\n  float TotalMoney;\n};\n\nint main() {\n  cout << \"请输入工作周：\";\n  int week;\n  cin >> week;\n  Manager m(\"小王\", \"11111111\", week);\n  m.print();\n  cout << \"请输入销售利润：\";\n  int profit;\n  cin >> profit;\n  cout << \"请输入销售件数：\";\n  int x;\n  cin >> x;\n  SaleWorker s(\"小李\", \"222222\", profit, x);\n  s.print();\n  cout << \"请输入工作小时：\";\n  int hour;\n  cin >> hour;\n  HourWorker h(\"小何\", \"333333\", hour);\n  h.print();\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day6/virtual_func/virtual.cpp",
    "content": "/* 虚函数例子.cpp */\n\n// Eg7-1.cpp\n//基类指针或引用指向派生类对象时,虚函数与非虚函数区别：\n//声明Employee的print为虚函数，则可访问到Manager的print函数，非虚函数，则只能访问到Employee的print\n#include <iostream>\n#include <string>\nusing namespace std;\nclass Employee {\npublic:\n  Employee(string name, string id);\n  string getName();\n  string getId();\n  float getSalary();\n  virtual void print();\n\nprivate:\n  string Name;\n  string Id;\n};\nEmployee::Employee(string name, string id) {\n  Name = name;\n  Id = id;\n}\nstring Employee::getName() { return Name; }\nstring Employee::getId() { return Id; }\nfloat Employee::getSalary() { return 0.0; }\nvoid Employee::print() {\n  cout << \"姓名：\" << Name << \"\\t\"\n       << \"编号：\" << Id << endl;\n}\n\nclass Manager : public Employee {\npublic:\n  Manager(string name, string id, float s = 0.0) : Employee(name, id) {\n    weeklySalary = s;\n  }\n  void setSalary(float s) { weeklySalary = s; } //设置经理的周薪\n  float getSalary() { return weeklySalary; }    //获取经理的周薪\n  void print() { //打印经理姓名、身份证、周薪\n    cout << \"经理：\" << getName() << \"\\t\\t 编号: \" << getId()\n         << \"\\t\\t 周工资: \" << getSalary() << endl;\n  }\n\nprivate:\n  float weeklySalary; //周薪\n};\n\n/*\n不论哪种赋值方式，都只能通过基类对象（或基类对象的指针或引用）访问到派生类对象从基类中继承到的成员，\n不能借此访问派生类定义的成员。而虚函数使得可以通过基类对象的指针或引用访问派生类定义的成员。\n*/\nint main() {\n  Employee e(\"小米\", \"NO0001\"), *pM;\n  Manager m(\"小汪\", \"NO0002\", 128);\n  m.print();\n  pM = &m;\n  pM->print();\n  Employee &rM = m;\n  rM.print();\n  \n  return 0;\n}\n// Virtual关键字其实质是告知编译系统，被指定为virtual的函数采用动态联编的形式编译。\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day6/virtual_func/virtual_dtor.cpp",
    "content": "/* 虚析构函数.cpp */\n\n#include <iostream>\nusing namespace std;\nclass A {\npublic:\n  virtual ~A() { cout << \"call A::~A()\" << endl; }\n};\nclass B : public A {\n  char *buf;\n\npublic:\n  B(int i) { buf = new char[i]; }\n  ~B() {\n    delete[] buf;\n    cout << \"call B::~()\" << endl;\n  }\n};\nint main() {\n  A *a = new B(10);\n  delete a;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day6/virtual_func/virtual_feature.cpp",
    "content": "/* 虚函数特性.cpp */\n#include <iostream>\n#include <string>\nusing namespace std;\nclass A {\npublic:\n  void f(int i) { cout << \"A::f()\" << endl; };\n};\nclass B : public A {\npublic:\n  virtual void f(int i) { cout << \"B::f()\" << endl; }\n};\nclass C : public B {\npublic:\n  void f(int i) { cout << \"C::f()\" << endl; }\n};\n//一旦将某个成员函数声明为虚函数后，它在继承体系中就永远为虚函数了\nclass D : public C {\npublic:\n  void f(int) { cout << \"D::f()\" << endl; }\n};\nint main() {\n  A *pA, a;\n  B *pB, b;\n  C c;\n  D d;\n  pA = &a;\n  pA->f(1); //调用A::f\n  pB = &b;\n  pB->f(1); //调用B::f\n  pB = &c;\n  pB->f(1); //调用C::f\n  pB = &d;\n  pB->f(1); //调用D::f\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day6/virtual_func/vis.cpp",
    "content": "/* 从基类继承的成员将访问到派生类版本.cpp */\n#include <iostream>\nusing namespace std;\nclass B {\npublic:\n  void f() { g(); }\n  virtual void g() { cout << \"B::g\"; }\n};\nclass D : public B {\npublic:\n  void g() { cout << \"D::g\\n\"; }\n};\nint main() {\n  D d;\n  d.f();\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/binary_operator/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day7/binary_operator:operator`\n# please run `bazel run //practical_exercises/10_day_practice/day7/binary_operator:friend_operator`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"operator\",\n    srcs = [\"operator.cpp\"],\n)\ncc_binary(\n    name = \"friend_operator\",\n    srcs = [\"friend_operator.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/binary_operator/friend_operator.cpp",
    "content": "/* 2.友元运算符实现.cpp */\n\n/*\n在C++的标准命名空间std中预定义了复数类Complex，\n并且通过友元重载了Complex的各种运算，包括本程序中重载的+、-、*、/。\n若使用using namespace std将std名字空间引入本程序，\n则本程序中的Complex将与std名字空间中的Complex类的运算符重载函数产生冲突，引起编译错误。\n*/\n/*\n对于不要求左值且可以交换参数次序的运算符（如+、-、*、/ 等运算符），\n最好用非成员形式（包括友元和普通函数）的重载运算符函数实现。\n*/\n\n//解决前面的2+b问题。\n#include <iostream>\n\nclass Complex {\nprivate:\n  double r, i;\n\npublic:\n  Complex(double R = 0, double I = 0) : r(R), i(I){};\n  friend Complex operator+(Complex a, Complex b);\n  friend Complex operator-(Complex a, Complex b);\n  friend Complex operator*(Complex a, Complex b);\n  friend Complex operator/(Complex a, Complex b);\n\n  friend Complex operator+(Complex a, double b) {\n    return Complex(a.r + b, a.i);\n  }\n  friend Complex operator+(double a, Complex b) {\n    return Complex(a + b.r, b.i);\n  }\n\n  void display();\n};\n\nComplex operator+(Complex a, Complex b) {\n  return Complex(a.r + b.r, a.i + b.i);\n}\nComplex operator-(Complex a, Complex b) {\n  return Complex(a.r - b.r, a.i - b.i);\n}\nComplex operator*(Complex a, Complex b) {\n  Complex t;\n  t.r = a.r * b.r - a.i * b.i;\n  t.i = a.r * b.i + a.i * b.r;\n  return t;\n}\nComplex operator/(Complex a, Complex b) {\n  Complex t;\n  double x;\n  x = 1 / (b.r * b.r + b.i * b.i);\n  t.r = x * (a.r * b.r + a.i * b.i);\n  t.i = x * (a.i * b.r - a.r * b.i);\n  return t;\n}\n\nvoid Complex::display() {\n  std::cout << r;\n  if (i > 0)\n    std::cout << \"+\";\n  if (i != 0)\n    std::cout << i << \"i\" << std::endl;\n}\nint main(void) {\n  Complex c1(1, 2), c2(3, 4), c3, c4, c5, c6;\n  Complex a, b(2, 3);\n  Complex a1 = b + 2;\n  Complex a2 = 2 + b;\n  a1.display();\n  a2.display();\n  c3 = c1 + c2;\n  c4 = c1 - c2;\n  c5 = c1 * c2;\n  c6 = c1 / c2;\n  c1.display();\n  c2.display();\n  c3.display();\n  c4.display();\n  c5.display();\n  c6.display();\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/binary_operator/operator.cpp",
    "content": "/* 1.类运算符重载.cpp */\n//有复数类Complex，利用运算符重载实现复数的加、减、乘、除等复数运算。\n#include <iostream>\nusing namespace std;\nclass Complex {\nprivate:\n  double r, i;\n\npublic:\n  Complex(double R = 0, double I = 0) : r(R), i(I){};\n  Complex operator+(Complex b);\n  Complex operator-(Complex b);\n  Complex operator*(Complex b);\n  Complex operator/(Complex b);\n  void display();\n};\n\nComplex Complex::operator+(Complex b) { return Complex(r + b.r, i + b.i); }\nComplex Complex::operator-(Complex b) { return Complex(r - b.r, i - b.i); }\n//求复数相乘的算法\nComplex Complex::operator*(Complex b) {\n  Complex t;\n  t.r = r * b.r - i * b.i;\n  t.i = r * b.i + i * b.r;\n  return t;\n}\n//求复数相除的算法\nComplex Complex::operator/(Complex b) {\n  Complex t;\n  double x;\n  x = 1 / (b.r * b.r + b.i * b.i);\n  t.r = x * (r * b.r + i * b.i);\n  t.i = x * (i * b.r - r * b.i);\n  return t;\n}\nvoid Complex::display() {\n  cout << r;\n  if (i > 0)\n    cout << \"+\";\n  if (i != 0)\n    cout << i << \"i\" << endl;\n  //}display();\n};\n\nint main(void) {\n  Complex c1(1, 2), c2(3, 4), c3, c4, c5, c6;\n  Complex a, b(2, 3);\n  a = b + 2; //正确\n  // a=2+b;       \t\t\t//错误\n  a.display();\n  c3 = c1 + c2;\n  c4 = c1 - c2;\n  c5 = c1 * c2;\n  c6 = c1 / c2;\n  c1.display();\n  c2.display();\n  c3.display();\n  c4.display();\n  c5.display();\n  c6.display();\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/brackets/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day7/brackets:brac`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"brac\",\n    srcs = [\"brac.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/brackets/brac.cpp",
    "content": "/* 重载()的时钟.cpp */\n#include <iostream>\nusing namespace std;\nclass Time {\nprivate:\n  int hh, mm, ss;\n\npublic:\n  Time(int h = 0, int m = 0, int s = 0) : hh(h), mm(m), ss(s) {}\n  void operator()(int h, int m, int s) {\n    hh = h;\n    mm = m;\n    ss = s;\n  }\n  void ShowTime() { cout << hh << \":\" << mm << \":\" << ss << endl; }\n};\nint main() {\n  Time t1(12, 10, 11);\n  t1.ShowTime();\n  t1.operator()(23, 20, 34);\n  t1.ShowTime();\n  t1(10, 10, 10);\n  t1.ShowTime();\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/equal_operator/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day7/equal_operator:equal_operator`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"equal_operator\",\n    srcs = [\"equal_operator.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/equal_operator/equal_operator.cpp",
    "content": "/* =运算符重载.cpp */\n//例题ch.cppi\n#include <iostream>\nusing namespace std;\nclass X {\npublic:\n  X &operator=(const X &x) {\n    cout << \"a:\";\n    return *this;\n  };\n};\nint main() {\n  X obj1, obj2, obj3;\n  obj1 = obj2;          //调用重载“=”\n  obj1.operator=(obj2); //调用重载“=”\n  obj1 = obj2 = obj3;   //调用重载“=”\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/example/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day7/example:example`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"example\",\n    srcs = [\"example.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/example/example.cpp",
    "content": "//设计一个字符串类String，通过运算符重载实现字符串的输入、输出以及+=、==、!=、<、>、>=、[\n//]等运算。\n#include <cstring>\n#include <iostream>\nusing namespace std;\nclass String {\nprivate:\n  int length; //字符串长度\n  char *sPtr; //存放字符串的指针\n  void setString(const char *s2);\n  friend ostream &operator<<(ostream &os, const String &s) {\n    return os << s.sPtr;\n  };\n  friend istream &operator>>(istream &is, String &s) {\n    return is >> s.sPtr;\n  }; //重载输入运算符\npublic:\n  String(const char * = \"\");\n  const String &operator=(const String &R) {\n    length = R.length;\n    strcpy(sPtr, R.sPtr);\n    return *this;\n  };                                         //重载赋值运算符 =\n  const String &operator+=(const String &R); //字符串的连接 +=\n  bool operator==(const String &R);          //字符串的相等比较 ==\n  bool operator!=(const String &R);          //字符串的不等比较 !=\n  bool operator!();                          //判定字符串是否为空\n  bool operator<(const String &R) const;     //字符串的小于比较 <\n  bool operator>(const String &R);           //字符串的大于比较 >\n  bool operator>=(const String &R);          //字符串的大于等于比较\n  char &operator[](int);                     //字符串的下标运算\n  ~String(){};\n};\nconst String &String::operator+=(const String &R) {\n  char *temp = sPtr;\n  length += R.length;\n  sPtr = new char[length + 1];\n  strcpy(sPtr, temp);\n  strcat(sPtr, R.sPtr);\n  delete[] temp;\n  return *this;\n}\nString::String(const char *str) {\n  sPtr = new char[strlen(str) + 1];\n  strcpy(sPtr, str);\n  length = strlen(str);\n};\nbool String::operator==(const String &R) { return strcmp(sPtr, R.sPtr) == 0; }\nbool String::operator!=(const String &R) { return !(*this == R); }\nbool String::operator!() { return length == 0; }\nbool String::operator<(const String &R) const {\n  return strcmp(sPtr, R.sPtr) < 0;\n}\nbool String::operator>(const String &R) { return R < *this; }\nbool String::operator>=(const String &R) { return !(*this < R); }\nchar &String::operator[](int subscript) { return sPtr[subscript]; }\nint main() {\n  String s1(\"happy\"), s2(\"new year\"), s3;\n  cout << \"s1 is \" << s1 << \"\\ns2 is \" << s2 << \"\\ns3 is \" << s3\n       << \"\\n比较s2和s1:\"\n       << \"\\ns2 ==s1结果是 \" << (s2 == s1 ? \"true\" : \"false\")\n       << \"\\ns2 != s1结果是 \" << (s2 != s1 ? \"true\" : \"false\")\n       << \"\\ns2 >  s1结果是 \" << (s2 > s1 ? \"true\" : \"false\")\n       << \"\\ns2 <  s1结果是 \" << (s2 < s1 ? \"true\" : \"false\")\n       << \"\\ns2 >= s1结果是 \" << (s2 >= s1 ? \"true\" : \"false\");\n  cout << \"\\n\\n测试s3是否为空: \";\n  if (!s3) {\n    cout << \"s3是空串\" << endl; // L3\n    cout << \"把s1赋给s3的结果是：\";\n    s3 = s1;\n    cout << \"s3=\" << s3 << \"\\n\"; // L5\n  }\n  cout << \"s1 += s2 的结果是：s1=\"; // L6\n  s1 += s2;\n  cout << s1; // L7\n\n  cout << \"\\ns1 +=  to you 的结果是：\"; // L8\n  s1 += \" to you\";\n  cout << \"s1 = \" << s1 << endl; // L9\n  s1[0] = 'H';\n  s1[6] = 'N';\n  s1[10] = 'Y';\n  cout << \"s1 = \" << s1 << \"\\n\"; // L10\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/index_parentheses/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day7/subscript_operator:example`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"exexampleample1\",\n    srcs = [\"example.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/index_parentheses/example.cpp",
    "content": "/* []与() 运算符重载 */\n#include <iostream>\nusing namespace std;\nclass X {\npublic:\n  int operator()(int i = 0) {\n    cout << \"X::operator(\" << i << \")\" << endl;\n    return i;\n  };\n  int operator()(int i, int j) {\n    cout << \"X::operator(\" << i << \",\" << j << \")\" << endl;\n    return i;\n  };\n  int operator[](int i) {\n    cout << \"X::operator[\" << i << \"]\" << endl;\n    return i;\n  };\n  int operator[](char *cp) {\n    cout << \"X::operator[\" << cp << \"]\" << endl;\n    return 0;\n  };\n};\nint main(void) {\n  X obj;\n  int i = obj(obj(1), 2);\n  int a = obj[i];\n  int b = obj[\"abcd\"];\n  cout << \"a=\" << a << endl;\n  cout << \"b=\" << b << endl;\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/readme.md",
    "content": "运算符重载是C++的一项强大功能。通过重载，可以扩展C++运算符的功能，使它们能够操作用户自定义的数据类型，增加程序代码的直观性和可读性。\n\n本章主要介绍 类成员运算符重载与友元运算符重载,  二元运算符与一元运算符重载,  运算符++、--、[]、（）重载,  this指针与运算符重载及 流运算符<<和>>的重载\n\n一、重载二元运算符\n\n1、二元运算符的调用形式与解析\n\naa@bb    可解释成  aa.operator@(bb)\n               或解释成 operator@(aa,bb)\n\n如果两者都有定义,就按照重载解析\n\n```c++\nclass X{\npublic:\n\tvoid operator+(int);\n\tX(int);\n};\nvoid operator+(X,X);\nvoid operator+(X,double);\n```\n2、类运算符重载形式\n\n（1）非静态成员运算符重载\n\n以类成员形式重载的运算符参数比实际参数少一个，第1个参数是以this指针隐式传递的。 \n\n```c++\nclass Complex{\n\t\tdouble real,image;\npublic:\n\t\tComplex operator+(Complex b){……}\n......\n};\n```\n（2） 友元运算符重载\n\n如果将运算符函数作为类的友元重载，它需要的参数个数就与运算符实际需要的参数个数相同。比如，若用友元函数重载Complex类的加法运算符，则形式如下：\n\n```c++\nclass Complex{\n……\n\t\tfriend Complex operator+(Complex a,Complex b);\t\t//声明\n//......\n};\n\nComplex  operator+(Complex a,Complex b){……}     \t\t//定义\n```\n\n二、重载一元运算符 \n\n1、一元运算符\n\n一元运算符只需要一个运算参数，如取地址运算符（&）、负数（?）、自增加（++）等。\n\n2、一元运算符常见调用形式为：\n\n@a     或    a@ \t\t//隐式调用形式\n\na.operator@()                   \t// 显式调用一元运算符@\n\n其中的@代表一元运算符，a代表操作数。\n\n@a代表前缀一元运算，如“++a”；\n\na@表示后缀运算，如“a++”。\n\n3、@a将被C++解释为下面的形式之一\n\na.operator@()\n\noperator@(a) \n\n4.一元运算符作为类成员函数重载时不需要参数，其形式如下：\n\n```c++\nclass X{\n……\n\t\tT operator@(){……};\n}\n```\nT是运算符@的返回类型。从形式上看，作为类成员函数重载的一元运算符没有参数，但实际上它包含了一个隐含参数\n\n即调用对象的this指针。\n\n5.前自增(减)与后自增(减)\n\nC++编译器可以通过在运算符函数参数表中是否插入关键字int 来区分这两种方式\n\n```c++\n//前缀\noperator -- ();\noperator -- (X & x);\n//后缀\noperator -- (int);\noperator -- (X & x, int);\n```\n三、重载赋值运算符=\n\n1、赋值运算符“=”的重载特殊性\n\n赋值运算进行时将调用此运算符\n\n只能用成员函数重载\n\n如果需要而没有定义时，编译器自动生成，该版本进行bit-by-bit拷贝\n\n四、重载赋值运算符[]\n\n1、[ ]是一个二元运算符，其重载形式如下：\n\n```c++\nclass X{\n……\n\t\tX& operator[](int n);\n};\n```\n2、重载[]需要注意的问题\n- []是一个二元运算符，其第1个参数是通过对象的this指针传递的，第2个参数代表数组的下标\n- 由于[]既可以出现在赋值符“=”的左边，也可以出现在赋值符“=”的右边，所以重载运算符[]时常返回引用。\n- **[]只能被重载为类的非静态成员函数，不能被重载为友元和普通函数**。\n\n五、重载( ) \n\n1、运算符( )是函数调用运算符，也能被重载。且只能被重载为类的成员函数。\n\n2、运算符( )的重载形式如下：\n\n```c++\nclass X{\n……\n\t\tX& operator( )(参数表);\n}；\n```\n\n其中的参数表可以包括任意多个参数。\n\n3、运算符( )的调用形式如下：\n\nX Obj;              \t\t//对象定义\n\nObj()(参数表);  \t\t//调用形式1\n\nObj(参数表);       \t\t//调用形式2"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/subscript_operator/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day7/subscript_operator:subscript_operator`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"subscript_operator\",\n    srcs = [\"subscript_operator.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/subscript_operator/subscript_operator.cpp",
    "content": "/* 重载[]运算符 */\n// Eg8-9.cpp\n#include <cstring>\n#include <iostream>\nusing namespace std;\nstruct Person { //职工基本信息的结构\n  double salary;\n  char *name;\n};\nclass SalaryManaege {\n  Person *employ; //存放职工信息的数组\n  int max;        //数组下标上界\n  int n;          //数组中的实际职工人数\npublic:\n  SalaryManaege(int Max = 0) {\n    max = Max;\n    n = 0;\n    employ = new Person[max];\n  }\n  double &operator[](char *Name) { //重载[]，返回引用\n    Person *p;\n    for (p = employ; p < employ + n; p++)\n      //如果存在处理\n      if (strcmp(p->name, Name) == 0)\n        return p->salary;\n    //不存在情况处理\n    p = employ + n++;\n    p->name = new char[strlen(Name) + 1];\n    strcpy(p->name, Name);\n    p->salary = 0;\n    return p->salary;\n  }\n\n  void display() {\n    for (int i = 0; i < n; i++)\n      cout << employ[i].name << \"   \" << employ[i].salary << endl;\n  }\n};\nint main() {\n  SalaryManaege s(3);\n  s[\"张三\"] = 2188.88;\n  s[\"里斯\"] = 1230.07;\n  s[\"王无\"] = 3200.97;\n  cout << \"张三\\t\" << s[\"张三\"] << endl;\n  cout << \"里斯\\t\" << s[\"里斯\"] << endl;\n  cout << \"王无\\t\" << s[\"王无\"] << endl;\n\n  cout << \"-------下为display的输出--------\\n\\n\";\n  s.display();\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/unary_operator/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day7/unary_operator:time_increase`\n# please run `bazel run //practical_exercises/10_day_practice/day7/unary_operator:time_counter`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"time_increase\",\n    srcs = [\"time_increase.cpp\"],\n)\ncc_binary(\n    name = \"time_counter\",\n    srcs = [\"time_counter.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/unary_operator/time_counter.cpp",
    "content": "/* 计数器前后自增.cpp */\n//设计一个计数器counter，用类成员重载自增运算符实现计数器的自增，用友元重载实现计数器的自减。\n#include <iostream>\nusing namespace std;\nclass Counter {\nprivate:\n  int n;\n\npublic:\n  Counter(int i = 0) : n(i){};\n  Counter operator++();\n  Counter operator++(int);\n  friend Counter operator--(Counter &c);\n  friend Counter operator--(Counter &c, int);\n  void display();\n};\nCounter Counter::operator++() {\n  ++n;\n  return *this;\n}\nCounter Counter::operator++(int) {\n  Counter t = *this;\n  n++;\n  return t;\n}\nCounter operator--(Counter &c) {\n  --c.n;\n  return c;\n}\nCounter operator--(Counter& c, int) {\n    Counter t(c.n);\n    c.n--;\n    return t;\n}\nvoid Counter::display() { cout << \"counter number=\" << n << endl; }\nint main(int argc, char const *argv[]) {\n  Counter a;\n  ++a;\n  a.display();\n  a++;\n  a.display();\n  --a;\n  a.display();\n  a--;\n  a.display();\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day7/unary_operator/time_increase.cpp",
    "content": "/* 秒钟自增运算.cpp */\n//设计一个时间类Time，它能够完成秒钟的自增运算。\n\n#include <iostream>\nusing namespace std;\n\nclass Time {\nprivate:\n  int hour, minute, second;\n\npublic:\n  Time(int h, int m, int s);\n  Time operator++();\n  //友元重载需要参数\n  friend Time operator--(Time &t);\n  void display();\n};\n\nTime::Time(int h, int m, int s) {\n  hour = h;\n  minute = m;\n  second = s;\n  if (hour >= 24)\n    hour = 0;\n  if (minute >= 60)\n    minute = 0;\n  if (second >= 60)\n    second = 0;\n}\n\nTime Time::operator++() {\n  ++second;\n  if (second >= 60) {\n    second = 0;\n    ++minute;\n    if (minute >= 60) {\n      minute = 0;\n      ++hour;\n      if (hour >= 24)\n        hour = 0;\n    }\n  }\n  return *this;\n}\nTime operator--(Time &t) {\n  --t.second;\n  if (t.second < 0) {\n    t.second = 59;\n    --t.minute;\n    if (t.minute < 0) {\n      t.minute = 59;\n      --t.hour;\n      if (t.hour < 0)\n        t.hour = 23;\n    }\n  }\n  return t;\n}\nvoid Time::display() { cout << hour << \":\" << minute << \":\" << second << endl; }\nint main(int argc, char const *argv[]) {\n  Time t1(23, 59, 59);\n  t1.display();\n  ++t1; //隐式调用\n  t1.display();\n  t1.operator++(); //显式调用\n  t1.display();\n  Time t2(24, 60, 60);\n  t2.display();\n  ++t2;\n  t2.display();\n  --t2;\n  t2.display();\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day8/class_template/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day8/class_template:stack`\n# please run `bazel run //practical_exercises/10_day_practice/day8/class_template:spec`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"stack\",\n    srcs = [\"stack.cpp\"],\n)\ncc_binary(\n    name = \"spec\",\n    srcs = [\"spec.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day8/class_template/spec.cpp",
    "content": "/* 类模板特化.cpp */\n//设计一通用数组类，它能够直接存取数组元素，并能够对数组进行从大到小的排序。\n\n#include <cstring>\n#include <iostream>\nusing namespace std;\nconst int Size = 5;\ntemplate <class T> class Array {\nprivate:\n  T a[Size];\n\npublic:\n  Array() {\n    for (int i = 0; i < Size; i++) {\n      a[i] = 0;\n    }\n  }\n  T &operator[](int i);\n  void Sort();\n};\n\ntemplate <class T> T &Array<T>::operator[](int i) {\n  if (i < 0 || i > Size - 1) {\n    cout << \"\\n数组下标越界！\" << endl;\n    exit(1);\n  }\n  return a[i];\n}\n\ntemplate <class T> void Array<T>::Sort() {\n  int p;\n  for (int i = 0; i < Size - 1; i++) {\n    p = i;\n    for (int j = i; j < Size; j++) {\n      if (a[p] < a[j])\n        p = j;\n    }\n    T t = a[p];\n    a[p] = a[i];\n    a[i] = t;\n  }\n}\n// template <> 返回类型 类模板名<特化的数据类型>::特化成员函数名(参数表){}\ntemplate <> void Array<char *>::Sort() {\n  int p;\n  for (int i = 0; i < Size - 1; i++) {\n    p = i;\n    for (int j = i + 1; j < Size; j++)\n      if (strcmp(a[p], a[j]) < 0)\n        p = j;\n    char *t = a[p];\n    a[p] = a[i];\n    a[i] = t;\n  }\n}\n\nint main() {\n  Array<int> a1;\n  Array<char *> b1;\n  a1[0] = 1;\n  a1[1] = 23;\n  a1[2] = 6;\n  a1[3] = 3;\n  a1[4] = 9;\n  a1.Sort();\n  for (int i = 0; i < 5; i++)\n    cout << a1[i] << \"\\t\";\n  cout << endl;\n  b1[0] = \"x1\";\n  b1[1] = \"ya\";\n  b1[2] = \"ad\";\n  b1[3] = \"be\";\n  b1[4] = \"bc\";\n  b1.Sort();\n  for (int i = 0; i < 5; i++)\n    cout << b1[i] << \"\\t\";\n  cout << endl;\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day8/class_template/stack.cpp",
    "content": "/* 模拟栈.cpp */\n/*\n设计一个堆栈的类模板Stack，在模板中用类型参数T表示栈中存放的数据，用非类型参数MAXSIZE代表栈的大小。\n*/\n#include <iostream>\nusing namespace std;\n\ntemplate <class T, int MAXSIZE> class Stack {\nprivate:\n  T elem[MAXSIZE];\n  int top;\n\npublic:\n  Stack() { top = 0; };\n  void push(T e);\n  T pop();\n  bool empty() {\n    if (top <= -1)\n      return 1;\n    else\n      return 0;\n  }\n  void setEmpty() { top = -1; }\n  bool full() {\n    if (top >= MAXSIZE - 1) {\n      return 1;\n    } else\n      return 0;\n  }\n};\n/*\n原型：\ntemplate <模板参数列表>\n返回值类型 类模板名<模板参数名表>::成员函数名 (参数列表){};\n*/\ntemplate <class T, int MAXSIZE> void Stack<T, MAXSIZE>::push(T e) {\n  if (full()) {\n    cout << \"栈已满，不能再添加元素了！\";\n    return;\n  }\n  elem[++top] = e;\n}\n\ntemplate <class T, int MAXSIZE> T Stack<T, MAXSIZE>::pop() {\n  if (empty()) {\n    cout << \"栈已空，不能再弹出元素了！\" << endl;\n    return 0;\n  }\n  return elem[top--];\n}\n\nint main(int argc, char const *argv[]) {\n  //类模板实例化\n  Stack<int, 10> iStack;\n  Stack<char, 10> cStack;\n  iStack.setEmpty();\n  cStack.setEmpty();\n  cout << \"-------intStack----\\n\";\n  int i;\n  for (i = 1; i < 11; i++)\n    iStack.push(i);\n  for (i = 1; i < 11; i++)\n    cout << iStack.pop() << \"\\t\";\n  cout << \"\\n\\n-------charStack----\\n\";\n  cStack.push('A');\n  cStack.push('B');\n  cStack.push('C');\n  cStack.push('D');\n  cStack.push('E');\n  for (i = 1; i < 6; i++)\n    cout << cStack.pop() << \"\\t\";\n  cout << endl;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day8/func/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day8/func:main`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:max_spec`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:max`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:sort`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"main\",\n    srcs = [\"main.cpp\"],\n)\ncc_binary(\n    name = \"max_spec\",\n    srcs = [\"max_spec.cpp\"],\n)\ncc_binary(\n    name = \"max\",\n    srcs = [\"max.cpp\"],\n)\ncc_binary(\n    name = \"sort\",\n    srcs = [\"sort.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day8/func/main.cpp",
    "content": "/* 重要.cpp */\n// Eg9-1.cpp\n#include <iostream>\n//注意一点，max与min使用的时候，容易引起冲突，如果写了下面这一行代码，则要改变函数模板名字，否则直接使用std::cout与std::endl\nusing namespace std;\n/*\n不要把这里的class与类的声明关键字class混淆在一起，虽然它们由相同的字母组成，但含义是不同的。\n这里的class表示T是一个类型参数，可以是任何数据类型，如int、float、char等，或者用户定义的struct、enum或class等自定义数据类型。\n*/\ntemplate <class T> T Min(T a, T b) { return (a < b) ? a : b; }\n/*\n为了区别类与模板参数中的类型关键字class，标准C++提出?了用typename作为模板参数的类型关键字，同时也支持使用class。\n比如，把min定义的template <class T>写成下面的形式是完全等价的：\n*/\ntemplate <typename T> T myMin(T a, T b) { return (a < b) ? a : b; }\n\n/*\n模板实例化发生在调用模板函数时。当编译器遇到程序中对函数模板的调用时，\n它才会根据调用语句中实参的具体类型，确定模板参数的数据类型，\n并用此类型替换函数模板中的模板参数，生成能够处理该类型的函数代码，即模板函数。\n当多次发生类型相同的参数调用时，只在第1次进行实例化。编译器只在第1次调用时生成模板函数，\n当之后遇到相同类型的参数调用时，不再生成其他模板函数，它将调用第1次实例化生成的模板函数。\n*/\nint main() {\n  double a = 2, b = 3.4;\n  float c = 2.3, d = 3.2;\n  cout << \"2，3    的最小值是：\" << Min<int>(2, 3) << endl; //显式调用\n  cout << \"2，3.4  的最小值是：\" << Min(a, b) << endl;      //隐式调用\n  cout << \"'a'，'b'\t  的最小值是：\" << Min('a', 'b') << endl;\n  cout << \"2.3，3.2的最小值是：\" << Min(c, d) << endl;\n  cout << \"2.3，3.2的最大值是：\" << std::min(c, d)\n       << endl; //引用命名空间内部的最小值函数\n  cout << \"2.3，3.2的最小值是：\" << myMin(c, d) << endl; //更换class为typename\n  // cout<<\"2，'a'    的最小值是：\"<<Min(2,'a')<<endl;\n  // //报错,不同类型无法处理，请看9-3-1.cpp\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day8/func/max.cpp",
    "content": "/* 求最大值.cpp */\n// Eg9-2.cpp\n#include <iostream>\nusing namespace std;\ntemplate <class T> T Max(T a, T b) { return (a > b) ? a : b; }\n/*\nC++在实例化函数模板的过程中，只是简单地将模板参数替换成调用实参的类型，并以此生成模板函数，不会进行参数类型的任何转换。\n*/\nint main() {\n  double a = 2, b = 3.4;\n  float c = 5.1, d = 3.2;\n  //在模板调用时进行参数类型的强制转换\n  cout << \"2, 3.2    的最大值是：\" << Max(double(2), 3.2) << endl;\n  cout << \"a, c    的最大值是：\" << Max(float(a), c) << endl;\n  //显示指定函数模板实例化的参数类型\n  cout << \"'a', 3    的最大值是：\" << Max<int>('a', 3) << endl;\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day8/func/max_spec.cpp",
    "content": "/* 模板特化.cpp */\n// Eg9-6.cpp\n#include <cstring>\n#include <iostream>\nusing namespace std;\ntemplate <class T> T Max(T a, T b) { return (a > b) ? a : b; }\n//特化\n// template <> 返回类型 函数名<特化的数据类型>(参数表) {}\ntemplate <> const char *Max<const char *>(const char *a, const char *b) {\n  return (strcmp(a, b) >= 0) ? a : b;\n}\ntemplate <> char *Max<char *>(char *a, char *b) {\n  return (strcmp(a, b) >= 0) ? a : b;\n}\nint main() {\n  float c = 5.1, d = 3.2;\n  char s1[] = \"xyce\", s2[] = \"xbv\";\n  cout << \"2,3的最大值是：\" << Max(3, 2) << endl;\n  cout << \"c,d的最大值是：\" << Max(c, d) << endl;\n  cout << Max(\"xbv\", \"xyce\") << endl;\n  cout << Max(s1, s2) << endl;\n  \n}\n/*\n① 当程序中同时存在模板和它的特化时，特化将被优先调用；\n②\n在同一个程序中，除了函数模板和它的特化外，还可以有同名的普通函数。其区别在于C++会对普通函数的调用实参进行隐式的类型转换，\n但不会对模板函数及特化函数的参数进行任何形式的类型转换。\n\n*/"
  },
  {
    "path": "practical_exercises/10_day_practice/day8/func/sort.cpp",
    "content": "/* 例1.cpp */\n//函数模板参数可以是类属参数，也可以包括普通类型的参数\n#include <iostream>\nusing namespace std;\n\ntemplate <class T>\n//实现降序\nvoid sort(T *a, int n) {\n  for (int i = 0; i < n; i++) {\n    int p = i;\n    for (int j = i; j < n; j++)\n      if (a[p] < a[j])\n        p = j;\n    T t = a[i];\n    a[i] = a[p];\n    a[p] = t;\n  }\n}\ntemplate <class T> void display(T &a, int n) {\n  for (int i = 0; i < n; i++)\n    cout << a[i] << \"\\t\" << endl;\n}\n\nint main(int argc, char const *argv[]) {\n  int a[] = {1, 41, 2, 5, 8, 21, 23};\n  char b[] = {'a', 'x', 'y', 'e', 'q', 'g', 'o', 'u'};\n  sort(a, 7);\n  sort(b, 8);\n  display(a, 7);\n  display(b, 8);\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day8/readme.md",
    "content": "ģ壨templateC++ʵִûƵҪߣǷͼ޹صͨóƼĻ\nģC++Խµ?Իƣʵ޹صͨ㷨ƣܹЧʣǳ븴õǿߡ \n\nҪ˺ģģ࣬ԼSTLеļģ͡\nһģ \n1ģ\nģǶԾͬԵĺٳģһֲ̬ԵĹߣΪ߼ͬͲͬĳṩһִ빲Ļơ\nһģ岢һʵʵڵĺ࣬һǲĺࡣ\n2ģ\nģ\nģ\n3.ģģ庯\nģṩһͨõĺΪúΪöֲͬͽеãݵԶʵΪ͵ĺ룬Ҳ˵ģһ塣\nͨȣģĳЩԪصδȷģЩԪصͽʹʱغȣģ岻ҪԱظд룬Զ๦ͬͷֵͲͬĺ\nģ\n1ģĶ\n```c++\ntemplate <class T1, class T2,> (){\n   \t \t\t\t\t\t\t\t\t//ģ嶨\n}\n```\ntemplateǶģĹؼ֣дһ<>еT1T2ģеclassʾĲ͡\nģΪͲģʵģ庯ʱʱҪݵʵһͣintdouble֮ࡣ\nģĲгģT1T2\n2ʹúģע\n ڶģʱtemplate뺯ģ嶨֮κ䡣\n```c++\ntemplate <class T>\nint x;                 //󣬲ڴλκ\nT min(T a,T b){}\n```\n ģжͲÿͲùؼclasstypename޶⣬ģлԳȷͲΪͲ\n```c++\ntemplate <class T1,class T2,class T3,int T4>\nT1 fx(T1 a, T 2 b, T3 c){}\n```\nڴʵʱͲT4ֻʹó\n ҪclassؼclassһȻͬĸɣǲͬġclassʾTһͲκͣintfloatcharȣûstructenumclassԶ͡\n Ϊģе͹ؼclass׼C++?typenameΪģ͹ؼ֣ͬʱҲ֧ʹclass磬mintemplate <class T>дʽȫȼ۵ģ\n```c++\ntemplate <typename T> \nT min(T a,T b){}\n```\n\n3.ģʵ\n- ʵʱ\nģʵڵģ庯ʱжԺģĵʱŻݵʵεľͣȷģͣô滻ģеģܹ͵ĺ룬ģ庯\n- ηͬĲʱֻڵ1νʵĺã\n```c++\nint x=min(2,3);     \nint y=min(3,9);\nint z=min(8.5);\n```\nֻڵ1εʱģ庯֮ͬ͵Ĳʱģ庯õ1ʵɵģ庯\n\n- ʵķʽ\n\n**ʽʵ**\n\nܹжģʱԶʵģΪģ庯\n```c++\ntemplate <typename T> T max (T, T);\n\nint i = max (1, 2); \nfloat f = max (1.0, 2.0);\nchar ch = max (a, A);\n\n```\nʽʵڵģ壬ʵǵʵ\n\n**ʾʵexplicit instantiation**\nʱ,жģͻֵ\nҪʹضʵ\n﷨ʽ::\nģ<,,ֵ,> ()\n```c++\ntemplate <class T> T max (T, T);\n\nint i = max (1, 2); \n// error: data type cant be deduced\nint i = max<int> (1, 2);\n\n```\n4.ģػ\n- ػԭ\nĳЩ£ģͨ㷨ʺضĳϣ͵ȣ\n磺max\n```c++\nchar * cp = max (abcd, 1234);\nʵΪchar * max (char * a, char * b){return a > b ? a : b;}\n```\n϶ģΪַıȽΪ\n```c++\nchar * max (char * a, char * b)\n{\treturn strcmp(a, b)>0 ? a : b;   }\n```\n- ػ\nνػģ岻ܴͣдģͬ⺯רŴЩ͡\nģػĶʽ\ntemplate <>  <ػ>() {\n\t\t \t\t\t\t\t\t\t\n}\n˵\n template < >ģػĹؼ֣< >вҪκݣ\n < >Ҫػ͡\n\n5.˵\n ͬʱģػʱػȵã\n ͬһУ˺ģػ⣬ͬͨC++ͨĵʵνʽתģ庯ػĲκʽת\n6.˳\nͬһоģͨʱƥ˳£\nȫƥķģ庯\nȫƥģ庯\nݵķģ庯\nģ\n1.ģĸ\nģƽṹͳԱȫͬͲͬͨࡣ\nջ\n˫ջ\n```c++\nclass doubleStack{\n    private:\n    double data[size];\n    \n};\n```\nַջ\n```c++\nclass charStack{\n    private:\n    char data[size];\n    \n};\n```\nЩջ֮⣬ȫͬͿģʵ֡\n\n2.ģ\n```c++\ntemplate<class T1,class T2,>\nclass {\n    \t\t\t\t\t\t\t\t\t// Ա붨\n}\n```\nT1T2Ͳ\nģпжģͲͷͲ\n\n3.Ͳ\n\nͲָĳ־ͣڵģʱֻΪṩӦ͵ĳֵͲƵģͨ͡ö͡ãԼ󡢺Աָ룬**øͣ˫ͣvoidΪͲ**\n\nģУT1T2ͲT3ǷͲ\n```c++\ntemplate<class T1,class T2,int T3>\n```\nʵʱΪT1T2ṩһͣΪT3ָһ10ģܱȷʵ\n\n4.ģĳԱĶ\n1ģⶨ壬﷨\ntemplate <ģб> \u000bֵ ģ<ģ>::Ա (б)\u000b{\u000b\t\u000b};\n2Ա壬볣ԱĶƣ\nģбġͱʶΪʹ\nģбġͨͳΪʹ\n5.ģػ\n\nػ\n9-9ķػģͬΪĳרдһģࡣ \nģػʽһػģ壬һػԱ \nػԱķ\n```c++\ntemplate <>  ģ<ػ>::ػԱ(){\n    \t\t\t\t\t\t\t\t//\n}\n```\n\n```c++\ntemplate<>  void Array<char *>::Sort(){\n    for(int i=0;i<Size-1;i++){\n        int p=i;\n        for(int j=i+1;j<Size;j++)\n            if(strcmp(a[p],a[j])<0)\n                p=j;\n        char* t=a[p];\n        a[p]=a[i];\n        a[i]=t;\n    }\n}\n```"
  },
  {
    "path": "practical_exercises/10_day_practice/day8/stl/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day8/stl:map`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"map\",\n    srcs = [\"map.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day8/stl/map.cpp",
    "content": "#include <iostream>\n#include <map>\n#include <string>\nusing namespace std;\n\nint main(int argc, char const *argv[]) {\n  string name[] = {\"张三\", \"李四\", \"王麻子\"};\n  double salary[] = {1200, 2000, 1450};\n  map<string, double> sal;\n  map<string, double>::iterator p;\n  for (int i = 0; i < 3; i++) {\n    sal.insert(make_pair(name[i], salary[i]));\n  }\n  sal[\"tom\"] = 6156;\n  sal[\"bob\"] = 5999;\n  for (p = sal.begin(); p != sal.end(); p++) {\n    cout << p->first << \"\\t\" << p->second << endl;\n  }\n  string person;\n  cout << \"输入查找人员的姓名:\";\n  cin >> person;\n  int flag = 1;\n  for (p = sal.begin(); p != sal.end(); p++)\n    if (p->first == person) {\n      cout << p->second << endl;\n      flag = 0;\n    }\n  if (flag)\n    cout << \"没查找到对应的结果！\" << endl;\n\n  cout << \"输入待删除的人员的姓名:\";\n  cin >> person;\n  map<string, double>::iterator it;\n  it = sal.find(person);\n  if (it != sal.end()) {\n    cout << \"查找成功：\" << (*it).first << \":\" << (*it).second << endl;\n    sal.erase(it);\n    cout << \"删除成功\" << endl;\n  }\n  cout << \"删除后的结果为\" << endl;\n  for (p = sal.begin(); p != sal.end(); p++) {\n    cout << p->first << p->second << endl;\n  }\n\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/exception/1.cpp",
    "content": "// Eg10-1.cpp\n#include <iostream>\nusing namespace std;\nint main() {\n  cout << \"1--befroe try block...\" << endl;\n  try {\n    cout << \"2--Inside try block...\" << endl;\n    throw 10;\n    cout << \"3--After throw ....\" << endl;\n  } catch (int i) {\n    cout << \"4--In catch block1 ... exception..errcode  is..\" << i << endl;\n  } catch (char *s) {\n    cout << \"5--In catch block2 ... exception..errcode is..\" << s << endl;\n  }\n  cout << \"6--After Catch...\";\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/exception/10.cpp",
    "content": "// Eg10-11.cpp\n#include <iostream>\nusing namespace std;\nconst int MAX = 3;\nclass Full {\n  int a;\n\npublic:\n  Full(int i) : a(i) {}\n  int getValue() { return a; }\n};\nclass Empty {};\nclass Stack {\nprivate:\n  int s[MAX];\n  int top;\n\npublic:\n  Stack() { top = -1; }\n  void push(int a) {\n    if (top >= MAX - 1)\n      throw Full(a);\n    s[++top] = a;\n  }\n  int pop() {\n    if (top < 0)\n      throw Empty();\n    return s[top--];\n  }\n};\nint main() {\n  Stack s;\n  try {\n    s.push(10);\n    s.push(20);\n    s.push(30);\n    s.push(40);\n  } catch (Full e) {\n    cout << \"Exception: Stack Full...\" << endl;\n    cout << \"The value not push in stack:\" << e.getValue() << endl;\n  }\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/exception/2.cpp",
    "content": "// Eg10-2.cpp\n#include <iostream>\nusing namespace std;\nint main() {\n  cout << \"1--befroe try block...\" << endl;\n  try {\n    cout << \"2--Inside try block...\" << endl;\n    throw 10;\n    cout << \"3--After throw ....\" << endl;\n  } catch (double i) { //仅此与例10.1不同\n    cout << \"4--In catch block1 .. an int type is..\" << i << endl;\n  }\n  cout << \"5--After Catch...\";\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/exception/3.cpp",
    "content": "#include <iostream>\nusing namespace std;\nvoid temperature(int t) {\n  try {\n    if (t == 100)\n      throw \"It's at the boiling point.\";\n    else if (t == 0)\n      throw \"It reached the freezing point\";\n    else\n      cout << \"the temperature is OK...\" << endl;\n  } catch (int x) {\n    cout << \"temperature=\" << x << endl;\n  } catch (char const *s) {\n    cout << s << endl;\n  }\n}\nint main() {\n  temperature(0);   // L1\n  temperature(10);  // L2\n  temperature(100); // L3\n  \n  return 0;\n}"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/exception/4.cpp",
    "content": "#include <iostream>\nusing namespace std;\nvoid temperature(int t) {\n\n  if (t == 100)\n    throw \"沸点！\";\n  else if (t == 0)\n    throw \"冰点！\";\n  else {\n    cout << \"temperatore=\" << t << endl;\n  }\n}\nint main() {\n  try {\n    temperature(0);   // L1\n    temperature(10);  // L2\n    temperature(100); // L3\n  } catch (char const *s) {\n    cout << s << endl;\n  }\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/exception/5.cpp",
    "content": "// Eg10-5.cpp\n#include <iostream>\nusing namespace std;\n\nvoid handler(int n) throw(int, char, double) {\n  if (n == 1)\n    throw n;\n  if (n == 2)\n    throw 'x';\n  if (n == 3)\n    throw 1.1;\n}\nint main() {\n  cout << \"Before handler...\" << endl;\n  try {\n    handler(1);\n  } catch (int i) {\n    cout << \"catch an integer...\" << endl;\n  } catch (char c) {\n    cout << \"catch an char...\" << endl;\n  } catch (double d) {\n    cout << \"catch an double...\" << endl;\n  }\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/exception/6.cpp",
    "content": "// Eg10-6.cpp\n#include <iostream>\nusing namespace std;\nvoid Errhandler(int n) throw() {\n  try {\n    if (n == 1)\n      throw n;\n    if (n == 2)\n      throw \"dx\";\n    if (n == 3)\n      throw 1.1;\n  } catch (...) {\n    cout << \"catch an exception...\" << endl;\n  }\n}\nint main() {\n  Errhandler(1);\n  Errhandler(2);\n  Errhandler(3);\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/exception/7-1.cpp",
    "content": "// Eg10-9.cpp\n#include <iostream>\nusing namespace std;\nclass A {\n  int a;\n\npublic:\n  A(int i = 0) : a(i) {}\n  ~A() { cout << \"in A destructor...\" << endl; }\n};\nclass B {\n  A obj[3];\n  double *pb[10];\n\npublic:\n  B(int k) {\n    cout << \"int B constructor...\" << endl;\n    for (int i = 0; i < 10; i++) {\n      pb[i] = new double[20000000];\n      if (pb[i] == 0)\n        throw i;\n      else\n        cout << \"Allocated 20000000 doubles in pb[\" << i << \"]\" << endl;\n    }\n  }\n};\nint main() {\n  try {\n    B b(2);\n  } catch (int e) {\n    cout << \"catch an exception when allocated pb[\" << e << \"]\" << endl;\n  }\n  \n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/exception/7.cpp",
    "content": "// Eg10-7.cpp\n#include <iostream>\nusing namespace std;\n//内部再次throw异常的时候，函数不要带throw()\nvoid Errhandler(int n) {\n  try {\n    if (n == 1)\n      throw n;\n    cout << \"all is ok...\" << endl;\n  } catch (int n) {\n    cout << \"catch an int exception inside...\" << n << endl;\n    throw n; //再次抛出本catch捕获的异常\n  }\n}\nint main() {\n  try {\n    Errhandler(1);\n  } catch (int x) {\n    cout << \"catch int an exception in main...\" << x << endl;\n  }\n  cout << \"....End...\" << endl;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/exception/8.cpp",
    "content": "// Eg10-10.cpp\n#include <iostream>\nusing namespace std;\nconst int MAX = 3;\nclass Full {};  // L1  堆栈满时抛出的异常类\nclass Empty {}; // L2  堆栈空时抛出的异常类\nclass Stack {\nprivate:\n  int s[MAX];\n  int top;\n\npublic:\n  void push(int a);\n  int pop();\n  Stack() { top = -1; }\n};\nvoid Stack::push(int a) {\n  if (top >= MAX - 1)\n    throw Full();\n  s[++top] = a;\n}\nint Stack::pop() {\n  if (top < 0)\n    throw Empty();\n  return s[top--];\n}\nint main() {\n  Stack s;\n  try {\n    s.push(10);\n    s.push(20);\n    s.push(30);\n    // s.push(40);\t//L5  将产生栈满异常\n    cout << \"stack(0)= \" << s.pop() << endl;\n    cout << \"stack(1)= \" << s.pop() << endl;\n    cout << \"stack(2)= \" << s.pop() << endl;\n    cout << \"stack(3)= \" << s.pop() << endl; // L6\n  } catch (Full) {\n    cout << \"Exception: Stack Full\" << endl;\n  } catch (Empty) {\n    cout << \"Exception: Stack Empty\" << endl;\n  }\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/exception/9-2.cpp",
    "content": "#include <iostream>\nusing namespace std;\nclass BasicException {\npublic:\n  virtual string Where() { return \"BasicException...\"; }\n};\nclass FileSysException : public BasicException {\npublic:\n  virtual string Where() { return \"FileSysException...\"; }\n};\nclass FileNotFound : public FileSysException {\npublic:\n  virtual string Where() { return \"FileNotFound...\"; }\n};\nclass DiskNotFound : public FileSysException {\npublic:\n  virtual string Where() { return \"DiskNotFound...\"; }\n};\nint main() {\n  try {\n    //        .....  //程序代码\n    DiskNotFound err;\n    throw &err;\n  } catch (BasicException *p) {\n    cout << p->Where() << endl;\n  }\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/exception/9.cpp",
    "content": "// Eg10-12.cpp\n#include <iostream>\nusing namespace std;\nclass BasicException {\npublic:\n  char *Where() { return \"BasicException...\"; }\n};\nclass FileSysException : public BasicException {\npublic:\n  char *Where() { return \"FileSysException...\"; }\n};\nclass FileNotFound : public FileSysException {\npublic:\n  char *Where() { return \"FileNotFound...\"; }\n};\nclass DiskNotFound : public FileSysException {\npublic:\n  char *Where() { return \"DiskNotFound...\"; }\n};\nint main() {\n  try {\n    //         .....  //程序代码\n    throw FileSysException();\n  } catch (DiskNotFound p) {\n    cout << p.Where() << endl;\n  } catch (FileNotFound p) {\n    cout << p.Where() << endl;\n  } catch (FileSysException p) {\n    cout << p.Where() << endl;\n  } catch (BasicException p) {\n    cout << p.Where() << endl;\n  }\n  try {\n    //        .....  //程序代码\n    throw DiskNotFound();\n  } catch (BasicException p) {\n    cout << p.Where() << endl;\n  } catch (FileSysException p) {\n    cout << p.Where() << endl;\n  } catch (DiskNotFound p) {\n    cout << p.Where() << endl;\n  } catch (FileNotFound p) {\n    cout << p.Where() << endl;\n  }\n}\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/exception/BUILD",
    "content": "# please run `bazel run //practical_exercises/10_day_practice/day8/func:3`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:7-1`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:6`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:1`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:9-2`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:10`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:2`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:7`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:5`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:8`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:9`\n# please run `bazel run //practical_exercises/10_day_practice/day8/func:4`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"3\",\n    srcs = [\"3.cpp\"],\n)\ncc_binary(\n    name = \"7-1\",\n    srcs = [\"7-1.cpp\"],\n)\ncc_binary(\n    name = \"6\",\n    srcs = [\"6.cpp\"],\n)\ncc_binary(\n    name = \"1\",\n    srcs = [\"1.cpp\"],\n)\ncc_binary(\n    name = \"9-2\",\n    srcs = [\"9-2.cpp\"],\n)\ncc_binary(\n    name = \"10\",\n    srcs = [\"10.cpp\"],\n)\ncc_binary(\n    name = \"2\",\n    srcs = [\"2.cpp\"],\n)\ncc_binary(\n    name = \"7\",\n    srcs = [\"7.cpp\"],\n)\ncc_binary(\n    name = \"5\",\n    srcs = [\"5.cpp\"],\n)\ncc_binary(\n    name = \"8\",\n    srcs = [\"8.cpp\"],\n)\ncc_binary(\n    name = \"9\",\n    srcs = [\"9.cpp\"],\n)\ncc_binary(\n    name = \"4\",\n    srcs = [\"4.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/10_day_practice/day9/readme.md",
    "content": "# 异常处理\n1.catch捕获异常时，不会进行数据类型的默认转换。\n2.限制异常的方法\n- 当一个函数声明中不带任何异常描述时，它可以抛出任何异常。例如：\n```c++\nint f(int,char);                 //函数f可以抛出任何异常\n```\n- 在函数声明的后面添加一个throw参数表，在其中指定函数可以抛出的异常类型。例如：\n```c++\nint g(int,char)  throw(int,char);  //只允许抛出int和char异常。\n```\n- 指定throw限制表为不包括任何类型的空表，不允许函数抛出任何异常。如：\n```c++\nint h(int,char) throw();//不允许抛出任何异常\n```\n3.捕获所有异常\n在多数情况下，catch都只用于捕获某种特定类型的异常，但它也具有捕获全部异常的能力。其形式如下：\n```c++\ncatch(…) {\n    ……                        //异常处理代码\n}\n```\n4.再次抛出异常\n如是catch块无法处理捕获的异常，它可以将该异常再次抛出，使异常能够在恰当的地方被处理。再次抛出的异常不会再被同一个catch块所捕获，它将被传递给外部的catch块处理。要在catch块中再次抛出同一异常，只需在该catch块中添加不带任何参数的throw语句即可。\n5.异常的嵌套调用\ntry块可以嵌套，即一个try块中可以包括另一个try块，这种嵌套可能形成一个异常处理的调用链。\n"
  },
  {
    "path": "practical_exercises/10_day_practice/readme.md",
    "content": "# 10日c++实战狂练\n\n- [x] [day1](day1)\n\n基本c++语法\n\n- [x] [day2](day2)\n\n递归、结构体、枚举、静态变量等\n\n- [x] [day3](day3)\n\n函数\n\n- [x] [day4](day4)\n\n函数深入\n\n- [x] [day5](day5)\n\n继承多态\n\n- [x] [day6](day6)\n\n虚函数、抽象类\n\n- [x] [day7](day7)\n\n运算符重载\n\n- [x] [day8](day8)\n\n模板与STL\n\n- [x] [day9](day9)\n\n异常\n\n- [x] [day10](day10)\n\n文件与流"
  },
  {
    "path": "practical_exercises/key_exercises/BUILD",
    "content": "# please run `bazel run //practical_exercises/key_exercises:output`\n# please run `bazel run //practical_exercises/key_exercises:clock`\n# please run `bazel run //practical_exercises/key_exercises:read_file`\n# please run `bazel run //practical_exercises/key_exercises:operator_circle`\n# please run `bazel run //practical_exercises/key_exercises:io_operator`\n# please run `bazel run //practical_exercises/key_exercises:stack`\n# please run `bazel run //practical_exercises/key_exercises:try`\n# please run `bazel run //practical_exercises/key_exercises:override`\n# please run `bazel run //practical_exercises/key_exercises:io_operator_overload`\n# please run `bazel run //practical_exercises/key_exercises:array_template`\n# please run `bazel run //practical_exercises/key_exercises:array`\n# please run `bazel run //practical_exercises/key_exercises:map_insert_look`\n# please run `bazel run //practical_exercises/key_exercises:func_temp`\n# please run `bazel run //practical_exercises/key_exercises:bracket_overloading`\n# please run `bazel run //practical_exercises/key_exercises:operator_cast`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"output\",\n    srcs = [\"output.cpp\"],\n)\ncc_binary(\n    name = \"clock\",\n    srcs = [\"clock.cpp\"],\n)\ncc_binary(\n    name = \"read_file\",\n    srcs = [\"read_file.cpp\"],\n)\ncc_binary(\n    name = \"operator_circle\",\n    srcs = [\"operator_circle.cpp\"],\n)\ncc_binary(\n    name = \"io_operator\",\n    srcs = [\"io_operator.cpp\"],\n)\ncc_binary(\n    name = \"stack\",\n    srcs = [\"stack.cpp\"],\n)\ncc_binary(\n    name = \"try\",\n    srcs = [\"try.cpp\"],\n)\ncc_binary(\n    name = \"override\",\n    srcs = [\"override.cpp\"],\n)\ncc_binary(\n    name = \"io_operator_overload\",\n    srcs = [\"io_operator_overload.cpp\"],\n)\ncc_binary(\n    name = \"array_template\",\n    srcs = [\"array_template.cpp\"],\n)\ncc_binary(\n    name = \"array\",\n    srcs = [\"array.cpp\"],\n)\ncc_binary(\n    name = \"map_insert_look\",\n    srcs = [\"map_insert_look.cpp\"],\n)\ncc_binary(\n    name = \"func_temp\",\n    srcs = [\"func_temp.cpp\"],\n)\ncc_binary(\n    name = \"bracket_overloading\",\n    srcs = [\"bracket_overloading.cpp\"],\n)\ncc_binary(\n    name = \"operator_cast\",\n    srcs = [\"operator_cast.cpp\"],\n)\n"
  },
  {
    "path": "practical_exercises/key_exercises/README.md",
    "content": "## 重点实战练习\n\n├── [中括号重载.cpp](bracket_overloading.cpp)\n├── [时钟++运算符重载.cpp](clock.cpp)\n├── [运算符重载之强制转换.cpp](operator_cast.cpp)\n└── [重载圆括号的时钟.cpp](operator_circle.cpp)\n\n├── [函数模板.cpp](func_temp.cpp)\n\n├── [动态数组.cpp](array.cpp)\n\n├── [字典插入与查找.cpp](map_insert_look.cpp)\n\n├── [异常捕获.cpp](try.cpp)\n\n├── [类模板之栈.cpp](stack.cpp)\n\n├── [类模板特化之数组.cpp](array_template.cpp)\n\n├── [继承与封装.cpp](override.cpp)\n\n├── [读写文件综合题.cpp](read_file.cpp)\n├── [输入输出运算符重载.cpp](io_operator_overload.cpp)\n├── [输入输出重载.cpp](io_operator.cpp)\n├── [输出格式.cpp](output.cpp)\n\n"
  },
  {
    "path": "practical_exercises/key_exercises/array.cpp",
    "content": "/* 数组.cpp */\n#include <cstring>\n#include <iostream>\n\nint main() {\n\n  char *sPtr;\n  const char *s = \"hello\";\n  sPtr = new char[strlen(s) + 1];\n  strncpy(sPtr, s, strlen(s));\n  std::cout << sPtr << std::endl;\n  delete sPtr;\n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/key_exercises/array_template.cpp",
    "content": "/* 类模板特化之数组.cpp */\n#include <cstring>\n#include <iostream>\nusing namespace std;\n#define MAXSIZE 5\ntemplate <class T> class Array {\npublic:\n  Array() {\n    for (int i = 0; i < MAXSIZE; i++) {\n      array[i] = 0;\n    }\n  }\n\n  T &operator[](int i);\n  void Sort();\n\nprivate:\n  T array[MAXSIZE];\n};\n\ntemplate <class T> T &Array<T>::operator[](int i) {\n  if (i < 0 || i > MAXSIZE - 1) {\n    cout << \"数组下标越界！\" << endl;\n    exit(0);\n  }\n  return array[i];\n}\n\ntemplate <class T> void Array<T>::Sort() {\n  int p, j;\n  for (int i = 0; i < MAXSIZE - 1; i++) {\n    p = i;\n    for (j = i + 1; j < MAXSIZE; j++) {\n      if (array[p] < array[j])\n        p = j;\n    }\n    T t;\n    t = array[i];\n    array[i] = array[p];\n    array[p] = t;\n  }\n}\ntemplate <> void Array<char *>::Sort() {\n  int p, j;\n  for (int i = 0; i < MAXSIZE - 1; i++) {\n    p = i;\n    for (j = i + 1; j < MAXSIZE; j++) {\n      if (strcmp(array[p], array[j]) < 0)\n        p = j;\n    }\n    char *t = array[i];\n    array[i] = array[p];\n    array[p] = t;\n  }\n}\n\nint main(int argc, char const *argv[]) {\n  Array<int> a1;\n  Array<char *> b1;\n  a1[0] = 1;\n  a1[1] = 23;\n  a1[2] = 6;\n  a1[3] = 3;\n  a1[4] = 9;\n  a1.Sort();\n  for (int i = 0; i < 5; i++)\n    cout << a1[i] << \"\\t\";\n  cout << endl;\n  b1[0] = \"x1\";\n  b1[1] = \"ya\";\n  b1[2] = \"ad\";\n  b1[3] = \"be\";\n  b1[4] = \"bc\";\n  b1.Sort();\n  for (int i = 0; i < 5; i++)\n    cout << b1[i] << \"\\t\";\n  cout << endl;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/key_exercises/bracket_overloading.cpp",
    "content": "/* 中括号重载.cpp */\n#include <cstring>\n#include <iostream>\nusing namespace std;\nstruct Person { //职工基本信息的结构\n  double salary;\n  char *name;\n};\nclass SalaryManaege {\n  Person *employ; //存放职工信息的数组\n  int max;        //数组下标上界\n  int n;          //数组中的实际职工人数\npublic:\n  SalaryManaege(int Max = 0) {\n    max = Max;\n    n = 0;\n    employ = new Person[max];\n  }\n  //返回引用特性是可以直接在放在左值，直接使用\n  double &operator[](char *Name) { //重载[]，返回引用\n    Person *p;\n    for (p = employ; p < employ + n; p++)\n      //如果存在处理\n      if (strcmp(p->name, Name) == 0)\n        return p->salary;\n    //不存在情况处理\n    p = employ + n++;\n    p->name = new char[strlen(Name) + 1];\n    strcpy(p->name, Name);\n    p->salary = 0;\n    return p->salary;\n  }\n\n  void display() {\n    for (int i = 0; i < n; i++)\n      cout << employ[i].name << \"   \" << employ[i].salary << endl;\n  }\n  ~SalaryManaege() { delete[] employ; }\n};\nint main() {\n  SalaryManaege s(3);\n  s[\"张三\"] = 2188.88;\n  s[\"里斯\"] = 1230.07;\n  s[\"王无\"] = 3200.97;\n  cout << \"张三\\t\" << s[\"张三\"] << endl;\n  cout << \"里斯\\t\" << s[\"里斯\"] << endl;\n  cout << \"王无\\t\" << s[\"王无\"] << endl;\n\n  cout << \"-------下为display的输出--------\\n\\n\";\n  s.display();\n}\n"
  },
  {
    "path": "practical_exercises/key_exercises/clock.cpp",
    "content": "/* 时钟++运算符重载.cpp */\n#include <cmath>\n#include <iostream>\nusing namespace std;\n\n/*\n *  时钟类\n */\nclass Clock {\nprivate:\n  int Hour, Minute, Second;\n\npublic:\n  Clock(int h = 0, int m = 0, int s = 0);\n  void ShowTime();\n  Clock &operator++();\n  Clock operator++(int);\n};\n\n/*\n *  时钟类构造函数\n */\nClock::Clock(int h, int m, int s) {\n  if (h >= 0 && h < 24 && m >= 0 && m < 60 && s >= 0 && s < 60) {\n    Hour = h;\n    Minute = m;\n    Second = s;\n  } else\n    cout << \"输入的时间格式错误!\" << endl;\n}\n\n/*\n *  显示时间\n */\nvoid Clock::ShowTime() {\n  cout << Hour << \":\" << Minute << \":\" << Second << endl;\n}\n\n/*\n *  时间递增一秒（重载前缀++运算符）\n */\nClock &Clock::operator++() {\n  Second++;\n  if (Second >= 60) {\n    Second = Second - 60;\n    Minute++;\n    if (Minute >= 60) {\n      Minute = Minute - 60;\n      Hour++;\n      Hour = Hour % 24;\n    }\n  }\n  return *this;\n}\n\n/*\n *  时间递增一秒（重载后缀++运算符）\n */\nClock Clock::operator++(int) {\n  Clock old = *this;\n  ++(*this);\n  return old;\n}\n\n/*\n *  主函数\n */\nint main() {\n  Clock myClock(23, 59, 59);\n  cout << \"初始化显示时间为:\\t\\t\";\n  myClock.ShowTime();\n\n  cout << \"执行myClock++后的时间为:\\t\";\n\n  //先执行ShowTime()，输出myClock=23:59:59，\n  //再执行myClock++，此时myClock=00:00:00\n  (myClock++).ShowTime();\n\n  cout << \"执行++myClock后的时间为:\\t\";\n\n  //先执行++myClock，此时myClock=00:00:01\n  //再执行ShowTime()，输出myClock=00:00:01\n  (++myClock).ShowTime();\n  \n}"
  },
  {
    "path": "practical_exercises/key_exercises/func_temp.cpp",
    "content": "/* 函数模板.cpp */\n#include <cstring>\n#include <iostream>\nusing namespace std;\n\ntemplate <class T> T compareMax(T t1, T t2) { return t1 > t2 ? t1 : t2; }\n\ntemplate <>\nconst char *compareMax<const char *>(const char *s1, const char *s2) {\n  cout << \"[for debug]\"\n       << \" call compareMax template\" << endl;\n  return strcmp(s1, s2) >= 0 ? s1 : s2;\n}\nint main(int argc, char const *argv[]) {\n  cout << compareMax(1, 2) << endl;\n  cout << compareMax(\"asda\", \"qweq\") << endl;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/key_exercises/io_operator.cpp",
    "content": "/* 输入输出重载.cpp */\n#include <iostream>\nusing namespace std;\n#include <cstring>\nclass Sales {\nprivate:\n  char name[10];\n  char id[18];\n  int age;\n\npublic:\n  Sales(char *Name, char *ID, int Age);\n  friend Sales &operator<<(ostream &os, Sales &s);\n  friend Sales &operator>>(istream &is, Sales &s);\n};\n\nSales::Sales(char *Name, char *ID, int Age) {\n  strcpy(name, Name);\n  strcpy(id, ID);\n  age = Age;\n}\n\nSales &operator<<(ostream &os, Sales &s) {\n  os << s.name << \"\\t\" << s.id << \"\\t\" << s.age << endl;\n  return s;\n}\n\nSales &operator>>(istream &is, Sales &s) {\n  cout << \"输入雇员的姓名，身份证，年龄：\\n\";\n  is >> s.name >> s.id >> s.age;\n}\n\nint main(int argc, char const *argv[]) {\n  Sales s(\"张三\", \"15611\", 26);\n  cout << s;\n  cin >> s;\n  cout << s;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/key_exercises/io_operator_overload.cpp",
    "content": "/* 输入输出运算符重载.cpp */\n/*\n   有一销售人员类Sales，其数据成员有姓名name，身份证号id，年龄age。\n   重载输入/输出运算符实现对Sales类数据成员的输入和输出。\n */\n#include <cstring>\n#include <iostream>\nusing namespace std;\nclass Sales {\nprivate:\n  char name[10];\n  char id[18];\n  int age;\n\npublic:\n  Sales(char *Name, char *ID, int Age);\n\n  friend Sales &operator<<(ostream &os, Sales &s); //重载输出运算符\n  friend Sales &operator>>(istream &is, Sales &s); //重载输入运算符\n};\nSales::Sales(char *Name, char *ID, int Age) {\n  strcpy(name, Name);\n  strcpy(id, ID);\n  age = Age;\n}\nSales &operator<<(ostream &os, Sales &s) {\n  os << s.name << \"\\t\"; //输出姓名\n  os << s.id << \"\\t\";   //输出身份证号\n  os << s.age << endl;  //输出年龄\n  return s;\n}\nSales &operator>>(istream &is, Sales &s) {\n  cout << \"输入雇员的姓名，身份证号，年龄\" << endl;\n  is >> s.name >> s.id >> s.age;\n  return s;\n}\nint main() {\n  Sales s1(\"杜康\", \"214198012111711\", 40); // L1\n  cout << s1;                              // L2\n  cout << endl;                            // L3\n  cin >> s1;                               // L4\n  cout << s1;                              // L5\n}\n"
  },
  {
    "path": "practical_exercises/key_exercises/map_insert_look.cpp",
    "content": "/* 字典插入与查找.cpp */\n#include <cstring>\n#include <iostream>\n#include <map>\nusing namespace std;\n\nint main(int argc, char const *argv[]) {\n  map<const char *, const char *> mp;\n  map<const char *, const char *>::iterator iter;\n  const char key[3][20] = {\"img\", \"system\", \"ip\"};\n  const char value[3][20] = {\"d:/a.img\", \"win7\", \"193.68.6.3\"};\n\n  // make_pair插入\n  for (int i = 0; i < 2; i++) {\n    mp.insert(make_pair(key[i], value[i]));\n  }\n  // pair<const char*,const char*>插入\n  mp.insert(pair<const char *, const char *>(key[2], value[2]));\n\n  //数组插入方式\n  mp[\"addr\"] = \"中国\";\n  //迭代器取出元素\n  //循环取出元素\n  for (iter = mp.begin(); iter != mp.end(); iter++) {\n    cout << iter->first << \"\\t\" << iter->second << endl;\n  }\n  char key1[20];\n  cout << \"请输入按key查找：\";\n  cin.getline(key1, 20);\n  //查找元素\n  for (iter = mp.begin(); iter != mp.end(); iter++) {\n    if (strcmp(iter->first, key1) == 0) {\n      cout << iter->first << \"查找出来了！\"\n           << \"对应的值为：\" << iter->second << endl;\n    }\n  }\n\n  //第一种删除方式\n  // find只能用于查找数组建立的形式！并且不支持输入用cin等\n  iter = mp.find(\"addr\");\n  if (iter != mp.end()) {\n    cout << iter->first << \"按照key查找出来了！\"\n         << \"对应的value为：\" << iter->second << endl;\n    cout << \"开始删除元素！\" << endl;\n    mp.erase(iter);\n    // break;\n  }\n  //第二种方式删除\n  //按照key删除元素\n  char drop_key[20];\n  //按照value删除元素\n  char drop_value[20];\n  cout << \"请输入按key删除：\";\n  cin.getline(drop_key, 20);\n  cout << \"请输入按value删除：\";\n  cin.getline(drop_value, 20);\n  for (iter = mp.begin(); iter != mp.end(); iter++) {\n    if (strcmp(iter->first, drop_key) == 0) {\n      cout << iter->first << \"按照key查找出来了！\"\n           << \"对应的value为：\" << iter->second << endl;\n      cout << \"开始删除元素！\" << endl;\n      mp.erase(iter);\n      break;\n    }\n    if (strcmp(iter->second, drop_value) == 0) {\n      cout << iter->second << \"value查找出来了！\"\n           << \"对应的key为：\" << iter->first << endl;\n      cout << \"开始删除元素！\" << endl;\n      mp.erase(iter);\n      break;\n    }\n  }\n  cout << \"------删除元素以后--------\\n\";\n  //循环取出元素\n  for (iter = mp.begin(); iter != mp.end(); iter++) {\n    cout << iter->first << \"\\t\" << iter->second << endl;\n  }\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/key_exercises/operator_cast.cpp",
    "content": "/* 运算符重载之强制转换.cpp */\n/*\n有一个类Circle，设计该类的类型转换函数，当将Circle对象转换成int型时，返回圆的半径；当将它转换成double型时，就返回圆的周长；当将它转换成float型时，就返回圆的面积。\n*/\n\n/*\n\n类型转换函数没有参数。\n类型转换函数没有返回类型。\n类型转换函数必须返回将要转换成的type类型数据。\n\n*/\n\n#include <iostream>\nusing namespace std;\nclass Circle {\nprivate:\n  double x, y, r;\n\npublic:\n  Circle(double x1, double y1, double r1) {\n    x = x1;\n    y = y1;\n    r = r1;\n  }\n  operator int() { return int(r); }\n  operator double() { return 2 * 3.14 * r; }\n  operator float() { return (float)3.14 * r * r; }\n};\nint main() {\n  Circle c(2.3, 3.4, 2.5);\n  int r = c;         //调用operator int()，将Circle类型转换成int\n  double length = c; //调用operator double()，转换成double\n  float area = c;    //调用operator float()，将Circle类型转换成float\n  double len = (double)c; //将Cirlce类型对象强制转换成double\n  cout << r << endl;\n  cout << length << endl;\n  cout << len << endl;\n  cout << area << endl;\n  \n}\n"
  },
  {
    "path": "practical_exercises/key_exercises/operator_circle.cpp",
    "content": "/* 重载圆括号的时钟.cpp */\n#include <iostream>\nusing namespace std;\nclass Time {\nprivate:\n  int hh, mm, ss;\n\npublic:\n  Time(int h = 0, int m = 0, int s = 0) : hh(h), mm(m), ss(s) {}\n  void operator()(int h, int m, int s) {\n    hh = h;\n    mm = m;\n    ss = s;\n  }\n  void ShowTime() { cout << hh << \":\" << mm << \":\" << ss << endl; }\n};\nint main() {\n  Time t1(12, 10, 11);\n  t1.ShowTime();\n  t1.operator()(23, 20, 34);\n  t1.ShowTime();\n  t1(10, 10, 10);\n  t1.ShowTime();\n  \n}\n"
  },
  {
    "path": "practical_exercises/key_exercises/output.cpp",
    "content": "/* 输出格式.cpp */\n#include <iomanip>\n#include <iostream>\nusing namespace std;\n\nint main(int argc, char const *argv[]) {\n  char s[20] = \"this is a string\";\n  double digit = -36.96656;\n  cout << setw(30) << left << setfill('*') << s << endl;\n  cout << dec << setprecision(4) << digit << endl;\n  cout << dec << 15 << endl;\n  // setbase(int x)设置进制后，后面所有操作都是按照这个进制来计算！\n  cout << setbase(10) << 15 << endl;\n  //四舍五入,并保留2位有效数组\n  float x = 6.6937;\n  cout << float(int(x * 1000 + 0.5) / 1000.0) << endl;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/key_exercises/override.cpp",
    "content": "/* 继承与封装.cpp */\n#include <cstring>\n#include <iostream>\n\nusing namespace std;\nclass Employee {\npublic:\n  Employee(const char *name, const char *id) {\n    strcpy(Name, name);\n    strcpy(Id, id);\n  }\n  char *getName() { return Name; }\n  char *getId() { return Id; }\n  void display() { cout << Name << \"\\t\" << Id << endl; }\n\nprivate:\n  char Name[20];\n  char Id[20];\n};\n\nclass Manager : public Employee {\npublic:\n  //直接调用构造方法传递，基类构造方法有参数，派生类必须通过构造方法，在初始化列表中传递参数\n  Manager(const char *name, const char *id, int week) : Employee(name, id) {\n    WeeklySalary = week * 1000;\n  }\n  void display() {\n    cout << \"经理：\" << getName() << \"\\t\" << getId() << \"\\t\" << WeeklySalary\n         << endl;\n  }\n\nprivate:\n  int WeeklySalary;\n};\n\nclass SaleWorker : public Employee {\npublic:\n  SaleWorker(const char *name, const char *id, int profit, int x)\n      : Employee(name, id) {\n    workerMoney = baseMoney + x * 0.05 * profit;\n  }\n  void display() {\n    cout << \"销售员：\" << getName() << \"\\t\" << getId() << \"\\t\" << workerMoney\n         << endl;\n  }\n\nprivate:\n  float baseMoney = 800.0;\n  float workerMoney;\n};\nclass HourWorker : public Employee {\npublic:\n  HourWorker(const char *name, const char *id, int h) : Employee(name, id) {\n    TotalMoney = h * hourMoney;\n  }\n  void display() {\n    cout << \"小时工：\" << getName() << \"\\t\" << getId() << \"\\t\" << TotalMoney\n         << endl;\n  }\n\nprivate:\n  float hourMoney = 100.0;\n  float TotalMoney;\n};\n\nint main(int argc, char const *argv[]) {\n  cout << \"请输入工作周：\";\n  int week;\n  cin >> week;\n  Manager m(\"小王\", \"11111111\", week);\n  m.display();\n  cout << \"请输入销售利润：\";\n  int profit;\n  cin >> profit;\n  cout << \"请输入销售件数：\";\n  int x;\n  cin >> x;\n  SaleWorker s(\"小李\", \"222222\", profit, x);\n  s.display();\n  cout << \"请输入工作小时：\";\n  int hour;\n  cin >> hour;\n  HourWorker h(\"小何\", \"333333\", hour);\n  h.display();\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/key_exercises/read_file.cpp",
    "content": "/* 读写文件综合题.cpp */\n#include <cstring>\n#include <fstream>\n#include <iostream>\n#include <vector>\nusing namespace std;\n\nclass Person {\npublic:\n  Person() {}\n  Person(char *name, char *id, int math, int chinese, int english) {\n    strcpy(Name, name);\n    strcpy(Id, id);\n    Math = math;\n    Chinese = chinese;\n    English = english;\n    Sum = Math + Chinese + English;\n  }\n\n  void display() {\n    cout << Name << \"\\t\" << Id << \"\\t\" << Math << \"\\t\" << Chinese << \"\\t\"\n         << English << \"\\t\" << Sum << endl;\n  }\n\nprivate:\n  char Name[20];\n  char Id[20];\n  int Math;\n  int Chinese;\n  int English;\n  int Sum;\n};\n\nint main(int argc, char const *argv[]) {\n  char ch;\n  char Name[20], Id[20];\n  int Math, Chinese, English;\n  fstream ioFile;\n  ioFile.open(\"d:/per.dat\", ios::out | ios::app);\n  cout << \"---------建立学生档案信息----------\\n\";\n  do {\n    cout << \"请输入姓名：\";\n    cin >> Name;\n    cout << \"请输入身份证号：\";\n    cin >> Id;\n    cout << \"请输入数学成绩：\";\n    cin >> Math;\n    cout << \"请输入汉语成绩：\";\n    cin >> Chinese;\n    cout << \"请输入英语成绩：\";\n    cin >> English;\n    Person per(Name, Id, Math, Chinese, English);\n    ioFile.write((char *)&per, sizeof(per));\n    cout << \"您是否继续建档？(Y/y)  \";\n    cin >> ch;\n  } while (ch == 'y' || ch == 'Y');\n  ioFile.close();\n  ioFile.open(\"d://per.dat\", ios::in);\n  Person p;\n  ioFile.read((char *)&p, sizeof(p));\n  vector<Person> v;\n  vector<Person>::iterator vt;\n  while (!ioFile.eof()) {\n    v.push_back(p);\n    ioFile.read((char *)&p, sizeof(p));\n  }\n  ioFile.close();\n  cout << \"---------输出档案信息-----------\" << endl;\n  for (vt = v.begin(); vt != v.end(); vt++) {\n    (*vt).display();\n  }\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/key_exercises/stack.cpp",
    "content": "/* 类模板之栈.cpp */\n#include <iostream>\n\nusing namespace std;\n\ntemplate <typename T, int MAXSIZE>\n\nclass Stack {\npublic:\n  Stack() {}\n  void init() { top = -1; }\n  bool isFull() {\n    if (top >= MAXSIZE - 1)\n      return true;\n    else\n      return false;\n  }\n  bool isEmpty() {\n    if (top == -1)\n      return true;\n    else\n      return false;\n  }\n  void push(T e);\n  T pop();\n\nprivate:\n  T elems[MAXSIZE];\n  int top;\n};\n\ntemplate <typename T, int MAXSIZE> void Stack<T, MAXSIZE>::push(T e) {\n  if (!isFull()) {\n    elems[++top] = e;\n  } else {\n    cout << \"栈已满，请不要再加入元素！\";\n    return;\n  }\n}\ntemplate <typename T, int MAXSIZE> T Stack<T, MAXSIZE>::pop() {\n  if (!isEmpty()) {\n    return elems[top--];\n  } else {\n    cout << \"栈已空，请不要再弹出元素！\";\n    return 0;\n  }\n}\n\nint main(int argc, char const *argv[]) {\n  Stack<int, 10> s1;\n  s1.init();\n  int i;\n  for (i = 1; i < 11; i++)\n    s1.push(i);\n  for (i = 1; i < 11; i++)\n    cout << s1.pop() << \"\\t\";\n  cout << endl;\n  \n  return 0;\n}\n"
  },
  {
    "path": "practical_exercises/key_exercises/try.cpp",
    "content": "/* 异常捕获.cpp */\n#include <iostream>\nusing namespace std;\n//函数异常可以抛出去由主函数来处理\nvoid fun(int x) {\n  try {\n    if (x == 0)\n      throw \"异常\";\n  } catch (...) {\n    cout << \"in fun\" << endl;\n    throw 1;\n  }\n}\n\nint main(int argc, char const *argv[]) {\n\n  try {\n    fun(0);\n  } catch (int n) {\n    cout << \"in main\" << endl;\n  }\n\n  \n  return 0;\n}\n"
  },
  {
    "path": "proj/README.md",
    "content": "- [重磅项目](#重磅项目)\n  - [学习方式](#学习方式)\n  - [HOT: 新增项目](#hot-新增项目)\n    - [线程池](#线程池)\n    - [内存池](#内存池)\n  - [手把手100行写一个简易版Mutex](#手把手100行写一个简易版mutex)\n    - [内容](#内容)\n    - [涉及内容](#涉及内容)\n  - [七种方式玩转condition\\_variable](#七种方式玩转condition_variable)\n    - [内容](#内容-1)\n    - [涉及内容](#涉及内容-1)\n  - [duckdb + levledb](#duckdb--levledb)\n  - [C++那些事十日狂练升级版](#c那些事十日狂练升级版)\n\n\n# 重磅项目\n\n## 学习方式\n\n深入学习以下项目，可扫码加入知识星球或添加个人wx: **gcxzhang**，备注: 加入星球\n\n\n<img src=\"../img/cpp.jpg\" alt=\"知识星球\" width=\"400\" height=\"300\">\n\n\n## HOT: 新增项目\n\n### 线程池\n\n手把手教会该线程池，可以戳这里，附：视频+文字教程，包会。\n\nhttps://mp.weixin.qq.com/s/ceQoyHVMVmaXL3BNt25O4g\n\n这个项目的线程池是基于Apache Arrow项目的衍生版本。我们将Arrow项目中复杂的核心结构——线程池——完全剥离出来，形成了这个独立的项目。由于原>始的线程池与Arrow项目本身的工具有深度依赖关系，因此我们在这个项目中对线程池进行了一些深度移除和改造，以保持与原始Arrow线程池的基础功能\n一致。一些改动包括：\n\n- 将Arrow的Future替换为std::future\n- 将Arrow的Result替换为std::optional\n- 重构了Submit接口，使用promise进行实现\n\n通过这些改动，我们的目标是：\n\n- 使线程池更方便地作为其他项目的依赖库使用\n- 提供简单的方式来引入本项目的so库和头文件，以使用线程池功能\n\n此外，这个项目还可以作为深入学习线程池设计与实现的资源。我们欢迎您探索并使用这个经过精心改进的线程池。\n\n[线程池-飞书文档](https://hmpy6adnp5.feishu.cn/docx/BEKBdq0egoxmxNx3X3ZcHzkUnwd)\n\n\n## 手把手100行写一个简易版Mutex\n### 内容\n\n项目名称：简易互斥锁（SimpleMutex）\n\n项目描述：简易互斥锁（SimpleMutex）是一个基于原子变量和信号量的互斥锁实现，用于保护并管理多线程环境下的共享资源访问。它提供了一种简单而有效的方式来确保在多线程并发访问时，只有一个线程可以同时访问受保护的资源，从而避免数据竞争和不一致性。基于 POSIX 标准的信号量库实现，包含 Catch2 单元测试，附带了基于 Catch2 框架的单元测试，用于验证互斥锁的正确性和稳定性，使用bazel编译，google编码规范。\n\n[https://mp.weixin.qq.com/s/SqtdadmCaJNSK10fScFlUw](https://mp.weixin.qq.com/s/SqtdadmCaJNSK10fScFlUw)\n\n\n### 涉及内容\n\n其中涉及C++知识（RAII、信号量、lock_guard、线程安全编程），还能学习以下内容：\n\n1.Bazel构建系统：了解如何使用Bazel来管理和构建C++项目。Bazel是一种高效、可扩展的构建工具，可用于管理复杂的项目结构、依赖关系和构建流程。\n\n2.Catch2测试框架：掌握如何使用Catch2来编写单元测试。Catch2是一个功能强大且易于使用的C++测试框架，可以帮助你编写清晰、可读性高的测试用例，提高代码质量和可靠性。\n\n3.原子操作：了解原子操作的概念和使用方法。原子操作是一种并发编程技术，可以确保对共享变量的操作是不可中断的，避免了竞态条件和数据不一致的问题。\n\n4.fetch_add函数：学习如何使用fetch_add函数进行原子的加法操作。fetch_add是一种原子操作函数，它可以在并发环境中安全地对共享变量进行原子加法操作，避免了数据竞争。\n\n5.内存序：了解不同的内存序（memory order）对多线程程序的影响。\n\n6.锁的自动管理：引入lock_guard类，实现锁的自动获取和释放。lock_guard是C++标准库提供的一种锁管理工具，它在构造函数中获取锁，在析构函数中释放锁，确保在任何情况下都能正确释放锁，避免因为异常或早期返回等情况导致锁没有释放的问题。\n\n7.RAII（Resource Acquisition Is Initialization）原则：了解如何使用对象的生命周期来管理资源的获取和释放，确保资源的正确获取和释放，避免资源泄漏。\n\n## 七种方式玩转condition_variable\n\n[https://mp.weixin.qq.com/s/33ZXOA-tzhbBKMFWS26kCw](https://mp.weixin.qq.com/s/33ZXOA-tzhbBKMFWS26kCw)\n\n### 内容\n\n1.CPU轮询等待版单生产者单消费者：\n\n该版本使用了简单的轮询机制，生产者不断地检查消费者是否已经消费完数据。这种模式简单直接，但效率较低，因为生产者在没有数据时仍然在忙等待。\n\n2.等待通知版单生产者单消费者：\n\n该版本引入了等待通知机制，生产者在没有数据时会等待消费者的通知。这种模式避免了忙等待，提高了效率，同时减少了资源消耗。\n\n3.等待通知版单生产者多消费者：\n\n在这个版本中，引入了多个消费者，它们共享生产者的数据。生产者在产生数据后，通知所有消费者进行处理。\n\n4.等待通知版多生产者多消费者：\n\n这个版本支持多个生产者和多个消费者，生产者之间和消费者之间共享数据。生产者在产生数据后，通知所有消费者进行处理。\n\n5.单生产者多消费者并行版：\n\n在这个版本中，引入了并行处理机制，多个消费者可以同时处理数据。生产者产生数据后，多个消费者并行处理，提高了整体处理速度。\n\n6.多生产者多消费者并行版：\n\n这个版本支持多个生产者和多个消费者，并且允许并行处理。多个生产者并行产生数据，多个消费者并行处理数据，提高了整体并发能力。\n\n7.支持Lambda回调的优雅停止版：\n\n在这个版本中，引入了Lambda回调函数，用于优雅地停止并发处理。可以通过调用回调函数来停止生产者和消费者的处理，并进行清理工作。\n\n### 涉及内容\n\n1.多线程和并发编程：通过这些版本的描述，您可以了解到如何在C++中使用多线程和并发编程来处理并行任务。您将学习如何创建线程、控制线程的执行、线程间的数据共享和同步等。\n\n2.同步机制：在这些版本中，介绍了不同的同步机制，如轮询等待、等待通知和回调函数。您将学习如何使用互斥锁、条件变量、信号量等来实现线程间的同步和协调。\n\n3.Lambda表达式：在支持Lambda回调的版本中，您将学习如何使用C++11引入的Lambda表达式来编写简洁而灵活的回调函数，以实现优雅的停止机制。\n\n4.代码组织和构建工具：提到了支持bazel编译的能力，这可以让您学习如何使用构建工具来组织和管理复杂的C++项目。\n\n5.condition_variable、mutex、unique_lock\n\n## duckdb + levledb\n\nleveldb 源码剖析已更毕\n\nduckdb 持续更新\n\n## C++那些事十日狂练升级版\n\n已更毕\n"
  },
  {
    "path": "src_analysis/stl/array.md",
    "content": "# C++ STL源码剖析 tr1与std array\n\n## 0.导语\n\n源码剖析版本为gcc4.9.1。\n\nC++ tr1全称Technical Report 1，是针对C++标准库的第一次扩展。C++标准c++0x会包括它，以及一些语言本身的扩充。tr1包括大家期待已久的smart pointer，正则表达式以及其他一些支持范型编程的内容。草案阶段，新增的类和模板的名字空间是std::tr1。\n\n## 1.std::tr1::array\n\n使用：\n```\n#include <tr1/array>\nstd::tr1::array<int ,10> a;\n```\n\ntr1中的array比较简单，模拟语言本身的数组，并且让其支持迭代器操作，使其同其他容器一样，能够调用算法。对于tr1中array没有构造与析构。迭代器是直接使用传递进来的类型定义指针。\n\n简单的看一下这个静态数组array源码：\n\n```cpp\ntemplate<typename _Tp, std::size_t _Nm>\nstruct array\n{\n    typedef _Tp \t    \t\t\t      value_type;\n    typedef value_type&                   \t      reference;\n    typedef const value_type&             \t      const_reference;\n    typedef value_type*          \t\t      iterator;\n    typedef const value_type*\t\t\t      const_iterator;\n    typedef std::size_t                    \t      size_type;\n    typedef std::ptrdiff_t                   \t   difference_type;\n    typedef std::reverse_iterator<iterator>\t      reverse_iterator;\n    typedef std::reverse_iterator<const_iterator>   const_reverse_iterator;\n}\n```\n里面使用`reverse_iterator`作为rbegin与rend操作的迭代器。\n看上去上面一个迭代器，实际上两个，还有一个iterator,这个直接使用传递进来的类型定义指针，作为迭代器。\n\n可以将其对比为vector中的正向与反向迭代器。\n\n值得注意的是，在tr1::array中，支持传递数组大小为0，例如我们使用如下：\n\n```\nstd::tr1::array<int,0> a;\n```\n对于这样的写法，会对应到下面：\n```\n// Support for zero-sized arrays mandatory.\nvalue_type _M_instance[_Nm ? _Nm : 1];\n```\n根据传递进来的大小，如果不为0，就是传递进来的大小，否则为1。\n\n\n## 2.std::array\n\n使用\n```\nstd::array<int ,10> a;\n```\n\nstd中的array包含了\n\n![std_array.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/std_array.png)\n\n对比tr1与std的array\n\n```cpp\ntemplate<typename _Tp, std::size_t _Nm>\nstruct array\n{\n    typedef _Tp \t    \t\t\t      value_type;\n    typedef value_type*\t\t\t      pointer;\n    typedef const value_type*                       const_pointer;\n    typedef value_type&                   \t      reference;\n    typedef const value_type&             \t      const_reference;\n    typedef value_type*          \t\t      iterator;\n    typedef const value_type*\t\t\t      const_iterator;\n    typedef std::size_t                    \t      size_type;\n    typedef std::ptrdiff_t                   \t      difference_type;\n    typedef std::reverse_iterator<iterator>\t      reverse_iterator;\n    typedef std::reverse_iterator<const_iterator>   const_reverse_iterator;\n\n    // Support for zero-sized arrays mandatory.\n    typedef _GLIBCXX_STD_C::__array_traits<_Tp, _Nm> _AT_Type;    // # define _GLIBCXX_STD_C std\n    typename _AT_Type::_Type                         _M_elems;\n}\n```\n\n发现array里面有两处值得注意的地方：\n```cpp\n// Support for zero-sized arrays mandatory.\ntypedef _GLIBCXX_STD_C::__array_traits<_Tp, _Nm> _AT_Type;    // # define _GLIBCXX_STD_C std\ntypename _AT_Type::_Type                         _M_elems;\n```\n\n在源码中去找__array_traits，看到：\n```cpp\ntemplate<typename _Tp, std::size_t _Nm>\nstruct __array_traits\n{\n    typedef _Tp _Type[_Nm];\n\n    static constexpr _Tp&\n    _S_ref(const _Type& __t, std::size_t __n) noexcept\n    { return const_cast<_Tp&>(__t[__n]); }\n};\n```\n\n上面两行的代码可以理解为下面：\n\n```cpp\ntypedef _Tp _Type[100];\ntypedef _Type _M_elems;  // 一个含有100个元素的数组。\n```\n\n在实际写代码的时候，如果要定义一个数组，我们可以这样写：\n```cpp\nint a[100];\n//或者\ntypedef int T[100];\ntypedef T a;\n```\n针对传进来的size处理，相比于tr1，更加复杂，使用了模板偏特化来处理传递size为0情况。\n\n```cpp\ntemplate<typename _Tp, std::size_t _Nm>\nstruct __array_traits\n{\n    typedef _Tp _Type[_Nm];\n\n    static constexpr _Tp&\n    _S_ref(const _Type& __t, std::size_t __n) noexcept\n    { return const_cast<_Tp&>(__t[__n]); }\n};\n\ntemplate<typename _Tp>\nstruct __array_traits<_Tp, 0>\n{\n    struct _Type { };\n\n    static constexpr _Tp&\n    _S_ref(const _Type&, std::size_t) noexcept\n    { return *static_cast<_Tp*>(nullptr); }\n};\n```\n\n"
  },
  {
    "path": "src_analysis/stl/deque.md",
    "content": "\n# C++ STL源码剖析之序列式容器deque\n## 0.导语\ndeque是一种双向开口的分段连续线性空间(简单理解为：双端队列)，可以在头尾端进行元素的插入和删除。\n\ndeque与vector最大的差异就是：\n\n- deque允许于常数时间内对头端进行插入或删除元素；\n\n- deque是分段连续线性空间，随时可以增加一段新的空间；\n\ndeque不像vector那样，vector当内存不够时，需重新分配/复制数据/释放原始空间；不过deque的迭代器设置比vector复杂，因为迭代器不能使用普通指针，因此尽量使用vector。\n\n## 1.deque中控器\n\n用户看起来deque使用的是连续空间，实际上是**分段连续线性空间**。为了管理分段空间deque容器引入了map，称之为中控器，map是一块连续的空间，其中每个元素是指向缓冲区的指针，缓冲区才是deque存储数据的主体。\n\n\n![deque_r.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/deque_r.png)\n\n在上图中，buffer称为缓冲区，显示map size的一段连续空间就是中控器。\n\n中控器包含了map size,指向buffer的指针，deque的开始迭代器与结尾迭代器。\n\n```cpp\n_Tp\t\t**_M_map;\nsize_t\t\t_M_map_size;\niterator\t_M_start;\niterator\t_M_finish;\n```\n\n由于buffer也是指针，所以`_Tp`是指针的指针。\n\ndeque继承自`_Deque_base`,而`_Deque_base`里面有一个`_M_impl`。\n\n![deque_base.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/deque_bacse.png)\n\n根据下图与上述描述，可以知道，中控器是由`_Deque_impl`实现的。\n\n![impl.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/impl.png)\n\n而deque是使用基类`_Deque_base`来完成内存管理与中控器管理。\n\n\n## 2.高端的迭代器\n\n对于deque来说，它的迭代器设计的非常棒！\n\n如下图所示：\n![deque_iterator.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/deque_iterator.png)\n\n首先来看一下比较重要的成员：\n\n```cpp\ntypedef _Tp\t\t\t\t**_Map_pointer;\n_Tp\t\t*_M_cur;\n_Tp\t\t*_M_first;\n_Tp\t\t*_M_last;\n_Map_pointer\t_M_node;\n```\n\n这几个究竟是什么呢，根据名字，很容易知道啥意思，对于deque来说，是分段连续空间，迭代器执行操作，上述的`_M_cur`指向具体的元素，`_M_first`指向这段buffer中的第一个元素,`_M_last`指向最后一个元素(不是有效的元素)，而`_M_node`则是指向中控器。所以它是一个指针的指针。\n\n\n例如现在迭代器执行++操作，当前buffer不够用了，那么此时需要一个指针能够回到中控器，取下一段buffer，重置`_M_first`与`_M_last`的指针位置，`_M_cur`指向新段buffer中的指定位置。\n\n我们现在回到一开始的图：\n\n![deque_r.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/deque_r.png)\n\n最上面的的iterator就是上面几个指针的区块配图。\n\n\n那buffer计算是什么实现的呢?\n\n在源码中计算是根据传递进来的类型，如果传递的类型小于512字节，那么buffersize就是512/sizeof(_Tp)，超过512，就是1。\n\n\n```cpp\nstatic size_t _S_buffer_size()\n_GLIBCXX_NOEXCEPT\n{\n    return(__deque_buf_size( sizeof(_Tp) ) );\n}\n\n```\n`__deque_buf_size`实现\n\n```cpp\n#ifndef _GLIBCXX_DEQUE_BUF_SIZE\n#define _GLIBCXX_DEQUE_BUF_SIZE 512\n#endif\ninline size_t\n__deque_buf_size( size_t\n            __size )\n{\n    return(__size < _GLIBCXX_DEQUE_BUF_SIZE\n            ? size_t( _GLIBCXX_DEQUE_BUF_SIZE / __size ) : size_t( 1 ) );\n}\n```\n前面几节源码中提到了萃取机技术，针对每个迭代器都需要嵌入下面五种typedef：\n\n```cpp\ntypedef std::random_access_iterator_tag iterator_category;\ntypedef _Tp\t\t\t\tvalue_type;\ntypedef _Ptr\t\t\t\tpointer;\ntypedef _Ref\t\t\t\treference;\ntypedef ptrdiff_t\t\t\tdifference_type;\n```\n\n据此，也可以知道deque迭代器的使用的是随机访问迭代器：`random_access_iterator_tag`。\n\n而vector使用的迭代器也是这个，根据侯捷老师所讲，连续的buffer是vector，这与迭代器的tag类型不谋而合。\n\n\n下面来看一下这个强大的迭代器的一些操作符重载：\n\n具体的讲解在代码里面说。\n\n> 取值操作符\n\n```cpp\nreference\noperator*() const\n_GLIBCXX_NOEXCEPT\n{\n    return(*_M_cur);\n}\n\n\npointer\noperator->() const\n_GLIBCXX_NOEXCEPT\n{\n    return(_M_cur);\n}\n```\n当然上述的`->`也可以直接调用`*`操作符来实现，例如：\n\n```cpp\npointer\noperator->() const\n_GLIBCXX_NOEXCEPT\n{\n    return &(operator*());\n}\n```\n\n> ++与--操作符\n\n```cpp\n\n// 前置++操作符\n_Self &\noperator++()\n_GLIBCXX_NOEXCEPT\n{\n    // 先++，判断是否到了buffer的末尾，如果到了末尾，就要跳到下一个buffer。\n    ++_M_cur;\n    if ( _M_cur == _M_last ) // _M_last指向的不是有效元素，保留节点  \n    {\n        _M_set_node( _M_node + 1 );\n        _M_cur = _M_first;\n    }\n    return(*this);\n}\n\n// 后置++操作符\n_Self\noperator++( int )\n_GLIBCXX_NOEXCEPT\n{\n    _Self __tmp = *this;\n    ++*this;\n    return(__tmp);\n}\n\n// 前置--操作符\n_Self &\noperator--()\n_GLIBCXX_NOEXCEPT\n{\n    // 先判断是否到了起始位置，如果到了，由于需要进行--操作，那么就应该进入前一个buffer\n    if ( _M_cur == _M_first )\n    {\n        _M_set_node( _M_node - 1 );\n        _M_cur = _M_last;\n    }\n    --_M_cur;\n    return(*this);\n} //先在容器头部插入与第一个元素相同的元素\n\n// 后置--操作符\n_Self\noperator--( int )\n_GLIBCXX_NOEXCEPT\n{\n    _Self __tmp = *this;    /* 定义一个副本 */\n    --*this;                /* 迭代器自减操作 */\n    return(__tmp);\n}\n```\n\n> 跳跃n个距离操作符\n\n\n```cpp\n/*\n* 实现随机取,迭代器可以直接跳跃n个距离\n* 将迭代器前移n个距离,当n负值时就为下面的operator-=操作\n*/\n_Self &\noperator+=( difference_type __n )\n\n_GLIBCXX_NOEXCEPT\n{\n    const difference_type __offset = __n + (_M_cur - _M_first);\n    /*\n        * 若前移n个距离后，目标依然在同一个缓冲区\n        * 则直接前移n个距离\n        */\n    if ( __offset >= 0 && __offset < difference_type( _S_buffer_size() ) )\n        _M_cur += __n;\n    else {\n        /*\n            * 若前移n个距离后,目标超出了缓冲区范围\n            * __offset>0   __offset / difference_type(_S_buffer_size())计算向后移动多少个缓冲区\n            * __offset<=0  -difference_type((-__offset - 1) / _S_buffer_size()) - 1计算向前移动多少个缓冲区\n            */\n        const difference_type __node_offset =\n            __offset > 0 ? __offset / difference_type( _S_buffer_size() )\n            : -difference_type( (-__offset - 1)\n                        / _S_buffer_size() ) - 1;\n        /* 调整到正确的缓冲区,此时_M_first已经修改了 */\n        _M_set_node( _M_node + __node_offset );\n        /* 修改为正确的指针位置 */\n        _M_cur = _M_first + (__offset - __node_offset\n                        * difference_type( _S_buffer_size() ) );\n    }\n    return(*this);\n}\n\n```\n下面这几个操作符都是调用上面的`+=`操作符实现：\n\n\n```cpp\n\n/*\n    * 操作符+重载\n    * 返回操作之后的副本\n    */\n_Self\noperator+( difference_type __n ) const\n_GLIBCXX_NOEXCEPT\n{\n    _Self __tmp = *this;\n    /* 调用operator+=操作 */\n    return(__tmp += __n);\n}\n\n\n/* 利用operator+=操作实现 */\n_Self &\noperator-=( difference_type __n )\n_GLIBCXX_NOEXCEPT\n{\n    return(*this += -__n);\n}\n\n\n/*\n    * 操作符-重载\n    * 返回操作之后的副本\n    */\n_Self\noperator-( difference_type __n ) const\n_GLIBCXX_NOEXCEPT\n{\n    _Self __tmp = *this;    /*  保存副本 */\n    return(__tmp -= __n);   /* 调用operator-=操作符 */\n}\n\n\n/* 返回指定位置的元素，即实现随机存取 */\nreference\noperator[]( difference_type __n ) const\n_GLIBCXX_NOEXCEPT\n{\n    return(*(*this + __n) );    /* 该函数调用operator+,operator* */\n}\n\n```\n\n\n> buffer跳跃\n\n前面的++与--等操作符，会调用到`_M_set_node`函数，该函数的作用是能够进行buffer之间的跳跃，修改`_M_node`、`_M_first`、`_M_last`的指向。\n\n```cpp\n/**\n    *  Prepares to traverse new_node.  Sets everything except\n    *  _M_cur, which should therefore be set by the caller\n    *  immediately afterwards, based on _M_first and _M_last.\n    */\nvoid\n_M_set_node( _Map_pointer __new_node )\n_GLIBCXX_NOEXCEPT\n{\n    _M_node\t\t= __new_node;                                           /* 指向新的节点 */\n    _M_first\t= *__new_node;                                          /* 指向新节点的头部 */\n    _M_last\t\t= _M_first + difference_type( _S_buffer_size() );       /* 指向新节点的尾部 */\n}\n```\n据此，我们就把deque的迭代器实现细节讲解完毕了。\n\n\n\n## 3.deque\n\n> begin()函数\n\n\n返回`_M_start`。\n\n```cpp\niterator\nbegin()\n_GLIBCXX_NOEXCEPT\n{\n    return(this->_M_impl._M_start);\n}\n```\n\n> end()函数\n\n\n返回`_M_finish`。\n\n```cpp\niterator\nend()\n_GLIBCXX_NOEXCEPT\n{\n    return(this->_M_impl._M_finish);\n}\n\n```\n\n> size()函数\n\n```cpp\nsize_type\nsize() const\n\n_GLIBCXX_NOEXCEPT\n{\n    return(this->_M_impl._M_finish - this->_M_impl._M_start);\n}\n```\n\n> resize()函数\n\n根据传递进来的大小，如果超过了总size，就重新分配扩充`__new_size-size()`空间，否则删除从`size()-__new_size`数据，例如现在有20个空间，resize(12)，就会把后面8个空间数据删除及空间释放。\n\n```cpp\nvoid\nresize( size_type __new_size )\n{\n    const size_type __len = size();\n    if ( __new_size > __len )\n        _M_default_append( __new_size - __len );\n    else if ( __new_size < __len )\n        _M_erase_at_end( this->_M_impl._M_start\n                    + difference_type( __new_size ) );\n}\n```\n> empty()函数\n\n判断两个指针位置即可。\n\n```cpp\nbool\nempty() const\n\n_GLIBCXX_NOEXCEPT\n{\n    return(this->_M_impl._M_finish == this->_M_impl._M_start);\n}\n```\n> back函数\n\n\n```cpp\nreference\nback()\n_GLIBCXX_NOEXCEPT       // 指向finish的前一个位置\n{\n    iterator __tmp = end();\n    --__tmp;\n    return(*__tmp);\n}\n```\n\n> push_front函数\n\n\n```cpp\nvoid\npush_front( const value_type &__x )\n{\n    //若当前缓冲区存在可用空间\n    if ( this->_M_impl._M_start._M_cur != this->_M_impl._M_start._M_first )\n    {\n        this->_M_impl.construct( this->_M_impl._M_start._M_cur - 1, __x );// 直接构造对象\n        --this->_M_impl._M_start._M_cur;    // 调整指针所指位置\n    } else\n        _M_push_front_aux( __x );   // 需分配一段新的连续空间\n}\n```\n\n> push_back函数\n\n\n```cpp\nvoid\npush_back( const value_type &__x )\n{\n    //若当前缓冲区存在可用空间\n    if ( this->_M_impl._M_finish._M_cur\n            != this->_M_impl._M_finish._M_last - 1 )\n    {\n        this->_M_impl.construct( this->_M_impl._M_finish._M_cur, __x ); // 直接构造对象\n        ++this->_M_impl._M_finish._M_cur;       //调整指针所指位置\n    } else     // 若当前缓冲区不存在可用空间\n    // 需分配一段新的连续空间\n        _M_push_back_aux( __x );\n}\n```\n上述对应的pop动作与之相反。\n\n> insert()函数\n\ninsert函数比较有意思，根据传递进来的迭代器位置，看是不在开头与结尾，如果是在开头直接调用`push_front`函数，结尾直接调`push_back`函数，否则在容器中直接插入元素。\n\n```cpp\ntemplate <typename _Tp, typename _Alloc>\ntypename deque<_Tp, _Alloc>::iterator\ndeque<_Tp, _Alloc>::\ninsert(iterator __position, const value_type& __x)\n{\n        if (__position._M_cur == this->_M_impl._M_start._M_cur)   \n    {\n        push_front(__x);\n        return this->_M_impl._M_start;\n    }\n        else if (__position._M_cur == this->_M_impl._M_finish._M_cur)\n    {\n        push_back(__x);\n        iterator __tmp = this->_M_impl._M_finish;\n        --__tmp;\n        return __tmp;\n    }\n        else  //否则在容器直接插入数据\n        return _M_insert_aux(__position._M_const_cast(), __x);\n}\n\n```\n而上述在容器中直接插入元素函数，会计算插入点，如果比较靠前面，就在前面插入，靠近后面就在后面插入：\n\n\n```cpp\n\ntemplate<typename _Tp, typename _Alloc>\ntypename deque<_Tp, _Alloc>::iterator\ndeque<_Tp, _Alloc>::\n_M_insert_aux(iterator __pos, const value_type& __x)\n{\n    value_type __x_copy = __x; // XXX copy\n    difference_type __index = __pos - this->_M_impl._M_start;  //计算插入点之前元素个数\n    if (static_cast<size_type>(__index) < size() / 2)   //若插入点之前的元素较少\n        {\n        push_front(_GLIBCXX_MOVE(front())); //先在容器头部插入与第一个元素相同的元素\n        iterator __front1 = this->_M_impl._M_start;\n        ++__front1;\n        iterator __front2 = __front1;\n        ++__front2;\n        __pos = this->_M_impl._M_start + __index;\n        iterator __pos1 = __pos;\n        ++__pos1;\n        _GLIBCXX_MOVE3(__front2, __pos1, __front1); // 元素搬移\n        }\n    else\n    {\n        push_back(_GLIBCXX_MOVE(back()));\n        iterator __back1 = this->_M_impl._M_finish;\n        --__back1;\n        iterator __back2 = __back1;\n        --__back2;\n        __pos = this->_M_impl._M_start + __index;\n        _GLIBCXX_MOVE_BACKWARD3(__pos, __back2, __back1);\n    }\n    *__pos = _GLIBCXX_MOVE(__x_copy);       // 在安插点上设定新值\n    return __pos;\n}\n```\n"
  },
  {
    "path": "src_analysis/stl/hashtable.md",
    "content": "# C++ STL源码剖析之哈希表\n\n## 0.导语\n\n哈希表，是作为`unordered_map`与`unordered_set`等的底层容器，自gcc2.9后源码量大增！\n\n这次阅读的代码仍旧是gcc4.9.1，代码量非常多，就不全部展开，重点研究底层哈希的艺术与技术，似乎这两个词语很押韵哦，哈哈，进入正文～\n\n\n## 1.Hashtable初识\n\n先来看一眼Hashtable源码：\n\n```cpp\ntemplate<typename _Key, typename _Value, typename _Alloc,\n    typename _ExtractKey, typename _Equal,\n    typename _H1, typename _H2, typename _Hash,\n    typename _RehashPolicy, typename _Traits>\nclass _Hashtable\n: 　public __detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal,\n    public __detail::_Map_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,  _H1, _H2, _Hash, _RehashPolicy, _Traits>,\n    public __detail::_Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>,\n    public __detail::_Rehash_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,_H1, _H2, _Hash, _RehashPolicy, _Traits>,\n    public __detail::_Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,_H1, _H2, _Hash, _RehashPolicy, _Traits>,\n    private __detail::_Hashtable_alloc<typename __alloctr_rebind<_Alloc,__detail::_Hash_node<_Value,_Traits::__hash_cached::value> >::__type>\n{\n};\n```\n\n没学过类模板的一脸懵逼，数一下模板参数都晕死。。。\n\n还有它的继承，一下子整出这么多父亲来。。。\n\n下面就来一一分析它的父亲，然后再回到哈希表。\n\n\n## 2._Hashtable_base\n\n其中注释中如下：\n\n> Helper class adding management of _Equal functor to _Hash_code_base type.\n\n\n帮助程序类，将仿函数_Equal的管理添加到_Hash_code_base中。\n\n对比代码就可以看出来是啥意思了：\n```cpp\ntemplate<typename _Key, typename _Value,\n    typename _ExtractKey, typename _Equal,\n    typename _H1, typename _H2, typename _Hash, typename _Traits>\nstruct _Hashtable_base\n: public _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash, _Traits::__hash_cached::value>,\nprivate _Hashtable_ebo_helper<0, _Equal>\n{\n\n};\n```\n对比一下`_Hash_code_base`与`_Hashtable_base`，两者就差一个`_Equal`，据此这句话解释完毕。\n\n它的基类又有两个分别是：\n\n```cpp\n__detail::_Hash_code_base\n__detail::_Hashtable_ebo_helper\n```\n\n我们继续追踪这两个类！\n\n### 2.1 _Hash_code_base\n\n这个类最后一个`__cache_hash_code`表示是否缓存hash code。\n```cpp\ntemplate<typename _Key, typename _Value, typename _ExtractKey,\ntypename _H1, typename _H2, typename _Hash,bool __cache_hash_code>\nstruct _Hash_code_base;\n```\n\n根据是否缓存，得到其偏特化版本：\n\n- 使用范围哈希(实际上就是我们通常说的除留余数法)，不缓存hash code。\n\n```cpp\ntemplate<typename _Key, typename _Value, typename _ExtractKey,\ntypename _H1, typename _H2, typename _Hash>\nstruct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash, false>\n: private _Hashtable_ebo_helper<0, _ExtractKey>,\nprivate _Hashtable_ebo_helper<1, _Hash>\n}\n```\n- 使用范围哈希(实际上就是我们通常说的除留余数法)，缓存hash code。\n\n对于这个偏特化，缓存是没有必要的，所以代码中只是声明，并没有定义！\n\n```cpp\ntemplate<typename _Key, typename _Value, typename _ExtractKey,\ntypename _H1, typename _H2, typename _Hash>\nstruct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash, true>;\n```\n- 有哈希函数以及范围哈希函数，不缓存hash code。\n\n```cpp\ntemplate<typename _Key, typename _Value, typename _ExtractKey,\ntypename _H1, typename _H2>\nstruct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,\n_Default_ranged_hash, false>\n: private _Hashtable_ebo_helper<0, _ExtractKey>,\nprivate _Hashtable_ebo_helper<1, _H1>,\nprivate _Hashtable_ebo_helper<2, _H2>\n{\n};\n```\n- 上述的缓存hash code\n\n```cpp\ntemplate<typename _Key, typename _Value, typename _ExtractKey,\n    typename _H1, typename _H2>\nstruct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,\n            _Default_ranged_hash, true>\n: private _Hashtable_ebo_helper<0, _ExtractKey>,\n    private _Hashtable_ebo_helper<1, _H1>,\n    private _Hashtable_ebo_helper<2, _H2>\n{\n```\n\n上述_H1与_H2大家肯定很迷惑，下面来看一下：\n\n（1） Default range hashing function(默认范围哈希函数)\n\n```cpp\nh1=hash<key>\n```\n下面这个就是：\n\n```cpp\nh2(h1(key),N)=h1(key)%N\n```\n具体可以在后面看到阐述。\n```cpp\nstruct _Mod_range_hashing\n{\n    typedef std::size_t first_argument_type;\n    typedef std::size_t second_argument_type;\n    typedef std::size_t result_type;\n\n    result_type\n    operator()(first_argument_type __num,\n            second_argument_type __den) const noexcept\n    { return __num % __den; }\n};\n```\n别看使用一个struct定义的，大家会以为是类，实际上重载了()操作符，就是个仿函数。\n\n上面对应到哈希表数据结构中，就是大家知道的散列函数：**除留余数法**。\n\n```\nf(__num) = __num mod __den(__den<=__num)\n```\n其次，是`_Default_ranged_hash`:\n\n```cpp\nstruct _Default_ranged_hash { };\n```\n这个只是作为标记用，默认已经计算的范围哈希函数( Default ranged hash function):\n```\nh(k, N) = h2(h1(k), N),\n```\n所以到这，底层的哈希表的散列函数很明显了，默认就是这样的。\n而刚才提到的标记就是由于类型H1与H2的对象组合成H，会消耗额外的拷贝操作，因此这里引出了这个标记。\n\n至此，上面提到的_H1与_H2讲解完毕，就是分别对应上述两个函数。\n\n\n（2） rehash操作\n\n紧接着，还有个比较重要的称为rehash，相信大家很清楚rehash，当散列表的冲突到达一定程度，那么就需要重新将key放到合适位置，而哈希表的底层源码就是这样做的，这里封装成了一个rehash policy：\n```cpp\nstruct _Prime_rehash_policy\n{\n   //...\n};\n```\nrehash操作中提到：桶的大小(bucket size) 默认通常是最小的素数，从而保证装载因子(load factor 容器当前元素数量与桶数量之比。)足够小。装载因子用来衡量哈希表满的程度，最大加载因子默认值为1.0.\n```cpp\n_Prime_rehash_policy(float __z = 1.0)\n: _M_max_load_factor(__z), _M_next_resize(0) { }\n\n\n> rehash计算下一个素数桶\n\n```\n当哈希冲突的时候，怎么rehash呢？\n```c++\n inline std::size_t\n_Prime_rehash_policy::\n_M_next_bkt(std::size_t __n) const\n{\n    const unsigned long* __p = std::lower_bound(__prime_list, __prime_list + _S_n_primes, __n);\n    _M_next_resize = \n        static_cast<std::size_t>(__builtin_ceil(*__p * _M_max_load_factor));\nreturn *__p;\n}\n```\n当发生哈希冲突的时候，该函数会返回一个不小于n的素数来作为一下个桶。\n\n> 素数表\n\n怎么查找素数呢？\n发现上面有个`__prime_list`,于是取查找，在`libstdc++v3/src/shared/hashtable-aux.cc`中找到了所有的素数表。\n里面总共有256+1+49或者256+49个。\n如果sizeof(unsigned long)!=8 就是256+1+49个，否则就是256+49个。\n\n\n```cpp\nextern const unsigned long __prime_list[] = // 256 + 1 or 256 + 48 + 1\n  {\n    2ul, 3ul, 5ul, 7ul, 11ul, 13ul, 17ul, 19ul, 23ul, 29ul, 31ul,\n    37ul, 41ul, 43ul, 47ul, 53ul, 59ul, 61ul, 67ul, 71ul, 73ul, 79ul,\n    83ul, 89ul, 97ul, 103ul, 109ul, 113ul, 127ul, 137ul, 139ul, 149ul，\n    // 后面还有很多\n  }\n```\n所以一切都变得非常清晰，那就是通过lower_bound在上述表中去找第一个大于等于给定值n的素数。\n\n```\nenum { _S_n_primes = sizeof(unsigned long) != 8 ? 256 : 256 + 48 };\n```\n> 计算元素对应的桶\n\n根据最大加载因子算出最小的桶，然后根据桶计算出对于每个元素对应的最小素数桶。\n\n```cpp\ninline std::size_t\n_Prime_rehash_policy::\n_M_bkt_for_elements(std::size_t __n) const\n{\n    // 获取最小的桶\n    const float __min_bkts = __n / _M_max_load_factor;\n    // 获取最小素数p\n    const unsigned long* __p = std::lower_bound(__prime_list, __prime_list\n                        + _S_n_primes, __min_bkts);\n    _M_next_resize =\n        static_cast<std::size_t>(__builtin_ceil(*__p * _M_max_load_factor));\n    return *__p;\n}\n```\n\n\n_Hashtable_ebo_helper就是前面学习过的EBO空基类 \n\n\n`_Map_base`主要是通过偏特化，实现重载操作符`[]`与`at`。\n\n｀_Insert｀主要完成插入相关。\n\n｀_Rehash_base｀主要完成上述rehash中的最大加载因子值的传递。\n\n｀_Equality_base｀主要是为类`_Equality`提供公共类型与函数。\n\n到现在为止，上述的`_Hashtable`继承的所有类都阐述完毕。\n\n## 2.hashtable中链表的节点结构\n\nhash node基类，这个只包含指针声明。\n\n```cpp\nstruct _Hash_node_base\n{\n    _Hash_node_base* _M_nxt;\n\n    _Hash_node_base() noexcept : _M_nxt() { }\n\n    _Hash_node_base(_Hash_node_base* __next) noexcept : _M_nxt(__next) { }\n};\n```\n带节点值的类继承上述基类\n```cpp\ntemplate<typename _Value>\nstruct _Hash_node_value_base : _Hash_node_base\n{\n    typedef _Value value_type;\n\n    __gnu_cxx::__aligned_buffer<_Value> _M_storage;\n\n    _Value*\n    _M_valptr() noexcept\n    { return _M_storage._M_ptr(); }\n\n    const _Value*\n    _M_valptr() const noexcept\n    { return _M_storage._M_ptr(); }\n\n    _Value&\n    _M_v() noexcept\n    { return *_M_valptr(); }\n\n    const _Value&\n    _M_v() const noexcept\n    { return *_M_valptr(); }\n};\n\n```\n前面提到节点是否还有hash code，故在节点中应该得带hash code，而具体在下面中实现：\n\n```cpp\n/**\n*  Primary template struct _Hash_node.\n*/\ntemplate<typename _Value, bool _Cache_hash_code>\nstruct _Hash_node;\n\n/**\n*  Specialization for nodes with caches, struct _Hash_node.\n*\n*  Base class is __detail::_Hash_node_value_base.\n*/\ntemplate<typename _Value>\nstruct _Hash_node<_Value, true> : _Hash_node_value_base<_Value>\n{\n    std::size_t  _M_hash_code;\n\n    _Hash_node*\n    _M_next() const noexcept\n    { return static_cast<_Hash_node*>(this->_M_nxt); }\n};\n\n/**\n*  Specialization for nodes without caches, struct _Hash_node.\n*\n*  Base class is __detail::_Hash_node_value_base.\n*/\ntemplate<typename _Value>\nstruct _Hash_node<_Value, false> : _Hash_node_value_base<_Value>\n{\n    _Hash_node*\n    _M_next() const noexcept\n    { return static_cast<_Hash_node*>(this->_M_nxt); }\n};\n\n```\n\n到这里就很明确了，对于节点，分为包含hash code与不包含，具体是根据传递的模板参数，来调用相应的偏特化版本。\n\n\n## 3.迭代器\n\n迭代器基类显示使用using的语法，这个语法类似于typedef，后面定义就可以直接使用`__node_type`语法来定义，`_M_incr`函数完成链表下一个节点获取。\n\n```cpp\n/// Base class for node iterators.\ntemplate<typename _Value, bool _Cache_hash_code>\nstruct _Node_iterator_base\n{\n    using __node_type = _Hash_node<_Value, _Cache_hash_code>;\n\n    __node_type*  _M_cur;\n\n    _Node_iterator_base(__node_type* __p) noexcept\n    : _M_cur(__p) { }\n\n    void\n    _M_incr() noexcept\n    { _M_cur = _M_cur->_M_next(); }\n};\n```\n\n节点迭代器：对下面代码研读，学习到两点：\n- 第一：using 的使用\n- hashtable的迭代器属于forward_iterator\n- 重载了++,--,*,->，这四个操作符\n\n```cpp\ntemplate<typename _Value, bool __constant_iterators, bool __cache>\nstruct _Node_iterator\n: public _Node_iterator_base<_Value, __cache>\n{\nprivate:\n    using __base_type = _Node_iterator_base<_Value, __cache>;\n    using __node_type = typename __base_type::__node_type;\n\npublic:\n    typedef _Value\t\t\t\t\tvalue_type;\n    typedef std::ptrdiff_t\t\t\t\tdifference_type;\n    typedef std::forward_iterator_tag\t\t\titerator_category;\n\n    using pointer = typename std::conditional<__constant_iterators,\n                    const _Value*, _Value*>::type;\n\n    using reference = typename std::conditional<__constant_iterators,\n                        const _Value&, _Value&>::type;\n\n    _Node_iterator() noexcept\n    : __base_type(0) { }\n\n    explicit\n    _Node_iterator(__node_type* __p) noexcept\n    : __base_type(__p) { }\n\n    reference\n    operator*() const noexcept\n    { return this->_M_cur->_M_v(); }\n\n    pointer\n    operator->() const noexcept\n    { return this->_M_cur->_M_valptr(); }\n\n    _Node_iterator&\n    operator++() noexcept\n    {\n        this->_M_incr();\n        return *this;\n    }\n\n    _Node_iterator\n    operator++(int) noexcept\n    {\n        _Node_iterator __tmp(*this);\n        this->_M_incr();\n        return __tmp;\n    }\n};\n```\n\n## 4.仔细研究hashtable的重要内部结构\n\n内部结构为在每个元素中维护一个单链表, 然后在单链表上执行元素的插入、搜寻、删除等操作，每个元素被称为桶(bucket)，底层构建先采用H1计算出key的hash code，再通过除留余数法H2得到其对应的桶。\n\n```cpp\ntemplate<typename _Key, typename _Value, typename _Alloc,\n    typename _ExtractKey, typename _Equal,\n    typename _H1, typename _H2, typename _Hash,\n    typename _RehashPolicy, typename _Traits>\nclass _Hashtable\nprivate:\n    __bucket_type*\t\t_M_buckets;  //_ Hash_node_base *\n    size_type\t\t\t_M_bucket_count;    // bucket 节点个数\n    __node_base\t\t_M_before_begin;    // _NodeAlloc::value_type\n    size_type\t\t\t_M_element_count;   // //hashtable中list节点个数\n    _RehashPolicy\t\t_M_rehash_policy;  // rehash策略\n    __bucket_type\t\t_M_single_bucket;  // 只需要一个桶用\n};\n```\nhashtable的一些重要函数：\n\n> begin函数\n\n```cpp\niterator\nbegin() noexcept\n{ return iterator(_M_begin()); }\n```\n\n调用`_M_begin`:\n\n可以把`_M_before_begin`想象成一个head节点，第一个节点就是下一个节点。\n\n```cpp\n__node_type*\n_M_begin() const\n{ return static_cast<__node_type*>(_M_before_begin._M_nxt); }\n```\n> end函数\n\n因为是单链表，返回最后一个即可。\n\n```cpp\niterator\nend() noexcept\n{ return iterator(nullptr); }\n```\n\n> size与empty函数\n\n```cpp\nsize_type\nsize() const noexcept\n{ return _M_element_count; }\n\nbool\nempty() const noexcept\n{ return size() == 0; }\n```\n\n> 桶数量\n\n```cpp\nsize_type\nbucket_count() const noexcept\n{ return _M_bucket_count; }\n```\n\n> 计算加载因子\n\n当前元素数量除以桶的数量\n\n```cpp\nfloat\nload_factor() const noexcept\n{\n    return static_cast<float>(size()) / static_cast<float>(bucket_count());\n}\n```\n> 桶的index计算\n\n根据传递进来的key获得桶的index。\n\n```cpp\nsize_type\nbucket(const key_type& __k) const\n{ return _M_bucket_index(__k, this->_M_hash_code(__k)); }\n```\n在`_Hash_code_base`中有如下实现：\n\n而`_M_h1`返回的是`_H1`，`_H1`不知道是什么情况下，我们可以在`unordered_map`中查找到是`_Hash=hash<_Key>`，因此下面这个函数就是数学表达式：\n`h1(k)`来获取hash code。\n\n返回桶的hash code。\n\n```cpp\n__hash_code\n_M_hash_code(const _Key& __k) const\n{ return _M_h1()(__k); }\n```\n\n返回桶的index。\n\n`_M_bucket_index`在同一文件后面找到定义：\n\n```cpp\nsize_type\n_M_bucket_index(const key_type& __k, __hash_code __c) const\n{ return __hash_code_base::_M_bucket_index(__k, __c, _M_bucket_count); }\n```\n我们继续去`__hash_code_base`查找`_M_bucket_index`，可在`bits/hashtable_policy.h`中找到：\n```cpp\nstd::size_t\n_M_bucket_index(const _Key&, __hash_code __c,\n        std::size_t __n) const\n{ return _M_h2()(__c, __n); }\n```\n同上述h1的查找，可以在`unordered_map`中查到`_H2`默认采用`_Mod_range_hashing`，再看这个源码：\n```cpp\nstruct _Mod_range_hashing\n{\n    typedef std::size_t first_argument_type;\n    typedef std::size_t second_argument_type;\n    typedef std::size_t result_type;\n\n    result_type\n    operator()(first_argument_type __num,\n            second_argument_type __den) const noexcept\n    { return __num % __den; }\n};\n```\n对应数学表达式就是`h2(c,n)`。\n\n\n因此上述`bucket`获取桶的index对应的数学表达式就是：\n\n```cpp\nh(k,hash(k))=h(k,hash(k),n)=h(k,hash(k)%n)\n```\n实际上就是最终的：\n\n```cpp\nhash(k)%n\n```\n这个就是桶的index计算。\n"
  },
  {
    "path": "src_analysis/stl/iterator.md",
    "content": "# C++ STL源码剖析之实现一个简单的iterator_category\n\n## 0.导语\n\n本节使用上节Traits特性，研究iterator源码，来实现一个简单的iterator_category，同时对iterator的源码结构进行分析。\n\n**知其然，知其所以然，源码面前了无秘密！**\n\n## 1.利用萃取机实现一个简单的iterator_category识别\n\n上一节指出了迭代器的作用，依旧如下图所示：\n\n![](https://raw.githubusercontent.com/Light-City/cloudimg/master/rela.png)\n\n迭代器是指向序列元素的指针的一种抽象。通过使用迭代器，我们可以访问序列中的某个元素、改变序列中的某个元素的值、使迭代器向前或向后行走等等。\n\n迭代器有常见有五种类型: value_type, difference_type, reference_type, pointer_type都比较容易在 traits 和相应偏特化中提取。\n\n但是，iterator_category一般也有5个，这个相应型别会引发较大规模的写代码工程。\n\n- 单向移动只读迭代器 Input Iterator\n- 单向移动只写迭代器 Output Iterator\n- 单向移动读写迭代器 Forward Iterator\n- 双向移动读写迭代器 Bidirectional Iterator\n\n![](https://raw.githubusercontent.com/Light-City/cloudimg/master/iterator.png)\n\n例如：我们实现了 advanceII, advanceBI, advanceRAI 分别代表迭代器类型是Input Iterator，Bidirectional Iterator和Random Access Iterator的对应实现。\n\n```c++\ntemplate<class Iterator>\nvoid advance(Iterator& i) {\n    if (is_random_access_iterator(i))\n        advanceRAI(i,n);\n    if (is_bidirectional_iterator(i))\n        advanceBI(i,n);\n    else\n        advanceII(i,n);\n}\n```\n\n但这样在执行时期才决定使用哪一个版本，会**影响程序效率**。最好能够在编译期就选择正确的版本。\n\n而**重载**这个函数机制可以达成这个目标。\n\n而对于`advanceXX()`都有两个函数参数，型别都未定(因为都是模板参数)。为了令其同名，形成重载函数，我们必须加上一个型别已确定的函数参数，使函数重载机制得以有效运作起来。\n\n设计如下：如果**traits**有能力萃取出迭代器的种类，我们便可利用这个\"迭代器类型\"相应型别作为advancexx的第三个参数，而这个相应型别必须是一个class type，不能只是数值号码类的东西，因为编译器需依赖它来进行**重载决议**。\n\n下面来进行实现，首先给出一个总体结构图：\n\n![](https://raw.githubusercontent.com/Light-City/cloudimg/master/itera.png)\n\n定义出下面tag：\n\n```c++\nstruct input_iterator_tag {};\nstruct output_iterator_tag {};\nstruct forward_iterator_tag : public input_iterator_tag {};\nstruct bidirectional_iterator_tag : public forward_iterator_tag {};\nstruct random_access_iterator_tag : public bidirectional_iterator_tag {};\n// 继承的好处就是，当函数需要用 input_iterator_tag 的时候\n// 假设你传进一个forward_iterator_tag，它会沿继承向上找，知道符合条件\n```\n\n声明了一些列 tag 之后，我们就可以重载 advance函数，我们把这些函数用下滑线来定义，表示在内部使用，外部不可见。\n\n```c++\n// 继承的好处就是，当函数需要用 input_iterator_tag 的时候\n// 假设你传进一个forward_iterator_tag，它会沿继承向上找，知道符合条件\n// input iterator\ntemplate<class inputIterator, class distance>\ninline void __advance(inputIterator&i, distance n,\n                      input_iterator_tag) {\n    std::cout << \"input tag\" << std::endl;\n}\n// output iterator\ntemplate<class outputIterator, class distance>\ninline void __advance(outputIterator&i, distance n,\n                      output_iterator_tag) {\n    std::cout << \"output tag\" << std::endl;\n}\n\n// forward iterator\ntemplate<class ForwardIterator, class Distance>\ninline void __advance(ForwardIterator &i, Distance n,\n                      forward_iterator_tag) {\n    std::cout << \"forward tag\" << std::endl;\n}\n\n// bidrectional iterator\ntemplate<class BidiectionalIterator, class Distance>\ninline void __advance(BidiectionalIterator &i, Distance n,\n                      bidiectional_iterator_tag) {\n    std::cout << \"bidrectional tag\" << std::endl;\n\n}\n\n// RandomAccess iterator\ntemplate<class RandomAccessIterator, class Distance>\ninline void __advance(RandomAccessIterator &i, Distance n,\n                      random_access_iterator_tag) {\n    std::cout << \"randomaccess tag\" << std::endl;\n\n}\n```\n\n定义萃取机：\n\n```c++\n// traits 型别\ntemplate<class I>\nstruct Iterator_traits {\n    typedef typename I::iterator_category iterator_category;\n};\n\n// 针对原生指针设计的\"偏特化版\"\ntemplate<class I>\nstruct Iterator_traits<I *> {\n    typedef random_access_iterator_tag iterator_category;\n};\ntemplate<class I>\nstruct Iterator_traits<const I *> {\n    typedef random_access_iterator_tag iterator_category;\n};\n```\n\n对外暴露接口：\n\n```c++\n// 对外接口\ntemplate<class InputIterator, class Distance>\ninline void advance(InputIterator &i, Distance n) {\n    // 通过Ierator_traits询问它的iterator_category是谁\n    typedef typename Iterator_traits<InputIterator>::iterator_category category;\n    __advance(i, n, category()); // 各型别的重载\n}\n\n```\n\n定义class type：\n\n```c++\n// class type\ntemplate<class Category>\nstruct iterator {\n    typedef Category iterator_category;\n};\n```\n\n开始测试，我们使用上述定义的class type与原生指针来测试，分别进入萃取机的普通萃取机与偏特化萃取机，看看是否得到相应的Tag。\n\n```c++\nint main() {\n    iterator<input_iterator_tag> input;\n    iterator<output_iterator_tag> output;\n    iterator<forward_iterator_tag> forward;\n    iterator<bidiectional_iterator_tag> bidect;\n    iterator<random_access_iterator_tag> random;\n    advance(input, 10);\n    advance(output, 10);\n    advance(forward, 10);\n    advance(bidect, 10);\n    advance(random, 10);\n    int *p=NULL;\n    advance(p,10);\n    return 0;\n}\n```\n\n输出结果：\n\n```c++\ninput tag\noutput tag\nforward tag\nbidrectional tag\nrandomaccess tag\nrandomaccess tag\n```\n\n一切如我们预期一样，通过萃取机，我们获得了每个迭代器的tag，以及原生指针的tag。\n\n我们再想得复杂一些，如果我们想知道advance的返回类型，那如何做呢？\n\n首先修改`advance`返回：\n\n```c++\n// 对外接口\ntemplate<class InputIterator, class Distance>\ninline typename Iterator_traits<InputIterator>::iterator_category\nadvance(InputIterator &i, Distance n) {\n    // 通过Ierator_traits询问它的iterator_category是谁\n    typedef typename Iterator_traits<InputIterator>::iterator_category category;\n    return __advance(i, n, category()); // 各型别的重载\n}\n```\n\n紧接着修改`__advance`返回：\n\n```c++\n// input iterator\ntemplate<class inputIterator, class distance>\ninline typename Iterator_traits<inputIterator>::iterator_category\n__advance(inputIterator &i, distance n,\n          input_iterator_tag) {\n    std::cout << \"input tag\" << std::endl;\n    return input_iterator_tag();\n}\n\n// output iterator\ntemplate<class outputIterator, class distance>\ninline typename Iterator_traits<outputIterator>::iterator_category\n__advance(outputIterator &i, distance n,\n          output_iterator_tag) {\n    std::cout << \"output tag\" << std::endl;\n    return output_iterator_tag();\n}\n\n// forward iterator\ntemplate<class ForwardIterator, class Distance>\ninline typename Iterator_traits<ForwardIterator>::iterator_category\n__advance(ForwardIterator &i, Distance n,\n          forward_iterator_tag) {\n    std::cout << \"forward tag\" << std::endl;\n    return forward_iterator_tag();\n}\n\n// bidrectional iterator\ntemplate<class BidiectionalIterator, class Distance>\ninline typename Iterator_traits<BidiectionalIterator>::iterator_category\n__advance(BidiectionalIterator &i, Distance n,\n          bidiectional_iterator_tag) {\n    std::cout << \"bidrectional tag\" << std::endl;\n    return bidiectional_iterator_tag();\n}\n\n// RandomAccess iterator\ntemplate<class RandomAccessIterator, class Distance>\ninline typename Iterator_traits<RandomAccessIterator>::iterator_category\n__advance(RandomAccessIterator &i, Distance n,\n          random_access_iterator_tag) {\n    std::cout << \"randomaccess tag\" << std::endl;\n    return random_access_iterator_tag();\n}\n```\n\n只需要把`void`修改为相应的萃取机即可。\n\n最后测试修改，添加上返回：\n\n```c++\nint main() {\n    iterator<input_iterator_tag> input;\n    iterator<output_iterator_tag> output;\n    iterator<forward_iterator_tag> forward;\n    iterator<bidiectional_iterator_tag> bidect;\n    iterator<random_access_iterator_tag> random;\n    input_iterator_tag inputIteratorTag = advance(input, 10);\n    output_iterator_tag outputIteratorTag = advance(output, 10);\n    forward_iterator_tag forwardIteratorTag = advance(forward, 10);\n    bidiectional_iterator_tag bidiectionalIteratorTag = advance(bidect, 10);\n    random_access_iterator_tag randomAccessIteratorTag = advance(random, 10);\n    int *p = NULL;\n    random_access_iterator_tag v = advance(p, 10);\n    return 0;\n}\n```\n\n至此，一个简单的迭代器类型在编译器判别实现完毕。\n\n## 2.STL源码剖析Iterator\n\n在`bits/stl_iterator_base_types.h`中也是如上述所示(实际上，上面就是STL源码的简单版，很接近)，来我们一起来看。\n\n（1）`tag`\n\n```c++\n ///  Marking input iterators.\n  struct input_iterator_tag { };\n\n  ///  Marking output iterators.\n  struct output_iterator_tag { };\n\n  /// Forward iterators support a superset of input iterator operations.\n  struct forward_iterator_tag : public input_iterator_tag { };\n\n  /// Bidirectional iterators support a superset of forward iterator\n  /// operations.\n  struct bidirectional_iterator_tag : public forward_iterator_tag { };\n\n  /// Random-access iterators support a superset of bidirectional\n  /// iterator operations.\n  struct random_access_iterator_tag : public bidirectional_iterator_tag { };\n```\n\n与我上面用的一样。\n\n（2）`iterator_traits`萃取机，里面包含五种，而上面只是实现其中的一种：`iterator_category`。所以在STL中容器与算法之间的桥梁iterator必须包含下面五种 typedef。\n\n```c++\ntemplate<typename _Iterator>\nstruct iterator_traits\n{\n  typedef typename _Iterator::iterator_category iterator_category;\n  typedef typename _Iterator::value_type        value_type;\n  typedef typename _Iterator::difference_type   difference_type;\n  typedef typename _Iterator::pointer           pointer;\n  typedef typename _Iterator::reference         reference;\n};\n```\n\n（3）`iterator`\n\n上面提到的class type为下面的简单版，对比一下，没有啥区别，就是模板参数多了一些，typedef多了。\n\n```c++\ntemplate<typename _Category, typename _Tp, typename _Distance = ptrdiff_t,\n       typename _Pointer = _Tp*, typename _Reference = _Tp&>\nstruct iterator\n{\n  /// One of the @link iterator_tags tag types@endlink.\n  typedef _Category  iterator_category;\n  /// The type \"pointed to\" by the iterator.\n  typedef _Tp        value_type;\n  /// Distance between iterators is represented as this type.\n  typedef _Distance  difference_type;\n  /// This type represents a pointer-to-value_type.\n  typedef _Pointer   pointer;\n  /// This type represents a reference-to-value_type.\n  typedef _Reference reference;\n};\n```\n\n至此，iterator与traits特性分析完毕。欢迎与我共同探讨STL源码奥秘，如侯捷老师所说：**源码面前了无秘密。**"
  },
  {
    "path": "src_analysis/stl/list.md",
    "content": "# C++ STL源码剖析之双向环形链表list\n\n## 0. 导语\n\n源码对应的版本为**gcc-4.9.1**\n\n### 1.list\n\nlist为双向环形链表，其结构为：\n\n![](https://raw.githubusercontent.com/Light-City/cloudimg/master/list_1.png)\n\n自己绘制的图如下：\n\n![list_all](https://raw.githubusercontent.com/Light-City/cloudimg/master/list_a.png)\n\n双向环状链表从节点值为3开始插入，红色框表示最后一个节点(end()指向的节点)。黄色线条表示指向前驱节点，黑色线条表示指向后继节点。\n\n### 1.1 list源码\n\n#### 1.1.1 类结构\n\n```c++\n template<typename _Tp, typename _Alloc = std::allocator<_Tp> >\n class list : protected _List_base<_Tp, _Alloc> \n {\n        \n }\n```\n\n`list`继承`_List_base`。\n\n#### 1.1.2 双向环形链表实现\n\n【**构造函数**】\n\n**（1）不带任何元素的list**\n\n```c++\nexplicit\nlist(const allocator_type &__a) _GLIBCXX_NOEXCEPT: _Base(_Node_alloc_type(__a)) {}\n```\n\n**（2）带n个元素且赋予初值的list**\n\n```c++\nexplicit list(size_type __n, const value_type &__value = value_type(),const allocator_type &__a = allocator_type()) : _Base(_Node_alloc_type(__a)) \n{ _M_fill_initialize(__n, __value); }\n```\n\n**（3）从一个范围中进行初始化list**\n\n```c++\ntemplate<typename _InputIterator>\nlist(_InputIterator __first, _InputIterator __last,\n     const allocator_type &__a = allocator_type())\n        : _Base(_Node_alloc_type(__a)) {\n    // Check whether it's an integral type.  If so, it's not an iterator.\n    typedef typename std::__is_integer<_InputIterator>::__type _Integral;\n    _M_initialize_dispatch(__first, __last, _Integral());\n}\n```\n\n【**创建节点**】\n\n做的事情：创建一个新的节点并动态分配内存，返回节点。\n\n```c++\n_Node *_M_create_node(const value_type &__x) {\n    _Node *__p = this->_M_get_node();\n    __try\n    {\n        _M_get_Tp_allocator().construct\n                (std::__addressof(__p->_M_data), __x);\n    }\n    __catch(...)\n    {\n        _M_put_node(__p);\n        __throw_exception_again;\n    }\n    return __p;\n}\n```\n\n注意到里面有两个重要的函数`_M_get_node`与`_M_put_node`，我们来查看后发现这些方法来自基类，源码为：\n\n```c++\n_List_node<_Tp> * _M_get_node() { return _M_impl._Node_alloc_type::allocate(1); }\n\nvoid _M_put_node(_List_node<_Tp> *__p)   _GLIBCXX_NOEXCEPT\n{ _M_impl._Node_alloc_type::deallocate(__p, 1); }\n```\n\n对应的就是创建节点动态分配内存，若创建过程中抛出异常，则释放内存。\n\n【**插入节点**】\n\n插入节点包括：\n\n- 尾部插入n个指定节点值的节点，对应的函数`_M_fill_initialize`\n\n在list的构造函数中使用：\n\n```c++\nexplicit list(size_type __n, const value_type &__value = value_type(),const allocator_type &__a = allocator_type()) : _Base(_Node_alloc_type(__a)) \n{ _M_fill_initialize(__n, __value); }\n```\n\n- 指定位置插入指定节点值的节点，对应的函数`_M_insert`\n\n其中大家经常使用的`push_back`与`push_front`底层就是调用`_M_insert`函数。\n\n两者函数区别是：\n\n```c++\nthis->_M_insert(end(), __x);  // push_back   尾部插入  \nthis->_M_insert(begin(), __x); // push_front 头部插入\n```\n\n- **双向环形链表插入函数**`_M_hook`   **(最重要！！！)**\n\n像前面提到的`push_back`、`push_front`、`_M_insert`，还有`insert`都是使用最基础的双向链表插入函数`_M_hook`实现的。\n\n**下面来深入研究一下：**\n\n其中`_M_fill_initialize`源码如下：\n\n```c++\nvoid _M_fill_initialize(size_type __n, const value_type &__x) {\n    for (; __n; --__n)\n        push_back(__x);\n}\n```\n\n其中`push_back`源码如下：\n\n```c++\nvoid push_back(const value_type &__x) { this->_M_insert(end(), __x); }\n```\n\n其中`_M_insert`，在指定的位置插入初始值为x的节点。\n\n```c++\nvoid _M_insert(iterator __position, const value_type &__x) {\n    _Node *__tmp = _M_create_node(__x);\n    __tmp->_M_hook(__position._M_node);\n}\n```\n\n其中`_M_hook`实现在`gcc-4.9.1/libstdc++-v3/src/c++98/list.cc`中,当然`_List_node_base`的其他函数，例如：`_M_unhook`也在这个文件中。\n\n```c++\n// 在指定的位置前插入this指向的节点\nvoid_List_node_base::_M_hook(_List_node_base* const __position) _GLIBCXX_USE_NOEXCEPT\n{\n  this->_M_next = __position;\t\t\n  this->_M_prev = __position->_M_prev;\n  __position->_M_prev->_M_next = this;\n  __position->_M_prev = this;\n}\n```\n\n所以上述细分为两个函数：我们把上述代码进行总结：\n\n（1）在指定的位置插入初始值为x的节点\n\n```c++\nvoid _M_insert(iterator __position, const value_type &__x) {\n    _Node *__tmp = _M_create_node(__x);\n    __tmp->_M_next = __position;\t\t\t// 第一步\n    __tmp->_M_prev = __position->_M_prev;\t// 第二步\n    __position->_M_prev->_M_next = __tmp;\t// 第三步\n    __position->_M_prev = __tmp;\t\t\t// 第四步\n}\n```\n\n这种插入是在指定位置前插入，(对应到代码就是)例如实现在指定`__position`节点为7前插入节点值9的节点(对应到代码就是__tmp)，下面阐述了具体的插入流程。\n\n![list_insert](https://raw.githubusercontent.com/Light-City/cloudimg/master/list_insert.png)\n\n（2）在末尾依次插入n个节点值为x的节点\n\n```c++\nvoid _M_fill_initialize(size_type __n, const value_type &__x) {\n\tfor (; __n; --__n)\n\t\t_M_insert(end(), __x);\n}\n```\n\n对于上述的代码大家或许会联想到`insert`,它有三个。`insert`实现文件在`libstdc++-v3/include/bits/list.tcc`。\n\n- **第一：在指定迭代器之前插入指定元素值节点。**\n\n实现是调用前面的`_M_hook`函数。\n\n```c++\ntemplate<typename _Tp, typename _Alloc>\ntypename list<_Tp, _Alloc>::iterator\nlist<_Tp, _Alloc>::\n#if __cplusplus >= 201103L\ninsert(const_iterator __position, const value_type& __x)\n#else\ninsert(iterator __position, const value_type& __x)\n#endif\n{\n  _Node* __tmp = _M_create_node(__x);\n  __tmp->_M_hook(__position._M_const_cast()._M_node);\n  return iterator(__tmp);\n}\n```\n\n- **第二：在指定迭代器之前插入n个指定节点值的节点。**\n\n```c++\nvoid insert(iterator __position, size_type __n, const value_type &__x) {\n    list __tmp(__n, __x, get_allocator());\n    splice(__position, __tmp);\n}\n```\n\n实现是先调用list构造函数，完成创建一个拥有n个指定节点值的list。\n\n```c++\nexplicit list(size_type __n, const value_type &__value = value_type(),const allocator_type &__a = allocator_type()) : _Base(_Node_alloc_type(__a)) \n{ _M_fill_initialize(__n, __value); }\n```\n\n然后使用`splice`函数完成从另一个list中来插入当前list。\n\n```c++\nvoid splice(iterator __position, list &__x)\n{\n    if (!__x.empty()) {\n        _M_check_equal_allocators(__x);\n\n        this->_M_transfer(__position._M_const_cast(),\n                          __x.begin(), __x.end());\n    }\n}\n```\n\n其中`_M_transfer`追踪代码，可以知道：\n\n```c++\n// Moves the elements from [first,last) before position.\nvoid\n_M_transfer(iterator __position, iterator __first, iterator __last) {\n    __position._M_node->_M_transfer(__first._M_node, __last._M_node);\n}\n```\n\n再次分析得到其来自list的基类`_List_node_base`，而`_M_transfer`实现文件在`gcc-4.9.1/libstdc++-v3/src/c++98/list.cc`中：\n\n```c++\nvoid _List_node_base::\n_M_transfer(_List_node_base * const __first,\n    _List_node_base * const __last) _GLIBCXX_USE_NOEXCEPT\n{\n  if (this != __last)\n  {\n      // Remove [first, last) from its old position.\n      __last->_M_prev->_M_next  = this;\n      __first->_M_prev->_M_next = __last;\n      this->_M_prev->_M_next    = __first;\n\n      // Splice [first, last) into its new position.\n      _List_node_base* const __tmp = this->_M_prev;\n      this->_M_prev                = __last->_M_prev;\n      __last->_M_prev              = __first->_M_prev;\n      __first->_M_prev             = __tmp;\n  }\n}\n```\n\n仍然是上述的图：\n\n![list_all](https://raw.githubusercontent.com/Light-City/cloudimg/master/list_a.png)\n\n经过前面分析，我们知道`splice`是将上述图的所代表的整个list插入指定迭代器前面，例如，我们想要在下面两个节点前面插入，具体图形步骤如下：\n\n`this`代表的节点为值为8的节点，下图描述的就是在节点10与节点8中间插入整个list。\n\n`__last`代表的是红色框节点，该节点为`end()`指向的节点，我们是不需要该节点的，所以在后面处理中，会把该节点从整个list中去除掉。\n\n`__first`代表的是图中节点值为3的节点。\n\n**第一步：先将最后一个有效节点，也就是红色框的前一个节点的next指针指向指定的节点8。**\n\n![](https://raw.githubusercontent.com/Light-City/cloudimg/master/step1.png)\n\n对应代码为：\n\n```\n__last->_M_prev->_M_next  = this;\t\n```\n\n**第二步：`_last`的next指针指向自己。**\n\n![](https://raw.githubusercontent.com/Light-City/cloudimg/master/step2.png)\n\n对应的代码为：\n\n```\n__first->_M_prev->_M_next = __last;\n```\n\n**第三步：让指定迭代器之前的节点的nex指向原先list的第一个节点(`__first`)。**\n\n![](https://raw.githubusercontent.com/Light-City/cloudimg/master/step3.png)\n\n对应的代码为：\n\n```\nthis->_M_prev->_M_next    = __first;\n```\n\n**第四步：保存指定迭代器的前驱节点(对应到哪图中的节点值为10的节点)。**\n\n```\n_List_node_base* const __tmp = this->_M_prev;\n```\n\n**第五步：指定迭代器的前驱节点指向原list中实际最后一个节点(end()前一节点)。**\n\n![](https://raw.githubusercontent.com/Light-City/cloudimg/master/step5.png)\n\n对应的代码为：\n\n```\nthis->_M_prev                = __last->_M_prev;\n```\n\n**第六步：让原list的最后一个节点(end()指向的节点)的prev指向自己。**\n\n![](https://raw.githubusercontent.com/Light-City/cloudimg/master/step6.png)\n\n对应的代码为：\n\n```\n__last->_M_prev              = __first->_M_prev;\n```\n\n**第七步：让原list第一个节点的prev指向第四步保存的节点。**\n\n![](https://raw.githubusercontent.com/Light-City/cloudimg/master/step7.png)\n\n对应的代码为：\n\n```\n__first->_M_prev             = __tmp;\n```\n\n这样经过以上七步操作，完成了在节点8与节点10之前插入一个list。\n\n- **第三：从一个list范围把数据插入到指定迭代器前面。**\n\n```c++\ntemplate<typename _InputIterator>\nvoid\ninsert(iterator __position, _InputIterator __first,\n       _InputIterator __last) {\n    list __tmp(__first, __last, get_allocator());\n    splice(__position, __tmp);\n}\n```\n\n原理同上，只不过这个`__tmp`是调用另外一个构造函数。\n\n【**删除节点**】\n\n- 删除指定节点\n\n删除指定节点分为两个，第一个通过迭代器删除，第二个是通过元素值删除。\n\n**（1）通过迭代器删除，对应函数为erase**\n\n其中`pop_front`与`pop_back`、`erase`、`remove`底层实现基于`_M_erase`函数。\n\n```c++\nthis->_M_erase(begin()); \t// pop_front 不断删除起点的元素\nthis->_M_erase(iterator(this->_M_impl._M_node._M_prev)); // pop_back移除最后一个元素\n```\n\n在`libstdc++-v3/include/bits/list.tcc`中：\n\n```c++\nerase(iterator __position)\n#endif\n{\n  iterator __ret = iterator(__position._M_node->_M_next);\n  _M_erase(__position._M_const_cast());\n  return __ret;\n}\n```\n\n**（2）通过元素值删除，对应函数为remove**\n\n特殊情况处理，当删除元素的地址与迭代器的地址一样的时候，先保存起来，最后判断保存的迭代器是不是end()，如果不是，就删除掉。底层仍旧是通过`_M_erase`删除。\n\n```c++\ntemplate<typename _Tp, typename _Alloc>\nvoid list<_Tp, _Alloc>::remove(const value_type& __value)\n{\n  iterator __first = begin();\n  iterator __last = end();\n  iterator __extra = __last;\n  while (__first != __last)\n  {\n      iterator __next = __first;\n      ++__next;\n      if (*__first == __value)\n        {\n          // _GLIBCXX_RESOLVE_LIB_DEFECTS\n          // 526. Is it undefined if a function in the standard changes\n          // in parameters?\n          if (std::__addressof(*__first) != std::__addressof(__value))\n        _M_erase(__first);\n          else\n        __extra = __first;\n        }\n      __first = __next;\n  }\n  if (__extra != __last)\n\t_M_erase(__extra);\n}\n```\n\n除了这个remove外，还有一个是`remove_if`，根据条件来删除。\n\n```c++\ntemplate<typename _Tp, typename _Alloc>\ntemplate <typename _Predicate>\nvoid list<_Tp, _Alloc>::\nremove_if(_Predicate __pred)\n{\n    iterator __first = begin();\n    iterator __last = end();\n\twhile (__first != __last)\n    {\n        iterator __next = __first;\n        ++__next;\n        if (__pred(*__first))\n          _M_erase(__first);\n        __first = __next;\n    }\n}\n\n```\n\n对上述的`remove`的if去掉，在里面添加一个判断即可。\n\n使用如下：\n\n```c++\nbool isone(int one) {\n    return one==2;\n}\nint main() {\n    list<int> t;\n    t={3,4,0,2,0,10,10};\n    for(auto i:t) cout<<i<<\" \";\t // 3 4 0 2 0 10 10 \n    cout<<endl;\n    t.remove_if(isone);\n    for(auto i:t) cout<<i<<\" \";  // 3 4 0 0 10 10 \n    cout<<endl;\n}\n```\n\n- 删除一系列节点\n- 删除所有节点，对应的函数`clear`\n\n**（1）删除指定节点详细分析**\n\n```c++\n_M_erase(iterator __position)\n_GLIBCXX_NOEXCEPT\n{\n    __position._M_node->_M_unhook();\n    _Node *__n = static_cast<_Node *>(__position._M_node);\n#if __cplusplus >= 201103L\n    _M_get_Node_allocator().destroy(__n);\n#else\n    _M_get_Tp_allocator().destroy(std::__addressof(__n->_M_data));\n#endif\n    _M_put_node(__n);\t\t// 释放内存\n}\n\n```\n\n其中`_M_unhook`实现在`gcc-4.9.1/libstdc++-v3/src/c++98/list.cc`，实现如下：\n\n```c++\nvoid _List_node_base::_M_unhook() _GLIBCXX_USE_NOEXCEPT\n{\n  _List_node_base* const __next_node = this->_M_next;\t// 第一步：保存后继节点\n  _List_node_base* const __prev_node = this->_M_prev;\t// 第二步：保存前驱节点\n  __prev_node->_M_next = __next_node;\t\t\t\t\t// 第三步：前驱节点的next指向后继节点\n  __next_node->_M_prev = __prev_node;\t\t\t\t\t// 第四步：后继节点的prev指向前驱节点\n}\n```\n\n例如：删除节点值为9的节点，第三与第四步图解：\n\n![list_erase](https://raw.githubusercontent.com/Light-City/cloudimg/master/list_erase.png)\n\n**（2）删除一系列元素详细分析**\n\n```c++\niterator\n#if __cplusplus >= 201103L\nerase(const_iterator __first, const_iterator __last) noexcept\n#else\nerase(iterator __first, iterator __last)\n#endif\n{\n    while (__first != __last)\n        __first = erase(__first);\n    return __last._M_const_cast();\n}\n```\n\n使用erase，删除给定迭代器范围内的数据。\n\n**（3）删除所有元素详细分析**\n\n清空元素，并初始化，回到list默认状态。\n\n```c++\nvoid clear()\n_GLIBCXX_NOEXCEPT\n{\n    _Base::_M_clear();\n    _Base::_M_init();\n}\n```\n\n其中`_M_clear`实现在：`libstdc++-v3/include/bits/list.tcc`中：\n\n```c++\n_List_base<_Tp, _Alloc>::\n_M_clear() _GLIBCXX_NOEXCEPT\n{\n  typedef _List_node<_Tp>  _Node;\n  _Node* __cur = static_cast<_Node*>(_M_impl._M_node._M_next);\n  while (__cur != &_M_impl._M_node)\n    {\n      _Node* __tmp = __cur;\t\t\t// 保存节点\n      __cur = static_cast<_Node*>(__cur->_M_next);\t// 往后遍历\n    #if __cplusplus >= 201103L\n      _M_get_Node_allocator().destroy(__tmp);\n    #else\n      _M_get_Tp_allocator().destroy(std::__addressof(__tmp->_M_data));\n    #endif\n      _M_put_node(__tmp);\t// 释放内存\n    }\n}\n```\n\n`_M_init`实现，全部指向自己即可。\n\n```c++\nvoid _M_init()\n_GLIBCXX_NOEXCEPT\n{\n    this->_M_impl._M_node._M_next = &this->_M_impl._M_node;\n    this->_M_impl._M_node._M_prev = &this->_M_impl._M_node;\n}\n```\n\n【**元素访问**】\n\n每个都实现了两个版本：引用与常引用。\n\n- front 返回第一个元素\n\n```c++\nreference front()\n_GLIBCXX_NOEXCEPT\n{ return *begin(); }\nconst_reference\nfront() const\n_GLIBCXX_NOEXCEPT\n{ return *begin(); }\n```\n\n- 返回最后一个元素\n\n```c++\nreference\nback()\n_GLIBCXX_NOEXCEPT\n{\n    iterator __tmp = end();\n    --__tmp;\n    return *__tmp;\n}\nconst_reference\nback() const\n_GLIBCXX_NOEXCEPT\n{\n    const_iterator __tmp = end();\n    --__tmp;\n    return *__tmp;\n}\n```\n\n【**算法**】\n\n- unique\n\n从容器中的每个连续的相等元素组中除去除第一个元素外的所有元素。\n\n请注意，只有与[列表](http://www.cplusplus.com/list)容器紧邻的元素相比，该元素才从[列表](http://www.cplusplus.com/list)容器中删除。因此，此功能对于排序列表特别有用。\n\n```c++\ntemplate<typename _Tp, typename _Alloc>\ntemplate <typename _BinaryPredicate>\n  void\n  list<_Tp, _Alloc>::\n  unique(_BinaryPredicate __binary_pred)\n  {\n    iterator __first = begin();\n    iterator __last = end();\n    if (__first == __last)\n  return;\n    iterator __next = __first;\n    while (++__next != __last)\n  {\n    // 满足条件就删除\n    if (__binary_pred(*__first, *__next))\n       // 删除\n      _M_erase(__next);\n    else\n      __first = __next;\n    __next = __first;\n  }\n  }\n```\n\n举例如下：\n\n```c++\n// list::unique\n#include <iostream>\n#include <cmath>\n#include <list>\n\n// a binary predicate implemented as a function:\nbool same_integral_part (double first, double second)\n{ return ( int(first)==int(second) ); }\n\n// a binary predicate implemented as a class:\nstruct is_near {\n  bool operator() (double first, double second)\n  { return (fabs(first-second)<5.0); }\n};\n\nint main ()\n{\n  double mydoubles[]={ 12.15,  2.72, 73.0,  12.77,  3.14,\n                       12.77, 73.35, 72.25, 15.3,  72.25 };\n  std::list<double> mylist (mydoubles,mydoubles+10);\n  \n  mylist.sort();             //  2.72,  3.14, 12.15, 12.77, 12.77,\n                             // 15.3,  72.25, 72.25, 73.0,  73.35\n\n  mylist.unique();           //  2.72,  3.14, 12.15, 12.77\n                             // 15.3,  72.25, 73.0,  73.35\n\n  mylist.unique (same_integral_part);  //  2.72,  3.14, 12.15\n                                       // 15.3,  72.25, 73.0\n\n  mylist.unique (is_near());           //  2.72, 12.15, 72.25\n\n  std::cout << \"mylist contains:\";\n  for (std::list<double>::iterator it=mylist.begin(); it!=mylist.end(); ++it)\n    std::cout << ' ' << *it;\n  std::cout << '\\n';\n\n  return 0;\n}\n```\n\n上述排序后会删除所有的重复元素，只剩下一个，而不排序只会删除重复且连续的元素。\n\n- merge\n\nmerge源码实现采用前面`_M_transfer`函数，假设现在有两个list，分别是list1与list2。list1中的元素与list2中的元素进行比较，如果list1中元素值小于list2中元素值，则对list1迭代器++，不进行任何操作，而如果list1中的元素值大于list2中的元素值，那么每次将list2这个小的元素对应的迭代器塞入`_M_transfer`函数中，通过这个函数完成向list1中刚才比较的迭代器前面插入list2较小的元素，那么最后所有元素都会被插入到list1中。\n\n当list1已经遍历完毕，而list2还没有遍历完毕，那么只需要执行一次`_M_transfer`，将list2链表从当前迭代器开始到最后的end插入到list1的末尾即可。\n\n```c++\ntemplate<typename _Tp, typename _Alloc>\nvoid\nlist<_Tp, _Alloc>::\n#if __cplusplus >= 201103L\nmerge(list&& __x)\n#else\nmerge(list& __x)\n#endif\n{\n  // _GLIBCXX_RESOLVE_LIB_DEFECTS\n  // 300. list::merge() specification incomplete\n  if (this != &__x)\n  {\n      _M_check_equal_allocators(__x);\n\n      iterator __first1 = begin();\n      iterator __last1 = end();\n      iterator __first2 = __x.begin();\n      iterator __last2 = __x.end();\n      while (__first1 != __last1 && __first2 != __last2)\n        if (*__first2 < *__first1)\n         {\n            iterator __next = __first2;\n            _M_transfer(__first1, __first2, ++__next);\n            __first2 = __next;\n         }\n        else\n          ++__first1;\n      if (__first2 != __last2)\n        _M_transfer(__last1, __first2, __last2);\n   }\n}\n```\n\n使用：\n\n```c++\nint main() {\n \tlist<int> l1 = {2,3,5,7};\n    list<int> l2 = {1,10,9,5};\n    l1.sort();\n    l2.sort();\n    l1.merge(l2);\n    for(auto i:l1) cout<<i<<\" \";\t// 1 2 3 5 5 7 9 10\n    return 0;\n}\n```\n\n- sort\n\n由于STL本身的排序算法sort接受的输入迭代器是随机访问迭代器，但是双向list链表容器的访问方式是双向迭代器，因此，不能使用STL本身的排序算法sort，必须自己定义属于自己访问的排序算法。我们从源码的剖析中，可以看到该排序算法思想类似于归并排序。\n\nsort中splice调用的是：\n\n```c++\nvoid splice(const_iterator __position, list& __x, const_iterator __i) noexcept\n{ splice(__position, std::move(__x), __i); }\n```\n\n进一步分析：\n\n```c++\nvoid\nsplice(iterator __position, list &__x, iterator __i)\n{\n    iterator __j = __i._M_const_cast();\n    ++__j;\n    if (__position == __i || __position == __j)\n        return;\n\n    if (this != &__x)\n        _M_check_equal_allocators(__x);\n\n    this->(__position._M_const_cast(),\n                      __i._M_const_cast(), __j);\n}\n```\n\n最后调用的是`_M_transfer`。\n\n在sort中还有一个函数`swap`，完成两个链表交换，实现代码在`gcc-4.9.1/libstdc++-v3/src/c++98/list.cc`中：\n\n```c++\nvoid\n_List_node_base::swap(_List_node_base& __x,\n          _List_node_base& __y) _GLIBCXX_USE_NOEXCEPT\n{\n      if ( __x._M_next != &__x )\n    {\n      if ( __y._M_next != &__y )\n        {\n          // Both __x and __y are not empty.\n          std::swap(__x._M_next,__y._M_next);\n          std::swap(__x._M_prev,__y._M_prev);\n          __x._M_next->_M_prev = __x._M_prev->_M_next = &__x;\n          __y._M_next->_M_prev = __y._M_prev->_M_next = &__y;\n        }\n      else\n        {\n              // __x is not empty, __y is empty.\n              __y._M_next = __x._M_next;\n              __y._M_prev = __x._M_prev;\n              __y._M_next->_M_prev = __y._M_prev->_M_next = &__y;\n              __x._M_next = __x._M_prev = &__x;\n            }\n        }\n      else if ( __y._M_next != &__y )\n        {\n          // __x is empty, __y is not empty.\n          __x._M_next = __y._M_next;\n          __x._M_prev = __y._M_prev;\n          __x._M_next->_M_prev = __x._M_prev->_M_next = &__x;\n          __y._M_next = __y._M_prev = &__y;\n        }\n}\n```\n\n具体的实现思路是，判断两个链表为空还是不为空，然后修改next指针与prev指针。\n\n下面来看看强大的sort：\n\n```c++\ntemplate<typename _Tp, typename _Alloc>\nvoid\nlist<_Tp, _Alloc>::\nsort() {\n    // Do nothing if the list has length 0 or 1.\n    if (this->_M_impl._M_node._M_next != &this->_M_impl._M_node\n        && this->_M_impl._M_node._M_next->_M_next != &this->_M_impl._M_node) {\n        list __carry; // 辅助链表，用于从a中提取元素以及临时保存两个链表的合并结果\n        list __tmp[64]; // 保存着当前每一个归并层次的结果, i号链表保存的元素个数为2的i次方或者0\n        list *__fill = &__tmp[0]; // 表示当前最大归并排序的层次，while循环之后__fill变成log2(list.size())\n        list *__counter;\n\n        do {\n            __carry.splice(__carry.begin(), *this, begin()); //把当前链表的第一个节点放在carry链表头\n\n            for (__counter = &__tmp[0];\n                 __counter != __fill && !__counter->empty();\n                 ++__counter) {\n                __counter->merge(__carry);\t// 两个有序链表合并\n                __carry.swap(*__counter); // 类似于交换链表carry和counter[i]内容\t\n            }\n            __carry.swap(*__counter); // 类似于交换链表carry和counter[i]内容\t\n            if (__counter == __fill)\n                ++__fill;\n        } while (!empty());\n\t\t// 每两个进行归并，依次网上，直到最后*(__fill-1)保存最后的排序结果。然后交换到当前list中。\n        for (__counter = &__tmp[1]; __counter != __fill; ++__counter)\n            __counter->merge(*(__counter - 1));\n        swap(*(__fill - 1)); \n    }\n}\n```\n\n上述代码看起来比较难懂，在网上查找后发现，G2.9中：\n\n```c++\ntemplate <class T, class Alloc>\nvoid list<T, Alloc> :: sort(){\n    // 判断链表是否为空或者只有一个元素\n    if(node->next == node || link_type(node->next)->next == node){\n        return;\n    }\n    \n    list<T, Alloc> carry;\n    list<T, alloc> counter[64];\n    int fill = 0;\n    while(!empty()){\n        carry.splice(carry.begin(), *this, begin());\n        int i = 0;\n        while(i < fill && !counter[i].empty()){\n            counter[i].merge(carry);\n            carry.swap(counter[i++]);\n        }\n        carry.swap(counter[i]);\n        if(i == fill){\n            ++fill;\n        } \n    }\n    \n    for(int i = 1; i < fill; ++i){\n        counter[i].merge(counter[i-1]);\n    }\n    swap(counter[fill-1]);\n}\n```\n\n对应的外部实现是：\n\n```c++\nvoid sortList(list<int> &l) {\n    if (l.size() <= 1) {\n        return;\n    }\n    list<int> carry;       // 辅助链表，用于从a中提取元素以及临时保存两个链表的合并结果\n    list<int> counter[64]; // 保存着当前每一个归并层次的结果, i号链表保存的元素个数为2的i次方或者0\n    int fill = 0;          // 表示当前最大归并排序的层次，while循环之后fill变成log2(a.size())\n\n    while (!l.empty()) {\n        carry.splice(carry.begin(), l, l.begin()); // 将链表a中的第一个元素移动至carry开头\n        int i = 0;\n        // 从小往大不断合并非空归并层次直至遇到空层或者到达当前最大归并层次\n        while (i < fill && !counter[i].empty()) {\n            counter[i].merge(carry);    // 链表合并，结果链表是有序的，必须保证合并前两个链表是有序的\n            carry.swap(counter[i++]);   // 链表元素互换\n        }\n        carry.swap(counter[i]);\n        if (i == fill) {       // i到达当前最大归并层次，说明得增加一层\n            ++fill;\n        }\n    }\n\n    for (int i = 1; i < fill; ++i) {  // 将所有归并层次的结果合并得到最终结果counter[fill - 1]\n        counter[i].merge(counter[i - 1]);\n    }\n    l.swap(counter[fill - 1]);\n}\n```\n\n这一块可以参考\n\n> https://blog.csdn.net/chenhanzhun/article/details/39337331\n\n上面给出了详细的过程图解。\n\n我们再次将G4.9转换对应的外部实现：\n\n```c++\nvoid sortList1(list<int> &l) {\n    typedef list<int> list;\n    if (l.size() <= 1) {\n        return;\n    }\n    list __carry;\n    list __tmp[64];\n    list *__fill = &__tmp[0];\n    list *__counter;\n    do {\n        __carry.splice(__carry.begin(), l, l.begin());\n        for (__counter = &__tmp[0];\n             __counter != __fill && !__counter->empty();\n             ++__counter) {\n            __counter->merge(__carry);\n            __carry.swap(*__counter);\n        }\n        __carry.swap(*__counter);\n        if (__counter == __fill) ++__fill;\n    } while (!l.empty());\n\n    for (__counter = &__tmp[1]; __counter != __fill; ++__counter)\n        __counter->merge(*(__counter - 1));\n\n    l.swap(*(__fill - 1));\n}\n```\n\n使用：\n\n```c++\nint main() {\n    list<int> l = {7, 5, 8, 1};\n    cout << \"===============排序前==============\" << endl;\n    for (auto i:l) cout << i << \" \";\n    cout << endl;\n    sortList1(l);\n    cout << \"===============排序后==============\" << endl;\n    for (auto i:l) cout << i << \" \";\n    cout << endl;\n\n    return 0;\n}\n```\n\n【**操作符重载**】\n\n```c++\ntemplate<typename _Tp, typename _Alloc>\ninline bool\noperator==(const list<_Tp, _Alloc> &__x, const list<_Tp, _Alloc> &__y) {\n    typedef typename list<_Tp, _Alloc>::const_iterator const_iterator;\n    const_iterator __end1 = __x.end();\n    const_iterator __end2 = __y.end();\n\n    const_iterator __i1 = __x.begin();\n    const_iterator __i2 = __y.begin();\n    while (__i1 != __end1 && __i2 != __end2 && *__i1 == *__i2) {\n        ++__i1;\n        ++__i2;\n    }\n    return __i1 == __end1 && __i2 == __end2;\n}\n```\n\n实现思路是，迭代判断两个迭代器是否都抵达末尾。\n\n剩下就是其他的操作符重载，比较简单，就不阐述了。其中`lexicographical_compare`实现在`c++-v3/src/c++98/stl_algobase.h`中，该函数是按照字典序测试[frist1,last1)是否小于[first2,last2)。该函数使用opeartor<或者是comp进行比较。其行为类似于：如果两个序列长度不同，并且短序列和长序列头部完全一样，例如example和examplee.那么，长度大的字典序比短序的大。\n\n```c++\n\ntemplate <class InputIterator1, class InputIterator2>\nbool lexicographical_compare (InputIterator1 first1, InputIterator1 last1,\nInputIterator2 first2, InputIterator2 last2)\n{\n    while (first1!=last1)\n    {\n        if (first2==last2 || *first2<*first1) return false;\n        else if (*first1<*first2) return true;\n        ++first1; ++first2;\n    }\n    return (first2!=last2);\n}\n```\n\n使用：\n\n```c++\nint main() {\n     vector<char> v1{'h','e','l','l','o'};\n    vector<char> v2{'h','e','l','l','o','o'};\n    vector<char> v3{'h','e','l','m','o'};\n    cout<<\"v1=\";\n    for(char i:v1)\n        cout<<i<<\" \";\n    cout<<endl;\n    cout<<\"v2=\";\n    for(char i:v2)\n        cout<<i<<\" \";\n    cout<<endl;\n    cout<<\"v3=\";\n    for(char i:v3)\n        cout<<i<<\" \";\n    cout<<endl;\n\n    if(lexicographical_compare(v1.begin(),v1.end(),v2.begin(),v2.end()))\n        cout<<\"v1 is less than v2 \"<<endl;\n    else\n        cout<<\"v2 is less than v1 \"<<endl;\n\n    if(lexicographical_compare(v1.begin(),v1.end(),v3.begin(),v3.end()))\n        cout<<\"v1 is less than v3 \"<<endl;\n    else\n        cout<<\"v3 is less than v1 \"<<endl;\n}\n```\n\n其它重载操作符如下：\n\n```c++\ntemplate<typename _Tp, typename _Alloc>\ninline bool\noperator<(const list<_Tp, _Alloc> &__x, const list<_Tp, _Alloc> &__y) {\n    return std::lexicographical_compare(__x.begin(), __x.end(),\n                                        __y.begin(), __y.end());\n}\n\n/// Based on operator==\ntemplate<typename _Tp, typename _Alloc>\ninline bool\noperator!=(const list<_Tp, _Alloc> &__x, const list<_Tp, _Alloc> &__y) { return !(__x == __y); }\n\n/// Based on operator<\ntemplate<typename _Tp, typename _Alloc>\ninline bool\noperator>(const list<_Tp, _Alloc> &__x, const list<_Tp, _Alloc> &__y) { return __y < __x; }\n\n/// Based on operator<\ntemplate<typename _Tp, typename _Alloc>\ninline bool\noperator<=(const list<_Tp, _Alloc> &__x, const list<_Tp, _Alloc> &__y) { return !(__y < __x); }\n\n/// Based on operator<\ntemplate<typename _Tp, typename _Alloc>\ninline bool\noperator>=(const list<_Tp, _Alloc> &__x, const list<_Tp, _Alloc> &__y) { return !(__x < __y); }\n```\n\n### 1.2 list基类源码\n\n`_list_base`中有一个结构体:`_List_impl`,而`_List_impl`中有一个`List_node_base`。\n\n```c++\nclass _List_base\n{\nprotected:\n  typedef typename _Alloc::template rebind<_List_node<_Tp> >::other\n    _Node_alloc_type;\n\n  typedef typename _Alloc::template rebind<_Tp>::other _Tp_alloc_type;\n\n  struct _List_impl\n  : public _Node_alloc_type\n  {\n    __detail::_List_node_base _M_node;\n\n    _List_impl()\n    : _Node_alloc_type(), _M_node()\n    { }\n\n    _List_impl(const _Node_alloc_type& __a) _GLIBCXX_NOEXCEPT\n    : _Node_alloc_type(__a), _M_node()\n    { }\n\n    #if __cplusplus >= 201103L\n    _List_impl(_Node_alloc_type&& __a) _GLIBCXX_NOEXCEPT\n    : _Node_alloc_type(std::move(__a)), _M_node()\n    { }\n    #endif\n  };\n\n  _List_impl _M_impl;\n};\n\n```\n\n最后形成的图就是:\n\n![list's iterator_design](https://raw.githubusercontent.com/Light-City/cloudimg/master/list_iterator_design.png)\n\n所以如果求:\n\n```c++\nsizeof(list<int>)=16\n```\n\n原因是:\n\n`list`的sizeof为1,所以sizeof来源于基类`_list_base`,而`_list_base`中有一个结构体:`_List_impl`,而`_List_impl`中有一个`_List_node_base`.\n\n我们知道`_List_node_base`,里面有两个指针,在64位上,每个为8字节,共16字节。\n\n## 2.list's Iterator剖析\n\n### 2.1 iterator\n\nlist的iterator定义\n\n```c++\ntemplate<typename _Tp>\nstruct _List_iterator\n{\n  typedef _List_iterator<_Tp>                _Self;\n  typedef _List_node<_Tp>                    _Node;\n\n  typedef ptrdiff_t                          difference_type;\n  typedef std::bidirectional_iterator_tag    iterator_category;\n  typedef _Tp                                value_type;\n  typedef _Tp*                               pointer;\n  typedef _Tp&                               reference;\n\n   // The only member points to the %list element.\n   __detail::_List_node_base* _M_node;         \n   //  _List_node(节点的数据部分) -> _List_node_base(前指针与后指针)\n\n  _List_iterator() _GLIBCXX_NOEXCEPT\n  : _M_node() { }\n\n  explicit\n  _List_iterator(__detail::_List_node_base* __x) _GLIBCXX_NOEXCEPT\n  : _M_node(__x) { }\n\n  _Self\n  _M_const_cast() const _GLIBCXX_NOEXCEPT\n  { return *this; }\n \n  // The only member points to the %list element.\n  __detail::_List_node_base* _M_node;\n};\n\n```\n\n内部重载函数:\n\n```c++\n// Must downcast from _List_node_base to _List_node to get to _M_data.\n// 重载*操作符\nreference operator*() const _GLIBCXX_NOEXCEPT\n{ \n\treturn static_cast<_Node*>(_M_node)->_M_data; \n}\n\n// 重载->操作符\npointer operator->() const _GLIBCXX_NOEXCEPT\n{ \n\treturn std::__addressof(static_cast<_Node*>(_M_node)->_M_data); \n}\n\n// 重载前置++操作符  ++i\n_Self& operator++() _GLIBCXX_NOEXCEPT   \n{ \n\t_M_node = _M_node->_M_next;\n\treturn *this;\n}\n\n// 重载后置++操作符 i++\n_Self operator++(int) _GLIBCXX_NOEXCEPT\n{\n    _Self __tmp = *this;\t\t\t // 记录原值  *调用的是拷贝构造函数\n    _M_node = _M_node->_M_next;\t\t // 进行操作\n    return __tmp;\t\t\t\t\t // 返回原值\n}\n\n// 重载前置--操作符  --i\n_Self& operator--() _GLIBCXX_NOEXCEPT\n{\n\t_M_node = _M_node->_M_prev;\n\treturn *this;\n}\n// 重载后置--操作符  --i\n_Self operator--(int) _GLIBCXX_NOEXCEPT\n{\n    _Self __tmp = *this;\n    _M_node = _M_node->_M_prev;\n    return __tmp;\n}\n// 重载++操作符\nbool operator==(const _Self& __x) const _GLIBCXX_NOEXCEPT\n{ \n\treturn _M_node == __x._M_node; \n}\n// 重载!=操作符\nbool operator!=(const _Self& __x) const _GLIBCXX_NOEXCEPT\n{ \n\treturn _M_node != __x._M_node; \n}\n```\n\n### 2.2 结点设计\n\niterator内部的`_List_node`,这里可以得到继承自`_List_node_base`.\n\n`_List_node`放数据部分\n\n`_List_node_base`放前后指针\n\n```c++\n/// An actual node in the %list.\ntemplate<typename _Tp>\nstruct _List_node : public __detail::_List_node_base\n{\n  ///< User's data.\n  _Tp _M_data;\n\n#if __cplusplus >= 201103L\n  template<typename... _Args>\n    _List_node(_Args&&... __args)\n: __detail::_List_node_base(), _M_data(std::forward<_Args>(__args)...) \n    { }\n#endif\n};\n```\n\n`_List_node_base`代码:\n\n```c++\nnamespace __detail\n{\n    _GLIBCXX_BEGIN_NAMESPACE_VERSION\n    /// Common part of a node in the %list. \n    struct _List_node_base\n    {\n      _List_node_base* _M_next;\n      _List_node_base* _M_prev;\n\n      static void\n      swap(_List_node_base& __x, _List_node_base& __y) _GLIBCXX_USE_NOEXCEPT;\n\n      void\n      _M_transfer(_List_node_base* const __first,\n          _List_node_base* const __last) _GLIBCXX_USE_NOEXCEPT;\n\n      void\n      _M_reverse() _GLIBCXX_USE_NOEXCEPT;\n\n      void\n      _M_hook(_List_node_base* const __position) _GLIBCXX_USE_NOEXCEPT;\n\n      void\n      _M_unhook() _GLIBCXX_USE_NOEXCEPT;\n    };\n\n    _GLIBCXX_END_NAMESPACE_VERSION\n} // namespace detail\n```\n\n迭代器在设计的时候,总是保持前闭后开原则,例如iter->begin()指向第一个元素,iter->end()指向实际最后一个元素的下一个元素,故最后的设计刻意在环形list尾部加一个空白结点,用以符合STL前闭后开原则."
  },
  {
    "path": "src_analysis/stl/map_multimap.md",
    "content": "# C++ STL源码剖析之map、multimap、initializer_list\nmap/multimap 以rb_tree为底层结构，因此有元素自动排序特点，排序的依据是key。\n\nmap/multimap提供\"遍历\"操作及iterators。按正常规则(++iter)遍历，便能够获得排序状态。\n\n我们无法使用map/multimap的iterators改变元素的key(因为key有其严谨排列规则)，但可以用它来改变元素的data。因此map/multimap内部自动将用户指定的key type设定为const，如此便能进制用户对元素key的赋值。\n\nmap元素的key必须独立无二，因此其insert使用的是rb_tree的`_M_insert_unique()`，而multimap元素的key可以重复，因此其insert使用的是rb_tree的`_M_insert_equal()`。\n\n对于本节，我们将从下面几个内容阐述：\n\n- map的key为key,value为key+data,与set是不同的，set是key就是value，value就是key。\n- map的key不可修改,map与multimap的插入调用函数不同，影响了其key是否对应value。\n- initializer_list使用\n- map有`[]`操作符，而multimap没有`[]`操作符。\n\n\n## 1.map\n\n> key为key，value为key+data\n\n下面map中我们可以看到value_type为一个pair。\n\n```cpp\ntemplate <typename _Key, typename _Tp, typename _Compare = std::less<_Key>,\ntypename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >\nclass map\n{\npublic:\n    typedef _Key                                          key_type;\n    typedef _Tp                                           mapped_type;\n    typedef std::pair<const _Key, _Tp>                    value_type;\n    typedef _Compare                                      key_compare;\n    typedef _Alloc                                        allocator_type;\nprivate:\n    // key为key,value为key+data\n    typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,\n            key_compare, _Pair_alloc_type> _Rep_type;\n\n    /// The actual tree structure.\n    _Rep_type _M_t;\n};\n```\n![map_data.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/map_data.png)\n\n上述默认的仿函数为`_Select1st`，我们在`stl_function`中看到源码如下：\n\n```cpp\ntemplate<typename _Pair>\nstruct _Select1st\n: public unary_function<_Pair, typename _Pair::first_type>\n{\n    typename _Pair::first_type&\n    operator()(_Pair& __x) const\n    { return __x.first; }\n};\n```\n我们看到上述的`_Select1st`为一个struct，怎么能说它是仿函数呢？\n因为里面重载了一个()操作符，哈哈～\n\n下面我们来自己实现一个：\n\n```cpp\ntemplate<typename _T1>\nstruct mySelect1st\n        : public unary_function<_T1, typename _T1::first_type>\n{\n    template<typename _T2>\n    typename _T2::first_type&\n    operator()(_T2& __x) const\n    { return __x.first; }\n};\nint main() {\n     typedef pair<const int,int> value_type;\n    _Rb_tree<int, value_type, mySelect1st<value_type>, less<int>> it;\n    it._M_insert_unique(make_pair(1,3));\n    it._M_insert_unique(make_pair(3,6));\n    for(auto each:it)\n        cout<<each.first<<\" \"<<each.second<<endl;\n}\n```\n> key不能改，data可以改\n\n上述源码中：自动为key添加一个const，所以key不能改。\n```cpp\ntypedef std::pair<const _Key, _Tp>                    value_type;\n```\n## 2.insert\n\n> insert里面采用`_M_insert_unique`\n\ninsert的几种方法：\n\n（1） 插入 pair\n\n```cpp\nstd::pair<iterator, bool> insert(const value_type& __x)\n{ return _M_t._M_insert_unique(__x); }\n```\n\nmap里面\n\n（2） 在指定位置，插入pair\n```cpp\niterator insert(iterator __position, const value_type& __x)\n{ return _M_t._M_insert_equal_(__position, __x); }\n```\n\n（3） 从一个范围进行插入\n\n```cpp\ntemplate<typename _InputIterator>\nvoid\ninsert(_InputIterator __first, _InputIterator __last)\n{ _M_t._M_insert_equal(__first, __last); }\n```\n（4）从list中插入\n\n```cpp\nvoid\ninsert(initializer_list<value_type> __l)\n{ this->insert(__l.begin(), __l.end()); }\n```\n\n针对最后一个insert，里面有个`initializer_list`，举个例子大家就知道了。\n\n\n## 3.initializer_list使用\n\n> 实际编程实践 \n\n\n```cpp\nvector<int> v={1,2,3};   // 底层调用vector的构造函数\nv={2,5,6};               // 底层调用vector的=操作符\ninitializer_list<int> ll={4,5,6};\nv.insert(v.begin(),ll);  // 底层调用下面insert函数\nfor(auto x:v) cout<<x<<\" \";\ncout<<endl;\nvector<int> vv(ll);      // 底层调用vector的构造函数\nvector<string> city{\"Berlin\", \"New York\", \"London\", \"Cairo\",\"Tokyo\", \"Cologne\"};\n```\n针对这个vector初始化，大家很熟悉了，为何可以这样初始化呢？\n我们看一下vector源码：\n\n```cpp\n vector&\noperator=(initializer_list<value_type> __l)\n{\n    this->assign(__l.begin(), __l.end());\n    return *this;\n}\n\niterator\ninsert(const_iterator __position, initializer_list<value_type> __l)\n{ return this->insert(__position, __l.begin(), __l.end()); }\n\nvector(initializer_list<value_type> __l,\nconst allocator_type& __a = allocator_type())\n: _Base(__a)\n{\n_M_range_initialize(__l.begin(), __l.end(),\n    random_access_iterator_tag());\n}\n```\n\n因为它的构造函数里面，参数有个`initializer_list`,因此我们可以针对这个对map进行使用。\n但是map没有类似的构造，它也应用在map构造函数，insert与`=`处，跟上面是一样的，都是三处，哈哈～\n\n使用`initializer_list`三处：\n\n```cpp\n// map构造\nmap(initializer_list<value_type> __l, const allocator_type& __a)\n: _M_t(_Compare(), _Pair_alloc_type(__a))\n{ _M_t._M_insert_unique(__l.begin(), __l.end()); }\n// =操作符重载\nmap&\noperator=(initializer_list<value_type> __l)\n{\n    this->clear();\n    this->insert(__l.begin(), __l.end());\n    return *this;\n}\n// insert插入\nvoid\ninsert(std::initializer_list<value_type> __list)\n{ insert(__list.begin(), __list.end()); }\n```\n> 实际编程实践\n\nmap使用`initializer_list`(set使用一样)：\n\n```cpp\n// 这里要注意，pair的first参数必[须是const\ninitializer_list<pair<const strin[g,int>> l = {{\"hello\", 1}, {\"world\", 2}};\nmap<string,int> mm(l);  // map构造函数\nmap<string, int> m2{{\"hello\", 1}, {\"world\", 2}}; // map构造函数\nmap<string, int> m1={{\"hello\", 1}, {\"world\", 2}};   // map构造函数\nm1 = {{\"h\", 1}, {\"w\", 3}};             // 底层调用map的=操作符\nmap<string, int> mp;\nmp.insert(l);       // insert插入[\n```\n\n上述会引出另一个问题：\n\n```cpp\nmap<string, int> m1={{\"hello\", 1}, {\"world\", 2}};   // map构造函数\nm1 = {{\"h\", 1}, {\"w\", 3}};             // 底层调用map的=操作符\n```\n这两个为何一个调用的构造，一个调用=操作符呢？\n\n在初始化的时候，定义及赋值的时候就直接调用构造，后面再次赋值，就是先调用拷贝构造(有可能会被编译器优化)，再调用=操作符。\n\n\n> 实例分析\n\n下面用一个具体实例来分析一下：\n\n```cpp\ntemplate <typename _Tp>\nclass Foo\n{\npublic:\n    Foo(initializer_list<_Tp> &list)\n    {\n        cout << \"Foo(initializer_list<_Tp> &list)\"<< endl;\n    }\n\n    Foo(int)\n    {\n        cout << \"Foo(int )\"<< endl;\n    }\n\n    Foo(const Foo& f)\n    {\n        cout << \"调用了拷贝构造函数\"<< endl;\n    }\n    Foo& operator=(initializer_list<_Tp> __l)\n    {\n        cout<<\"调用了=操作符重载\"<<endl;\n        return *this;\n    }\n\n};\n\n```\n调用：\n```cpp\nFoo<int> foo=ll;\nfoo={1,2};\n```\n编译器未被优化的结果：\n\n```\nFoo(initializer_list<_Tp> &list)\n调用了=操作符重载\n```\n我们通过禁用编译器优化：`g++ -o rb  rbtree.cpp  -std=c++11 -fno-elide-constructors`\n\n```\nFoo(initializer_list<_Tp> &list)\n调用了拷贝构造函数\n调用了=操作符重载\n```\n\n## 4.multimap\n\n同map一样multimap不允许修改key。但是插入使用的是`_M_insert_equal`。\n```cpp\ntemplate <typename _Key, typename _Tp,\ntypename _Compare = std::less<_Key>,\ntypename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >\nclass multimap\n{\npublic:                                      \n    typedef std::pair<const _Key, _Tp> value_type;   \n}\n```\n下面使用multimap与rbtree两种方式来联系。\n```cpp\nmultimap<int, string> c;\nc.insert(make_pair(1,\"asdqw\"));\nc.insert(make_pair(1,\"qweq\"));\n\nfor(auto each:c) cout<<each.first<<\" \"<<each.second<<endl;\n\ntypedef pair<const int,string> valueT;\n_Rb_tree<int, valueT, _Select1st<valueT>, less<int>> mulm;\nmulm._M_insert_equal(make_pair(2,\"abc\"));\nmulm._M_insert_equal(make_pair(2,\"cde\"));\nfor(auto each:mulm)\ncout<<each.first<<\" \"<<each.second<<endl;\n```\n输出：\n\n```\n1 asdqw\n1 qweq\n2 abc\n2 cde\n```\n\n## 5.map与multimap的重要操作符\n\nmap与multimap`[]`操作符，map的`[]`操作符返回传递给map的第二个参数。\n\n传递给`[]`一个key，如果查找到，就是value，否则就是默认值0。\n```cpp\nmapped_type&\noperator[](const key_type& __k)\n{\n    iterator __i = lower_bound(__k);// 找到__k第一个。\n    // __i->first is greater than or equivalent to __k.\n    if (__i == end() || key_comp()(__k, (*__i).first))\n    #if __cplusplus >= 201103L\n    __i = _M_t._M_emplace_hint_unique(__i, std::piecewise_construct,\n                    std::tuple<const key_type&>(__k),\n                    std::tuple<>());\n    #else\n        __i = insert(__i, value_type(__k, mapped_type()));\n    #endif\n    return (*__i).second;  // 返回key的value，此value为传递进map的第二个参数。\n}\n```\n\n但是multimap没有`[]`操作符！！！\n\n我们思考一下，因为multimap会根据key，有可能会对应多个value，那如果我们通过`[]`获取对应key的value，此时到底获取的是哪个value呢，所以在STL源码中没有重载这个操作符！\n\n"
  },
  {
    "path": "src_analysis/stl/myhashtable.md",
    "content": "# 从0到1打牢算法基础之手写一个哈希表\n\n## 0.导语\n\n\n目的：手写实现一个哈希表，采用拉链法构建，每个hash(key)对应的是一个红黑树。\n\n看起来很简单，但可以学到很多东西。实现语言：C++。\n\n为了打牢算法基础，github开了个仓库，来完成后面算法基础的应用与实现，地址：\n\n> https://github.com/Light-City/algPratice\n\n也可以点击原文阅读。\n\n## 1.简易版哈希表\n\n\n我们将哈希表封装在一个类中,完成遍历的定义与声明以及构造、析构的实现：\n\n```cpp\ntemplate<typename Key, typename Value>\nclass HashTable {\nprivate:\n    const static int upperTol = 3;\n    const static int lowerTol = 1;\n    const static int initCapacity = 1;\n    map<Key, Value> **hashtable;\n    int M;\n    int size;\npublic:\n    /**\n     * 传参构造\n     * @param M\n     */\n    HashTable(int M) : M(M), size(0) {\n        // 这里的括号是为了初始化为0,这就可以不用写下面的代码,当然在后面add之类的操作,就不需要动态分配内存.\n        // this->hashtable = new map<Key, Value> *[M]();\n        this->hashtable = new map<Key, Value> *[M];\n        for (int i = 0; i < M; i++) {\n            this->hashtable[i] = new map<Key, Value>;\n        }\n    }\n\n    /**\n     * 默认构造\n     */\n    HashTable() {\n        HashTable(initCapacity);\n    }\n\n    /**\n     * 析构函数,释放内存\n     */\n    ~HashTable() {\n        free(M);\n    }\nprivate:\n    /**\n     * 释放内存\n     * @param M\n     */\n    void free(int M) {\n        for (int i = 0; i < M; i++) {\n            if (hashtable[i])\n                delete hashtable[i];\n        }\n        delete[]hashtable;\n    \n};\n```\n\n对于哈希表实现，里面有一个比较重要的哈希函数，这里我们先自己定义一个：\n```cpp\n/**\n* 哈希函数\n* @param key\n* @return\n*/\nint hashFunc(Key key) {\n    std::hash<Key> h;\n    return (h(key) & 0x7fffffff) % M;\n}\n```\n这里使用std的hash得到值之后，将其`&`上`0x7fffffff`，去掉高位的负号，转为正数，然后余上M。\n\n\n现在有了这些我们来实现一下它的增删改查。\n\n> 增操作\n\n底层采用的是红黑树,插入是使用insert方法，通过构造一个pair来完成。\n而当key存在的时候，更新值即可，对于更新这一块，如果直接使用insert是不起作用的，比如下面测试：\n\n```cpp\n map<string,int> m{{\"a\",1},{\"b\",2}};\nfor(auto i:m) cout<<i.first<<\" \"<<i.second<<\" \";\ncout<<endl;\nm.insert({{\"a\",2}});\nfor(auto i:m) cout<<i.first<<\" \"<<i.second<<\" \";\ncout<<endl;\nm[\"a\"]=2;\nfor(auto i:m) cout<<i.first<<\" \"<<i.second<<\" \";\ncout<<endl;\n```\n\n输出：\n```cpp\na 1 b 2 \na 1 b 2 \na 2 b 2 \n```\n因此，如果要修改key对应的value，可以通过`[]`来修改，还可以先删除，再插入，这里就用这个方法。\n\n```cpp\n/**\n* 添加新元素\n* @param key\n* @param value\n*/\nvoid add(Key key, Value value) {\n    // 拉链法出来的map如果为空,就动态分配一个map,然后进行插入\n    // 如果key不存在就看内存是否存在,不存在,就分配,存在就插入\n    if (hashtable[hashFunc(key)] == NULL || hashtable[hashFunc(key)]->count(key) == 0) {\n        if (hashtable[hashFunc(key)] == NULL)\n            hashtable[hashFunc(key)] = new map<Key, Value>;\n        hashtable[hashFunc(key)]->insert(make_pair(key, value));\n        size++;\n        if (size >= maxCapacity())\n            resize(2 * M);\n    } else {\n        // 否则,修改value.\n        hashtable[hashFunc(key)]->erase(key);\n        hashtable[hashFunc(key)]->insert(make_pair(key, value));\n    }\n}\n```\n> 删操作\n\n如果key存在，就删除，size--，否则返回失败标记。\n```cpp\n/**\n* 移除Key\n* @param key\n* @return 0 success -1 fail\n*/\nValue remove(Key key) {\n    Value ret = -1;\n    // 是否包含key,若包含key,则直接删除\n    if (contains(key)) {\n        hashtable[hashFunc(key)]->erase(key);\n        size--;\n    //            if (size == 0) delete hashtable[hashFunc(key)];       // 可以添加这行来动态减少内存\n        ret = 0;\n        // initCapacity 保证不会越界\n        if (size < minCapacity() && M / 2 >= initCapacity) resize(M / 2);\n    }\n    return ret;\n}\n```\n> 改操作\n\n前面提到过，这里就直接放代码。\n\n```cpp\n/**\n* 重设value\n* @param key\n* @param value\n*/\nvoid set(Key key, Value value) {\n    // key不存在\n    if (!contains(key))\n        hrow \"key not exists!\";\n    // 修改value\n    hashtable[hashFunc(key)]->erase(key);\n    hashtable[hashFunc(key)]->insert(make_pair(key, value));\n}\n```\n\n> 查操作\n\n获取key对应的value。\n\n```cpp\n/**\n* 获取key对应的value\n* @param key\n* @return\n*/\nValue get(Key key) {\n    if (contains(key))\n        return hashtable[hashFunc(key)]->at(key);\n    return 0;\n}\n```\n\n最后，上面有`contains`与`resize`等函数未提。\n\n> key存在与否\n\n首先contains函数实现，就是判断key存在与否：\n\n```cpp\n/**\n    * 是否包含key\n    * @param key\n    * @return\n    */\nbool contains(Key key) {\n    return hashtable[hashFunc(key)] == NULL || this->hashtable[hashFunc(key)]->count(key) == 0 ? false : true;\n}\n```\n\n> 获取size\n\n```cpp\n/**\n* 获取哈希表元素个数\n* @return\n*/\nint getSize() {\n    return size;\n}\n```\n\n> 最大容量与最小容量\n\n```cpp\n/**\n    * 最大容量\n    * @return\n    */\nValue maxCapacity() {\n    return M * upperTol;\n}\n\n/**\n    * 最小容量\n    * @return\n    */\nValue minCapacity() {\n    return M * lowerTol;\n}\n```\n\n> resize函数\n\n完成动态调整内存，将原来内存中的内容拷贝到新分配的空间，释放原空间！\n\n```cpp\n/**\n* 动态调整内存,保证时间复杂度O(1)查找\n* 把扩容后的操作,平摊到前面每次操作,时间复杂度O(2),那就是O(1)了\n* @param newM\n*/\nvoid resize(int newM) {\n    cout << \"resize \" << newM << endl;\n    map<Key, Value> **newHashTable = new map<Key, Value> *[newM];\n    for (int i = 0; i < newM; i++) {\n        newHashTable[i] = new map<Key, Value>;\n    }\n    int oldM = M;\n    this->M = newM;\n    for (int i = 0; i < oldM; i++) {\n        map<Key, Value> m = *(hashtable[i]);\n        for (auto p:m)\n            newHashTable[hashFunc(p.first)]->insert(make_pair(p.first, p.second));\n    }\n\n    free(oldM);\n    this->hashtable = newHashTable;\n}\n\n```\n\n> 重载<< 操作符\n\n声明：\n```cpp\nprivate:\n    template<typename K, typename V>\n    // 重载<<操作符\n    friend ostream &operator<<(ostream &out, HashTable<K, V> &hashTable);\n```\n定义：\n```cpp\ntemplate<typename K, typename V>\nostream &operator<<(ostream &out, HashTable<K, V> &hashTable) {\n    hashTable.print();\n    return out;\n}\n```\n\n至此，上述哈希表实现完毕，现在来测试：\n\n\n```cpp\n#include \"hash.h\"\n#include <vector>\nint main() {\n\n    vector<string> words{\"java\", \"c++\", \"c\", \"c++\", \"c#\", \"python\", \"ruby\", \"python\",\n                         \"c\", \"c\", \"c++\", \"java\", \"c++\", \"rust\", \"python\"};\n    HashTable<string, int> ht(1);\n    for (string word : words) {\n        if (ht.contains(word)) {\n            ht.set(word, ht.get(word) + 1);\n        } else {\n            ht.add(word, 1);\n        }\n    }\n    cout<<ht;\n    cout<<\"size=\"<<ht.getSize()<<\",maxCapacity=\"<<ht.maxCapacity()<<\",minCapacity=\"<<ht.minCapacity()<<endl;\n    string w=\"c++\";\n    ht.remove(w);\n    if (ht.contains(w))\n        cout << \"\" << w << \": \" << ht.get(w) << endl;\n    else\n        cout << \"No word \" << w << \" in words\" << endl;\n    cout<<ht;\n    ht.remove(\"c#\");\n    ht.remove(\"java\");\n    ht.remove(\"c\");\n    cout<<\"size=\"<<ht.getSize()<<\",maxCapacity=\"<<ht.maxCapacity()<<\",minCapacity=\"<<ht.minCapacity()<<endl;\n    cout<<ht;\n\n    return 0;\n}\n```\n\n输出结果：\n```cpp\nresize 2\nresize 4\n{c#:1,java:2,ruby:1,c:3,rust:1,python:3,c++:4}\nsize=7,maxCapacity=12,minCapacity=4\nNo word c++ in words\n{c#:1,java:2,ruby:1,c:3,rust:1,python:3}\nresize 2\nsize=3,maxCapacity=6,minCapacity=2\n{python:3,ruby:1,rust:1}\n```\n至此，完成了一个简单的哈希表。\n\n## 1.优化哈希表\n\n在gcc2.9版本中，底层的哈希表是以素数作为容量动态修改的，因此这里的优化从这里出发：\n\n类内部开头添加下面数组：\n\n```cpp\n// 素数数组\nconst vector<int> capacity = {53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317,\n                                196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843,\n                                50331653, 100663319, 201326611, 402653189, 805306457, 1610612741};\n\n```\n去掉带参数的构造函数，修改默认构造为：\n\n```cpp\n/**\n* 默认构造\n* @param M\n*/\nHashTable()  {\n    M = capacity[capacityIndex], size = 0;\n    // 这里的括号是为了初始化为0,这就可以不用写下面的代码,当然在后面add之类的操作,就不需要动态分配内存.\n    // this->hashtable = new map<Key, Value> *[M]();\n    this->hashtable = new map<Key, Value> *[M];\n    for (int i = 0; i < M; i++) {\n        this->hashtable[i] = new map<Key, Value>;\n    }\n}\n```\n修改add函数：\n在size++后添加下面代码：\n```cpp\nif (size >= maxCapacity() && capacityIndex + 1 < capacity.size()) {\n    capacityIndex++;\n    resize(capacity[M]);\n}\n```\n每次resize从capacity中取值。\n\n> remove函数修改\n\n在size--后修改：\n\n```cpp\nif (size < minCapacity() && capacityIndex - 1 >= 0) {\n    capacityIndex--;\n    resize(capacityIndex);\n}\n```\n\n至此，哈希表完成！测试代码同上。\n"
  },
  {
    "path": "src_analysis/stl/queue_stack.md",
    "content": "# C++ STL源码剖析之容器配接器stack与queue、priority_queue\n\n## 0.导语\n为何stack与queue不被称为容器呢？\n\n下面本节带着这个问题来深入源码分析。\n\n\n## 1.stack\n\n在stack的源码中我们关注两点：\n- 默认`_Sequence`为`deque`\n- 内部函数实现是调用`_Sequence`对应容器的函数。\n\n\n![stack.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/stack.png)\n\n```cpp\ntemplate<typename _Tp, typename _Sequence = deque<_Tp> >\nclass stack\n{\n\npublic:\n    typedef typename _Sequence::value_type                value_type;\n    typedef typename _Sequence::reference                 reference;\n    typedef typename _Sequence::const_reference           const_reference;\n    typedef typename _Sequence::size_type                 size_type;\n    typedef          _Sequence                            container_type;\n\nprotected:\n    //  See queue::c for notes on this name.\n    _Sequence c;\n\npublic:\n     reference\n      top()\n      {\n        __glibcxx_requires_nonempty();\n        return c.back();\n      }\n    void\n      push(const value_type& __x)\n      { c.push_back(__x); }\n}\n\n```\n\n\n\n\n> 测试stack底层容器\n\n对于stack来说，底层容器可以是`vector`、`deque`、`list`，但不可以是`map`、`set`。\n由于编译器不会做全面性检查，当调用函数不存在的时候，就编译不通过，所以对于像set虽然不能作为底层容器，但如果具有某些函数，调用仍然是成功的，直到调用的函数不存在。\n\n```cpp\nint test_stack() {\n    cout<<\"============test_stack=============\"<<endl;\n    clock_t timeStart = clock();\n    stack<int, list<int>> c;\n    for (long i = 0; i < 100000; i++)\n        c.push(i+1);\n    cout << \"stack.size()= \" << c.size() << endl;\n    cout << \"stack.top()= \" << c.top() << endl;\n    c.pop();\n    cout << \"stack.size()= \" << c.size() << endl;\n    cout << \"stack.top()= \" << c.top() << endl;\n    cout << \"use stack milli-seconds : \" << (clock() - timeStart) << endl;\n    timeStart=clock();\n    stack<int, deque<int>> c1;\n    for (long i = 0; i < 100000; i++)\n        c1.push(i+1);\n    cout << \"stack.size()= \" << c1.size() << endl;\n    cout << \"stack.top()= \" << c1.top() << endl;\n    c1.pop();\n    cout << \"stack.size()= \" << c1.size() << endl;\n    cout << \"stack.top()= \" << c1.top() << endl;\n    cout << \"use stack milli-seconds : \" << (clock() - timeStart) << endl;\n\n    // vector可以作为stack的底层容器\n    stack<int, vector<int>> c2;\n    for (long i = 0; i < 100000; i++)\n        c2.push(i+1);\n    cout << \"stack.size()= \" << c2.size() << endl;\n    cout << \"stack.top()= \" << c2.top() << endl;\n    c2.pop();\n    cout << \"stack.size()= \" << c2.size() << endl;\n    cout << \"stack.top()= \" << c2.top() << endl;\n    cout << \"use stack milli-seconds : \" << (clock() - timeStart) << endl;\n}\n```\n\n\n## 2.queue\n\n在queue的源码中我们关注两点：\n- 默认`_Sequence`为`deque`\n- 内部函数实现是调用`_Sequence`对应容器的函数。\n\n```cpp\ntemplate<typename _Tp, typename _Sequence = deque<_Tp> >\nclass queue\n{\npublic:\n    typedef typename _Sequence::value_type                value_type;\n    typedef typename _Sequence::reference                 reference;\n    typedef typename _Sequence::const_reference           const_reference;\n    typedef typename _Sequence::size_type                 size_type;\n    typedef          _Sequence                            container_type;\n\nprotected:\n\n    _Sequence c;\n\npublic:\n\n    void push(const value_type& __x)\n    { c.push_back(__x); }\n\n    void pop()\n    { \n        __glibcxx_requires_nonempty();\n      c.pop_front();\n    }\n}\n```\n其对应的UML类图如下：\n\n![queue_.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/queue_.png)\n\n同理，优先队列则是使用`vector`作为默认容器。\n\n```cpp\ntemplate<typename _Tp, typename _Sequence = vector<_Tp>,\ntypename _Compare  = less<typename _Sequence::value_type> >\nclass priority_queue\n{\npublic:\n    typedef typename _Sequence::value_type                value_type;\n    typedef typename _Sequence::reference                 reference;\n    typedef typename _Sequence::const_reference           const_reference;\n    typedef typename _Sequence::size_type                 size_type;\n    typedef          _Sequence                            container_type;\n\nprotected:\n    //  See queue::c for notes on these names.\n    _Sequence  c;\n    _Compare   comp;\n\npublic:\n    reference\n    top() \n    {\n\t    __glibcxx_requires_nonempty();\n\t    return c.front();\n    }\n\n    void\n    push(const value_type& __x)\n    {\n\t    c.push_back(__x);\n\t    std::push_heap(c.begin(), c.end(), comp);\n    }\n\n}\n```\n\n\n![priority_queue.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/priority_queue.png)\n\n\n测试这两个容器配接器支持的底层容器：\n\n> queue\n\n\n对于queue底层容器可以是`deque`，也可以是`list`，但不能是`vector`,`map`,`set`，使用默认的deque效率在插入方面比其他容器作为底层要快！\n\n```cpp\nint test_queue() {\n    cout<<\"============test_queue=============\"<<endl;\n    clock_t timeStart = clock();\n    queue<int, list<int>> c;\n    for (long i = 0; i < 100000; i++) {\n        c.push(i+1);\n    }\n    cout << \"stack.size()= \" << c.size() << endl;\n    cout << \"stack.front()= \" << c.front() << endl;\n    c.pop();\n    cout << \"stack.size()= \" << c.size() << endl;\n    cout << \"stack.front()= \" << c.front() << endl;\n    cout << \"use list milli-seconds : \" << (clock() - timeStart) << endl;\n\n    timeStart=clock();\n    queue<int, deque<int>> c1;\n    for (long i = 0; i < 100000; i++) {\n        c1.push(i+1);\n    }\n    cout << \"stack.size()= \" << c1.size() << endl;\n    cout << \"stack.front()= \" << c1.front() << endl;\n    c1.pop();\n    cout << \"stack.size()= \" << c1.size() << endl;\n    cout << \"stack.front()= \" << c1.front() << endl;\n\n    cout << \"use deque milli-seconds : \" << (clock() - timeStart) << endl;\n}\n```\n\n> priority_queue\n\n对于优先队列来说，测试结果发现，采用`deque`要比默认的`vector`插入速度快！\n底层支持vector、deque容器，但不支持list、map、set。\n\n```cpp\nint test_priority_queue() {\n    cout<<\"============test_priority_queue=============\"<<endl;\n    clock_t timeStart = clock();\n\n    priority_queue<int, deque<int>> c1;\n    for (long i = 0; i < 100000; i++) {\n        c1.push(i+1);\n    }\n    cout << \"stack.size()= \" << c1.size() << endl;\n    cout << \"stack.top()= \" << c1.top() << endl;\n    c1.pop();\n    cout << \"stack.size()= \" << c1.size() << endl;\n    cout << \"stack.top()= \" << c1.top() << endl;\n\n    cout << \"use deque milli-seconds : \" << (clock() - timeStart) << endl;\n\n\n    priority_queue<int, vector<int>> c2;\n    for (long i = 0; i < 100000; i++)\n        c2.push(i+1);\n    cout << \"stack.size()= \" << c2.size() << endl;\n    cout << \"stack.top()= \" << c2.top() << endl;\n    c2.pop();\n    cout << \"stack.size()= \" << c2.size() << endl;\n    cout << \"stack.top()= \" << c2.top() << endl;\n    cout << \"use stack milli-seconds : \" << (clock() - timeStart) << endl;\n}\n```\n\n因此，stack、queue、priority_queue不被称为容器， 把它称为容器配接器。"
  },
  {
    "path": "src_analysis/stl/rb_tree.md",
    "content": "# C++ STL源码剖析之红黑树\n\n## 0.导语\n\n在STL源码中有两段话，简单翻译后如下：\n\nSTL中Red-black tree（红黑树）class,用来当做SLT关系式容器（如set,multiset,map, \nmultimap）.里面所用的insertion和deletion方法以\n《Introduction to Algorithms》一书为基础,但是有以下两点不同: \n \n(1)header不仅指向root,也指向红黑树的最左节点,以便用常数时间实现begin(),并且也指向红黑树的最右边节点,以便 \nset相关泛型算法（如set_union等等）可以有线性时间表现.\n\n(2)当要删除的节点有两个子节点时，其后继节点连接到其位置，而不是被复制，因此，唯一使无效的迭代器是引用已删除节点的迭代器。\n\n上述话翻译成图，如下，相比于普通的红黑树多了一个header节点，并且为红色。普通的红黑树是以100节点开始的，且满足下面五条性质：\n- 每个节点或是红色的,或是黑色的.\n- 根节点是黑色的.\n- 每个叶节点（NULL）是黑色的.\n- 如果一个节点是红色的，则它的两个孩子节点都是黑色的.\n- 对每个节点，从该节点到其所有后代叶节点的简单路径上，均包含相同数目的黑色节点.\n\n当然这里的rb_tree也是一样满足这几条性质，迭代器的begin指向红黑树根节点，也就是header的父亲，而end指向header节点。\n\n![st.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/rbt.png)。\n图中省略号表示节点没有画完，还有其他节点，所以省略。\n\n## 1.红黑树节点基类\n\n红黑树基类，非常简单，在文件开头定义了颜色标记。\n\n基类中包含了指向自己的指针，分别定义了left、right、parent，同时包含了一个颜色标记常量，而里面有两个核心函数，目的是获取红黑树中最小节点与最大节点。\n我们知道对于二分搜索树获取最小节点就是左子树一直往下搜，最大节点就是右子树一直往下搜即可。\n\n```cpp\n// 颜色标记\nenum _Rb_tree_color { _S_red = false, _S_black = true };\n\n// 基类\nstruct _Rb_tree_node_base\n{\n// typedef重命名\ntypedef _Rb_tree_node_base* _Base_ptr;\n\n// 颜色\n_Rb_tree_color\t_M_color;\n// 指向父亲\n_Base_ptr\t\t_M_parent;\n// 指向左孩子\n_Base_ptr\t\t_M_left;\n// 指向右孩子\n_Base_ptr\t\t_M_right;\n\n// 求红黑树的最小节点\nstatic _Base_ptr\n_S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT\n{\n    while (__x->_M_left != 0) __x = __x->_M_left;\n    return __x;\n}\n\n// 求红黑树最大节点\nstatic _Base_ptr\n_S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT\n{\n    while (__x->_M_right != 0) __x = __x->_M_right;\n    return __x;\n}\n\n};\n\n```\n## 2.红黑树节点\n\n红黑树节点继承自红黑树基类。\n```cpp\ntemplate<typename _Val>\nstruct _Rb_tree_node : public _Rb_tree_node_base\n{\n    typedef _Rb_tree_node<_Value>* _Link_type;//节点指针,指向数据节点\n    _Value _M_value_field;//节点数据域,即关键字\n};\n```\n\n## 3.红黑树迭代器\n\n红黑树迭代器里面有一个红黑树基类成员，然后通过该成员进行迭代器的相关操作。\n同时，我们可以知道该迭代器属于`bidirectional_iterator_tag`。\n\n里面也包含了萃取机相关需要的typedef。\n\n```cpp\ntemplate<typename _Tp>\nstruct _Rb_tree_iterator\n{\n    typedef _Tp  value_type;\n    typedef _Tp& reference;\n    typedef _Tp* pointer;\n\n    typedef bidirectional_iterator_tag iterator_category;\n    typedef ptrdiff_t                  difference_type;\n\n    typedef _Rb_tree_iterator<_Tp>        _Self;\n    typedef _Rb_tree_node_base::_Base_ptr _Base_ptr;\n    typedef _Rb_tree_node<_Tp>*           _Link_type;\n\n    _Base_ptr _M_node;\n};   \n\n```\n\n获取数据\n\n\n```cpp\nreference\noperator*() const _GLIBCXX_NOEXCEPT\n{ return *static_cast<_Link_type>(_M_node)->_M_valptr(); }\n\npointer\noperator->() const _GLIBCXX_NOEXCEPT\n{ return static_cast<_Link_type> (_M_node)->_M_valptr(); }\n\n```\n\n重载++操作符\n\n```cpp\n_Self&\noperator++() _GLIBCXX_NOEXCEPT\n{\n    _M_node = _Rb_tree_increment(_M_node);\n    return *this;\n}\n```\n而`_Rb_tree_increment`底层是`local_Rb_tree_increment`，如下实现：\n\n```cpp\nstatic _Rb_tree_node_base *\nlocal_Rb_tree_increment( _Rb_tree_node_base* __x ) throw ()\n{\n\tif ( __x->_M_right != 0 )       /* 存在右子树,那么下一个节点为右子树的最小节点 */\n\t{\n\t\t__x = __x->_M_right;\n\t\twhile ( __x->_M_left != 0 )\n\t\t\t__x = __x->_M_left;\n\t}else  {                        \n        /* 不存在右子树,那么分为两种情况：自底往上搜索,当前节点为父节点的左孩子的时候,父节点就是后继节点； */\n/* 第二种情况:_x为header节点了,那么_x就是最后的后继节点. 简言之_x为最小节点且往上回溯,一直为父节点的右孩子,直到_x变为父节点,_y为其右孩子 */\n\t\t_Rb_tree_node_base *__y = __x->_M_parent;\n\t\twhile ( __x == __y->_M_right )\n\t\t{\n\t\t\t__x\t= __y;\n\t\t\t__y\t= __y->_M_parent;\n\t\t}\n\t\tif ( __x->_M_right != __y )\n\t\t\t__x = __y;\n\t}\n\treturn (__x);\n}\n```\n重载--操作符：\n\n\n```cpp\n_Self&\noperator--() _GLIBCXX_NOEXCEPT\n{\n\t_M_node = _Rb_tree_decrement(_M_node);\n    return *this;\n}\n```\n同理，而`_Rb_tree_decrement`底层是`local_Rb_tree_decrement`，如下实现：\n\n```cpp\nstatic _Rb_tree_node_base *\nlocal_Rb_tree_decrement( _Rb_tree_node_base * __x )\nthrow ()\n{\n/* header节点 */\n\tif ( __x->_M_color ==\n\t     _S_red\n\t     && __x\n\t     ->_M_parent->_M_parent == __x )\n\t\t__x = __x->_M_right;\n\telse if ( __x->_M_left != 0 ) /* 左节点不为空,返回左子树中最大的节点 */\n\t{\n\t\t_Rb_tree_node_base *__y = __x->_M_left;\n\t\twhile ( __y->_M_right != 0 )\n\t\t\t__y = __y->_M_right;\n\t\t__x = __y;\n\t}else  {\n/* 自底向上找到当前节点为其父节点的右孩子,那么父节点就是前驱节点 */\n\t\t_Rb_tree_node_base *__y = __x->_M_parent;\n\t\twhile ( __x == __y->_M_left )\n\t\t{\n\t\t\t__x\t= __y;\n\t\t\t__y\t= __y->_M_parent;\n\t\t}\n\t\t__x = __y;\n\t}\n\treturn\n\t\t(__x);\n}\n```\n\n\n重载==与!=操作符，直接判断节点指针是否相等。\n\n```cpp\nbool\noperator==(const _Self& __x) const _GLIBCXX_NOEXCEPT\n{ return _M_node == __x._M_node; }\n\nbool\noperator!=(const _Self& __x) const _GLIBCXX_NOEXCEPT\n{ return _M_node != __x._M_node; }\n\n```\n\n\n\n其他重要函数，黑节点统计：\n\n```cpp\nunsigned int\n_Rb_tree_black_count(const _Rb_tree_node_base *__node,\n                     const _Rb_tree_node_base *__root) throw() {\n    if (__node == 0)\n        return 0;\n    unsigned int __sum = 0;\n    do {\n        if (__node->_M_color == _S_black)\n            ++__sum;\n        if (__node == __root)\n            break;\n        __node = __node->_M_parent;\n    } while (1);\n    return __sum;\n}\n```\n后面来阐述最重要的插入操作。\n\n## 4.红黑树操作\n\n比较重要的是，里面使用节点基类来声明了一个指针。还包含了一个`_Rb_tree_impl`用来对红黑树初始化操作与内存管理操作。里面还包含了两种迭代器，一个rbtree，另一个是reverse，说明支持rbegin,rend操作。\n```cpp\ntemplate<typename _Key, typename _Val, typename _KeyOfValue,\n           typename _Compare, typename _Alloc = allocator<_Val> >\nclass _Rb_tree\n{\n    \nprotected:\n    typedef _Rb_tree_node_base* \t\t_Base_ptr;\n\n template<typename _Key_compare, \n    bool _Is_pod_comparator = __is_pod(_Key_compare)>\n    struct _Rb_tree_impl : public _Node_allocator\n    {\n\t  _Key_compare\t\t_M_key_compare;\n\t  _Rb_tree_node_base \t_M_header;\n\t  size_type \t\t_M_node_count; // Keeps track of size of tree.\n\n\t  _Rb_tree_impl()\n\t  : _Node_allocator(), _M_key_compare(), _M_header(),\n\t    _M_node_count(0)\n\t  { _M_initialize(); }\n\n\t  _Rb_tree_impl(const _Key_compare& __comp, const _Node_allocator& __a)\n\t  : _Node_allocator(__a), _M_key_compare(__comp), _M_header(),\n\t    _M_node_count(0)\n\t  { _M_initialize(); }\n\n\tprivate:\n\t  void\n\t  _M_initialize()\n\t  {\n\t    this->_M_header._M_color = _S_red;\n\t    this->_M_header._M_parent = 0;\n\t    this->_M_header._M_left = &this->_M_header;\n\t    this->_M_header._M_right = &this->_M_header;\n\t  }\t \n  \n    };\npublic: \n    typedef _Rb_tree_iterator<value_type>       iterator;\n    typedef std::reverse_iterator<iterator>     reverse_iterator; \nprivate:\n_Rb_tree_impl<_Compare> _M_impl;\n};\n```\n> 获取红黑树根节点、最左与最右节点\n\n回到一开始的图：\n![rbt.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/rbt.png)。\n\n\n```cpp\n// 图中100 节点\n _Base_ptr&\n_M_root() _GLIBCXX_NOEXCEPT\n{ return this->_M_impl._M_header._M_parent; }\n\n// 图中most left标记\n_Base_ptr&\n_M_leftmost() _GLIBCXX_NOEXCEPT\n{ return this->_M_impl._M_header._M_left; }\n\n\n// 图中most right标记\n _Base_ptr&\n_M_rightmost() _GLIBCXX_NOEXCEPT\n{ return this->_M_impl._M_header._M_right; }\n _Link_type\n\n // 图中begin()标记\n_M_begin() _GLIBCXX_NOEXCEPT\n{ return static_cast<_Link_type>(this->_M_impl._M_header._M_parent); }\n\n// 图中end()标记\n_Link_type\n_M_end() _GLIBCXX_NOEXCEPT\n{ return reinterpret_cast<_Link_type>(&this->_M_impl._M_header); }\n\n```\n我们再看代码是不是非常清晰！\n\n\n\n\n## 5.红黑树插入\n\n\n\n### 5.1 旋转过程\n\n左旋转是将该节点的右节点设置为它的父节点，该节点将变成刚才右节点的左孩子\n\n直接看源码中的图与代码对比即可。\n\n在`tree.cc`源码中实现函数为`local_Rb_tree_rotate_left`与`local_Rb_tree_rotate_right`。\n下面我们将源码进行剖析成比较容易理解的代码，具体见注释。\n大家会发现函数名与变量名与源码不同，是因为下面是当时自己实现的，但是不影响源码阅读，就直接拿来对比了。\n\n```cpp\n/**\n* 当前节点的左旋转过程\n* 将该节点的右节点设置为它的父节点，该节点将变成刚才右节点的左孩子\n* @param _x\n*/\n//    _x                      _y\n//  /   \\     左旋转         /  \\\n// T1   _y   --------->   _x    T3\n//     / \\              /   \\\n//    T2 T3            T1   T2\nvoid leftRotate(Node *_x) {\n    // step1 处理_x的右孩子\n    // 右节点变为_x节点的父亲节点,先保存一下右节点\n    Node *_y = _x->right;\n    // T2变为node的右节点\n    _x->right = _y->left;\n    if (NULL != _y->left)\n        _y->left->parent = _x;\n\n    // step2 处理_y与父亲节点关系\n    _y->parent = _x->parent;      // 原来_x的父亲变为_y的父亲\n    // 说明原来_x为root节点,此时需要将_y设为新root节点\n    // 或者判断NULL == _y->parent\n    if (_x == root)\n        root = _y;\n    else if (_x == _x->parent->left)    // 原_x的父节点的左孩子连接新节点_y\n        _x->parent->left = _y;\n    else // 原_x的父节点的右孩子连接新节点_y\n        _x->parent->right = _y;\n\n    // step3 处理_x与_y关系\n    _y->left = _x;      // _y的左孩子为_x\n    _x->parent = _y;    // _x的父亲是_y\n}\n```\n\n同理，右旋转如下：\n```cpp\n//        _x                      _y\n//      /   \\     右旋转         /  \\\n//     _y    T2 ------------->  T0  _x\n//    /  \\                         /  \\\n//   T0  T1                       T1  T2\nvoid rightRotate(Node *_x) {\n    // step1 处理_x的左孩子\n    // 左节点变为_x节点的父亲节点,先保存一下左节点\n    Node *_y = _x->left;\n    // T1变为_x的左孩子\n    _x->left = _y->right;\n    if (NULL != _y->right)\n        _y->right->parent = _x;\n\n    // step2 处理_y与父节点之间的关系\n    // 或者判断_x->parent==NULL\n    if (_x == root)\n        root = _y;\n    else if (_x == _x->parent->right)\n        _x->parent->right = _y;\n    else\n        _x->parent->left = _y;\n\n    // step3 处理_x与_y关系\n    _y->right = _x;     // _y的右孩子为_x\n    _x->parent = _y;    // _x的父亲是_y\n}\n```\n\n\ncase 1.1: 父节点为红色且其叔叔节点也为红色，则将父亲、叔叔置为黑色，祖父置为红色。\n\n\n![rb_1.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/rb_1.png)\n\ncase 1.2 若无叔叔节点或者其叔叔节点为黑色分为下面两种：\n\n情况1.2.1：x的叔叔节点y是黑色且x是一个右孩子\n\n![rb1.2.1.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/rb1.2.1.png)\n\n情况1.2.2：x的叔叔节点y是黑色且x是一个左孩子\n\n![rb1.2.2.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/rb1.2.2.png)\n\n对应源代码中：\n```cpp\n_Rb_tree_node_base *const __y = __xpp->_M_right;    // 得到叔叔节点\nif (__y && __y->_M_color == _S_red)     // case1: 叔叔节点存在，且为红色\n{\n    /**\n        * 解决办法是：颜色翻转，父亲与叔叔的颜色都变为黑色,祖父节点变为红色,然后当前节点设为祖父，依次网上来判断是否破坏了红黑树性质\n        */\n    __x->_M_parent->_M_color = _S_black;    // 将其父节点改为黑色\n    __y->_M_color = _S_black;               // 将其叔叔节点改为黑色\n    __xpp->_M_color = _S_red;               // 将其祖父节点改为红色\n    __x = __xpp;                            // 修改_x,往上回溯\n} else {        // 无叔叔或者叔叔为黑色\n    if (__x == __x->_M_parent->_M_right) {          // 当前节点为父亲节点的右孩子\n        __x = __x->_M_parent;\n        local_Rb_tree_rotate_left(__x, __root);     // 以父节点进行左旋转\n    }\n    // 旋转之后,节点x变成其父节点的左孩子\n    __x->_M_parent->_M_color = _S_black;            // 将其父亲节点改为黑色\n    __xpp->_M_color = _S_red;                       // 将其祖父节点改为红色\n    local_Rb_tree_rotate_right(__xpp, __root);      // 以祖父节点右旋转\n}\n\n```\n\n另外一个是上述对称过程：\n\ncase 2.1: 父节点为红色且其叔叔节点也为红色，则将父亲、叔叔置为黑色，祖父置为红色。\n\n![rb2.1.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/rb2.1.png)\n\ncase 2.2 若无叔叔节点或者其叔叔节点为黑色\n\n![rb2.2.1.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/rb2.2.1.png)\n\n情况2.2.1：x的叔叔节点y是黑色且x是一个左孩子\n\n![rb2.2.2.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/rb2.2.2.png)\n\n\n\n\n```cpp\n_Rb_tree_node_base *const __y = __xpp->_M_left; // 保存叔叔节点\nif (__y && __y->_M_color == _S_red) {       // 叔叔节点存在且为红色\n    __x->_M_parent->_M_color = _S_black;    // 父亲节点改为黑色\n    __y->_M_color = _S_black;               // 祖父节点改为红色\n    __xpp->_M_color = _S_red;\n    __x = __xpp;\n} else {        // 若无叔叔节点或者其叔叔节点为黑色\n    if (__x == __x->_M_parent->_M_left) {   // 当前节点为父亲节点的左孩子\n        __x = __x->_M_parent;\n        local_Rb_tree_rotate_right(__x, __root);    // 以父节点右旋转\n    }\n    __x->_M_parent->_M_color = _S_black;        // 父节点置为黑色\n    __xpp->_M_color = _S_red;                   // 祖父节点置为红色\n    local_Rb_tree_rotate_left(__xpp, __root);   // 左旋转\n}\n        \n```\n\n\n\n`_Rb_tree_insert_and_rebalance`完整解析：\n\n```cpp\nvoid\n_Rb_tree_insert_and_rebalance(const bool __insert_left,\n                              _Rb_tree_node_base *__x,\n                              _Rb_tree_node_base *__p,\n                              _Rb_tree_node_base &__header) throw() {\n    _Rb_tree_node_base * &__root = __header._M_parent;\n\n    // Initialize fields in new node to insert.\n    __x->_M_parent = __p;\n    __x->_M_left = 0;\n    __x->_M_right = 0;\n    __x->_M_color = _S_red;\n\n    // 处理__header部分\n    // Insert.\n    // Make new node child of parent and maintain root, leftmost and\n    // rightmost nodes.\n    // N.B. First node is always inserted left.\n    if (__insert_left) {\n        __p->_M_left = __x; // also makes leftmost = __x when __p == &__header\n\n        if (__p == &__header) {\n            __header._M_parent = __x;\n            __header._M_right = __x;\n        } else if (__p == __header._M_left)\n            __header._M_left = __x; // maintain leftmost pointing to min node\n    } else {\n        __p->_M_right = __x;\n\n        if (__p == __header._M_right)\n            __header._M_right = __x; // maintain rightmost pointing to max node\n    }\n\n // Rebalance.\n    while (__x != __root\n           && __x->_M_parent->_M_color == _S_red)   // 若新插入节点不是为RB-Tree的根节点，且其父节点color属性也是红色,即违反了性质4.\n    {\n        _Rb_tree_node_base *const __xpp = __x->_M_parent->_M_parent;        // 祖父节点\n\n        if (__x->_M_parent == __xpp->_M_left)   // 父亲是祖父节点的左孩子\n        {\n            _Rb_tree_node_base *const __y = __xpp->_M_right;    // 得到叔叔节点\n            if (__y && __y->_M_color == _S_red)     // case1: 叔叔节点存在，且为红色\n            {\n                /**\n                 * 解决办法是：颜色翻转，父亲与叔叔的颜色都变为黑色,祖父节点变为红色,然后当前节点设为祖父，依次网上来判断是否破坏了红黑树性质\n                 */\n                __x->_M_parent->_M_color = _S_black;    // 将其父节点改为黑色\n                __y->_M_color = _S_black;               // 将其叔叔节点改为黑色\n                __xpp->_M_color = _S_red;               // 将其祖父节点改为红色\n                __x = __xpp;                            // 修改_x,往上回溯\n            } else {        // 无叔叔或者叔叔为黑色\n                if (__x == __x->_M_parent->_M_right) {          // 当前节点为父亲节点的右孩子\n                    __x = __x->_M_parent;\n                    local_Rb_tree_rotate_left(__x, __root);     // 以父节点进行左旋转\n                }\n                // 旋转之后,节点x变成其父节点的左孩子\n                __x->_M_parent->_M_color = _S_black;            // 将其父亲节点改为黑色\n                __xpp->_M_color = _S_red;                       // 将其祖父节点改为红色\n                local_Rb_tree_rotate_right(__xpp, __root);      // 以祖父节点右旋转\n            }\n        } else {        // 父亲是祖父节点的右孩子\n            _Rb_tree_node_base *const __y = __xpp->_M_left; // 保存叔叔节点\n            if (__y && __y->_M_color == _S_red) {       // 叔叔节点存在且为红色\n                __x->_M_parent->_M_color = _S_black;    // 父亲节点改为黑色\n                __y->_M_color = _S_black;               // 祖父节点改为红色\n                __xpp->_M_color = _S_red;\n                __x = __xpp;\n            } else {        // 若无叔叔节点或者其叔叔节点为黑色\n                if (__x == __x->_M_parent->_M_left) {   // 当前节点为父亲节点的左孩子\n                    __x = __x->_M_parent;\n                    local_Rb_tree_rotate_right(__x, __root);    // 以父节点右旋转\n                }\n                __x->_M_parent->_M_color = _S_black;        // 父节点置为黑色\n                __xpp->_M_color = _S_red;                   // 祖父节点置为红色\n                local_Rb_tree_rotate_left(__xpp, __root);   // 左旋转\n            }\n        }\n    }\n    //若新插入节点为根节点,则违反性质2\n    //只需将其重新赋值为黑色即可\n    __root->_M_color = _S_black;\n}\n```\n\n## 5.2插入总结\n\n根据上述插入过程与源码分析，我们得出下面三种：\n假设P代码父亲节点，N代表当前新插入节点，U代表叔叔节点，G代表祖父节点。\n\n\ncase 1:U为红色，P、N也都为红色，则可以通过改变颜色，自底向上递归调整，下次N就变味G，往上判断即可。如果碰巧将根节点染成了红色, 可以在算法的最后强制root改为黑。\n\n![1_1.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/1_1.png)\n\ncase 2:U为黑色，考虑N是P的左孩子还是右孩子。\n\ncase2.1 如果是右孩子,先进行左旋转，再进入下一种情况。\n\n\n![2_1.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/2_1.png)\n\ncase2.2 可能是上述情况变化而来，但不一定是！策略为：右旋转，改变颜色。\n\n![rb2_2.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/rb2_2.png)\n\n\n经过上述源码的分析得知，红黑树插入为镜像变换，另一种情况刚好相反。\n\n\n删除操作，比较复杂，这里就暂时没分析了，后面补上。。。\n\n\n\n## 6.使用\n\n前面说了那么多，如何使用呢？\n\n引入头文件：\n```\n#include<map>或者<set>\n```\n类定义：\n```\n_Rb_tree<int, int, _Identity<int>, less<int>> itree;\n```\n然后调用相应函数即可。\n"
  },
  {
    "path": "src_analysis/stl/set_multiset.md",
    "content": "\n# STL之set与multiset那些事\nset/multiset以rb_tree为底层结构，因此有元素自动排序特性。排序的依据是key，而set/multiset元素的value和key合二为一：value就是key。\n\n\n我们无法使用set/multiset的iterators改变元素值(因为key有其严谨排列规则)。\nset/multiset的iterator是其底部RB tree的const-iterator，就是为了禁止用户对元素赋值。\n\nset元素的key必须独一无二，因此其insert使用的是rb_tree的`_M_insert_unique()`，而multiset元素的key可以重复，因此其insert使用的是rb_tree的`_M_insert_equal()`。\n\n\n## 1.set\n\n针对set源码比较简单，故从下面几个问题出发。\n\n\n> 第一个问题：key是value,value也是key。\n\n\n具体代码再第二个问题中会有，这里给出我们通常写代码后内部逻辑，我们看到里面有个红黑树，而红黑树的定义key与value是一样的，所以回答了这个问题。(源码中typedef都是来自key)。\n\n```cpp\ntemplate<typename _Key, typename _Compare = std::less<_Key>,\n    typename _Alloc = std::allocator<_Key> >\nclass set\n{\n    // concept requirements\n    typedef typename _Alloc::value_type                   _Alloc_value_type;\n\npublic:\n    // typedefs:\n    //@{\n    /// Public typedefs.\n    typedef _Key     key_type;\n    typedef _Key     value_type; // value也是key\n    typedef _Compare key_compare;\n    typedef _Compare value_compare;\n    typedef _Alloc   allocator_type;\n    //@}\n\nprivate:\n\n    typedef _Rb_tree<key_type, value_type, _Identity<value_type>,\n            key_compare, _Key_alloc_type> _Rep_type;\n    _Rep_type _M_t;  // Red-black tree representing set.\n};\n```\n\n![set_key.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/set_key.png)\n\n> 第二个问题：无法使用迭代器改变元素值。\n\n无法使用迭代器改变元素值我们看后面迭代器，发现全部用的是`const_iterator`，所以第二个问题也回答完毕。\n\n```cpp\ntemplate<typename _Key, typename _Compare = std::less<_Key>,\n    typename _Alloc = std::allocator<_Key> >\nclass set\n{\nprivate:\n\n    typedef _Rb_tree<key_type, value_type, _Identity<value_type>,\n            key_compare, _Key_alloc_type> _Rep_type;\n    _Rep_type _M_t;  // Red-black tree representing set.\n\npublic:\n    typedef typename _Rep_type::const_iterator            iterator;\n    typedef typename _Rep_type::const_iterator            const_iterator;\n    typedef typename _Rep_type::const_reverse_iterator    reverse_iterator;\n    typedef typename _Rep_type::const_reverse_iterator const_reverse_iterator;\n};\n```\n\n经过前面分析，让我们想起了queue、priority_queue、stack，他们都使用的是底层的容器，所以称为容器适配器，而set也是使用底层的容器，所以也可以被称为container adapter,即容器适配器。\n\n> 第三个问题：插入是唯一的key。\n\n底部调用的是`_M_insert_unique`。\n```cpp\ntemplate<typename _InputIterator>\nset(_InputIterator __first, _InputIterator __last)\n: _M_t()\n{ _M_t._M_insert_unique(__first, __last); }\n```\n\n我们来简单看一下这个函数实现：\n下面`_M_get_insert_unique_pos`返回的是个pair，如果插入的key相同则pair的second为0，根据是否为0可以判断是否key重复，在下面代码中判断时候，当second不为0，也就是不重复的时候，那么就可以直接插入，此时直接构造一个second为true的pair,否则构造一个second为false的pair。\n\n```cpp\ntemplate<typename _Key, typename _Val, typename _KeyOfValue,\n\t typename _Compare, typename _Alloc>\n#if __cplusplus >= 201103L\ntemplate<typename _Arg>\n#endif\npair<typename _Rb_tree<_Key, _Val, _KeyOfValue,\n\t\t       _Compare, _Alloc>::iterator, bool>\n_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::\n_M_insert_unique( _Arg && __v )\n\n{\n\ttypedef pair<iterator, bool> _Res;\n\tpair<_Base_ptr, _Base_ptr> __res\n\t\t= _M_get_insert_unique_pos( _KeyOfValue() ( __v ) );\n\n\tif ( __res.second )\n\t\treturn(_Res( _M_insert_( __res.first, __res.second,\n\t\t\t\t\t _GLIBCXX_FORWARD( _Arg, __v ) ),\n\t\t\t     true ) );\n\n\treturn(_Res( iterator( static_cast<_Link_type>(__res.first) ), false ) );\n}\n\n```\n\n我们再看看上面提到的函数：\n```cpp\ntemplate<typename _Key, typename _Val, typename _KeyOfValue,typename _Compare, typename _Alloc>\npair<typename _Rb_tree<_Key, _Val, _KeyOfValue,\n_Compare, _Alloc>::_Base_ptr,typename _Rb_tree<_Key, _Val, _KeyOfValue,Compare, _Alloc>::_Base_ptr>\n_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::\n_M_get_insert_unique_pos(const key_type& __k)\n{\n    // typedef pair\n    typedef pair<_Base_ptr, _Base_ptr> _Res;\n    // _x表示当前节点,_y表示_x的父节点\n    _Link_type __x = _M_begin();\n    _Link_type __y = _M_end();\n    bool __comp = true;\n    \n    // 寻找插入点\n    while (__x != 0)\n    {\n        __y = __x;\n        // __k<__x是否为true\n        __comp = _M_impl._M_key_compare(__k, _S_key(__x));\n        // __k<__x就往左孩子查找，否则右孩子查找\n        __x = __comp ? _S_left(__x) : _S_right(__x);\n    }\n    iterator __j = iterator(__y);\n    // __k<__y，往__y的左孩子插入节点即可，不是做插入，是找到位置即可。\n    if (__comp)\n    {\n        // 特殊位置\n        if (__j == begin())\n        return _Res(__x, __y);\n        else\n        --__j;  // 左孩子 这里调用了--操作符\n    }\n    // __j<__k，返回当前节(__x=0)点与父节点\n    if (_M_impl._M_key_compare(_S_key(__j._M_node), __k))\n        return _Res(__x, __y);\n    // _j>=__k,插入失败\n    return _Res(__j._M_node, 0);\n}\n```\n\n\n上述pair的使用给了我一个启发，竟然可以这样用，那么我们来学习一下：\n```cpp\ncout<<\"flag: \"<<itree._M_insert_unique(5).second<<endl;  // 学习返回值\ntypedef pair<int ,bool> _Res;    // 也来用一下typedef后的pair\ncout<<_Res(1,true).first<<endl;  // 直接包裹\n_Res r=make_pair(2,false);    // 定义新对象\ncout<<r.first<<endl;   // 输出结果\n```\n\n\n## 2.multiset\n同理,multiset与set定义基本类似，不同之处，在于插入使用的是另一个函数,这样才使它能够完成重复key的插入！\n\n```cpp\n template<typename _InputIterator>\nmultiset(_InputIterator __first, _InputIterator __last)\n: _M_t()\n{ _M_t._M_insert_equal(__first, __last); }\n```\n下面来分析一下`_M_insert_equal`:\n\n```cpp\ntypename _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator\n_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::\n_M_insert_equal(_Arg&& __v)\n{\n    pair<_Base_ptr, _Base_ptr> __res = _M_get_insert_equal_pos(_KeyOfValue()(__v));\n    return _M_insert_(__res.first, __res.second, _GLIBCXX_FORWARD(_Arg, __v));\n}\n```\n\n我们继续追踪上述的`_M_get_insert_equal_pos`函数：\n\n```cpp\ntemplate<typename _Key, typename _Val, typename _KeyOfValue,\ntypename _Compare, typename _Alloc>\npair<typename _Rb_tree<_Key, _Val, _KeyOfValue,\n_Compare, _Alloc>::_Base_ptr,\ntypename _Rb_tree<_Key, _Val, _KeyOfValue,\n_Compare, _Alloc>::_Base_ptr>\n_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::\n_M_get_insert_equal_pos(const key_type& __k)\n{\n    typedef pair<_Base_ptr, _Base_ptr> _Res;\n    _Link_type __x = _M_begin();\n    _Link_type __y = _M_end();\n    while (__x != 0)\n    {\n        __y = __x;\n        __x = _M_impl._M_key_compare(__k, _S_key(__x)) ?\n            _S_left(__x) : _S_right(__x);\n    }\n    return _Res(__x, __y);\n}\n```\n我们对比multiset与set的这几个函数发现，返回的pair有着显著的差异，之前的set需要返回最终是否插入成功，因为key不可重复，而multiset不需要返回是否插入成功，所以pair中不存在bool类型，故它是直接返回的插入点所构成的pair，因此，与之前相比较而言，不管你有多少个key，重复如何，都可以插入进去。\n\n"
  },
  {
    "path": "src_analysis/stl/traits.md",
    "content": "# C++ STL 源码剖析之 Traits 编程技法\n\n## 0.导语\n\n在 STL 编程中，容器和算法是独立设计的，即数据结构和算法是独立设计的，连接容器和算法的桥梁就是迭代器了，迭代器使其独立设计成为可能。如下图所示：\n\n![](https://raw.githubusercontent.com/Light-City/cloudimg/master/rela.png)\n\n上图给出了 STL 的目标就是要把数据和算法分开，分别对其进行设计，之后通过一种名为 iterator 的东西，把这二者再粘接到一起。\n\n设计模式中，关于 iterator 的描述为：**一种能够顺序访问容器中每个元素的方法，使用该方法不能暴露容器内部的表达方式。而类型萃取技术就是为了要解决和 iterator 有关的问题的。**\n\n它将范型算法(find, count, find_if)用于某个容器中,最重要的是要给算法提供一个访问容器元素的工具，iterator 就扮演着这个重要的角色。\n\n而在算法中我们可能会定义简单的中间变量或者设定算法的返回变量类型，这时候需要知道迭代器所指元素的类型是什么，但是由于没有 typeof 这类判断类型的函数,我们无法直接获取，那该如何是好？本文就来具体阐述。\n\n对于迭代器来说就是一种智能指针，因此，它也就拥有了一般指针的所有特点——能够对其进行\\*和->操作。但是在遍历容器的时候，不可避免的要对遍历的容器内部有所了解，所以，干脆把迭代器的开发工作交给容器的设计者好了，如此以来，所有实现细节反而得以封装起来不被使用者看到，这正是为什么每一种 STL 容器都提供有专属迭代器的缘故。\n\n而 Traits 在`bits/stl_iterator_base_types.h`中：\n\n```c++\ntemplate<class _Tp>\nstruct iterator_traits<_Tp*>\n{\n    typedef ptrdiff_t difference_type;\n    typedef typename _Tp::value_type value_type;\n    typedef typename _Tp::pointer pointer;\n    typedef typename _Tp::reference reference;\n    typedef typename _Tp::iterator_category iterator_category;\n};\n```\n\n看的一脸懵逼吧，没事，看完本节，入门 STL，哈哈~\n\n## 1.template 参数推导\n\n首先，在算法中运用迭代器时，很可能会用到**其相应型别（associated type）**（迭代器所指之物的型别）。假设算法中有必要声明一个变量，以\"迭代器所指对象的型别\"为型别，该**怎么办呢？**\n\n**解决方法是：利用 function template 的参数推导机制。**\n\n例如：\n\n如果 T 是某个指向特定对象的指针，那么在 func 中需要指针所指向对象的型别的时候，怎么办呢？这个还比较容易，模板的参数推导机制可以完成任务，\n\n```c++\ntemplate <class I>\ninline\nvoid func(I iter) {\n    func_impl(iter, *iter); // 传入iter和iter所指的值，class自动推导\n}\n```\n\n通过模板的推导机制，我们轻而易举的或得了指针所指向的对象的类型。\n\n```c++\ntemplate <class I, class T>\nvoid func_impl(I iter, T t) {\n        T tmp; // 这里就是迭代器所指物的类别\n        // ... 功能实现\n}\n\nint main() {\n    int i;\n    func(&i);\n}\n```\n\n但是，**函数的\"template 参数推导机制\"推导的只是参数，无法推导函数的返回值类型。万一需要推导函数的传回值，就无能为力了**。因此，我们引出下面的方法。\n\n## 2.声明内嵌型别\n\n**迭代器所指对象的型别，称之为迭代器的 value type。**\n\n尽管在 func_impl 中我们可以把 T 作为函数的返回值，但是问题是用户需要调用的是 func。\n\n```c++\ntemplate <class I, class T>\nT func_impl(I iter, T t) {\n        T tmp; // 这里就是迭代器所指物的类别\n        // ... 功能实现\n}\ntemplate <class T>\n(*T) func(T t) { // !!!Wrong code\n    return func_impl(t, *t); // forward the task to func_impl\n}\nint main() {\n    int i  =10;\n\tcout<<func(&i)<<endl; // !!! Can’t pass compile\n}\n```\n\n如果去编译上述代码，编译失败！\n\n这个问题解决起来也不难，声明内嵌型别似乎是个好主意，这样我们就可以直接获取。只要做一个 iterator，然后在定义的时候为其指向的对象类型制定一个别名，就好了，像下面这样：\n\n```c++\ntemplate <class T>\nstruct MyIter {\n    typedef T value_type; // 内嵌型别声明\n    T* ptr;\n    MyIter(T* p = 0) : ptr(p) {}\n    T& operator*() const { return *ptr; }\n};\n\ntemplate <class I>\ntypename I::value_type\nfunc(I ite) {\n\tstd::cout << \"class version\" << std::endl;\n    return *ite;\n}\nint main() {\n    // ...\n    MyIter<int> ite(new int(8));\n    cout << func(ite);\t// 输出8\n}\n```\n\n很漂亮的解决方案，看上去一切都很完美。但是，实际上还是有问题，因为 func 如果是一个泛型算法，那么它也绝对要接受一个原生指针作为迭代器，但是显然，你无法让下面的代码编译通过：\n\n```c++\nint *p = new int(5);\ncout<<func(p)<<endl; // error\n```\n\n我们的 func 无法支持原生指针，这显然是不能接受的。此时，template partial specialization 就派上了用场。\n\n## 3.救世主 Traits\n\n前面也提到了，如果直接使用`typename I::value_type`，算法就无法接收原生指针，因为原生指针根本就没有 value_type 这个内嵌类型。\n\n因此，我们还需要加入一个中间层对其进行判断，看它是不是原生指针，注意，这就是 traits 技法的妙处所在。\n\n如果我们只使用上面的做法，也就是内嵌 value_type，那么对于没有 value_type 的指针，我们只能对其进行偏特化，这种偏特化是针对可调用函数 func 的偏特化，假如 func 有 100 万行行代码，那么就会造成极大的视觉污染。\n\n**（1）函数偏特化**\n\n函数偏特化：\n\n```c++\ntemplate <class T>\nstruct MyIter {\n    typedef T value_type; // 内嵌型别声明\n    T* ptr;\n    MyIter(T* p = 0) : ptr(p) {}\n    T& operator*() const { return *ptr; }\n};\n\ntemplate <class I>\ntypename I::value_type\nfunc(I ite) {\n    std::cout << \"class version\" << std::endl;\n    return *ite;\n}\ntemplate <class I>\nI\nfunc(I* ite) {\n    std::cout << \"pointer version\" << std::endl;\n    return *ite;\n}\ntemplate <class I>\nI func(const I* ite) {\n    std::cout << \"const pointer version\" << std::endl;\n    return *ite;\n}\nint main() {\n    // ...\n    MyIter<int> ite(new int(8));\n    cout << func(ite)<<endl;\n    int *p = new int(52);\n    cout<<func(p)<<endl;\n    const int k = 3;\n    cout<<func(&k)<<endl;\n}\n```\n\n输出：\n\n```c++\nclass version\n8\npointer version\n52\nconst pointer version\n3\n```\n\n**（2）加入中间层**\n\n在 STL 中 Traits 是什么呢？看下图：\n\n![](https://raw.githubusercontent.com/Light-City/cloudimg/master/trai.png)\n\n利用一个中间层`iterator_traits`固定了`func`的形式，使得重复的代码大量减少，唯一要做的就是稍稍特化一下 iterator_tartis 使其支持 pointer 和 const pointer:)\n\n```c++\n#include <iostream>\n\ntemplate <class T>\nstruct MyIter {\n    typedef T value_type; // 内嵌型别声明\n    T* ptr;\n    MyIter(T* p = 0) : ptr(p) {}\n    T& operator*() const { return *ptr; }\n};\n// class type\ntemplate <class T>\nstruct iterator_traits {\n    typedef typename T::value_type value_type;\n};\n// 偏特化1\ntemplate <class T>\nstruct iterator_traits<T*> {\n    typedef T value_type;\n};\n// 偏特化2\ntemplate <class T>\nstruct iterator_traits<const T*> {\n    typedef T value_type;\n};\n\ntemplate <class I>\ntypename iterator_traits<I>::value_type\n// 首先询问iterator_traits<I>::value_type,如果传递的I为指针,则进入特化版本,iterator_traits直接回答;如果传递进来的I为class type,就去询问T::value_type.\nfunc(I ite) {\n    std::cout << \"normal version\" << std::endl;\n    return *ite;\n}\nint main() {\n    // ...\n    MyIter<int> ite(new int(8));\n    std::cout << func(ite)<<std::endl;\n    int *p = new int(52);\n    std::cout<<func(p)<<std::endl;\n    const int k = 3;\n    std::cout<<func(&k)<<std::endl;\n}\n\n```\n\n上述的过程是首先询问`iterator_traits<I>::value_type`，如果传递的 I 为指针,则进入特化版本,`iterator_traits`直接回答`T`;如果传递进来的`I`为`class type`,就去询问`T::value_type`.\n\n上述的通俗解释为算法(func)问 iterator_traits(我)，但是 iterator_traits(我)发现手上是指针的时候，就由我来替它回答。如果是 class type，iterator_traits(我)就继续问(他---T::value_type)。\n\n**总结：通过定义内嵌类型，我们获得了知晓 iterator 所指元素类型的方法，通过 traits 技法，我们将函数模板对于原生指针和自定义 iterator 的定义都统一起来，我们使用 traits 技法主要是为了解决原生指针和自定义 iterator 之间的不同所造成的代码冗余，这就是 traits 技法的妙处所在。**\n\n学习书籍：\n\n> 侯捷《 STL 源码剖析》\n\n学习文章：\n\n> https://juejin.im/post/5b1a43fb51882513bf1795c6\n\n> https://www.cnblogs.com/mangoyuan/p/6446046.html\n\n> http://www.cppblog.com/nacci/archive/2005/11/03/911.aspx"
  },
  {
    "path": "src_analysis/stl/typename.md",
    "content": "# typename\n\nSTL底层源码有下面几行,typedef与typename联用,这几个看着好复杂,究竟啥意思,我们今天一起来剖析!\n\n```c++\ntemplate<typename _Iterator>\nstruct iterator_traits\n{\n  typedef typename _Iterator::iterator_category iterator_category;\n  typedef typename _Iterator::value_type        value_type;\n  typedef typename _Iterator::difference_type   difference_type;\n  typedef typename _Iterator::pointer           pointer;\n  typedef typename _Iterator::reference         reference;\n};\n```\n\n## typename的常见用法\n\n首先学习一下typename的常见用法:\n\n```c++\ntemplate <typename T>\nint compare(const T &a, const T &b)\n{\n    return a>b?a:b;\n}\n```\n\n上述只是个案例程序,如果想写的比较完整比较大小,还得考虑特化版本,也许你会想到上面这段代码中的`typename`换成`class`也一样可以，不错！那么这里便有了疑问，这两种方式有区别么？查看C++ Primer之后，发现两者完全一样.\n\n### 类作用域\n\n在类外部访问类中的名称时，可以使用类作用域操作符，形如`MyClass::name`的调用通常存在三种：**静态数据成员、静态成员函数和嵌套类型**：\n\n```c++\nstruct MyClass {\n    static int A; //静态成员\n    static int B(){cout<<\"B()\"<<endl; return 100;} //静态函数\n    typedef int C;\t//嵌套类型\n    struct A1 {\t//嵌套类型\n        static int s;\n    };\n};\n```\n\n调用的时候,可以直接调:\n\n```c++\ncout<<MyClass::A<<endl;\ncout<<MyClass::B()<<endl;\nMyClass:C c;\n...\n```\n\n## 完整例子尝试\n\n让我们回到一个typename的例子:\n\n```c++\ntemplate <class T>\nvoid foo() {\n    T::iterator * iter;\n    // ...\n}\n```\n\n这段代码的目的是什么？多数人第一反应可能是：作者想定义一个指针`iter`，它指向的类型是包含在类作用域`T`中的`iterator`。可能存在这样一个包含`iterator`类型的结构：\n\n```c++\nstruct MyIterator {\n    struct iterator {\n\n    };\n};\n```\n\n调用如下:\n\n```c++\nfoo<MyIterator>();\n```\n\n这样一来，`iter`那行代码就很明显了，它是一个`MyIterator::iterator`类型的指针。我们猜测是这样的,现实是不是呢?\n\n可是，如果是像`T::iterator`这样呢？`T`是模板中的类型参数，它只有等到模板实例化时才会知道是哪种类型，更不用说内部的`iterator`。通过前面类作用域的介绍，我们可以知道，`T::iterator`实际上可以是以下三种中的任何一种类型：\n\n- 静态数据成员\n- 静态成员函数\n- 嵌套类型\n\n前面例子中的`ContainsAType::iterator`是嵌套类型，完全没有问题。可如果是静态数据成员呢？如果实例化`foo`模板函数的类型是像这样的：\n\n```c++\nstruct MyIterator {\n    static int iterator;\n};\n```\n\n那么，`T::iterator * iter;`被编译器实例化为`MyIterator::iterator * iter;`，这是什么？前面是一个静态成员变量而不是类型，那么这便成了一个乘法表达式，只不过`iter`在这里没有定义，编译器会报错：\n\n```c++\nerror: no type named ‘iterator’ in ‘struct MyIterator’\n```\n\n### typename\n\n对于用于模板定义的依赖于模板参数的名称，只有在实例化的参数中存在这个类型名，或者这个名称前使用了`typename`关键字来修饰，编译器才会将该名称当成是类型。除了以上这两种情况，绝不会被当成是类型。\n\n因此，如果你想直接告诉编译器`T::iterator`是类型而不是变量，只需用`typename`修饰：\n\n```c++\ntemplate <class T>\nvoid foo() {\n    typename T::iterator * iter;\n}\n```\n\n这样编译器就可以确定`T::iterator`是一个类型，而不再需要等到实例化时期才能确定，因此消除了前面提到的歧义。\n\n### 剖析源码\n\n回到STL源码\n\n```c++\ntemplate<typename _Iterator>\nstruct iterator_traits\n{\n  typedef typename _Iterator::iterator_category iterator_category;\n  typedef typename _Iterator::value_type        value_type;\n  typedef typename _Iterator::difference_type   difference_type;\n  typedef typename _Iterator::pointer           pointer;\n  typedef typename _Iterator::reference         reference;\n};\n```\n\n看到上面的,我们就一下子清楚了,无非就是使用typename告诉编译器`_Iterator::iterator_category`是一个类型,然后使用typedef重命名一下,其余类似!\n\n"
  },
  {
    "path": "src_analysis/stl/unordered_map.md",
    "content": "# C++ STL源码剖析之unordered_map、unordered_multimap、unordered_set、unordered_multiset\n\n## 0.导语\n\n前面学到了hashtable，而这节是hashtable的容器适配器：unordered_map。\n\n所以无序map的底层容器采用hashtable。\n\nunordered_map与unordered_multimap的源码在`unordered_map.h`这个文件中。\n\n\n## 1.unordered_map与unordered_multimap本质区别\n先来看一下unordered_map源码：\n\n```cpp\ntemplate<class _Key, class _Tp,\nclass _Hash = hash<_Key>,\nclass _Pred = std::equal_to<_Key>,\nclass _Alloc = std::allocator<std::pair<const _Key, _Tp> > >\nclass unordered_map\n{\n    typedef __umap_hashtable<_Key, _Tp, _Hash, _Pred, _Alloc>  _Hashtable;\n    _Hashtable _M_h;\n};\n```\n\n去看底层容器的`__umap_hashtable`的声明：\n\n```cpp\ntemplate<bool _Cache>\nusing __umap_traits = __detail::_Hashtable_traits<_Cache, false, true>;\n\ntemplate<typename _Key,\n    typename _Tp,\n    typename _Hash = hash<_Key>,\n    typename _Pred = std::equal_to<_Key>,\n    typename _Alloc = std::allocator<std::pair<const _Key, _Tp> >,\n    typename _Tr = __umap_traits<__cache_default<_Key, _Hash>::value>>\nusing __umap_hashtable = _Hashtable<_Key, std::pair<const _Key, _Tp>,\n_Alloc, __detail::_Select1st,\n_Pred, _Hash,\n__detail::_Mod_range_hashing,\n__detail::_Default_ranged_hash,\n__detail::_Prime_rehash_policy, _Tr>;\n```\n可以得到下面结论:\nhashtable的模板参数：\n```cpp\ntemplate<typename _Key, typename _Value, typename _Alloc,\ntypename _ExtractKey, typename _Equal,\ntypename _H1, typename _H2, typename _Hash,\ntypename _RehashPolicy, typename _Traits>\n```\n默认情况下，unordered_map采用：\n- H1为hash<key>\n- H2为_Mod_range_hashing\n- _Hash为_Default_ranged_hash\n- _RehashPolicy为_Prime_rehash_policy\n- _Traits为_Tr\n对于最后的_Tr,非常重要，因为正是因为这个参数，才有unordered_multimap。\n具体分析看下面：\n\n\n_Tr如下：\n\n```cpp\ntypename _Tr = __umap_traits<__cache_default<_Key, _Hash>::value>>\n```\n_Tr使用了`__umap_traits`，我们继续往下看：\n```cpp\n\ntemplate<bool _Cache>\nusing __umap_traits = __detail::_Hashtable_traits<_Cache, false, true>;\n```\n可以对比上述两个发现__umap_traits里面有一串__cache_default，我们再看一下模板参数为bool类型，故可以打印出来是false还是true，经过实测，为false，表示不缓存hash code。\n\n```cpp\ntemplate<typename _Tp, typename _Hash>\nusing __cache_default\n=  __not_<__and_<__is_fast_hash<_Hash>,\n__detail::__is_noexcept_hash<_Tp, _Hash>>>;\n```\n继续看`__umap_traits`，这个实际上是调用`_Hashtable_traits`，我们继续往下：\n\n```cpp\ntemplate<bool _Cache_hash_code, bool _Constant_iterators, bool _Unique_keys>\nstruct _Hashtable_traits\n{\n    using __hash_cached = __bool_constant<_Cache_hash_code>;\n    using __constant_iterators = __bool_constant<_Constant_iterators>;\n    using __unique_keys = __bool_constant<_Unique_keys>;\n};\n```\n看到有三个using，理解为三个typedef，依次表示：hash code缓存与否，是否是常迭代器，是否是唯一的key，再往上回头看，传递进来的是三个模板参数，分别是false,false,true，也验证了unordered_map是唯一的key，那么对应的unordered_multimap就是不唯一的key，最后一个参数为false。我们翻阅到相应代码如下：\n\n```cpp\n/// Base types for unordered_multimap.\ntemplate<bool _Cache>\nusing __ummap_traits = __detail::_Hashtable_traits<_Cache, false, false>;\n```\n\n小结，在上面分析，我们知道了unordered_map与unordered_multimap的本质区别，也发现了如何在底层源码上用一个容器实现两个容器适配器！\n\n## 2.unordered_set与unordered_multiset本质区别\n分析同前面一样，先看unordered_set:\n\n```cpp\ntemplate<class _Value,\n    class _Hash = hash<_Value>,\n    class _Pred = std::equal_to<_Value>,\n    class _Alloc = std::allocator<_Value> >\nclass unordered_set\n{\n    typedef __uset_hashtable<_Value, _Hash, _Pred, _Alloc>  _Hashtable;\n    _Hashtable _M_h;\n}\n\ntemplate<bool _Cache>\nusing __uset_traits = __detail::_Hashtable_traits<_Cache, true, true>;\n\ntemplate<typename _Value,\n    typename _Hash = hash<_Value>,\n    typename _Pred = std::equal_to<_Value>,\n    typename _Alloc = std::allocator<_Value>,\n    typename _Tr = __uset_traits<__cache_default<_Value, _Hash>::value>>\nusing __uset_hashtable = _Hashtable<_Value, _Value, _Alloc,\n                __detail::_Identity, _Pred, _Hash,\n                __detail::_Mod_range_hashing,\n                __detail::_Default_ranged_hash,\n                __detail::_Prime_rehash_policy, _Tr>;\n```\n可以看到传递给`_Hashtable_traits`的是false,true,true。对于unordered_set来说使用的是const iterator与唯一的key,我们再看一下unordered_multiset：\n\n```cpp\ntemplate<bool _Cache>\nusing __umset_traits = __detail::_Hashtable_traits<_Cache, true, false>;\n```\n再将两者对比一下，本质就是unordered_set不允许key重复，而unordered_multiset允许key重复。\n\n## 3.三大结论\n\n现在，我们有了前面基础，依次列出前面四个容器适配器：\n\n(1) unordered_map\n```cpp\ntemplate<bool _Cache>\nusing __umap_traits = __detail::_Hashtable_traits<_Cache, false, true>;\n```\n(2) unordered_multimap\n```cpp\ntemplate<bool _Cache>\nusing __umap_traits = __detail::_Hashtable_traits<_Cache, false, false>;\n```\n(3) unordered_set\n\n```cpp\ntemplate<bool _Cache>\nusing __uset_traits = __detail::_Hashtable_traits<_Cache, true, true>;\n```\n(4) unordered_multiset\n\n```cpp\ntemplate<bool _Cache>\nusing __uset_traits = __detail::_Hashtable_traits<_Cache, true, false>;\n```\n\n对比后，得出\n\n- 结论1：unordered_map与unordered_set不允许key重复,而带multi的则允许key重复；\n- 结论2：unordered_map与unordered_multimap采用的迭代器是iterator，而unordered_set与unordered_multiset采用的迭代器是const_iterator。\n- 结论3：unordered_map与unordered_multimap的key是key，value是key+value；而unordered_set与unordered_multiset的key是Value，Value也是Key。\n\n最后一个结论对比看下面(我们看传递给hashtable的第一与第二个参数)：\n\nunordered_map与unordered_multimap：\n```cpp\nusing __umap_hashtable = _Hashtable<_Key, \nstd::pair<const _Key, _Tp>,\n_Alloc, __detail::_Select1st,\n_Pred, _Hash,\n__detail::_Mod_range_hashing,\n__detail::_Default_ranged_hash,\n__detail::_Prime_rehash_policy, _Tr>;\n```\nunordered_set与unordered_multiset：\n```cpp\ntemplate<typename _Value,\ntypename _Hash = hash<_Value>,\ntypename _Pred = std::equal_to<_Value>,\ntypename _Alloc = std::allocator<_Value>,\ntypename _Tr = __uset_traits<__cache_default<_Value, _Hash>::value>>\nusing __uset_hashtable = _Hashtable<_Value, _Value, _Alloc,\n__detail::_Identity, _Pred, _Hash,\n__detail::_Mod_range_hashing,\n__detail::_Default_ranged_hash,\n__detail::_Prime_rehash_policy, _Tr>;\n```\n\n## 4.unordered_map重要函数\n\n> 初始化\n\n可以在下面的构造函数中看到unordered_map的默认桶数为10。\n\n在unordered_map的底层默认采用hasher(),也就是H1,也就是std::hash\n\n```cpp\nunordered_map(size_type __n = 10,\n    const hasher& __hf = hasher(),\n    const key_equal& __eql = key_equal(),\n    const allocator_type& __a = allocator_type())\n: _M_h(__n, __hf, __eql, __a)\n{ }\n```\n下面测试是否采用默认的hash：\n\n```cpp\nunordered_map<string,int> um;\nhash<string> h;\ncout<<um.hash_function()(\"hhhhhawq\")<<endl;\ncout<<h(\"hhhhhawq\")<<endl;\n```\n输出：\n\n```cpp\n9074142923776869151\n9074142923776869151\n```\n进一步验证了采用默认的hash。\n\n> 是否空、大小、最大大小\n\n```cpp\nbool\nempty() const noexcept\n{ return _M_h.empty(); }\n///  Returns the size of the %unordered_map.\nsize_type\nsize() const noexcept\n{ return _M_h.size(); }\n\n///  Returns the maximum size of the %unordered_map.\nsize_type\nmax_size() const noexcept\n{ return _M_h.max_size(); }\n```\n\n> begin与end\n\n```cpp\niterator\nbegin() noexcept\n{ return _M_h.begin(); }\niterator\nend() noexcept\n{ return _M_h.end(); }\n```\n> insert\n五种插入方式\n\n```cpp\n// value\nstd::pair<iterator, bool>\ninsert(const value_type& __x)\n{ return _M_h.insert(__x); }\n\n// pair \nstd::pair<iterator, bool>\ninsert(_Pair&& __x)\n{ return _M_h.insert(std::forward<_Pair>(__x)); }\n\n// iterator+value\niterator\ninsert(const_iterator __hint, const value_type& __x)\n{ return _M_h.insert(__hint, __x); }\n\n\n// first到last范围插入\ntemplate<typename _InputIterator>\nvoid\ninsert(_InputIterator __first, _InputIterator __last)\n{ _M_h.insert(__first, __last); }\n\n// 初始化列表插入\n\nvoid\ninsert(initializer_list<value_type> __l)\n{ _M_h.insert(__l); }\n```\n\n> 删除\n\n三种删除方式\n\n```cpp\n// iterator\niterator\nerase(iterator __position)\n{ return _M_h.erase(__position); }\n\n// key\nsize_type\nerase(const key_type& __x)\n{ return _M_h.erase(__x); }\n\n// first到last范围\n\niterator\nerase(const_iterator __first, const_iterator __last)\n{ return _M_h.erase(__first, __last); \n```\n\n> 清除\n\n```cpp\nvoid\nclear() noexcept\n{ _M_h.clear(); }\n```\n> hash_function\n\n得到该unordered_map的hash_function\n```cpp\nhasher\nhash_function() const\n{ return _M_h.hash_function(); }\n```\n使用：\n```cpp\nunordered_map<string,int> um;\ncout<<um.hash_function()(\"hhhhhawq\")<<endl; //传递的内容要与上面key类型一致。\n```\n\n> key_eq\nkey_eq返回的是std::equal_to\n使用如下：\n```cpp\nunordered_map<string,int> um;\ncout<<um.key_eq()(\"1\",\"2\")<<endl;\n```\n> 查找与获取value\n\n```cpp\niterator\nfind(const key_type& __x)\n{ return _M_h.find(__x); }\nmapped_type&\noperator[](const key_type& __k)\n{ return _M_h[__k]; }\nmapped_type&\nat(const key_type& __k)\n{ return _M_h.at(__k); }\n```\n除了这些函数还有获取桶，最大桶数、加载因子、rehash等等，就是没有排序，因为hashtable没有提供排序功能。hashtable在查找、删除和插入节点是常数时间，优于RB-Tree红黑树。\n\n同理，unordered_set、unordered_multiset、unordered_multimap与unordered_map一样的函数，所以就不阐述了。"
  },
  {
    "path": "src_analysis/stl/vector.md",
    "content": "# STL源码剖析之vector\n\n## 0.导语\n\nvector的数据安排以及操作方式，与array非常相似。两者的唯一差别在于空间的运用的灵活性，array是静态的，一旦配置了就不能改变，而 vector是动态空间，随着元素的加入，它的内部机制会自行扩充空间以容纳新元素。下面一起来看一下vector的\"内部机制\"，怎么来实现空间配置策略的。\n\n## 1.vector\n\n在`_Vector_base`中开头有两行比较难理解，下面一个一个分析：\n\n### 1.1 _Tp_alloc_type\n开头处定义：\n```\n typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template rebind<_Tp>::other _Tp_alloc_type;\n```\n\n在`__gnu_cxx::__alloc_traits`中：对应文件为：`ext/alloc_traits.h`\n\n```cpp\n template<typename _Tp>\n      struct rebind\n      { typedef typename _Base_type::template rebind_alloc<_Tp> other; };\n```\n等价于\n```\ntypename __gnu_cxx::__alloc_traits<_Alloc>::template rebind<_Tp>::other \n```\n等价于：\n\n```cpp\ntypename _Base_type::template rebind_alloc<_Tp>\n```\n\n而`_Base_type`是：\n\n```cpp\ntypedef std::allocator_traits<_Alloc>           _Base_type;\n```\n\n所以上述等价于：\n\n```cpp\ntypename std::allocator_traits<_Alloc>::template rebind_alloc<_Tp>\n```\n\n继续到`allocator_traits`中寻找\n\n找到了：\n\n```cpp\n  template<typename _Up>\n\tusing rebind_alloc = allocator<_Up>;\n```\n\n于是：\n\n```cpp\nstd::allocator_traits<_Alloc>::template rebind_alloc<_Tp>\n```\n\n等价于：\n\n```cpp\nallocator<_Tp>\n```\n\n> 小结\n\n```cpp\n typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template rebind<_Tp>::other _Tp_alloc_type;\n```\n\n等价于：\n\n```cpp\ntypedef allocator<_Tp> _Tp_alloc_type\n```\n\n### 1.2 pointer\n\n而`pointer`：\n\n```cpp\n typedef typename __gnu_cxx::__alloc_traits<_Tp_alloc_type>::pointer\n       \tpointer;\n```\n\n等价于：\n\n```cpp\n typedef typename __gnu_cxx::__alloc_traits<allocator<_Tp>>::pointer\n       \tpointer;\n```\n\n根据下面两行：\n\n```cpp\ntypedef std::allocator_traits<_Alloc>           _Base_type;\ntypedef typename _Base_type::pointer            pointer;\n```\n\n又等价于：\n\n```\n typedef std::allocator_traits<_Alloc>::pointer\n       \tpointer;\n```\n\n在`allocator_traits`中找到下面：\n\n```cpp\n  /**\n   * @brief   The allocator's pointer type.\n   *\n   * @c Alloc::pointer if that type exists, otherwise @c value_type*\n  */\n  typedef __pointer pointer;\n```\n\n注释中说了如果存在就是`Alloc::pointer`，否则为` value_type *`。\n\n> 小结\n\n```cpp\n typedef typename __gnu_cxx::__alloc_traits<_Tp_alloc_type>::pointer\n       \tpointer;\n // 或者\n typedef typename __gnu_cxx::__alloc_traits<allocator<_Tp>>::pointer\n       \tpointer;\n```\n\n等价于\n\n```cpp\n/**\n* @brief   The allocator's pointer type.\n*\n* @c Alloc::pointer if that type exists, otherwise @c value_type*\n*/\ntypedef __pointer pointer;\n\n```\n\n如果存在`_Tp_alloc_type::pointer`也就是`allocator<_Tp>`存在就是`allocator<_Tp>::pointer`，\n\n这个看`allocator.h`源码：\n\n```cpp\ntypedef _Tp*       pointer;\n\n```\n\n否则为` value_type*`。而`value_type`为：\n\n```cpp\ntypedef typename _Alloc::value_type value_type;\n\n```\n\n所以`value_type*`推导出为：\n\n```cpp\n_Tp::value_type*\n\n```\n\n### 1.3 vector的内存管理\n\n`_Vector_base`中有一个**内存管理器**`_Vector_impl`类，该结构体继承`allocator`(根据上述1.1等价条件得出)。\n\n\n```cpp\ntemplate<typename _Tp, typename _Alloc>\nstruct _Vector_base {\n    //__alloc_traits -> allocator_traits\n    // typedef allocator<_Tp> _Tp_alloc_type\n    typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template\n    rebind<_Tp>::other _Tp_alloc_type;\n    //  _Tp::value_type* or _Tp*\n    typedef typename __gnu_cxx::__alloc_traits<_Tp_alloc_type>::pointer\n            pointer;\n\n    struct _Vector_impl\n            : public _Tp_alloc_type {\n        pointer _M_start;       // 使用空间起始位置    \n        pointer _M_finish;      // 使用空间结束位置\n        pointer _M_end_of_storage;   // 可使用空间结束位置 \n\n        _Vector_impl()\n                : _Tp_alloc_type(), _M_start(0), _M_finish(0), _M_end_of_storage(0) {}\n\n        _Vector_impl(_Tp_alloc_type const &__a)\n\n        // vector数据交换，只交换内存地址，不交换数据\n        void _M_swap_data(_Vector_impl &__x)\n\n        _GLIBCXX_NOEXCEPT\n        {\n            std::swap(_M_start, __x._M_start);\n            std::swap(_M_finish, __x._M_finish);\n            std::swap(_M_end_of_storage, __x._M_end_of_storage);\n        }\n    };\n\npublic:\n    typedef _Alloc allocator_type;\n    // 前面我们知道_Vector_impl继承自allocator分配器，而这个又是_Tp_alloc_type，所以这里返回的就是_M_impl的基类。\n    _Tp_alloc_type &\n    _M_get_Tp_allocator()\n\n    _GLIBCXX_NOEXCEPT\n    { return *static_cast<_Tp_alloc_type *>(&this->_M_impl); }\n\n    const _Tp_alloc_type &\n    _M_get_Tp_allocator() const\n\n    _GLIBCXX_NOEXCEPT\n    { return *static_cast<const _Tp_alloc_type *>(&this->_M_impl); }\n\n    allocator_type    // 获取传递进来的分配器      \n    get_allocator() const\n\n    _GLIBCXX_NOEXCEPT\n    { return allocator_type(_M_get_Tp_allocator()); }\n\n    _Vector_base()\n            : _M_impl() {}\n\n    _Vector_base(const allocator_type &__a)\n\n    _GLIBCXX_NOEXCEPT\n            : _M_impl(__a) {}\n\n    _Vector_base(size_t __n)\n            : _M_impl() { _M_create_storage(__n); }\n\n    _Vector_base(size_t __n, const allocator_type &__a)\n            : _M_impl(__a) { _M_create_storage(__n); }\n\n#if __cplusplus >= 201103L\n    _Vector_base(_Tp_alloc_type&& __a) noexcept\n    : _M_impl(std::move(__a)) { }\n    \n    // 移动构造函数，只交换3个指针，不copy数据\n    _Vector_base(_Vector_base&& __x) noexcept\n    : _M_impl(std::move(__x._M_get_Tp_allocator()))\n    { this->_M_impl._M_swap_data(__x._M_impl); }\n\n    _Vector_base(_Vector_base&& __x, const allocator_type& __a)\n    : _M_impl(__a)\n    {\n  if (__x.get_allocator() == __a)\n    this->_M_impl._M_swap_data(__x._M_impl);\n  else\n    {\n      size_t __n = __x._M_impl._M_finish - __x._M_impl._M_start;\n      _M_create_storage(__n);\n    }\n    }\n#endif\n\n    ~_Vector_base()\n\n    _GLIBCXX_NOEXCEPT\n    {\n        _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage\n                                              - this->_M_impl._M_start);\n    }\n\npublic:\n    _Vector_impl _M_impl;\n\n    pointer _M_allocate(size_t __n) {\n        typedef __gnu_cxx::__alloc_traits <_Tp_alloc_type> _Tr;\n        return __n != 0 ? _Tr::allocate(_M_impl, __n) : 0;  // 同_M_deallocate，一直往后调用的就是malloc函数\n    }\n\n    void _M_deallocate(pointer __p, size_t __n) {\n        typedef __gnu_cxx::__alloc_traits <_Tp_alloc_type> _Tr; \n        if (__p)\n            _Tr::deallocate(_M_impl, __p, __n); // 最后调用allocator_traits的deallocate,而该函数又是根据传递进来的_M_impl进行deallocate,一直往后，最后调用的就是free函数\n    }\n\nprivate:\n    void _M_create_storage(size_t __n) {\n        this->_M_impl._M_start = this->_M_allocate(__n);\n        this->_M_impl._M_finish = this->_M_impl._M_start;\n        this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;\n    }\n};\n```\n\n小结：`_Vector_base`专门负责`vector`的内存管理，内部类`_M_impl`通过继承`_Tp_alloc_type`(也就是allocator)得到内存分配释放的功能，_M_allocate和_M_deallocate分别分配和释放vector所用内存，vector只需要负责元素构造和析构。\n\n在vector中，默认内存分配器为`std::allocator<_Tp>`\n```cpp\ntemplate<typename _Tp, typename _Alloc = std::allocator<_Tp>>\nclass vector : protected _Vector_base<_Tp, _Alloc> {\n}\n```\nvector代码中使用基类的内存函数及typedef等：\n```cpp\ntemplate<typename _Tp, typename _Alloc = std::allocator<_Tp>>\nclass vector : protected _Vector_base<_Tp, _Alloc> {\n    typedef _Vector_base<_Tp, _Alloc> _Base;\n    typedef typename _Base::_Tp_alloc_type _Tp_alloc_type;\npublic:\n    typedef typename _Base::pointer pointer;\nprotected:\n    using _Base::_M_allocate;\n    using _Base::_M_deallocate;\n    using _Base::_M_impl;\n    using _Base::_M_get_Tp_allocator;\n}\n```\n## 2.vector迭代器\n在vector中使用了两种迭代器，分别是正向`__normal_iterator`与反向迭代器`reverse_iterator`:\n\n正向：\n\n```cpp\ntypedef __gnu_cxx::__normal_iterator <pointer, vector> iterator;\ntypedef __gnu_cxx::__normal_iterator <const_pointer, vector> const_iterator;\n```\n反向：\n```cpp\ntypedef std::reverse_iterator<const_iterator> const_reverse_iterator;\ntypedef std::reverse_iterator<iterator> reverse_iterator;\n```\n\n`__normal_iterator`与`reverse_iterator`都定义于stl_iterator.h，封装了vector元素的指针。\n\n### 2.1 正向\n\n```cpp\ntemplate<typename _Iterator, typename _Container>\nclass __normal_iterator\n{\nprotected:\n    _Iterator _M_current;\n\n    typedef iterator_traits<_Iterator>\t\t__traits_type;\n\npublic:\n    typedef _Iterator\t\t\t\t\titerator_type;\n\n    // iterator必须包含的五种typedef\n    typedef typename __traits_type::iterator_category iterator_category;\n    typedef typename __traits_type::value_type  \tvalue_type;\n    typedef typename __traits_type::difference_type \tdifference_type;\n    typedef typename __traits_type::reference \treference;\n    typedef typename __traits_type::pointer   \tpointer;\n\n    _GLIBCXX_CONSTEXPR __normal_iterator() _GLIBCXX_NOEXCEPT\n    : _M_current(_Iterator()) { }\n\n    explicit\n    __normal_iterator(const _Iterator& __i) _GLIBCXX_NOEXCEPT\n    : _M_current(__i) { }\n\n    // Allow iterator to const_iterator conversion\n    template<typename _Iter>\n    __normal_iterator(const __normal_iterator<_Iter,\n            typename __enable_if<\n            (std::__are_same<_Iter, typename _Container::pointer>::__value),\n            _Container>::__type>& __i) _GLIBCXX_NOEXCEPT\n    : _M_current(__i.base()) { }\n\n    // Forward iterator requirements\n    reference\n    operator*() const _GLIBCXX_NOEXCEPT\n    { return *_M_current; }\n\n    pointer\n    operator->() const _GLIBCXX_NOEXCEPT\n    { return _M_current; }\n\n    // 前置++\n    __normal_iterator&\n    operator++() _GLIBCXX_NOEXCEPT\n    {\n        ++_M_current;\n        return *this;\n    }\n\n    // 后置++\n    __normal_iterator\n    operator++(int) _GLIBCXX_NOEXCEPT\n    { return __normal_iterator(_M_current++); }\n\n    // 前置--\n    // Bidirectional iterator requirements\n    __normal_iterator&\n    operator--() _GLIBCXX_NOEXCEPT\n    {\n        --_M_current;\n        return *this;\n    }\n\n    // 后置--\n    __normal_iterator\n    operator--(int) _GLIBCXX_NOEXCEPT\n    { return __normal_iterator(_M_current--); }\n\n    // 随机访问迭代器都要重载[]操作符\n    // Random access iterator requirements\n    reference\n    operator[](difference_type __n) const _GLIBCXX_NOEXCEPT\n    { return _M_current[__n]; }\n\n    // +=操作符 跳跃n个difference_type\n    __normal_iterator&\n    operator+=(difference_type __n) _GLIBCXX_NOEXCEPT\n    { _M_current += __n; return *this; }\n\n    // +操作符 跳跃n个difference_type\n    __normal_iterator\n    operator+(difference_type __n) const _GLIBCXX_NOEXCEPT\n    { return __normal_iterator(_M_current + __n); }\n\n    // -=操作符 后退n个difference_type\n    __normal_iterator&\n    operator-=(difference_type __n) _GLIBCXX_NOEXCEPT\n    { _M_current -= __n; return *this; }\n\n    // -操作符 后退n个difference_type\n    __normal_iterator\n    operator-(difference_type __n) const _GLIBCXX_NOEXCEPT\n    { return __normal_iterator(_M_current - __n); }\n\n    const _Iterator&\n    base() const _GLIBCXX_NOEXCEPT\n    { return _M_current; }\n};\n```\n\n_M_current是指向迭代器位置的指针，这是一个随机访问型指针，operator+和operator-等移动操作可以直接移动到目的地，非随机访问型指针只能一步步移动。\n\n### 2.2 反向\n\n\nvector还会使用reverse_iterator，即逆序迭代器，顾名思义，其移动方向与普通迭代器相反\n\n```cpp\ntemplate<typename _Iterator>\nclass reverse_iterator\n: public iterator<typename iterator_traits<_Iterator>::iterator_category,\n            typename iterator_traits<_Iterator>::value_type,\n            typename iterator_traits<_Iterator>::difference_type,\n            typename iterator_traits<_Iterator>::pointer,\n                    typename iterator_traits<_Iterator>::reference>\n{\nprotected:\n    _Iterator current;\n\n    typedef iterator_traits<_Iterator>\t\t__traits_type;\n\npublic:\n    typedef _Iterator\t\t\t\t\titerator_type;\n    typedef typename __traits_type::difference_type\tdifference_type;\n    typedef typename __traits_type::pointer\t\tpointer;\n    typedef typename __traits_type::reference\t\treference;\n    \n    // 省略不重要的代码\n\n\n    // 该迭代器是从后面end()开始，需要往前一步，才可以获取到有效的迭代器位置\n    reference\n    operator*() const\n    {\n        _Iterator __tmp = current;\n        return *--__tmp;\n    }\n\n    // 通过调用上述*操作符直接实现\n    pointer\n    operator->() const\n    { return &(operator*()); }\n\n    \n    // 前置++操作符完成后退任务\n    reverse_iterator&\n    operator++()\n    {\n        --current;\n        return *this;\n    }\n\n    // 后置++\n    reverse_iterator\n    operator++(int)\n    {\n        reverse_iterator __tmp = *this;\n        --current;\n        return __tmp;\n    }\n\n    // 前置--操作符完成前进任务\n    reverse_iterator&\n    operator--()\n    {\n        ++current;\n        return *this;\n    }\n\n    // 后置--\n    reverse_iterator\n    operator--(int)\n    {\n        reverse_iterator __tmp = *this;\n        ++current;\n        return __tmp;\n    }\n\n    // +操作符\n    reverse_iterator\n    operator+(difference_type __n) const\n    { return reverse_iterator(current - __n); }\n\n    // +=操作符\n    reverse_iterator&\n    operator+=(difference_type __n)\n    {\n        current -= __n;\n        return *this;\n    }\n\n    // -操作符\n    reverse_iterator\n    operator-(difference_type __n) const\n    { return reverse_iterator(current + __n); }\n\n    // -=操作符\n    reverse_iterator&\n    operator-=(difference_type __n)\n    {\n        current += __n;\n        return *this;\n    }\n\n    // []操作符\n    reference\n    operator[](difference_type __n) const\n    { return *(*this + __n); }\n};\n\n```\n\n## 3.vector的数据结构\n\nvector内存由_M_impl中的M_start，_M_finish，_M_end_of_storage三个指针管理，所有关于地址，容量大小等操作都需要用到这三个指针：\n\n```cpp\niterator begin() _GLIBCXX_NOEXCEPT\n    { return iterator(this->_M_impl._M_start); }\niterator end() _GLIBCXX_NOEXCEPT\n    { return iterator(this->_M_impl._M_finish); }\nreverse_iterator  rbegin() noexcept\n    { return reverse_iterator(end()); }\nreverse_iterator rend() noexcept\n    { return reverse_iterator(begin()); }\nsize_type size() const _GLIBCXX_NOEXCEPT\n    { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }\nsize_type capacity() const _GLIBCXX_NOEXCEPT\n    { return size_type(this->_M_impl._M_end_of_storage - this->_M_impl._M_start); }\nbool empty() const _GLIBCXX_NOEXCEPT\n    { return begin() == end(); }\n```\n![vector_1.png](https://raw.githubusercontent.com/Light-City/cloudimg/master/vector_1.png)\n\n_M_finish和_M_end_of_storage之间的空间没有数据，有时候这是一种浪费，c++11提供了一个很有用的函数shrink_to_fit()，将这段未使用空间释放，主要调用了下面代码，\n\n\n\n```cpp\ntemplate<typename _Alloc>\nbool vector<bool, _Alloc>::\n_M_shrink_to_fit()\n{\n    if (capacity() - size() < int(_S_word_bit)) // 64位系统为64bytes\n        return false;\n    __try\n    {\n        _M_reallocate(size());\n        return true;\n    }\n    __catch(...)\n    { \n        return false; \n    }\n}\n```\n\n\n```cpp\n template<typename _Alloc>\nvoid vector<bool, _Alloc>::\n_M_reallocate(size_type __n)\n{\n    _Bit_type* __q = this->_M_allocate(__n);\n    this->_M_impl._M_finish = _M_copy_aligned(begin(), end(),\n                    iterator(__q, 0));\n    this->_M_deallocate();\n    this->_M_impl._M_start = iterator(__q, 0);\n    this->_M_impl._M_end_of_storage = __q + _S_nword(__n);\n}\n```\n而`_M_copy_aligned`通过两个std::copy实现:\n\n第一次swap把`__first`的指针与`__last`的指针之间的数据拷贝到`__result`指针所指向的起始位置。\n第二次swap获得`__last`的指针对应的迭代器。\n\n```cpp\niterator\n_M_copy_aligned(const_iterator __first, const_iterator __last,\n        iterator __result)\n{\n    // _Bit_type * _M_p; _Bit_type为unsigned long类型\n    _Bit_type* __q = std::copy(__first._M_p, __last._M_p, __result._M_p);\n    return std::copy(const_iterator(__last._M_p, 0), __last,\n            iterator(__q, 0));\n}\n\n```\n先分配size()大小的内存空间，用于存储`begin()`与`end()`之间的数据，释放原来的vector空间，新的vector只包含size()数量的数据，并修改`_M_start`与`_M_end_of_storage`指向。\n\n\n## 4.vector构造与析构\n\n```cpp\n//使用默认内存分配器\nvector() : _Base() { } \n//指定内存分配器\nexplicit vector(const allocator_type& __a) : _Base(__a) { }\n//初始化为n个__value值，如果没指定就使用该类型默认值\nexplicit vector(size_type __n, const value_type& __value = value_type(),\n             const allocator_type& __a = allocator_type()): _Base(__n, __a)\n{ _M_fill_initialize(__n, __value); }\n//拷贝构造函数\nvector(const vector& __x)\n    : _Base(__x.size(),\n    _Alloc_traits::_S_select_on_copy(__x._M_get_Tp_allocator()))\n{ this->_M_impl._M_finish =\n    std::__uninitialized_copy_a(__x.begin(), __x.end(),\n                                this->_M_impl._M_start,\n                                _M_get_Tp_allocator());\n}\n//c++11的移动构造函数，获取__x的M_start，_M_finish，_M_end_of_storage，并不需要数据拷贝\nvector(vector&& ) noexcept\n    : _Base(std::move(__x)) { }\n//从list中拷贝数据，也是c++11才有的\n vector(initializer_list<value_type> __l,\n        const allocator_type& __a = allocator_type())\n: _Base(__a)\n{\n    _M_range_initialize(__l.begin(), __l.end(), random_access_iterator_tag());\n}\n//支持vector使用两个迭代器范围内的值初始化，除了stl的迭代器，也可以是数组地址\ntemplate<typename _InputIterator,\n        typename = std::_RequireInputIter<_InputIterator>>\nvector(_InputIterator __first, _InputIterator __last,\n        const allocator_type& __a = allocator_type())\n: _Base(__a)\n{ _M_initialize_dispatch(__first, __last, __false_type()); }\n//只析构所有元素，释放内存由vector_base完成\n~vector() _GLIBCXX_NOEXCEPT\n{ std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,_M_get_Tp_allocator()); }\n```\n\n## 5.vector\n\n插入涉及到内存分配，动态调整，与一开始提到的vector与array区别，就在下面体现出：\n\n```cpp\ntypename vector<_Tp, _Alloc>::iterator\nvector<_Tp, _Alloc>::insert(iterator __position, const value_type& __x)\n{\n    const size_type __n = __position – begin();\n    //插入到最后一个位置，相当于push_back\n    if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage\n        && __position == end())\n    {\n        _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x);\n        ++this->_M_impl._M_finish;\n    }\n    else\n    {\n        _M_insert_aux(__position, __x);\n    }\n    return iterator(this->_M_impl._M_start + __n);\n}\n\n```\n其中`_M_insert_aux`实现：\n```cpp\ntemplate<typename _Tp, typename _Alloc>\nvoid vector<_Tp, _Alloc>::_M_insert_aux(iterator __position, const _Tp& __x)\n{   \n    //内存空间足够\n    if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)\n    { \n        _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,\n                                _GLIBCXX_MOVE(*(this->_M_impl._M_finish\n                                                - 1)));\n        ++this->_M_impl._M_finish;\n        //__position后的元素依次向后移动一个位置\n        _GLIBCXX_MOVE_BACKWARD3(__position.base(),\n                                this->_M_impl._M_finish - 2,\n                                this->_M_impl._M_finish – 1);\n        //目标地址赋值\n        *__position = _Tp(std::forward<_Args>(__args)...);\n    }\n    else\n    {\n        //内存加倍\n        const size_type __len =\n        _M_check_len(size_type(1), \"vector::_M_insert_aux\");\n        const size_type __elems_before = __position - begin();\n        pointer __new_start(this->_M_allocate(__len));\n        pointer __new_finish(__new_start);\n        __try\n        {\n            //给position位置赋值\n            _Alloc_traits::construct(this->_M_impl,\n                                    __new_start + __elems_before,\n                                    std::forward<_Args>(__args)...);\n                                    __x);\n            __new_finish = 0;\n            //拷贝position位置前元素\n            __new_finish = std::__uninitialized_move_if_noexcept_a\n            (this->_M_impl._M_start, __position.base(),\n                __new_start, _M_get_Tp_allocator());\n\n            ++__new_finish;\n            //拷贝position位置后元素\n            __new_finish\n            = std::__uninitialized_move_if_noexcept_a\n            (__position.base(), this->_M_impl._M_finish,\n                __new_finish, _M_get_Tp_allocator());\n            }\n        __catch(...)\n        {\n            if (!__new_finish)\n            _Alloc_traits::destroy(this->_M_impl,\n                                    __new_start + __elems_before);\n            else\n            std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator());\n            _M_deallocate(__new_start, __len);\n            __throw_exception_again;\n        }\n\n        //析构原vector所有元素\n        std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,\n                    _M_get_Tp_allocator());\n        //释放原vector内存空间\n        _M_deallocate(this->_M_impl._M_start,\n                    this->_M_impl._M_end_of_storage，this->_M_impl._M_start);\n        //vector内存地址指向新空间\n        this->_M_impl._M_start = __new_start;\n        this->_M_impl._M_finish = __new_finish;\n        this->_M_impl._M_end_of_storage = __new_start + __len;\n    }\n}\n```\n其中`_M_check_len`：\n\n```cpp\n\nsize_type\n_M_check_len(size_type __n, const char* __s) const\n{\n    if (max_size() - size() < __n)\n        __throw_length_error(__N(__s));\n\n    const size_type __len = size() + std::max(size(), __n); //如果n小于当前size，内存加倍，否则内存增长n。\n    return (__len < size() || __len > max_size()) ? max_size() : __len;\n}\n```\n内存分配策略并不是简单的加倍，如果n小于当前size，内存加倍，否则内存增长n。\n\n\n学习资料：\n> 侯捷《STL源码剖析》\n\n> https://www.cnblogs.com/coderkian/p/3888429.html"
  },
  {
    "path": "src_analysis/stl/谈谈STL设计之EBO优化.md",
    "content": "# STL设计之EBO(空基类优化)\n\n## 0.导语\n\nEBO简称Empty Base Optimization。\n\n本节从空类开始，到STL内部，到测试，再到我们自己实现一个EBO，对比性能，最后再测试，总结。\n\n## 1.空类\n\n定义一个空类：没有成员变量，没有继承，没有数据元素的类。\n\n```cpp\nclass Empty{\npublic:\n    void print() {\n        std::cout<<\"I am Empty class\"<<std::endl;\n    }\n};\n```\n\n由于它是空的，所以这个sizeof是多少呢？\n\n```cpp\nstd::cout<<sizeof(Empty)<<std::endl; //1\n```\n\n结果是1，**它是空的怎么不是0呢？**\n\n因为空类同样可以被实例化，每个实例在内存中都有一个独一无二的地址，为了达到这个目的，编译器往往会给一个空类隐含的加一个字节，这样空类在实例化后在内存得到了独一无二的地址．所以上述大小为1.\n\n根据上面的回答，估计大家或引出另一个问题：**为什么两个不同对象的地址应该不同？**\n\n现在有下面这个例子：\n\n```cpp\nclass Empty{\npublic:\n    void print() {\n        std::cout<<\"I am Empty class\"<<std::endl;\n    }\n};\ntemplate < typename T >\nbool isSame( T const & t1, T const & t2 )\n{\n    return &t1 == &t2;\n}\n```\n\n我们来测试一下：\n\n```cpp\nint main() {\n    Empty a,b;\n    assert(!isSame(a,b));  // 编译通过，a与b的地址不同\n\n    Empty *p=new Empty;\n    Empty *q=new Empty;\n    assert(!isSame(p,q)); // 编译通过，a与b的地址不同\n    return 0;\n}\n```\n\n上面测试了，两个不同对象地址是不同的，考虑下面场景：\n\n```cpp\nEmpty a,b;\n// 在同一地址具有两个对象将意味着在使用指针引用它们时将无法区分这两个对象。\nEmpty *p1=&a;\np1->print();\n```\n\n此时会发现，如果a的地址与b的地址一样，那么在同一地址具有两个对象将意味着在使用指针引用它们时将无法区分这两个对象。因此两个不同对象的地址不同。\n\n## 2.空基类优化\n\n现在对比一下下面两个用法，第一种，一个类中包含了两一个类作为成员，然后通过这个来获得被包含类的功能。\n\n```cpp\nclass notEbo  {\n    int i;\n    Empty e;\n    // do other things\n};\n```\n\n另一种直接采用继承的方式来获得基类的成员函数及其他功能等等。\n\n```cpp\nclass ebo:public Empty {\n    int i;\n    // do other things\n};\n```\n\n接下来做个测试：\n\n```cpp\nstd::cout<<sizeof(notEbo)<<std::endl;\nstd::cout<<sizeof(ebo)<<std::endl;\n```\n\n输出：\n\n```cpp\n8 \n4\n```\n\n第一种，会因为字节对齐，将其原来只占1字节，进行扩充到4的倍数，最后就是8字节。\n\n对比这两个发现，第二种通过继承方式来获得基类的功能，**并没有产生额外大小的优化称之为EBO(空基类优化)。**\n\n接下来，我们回到STL源码中，看看其中的使用！\n\n## 3.STL中的EBO世界\n\n不管是deque、rb_tree、list等容器，都离不开内存管理，在这几个容器中都包含了相应的内存管理，并通过`_xx_impl`来继承下面这几个类：\n\n```cpp\nstd::allocator<_Tp>\n__gnu_cxx::bitmap_allocator<_Tp>\n__gnu_cxx::bitmap_allocator<_Tp>\n__gnu_cxx::__mt_alloc<_Tp>\n__gnu_cxx::__pool_alloc<_Tp>\n__gnu_cxx::malloc_allocator<_Tp>\n```\n\n那这和我们的EBO有啥关系呢？\n\n实际上，上面所列出继承的基类都是内存管理的EBO(空基类)。\n\n在每个容器中的使用都是调用每个内存管理的`rebind<_Tp>::other`。\n\n例如红黑树源码结构：\n\n```cpp\ntypedef typename __gnu_cxx::__alloc_traits<_Alloc>::template\n        rebind<_Rb_tree_node<_Val> >::other _Node_allocator;\nstruct _Rb_tree_impl : public _Node_allocator\n{\n// do somethings\n};\n```\n\n接下来我们看上面列出的内存管理类里面的源码结构：这里拿`allocator`举例：\n\n```cpp\ntemplate<typename _Tp>\nclass allocator: public __allocator_base<_Tp>\n{\n\t template<typename _Tp1>\n     struct rebind { typedef allocator<_Tp1> other; };\n};\n```\n\n看到了没，通过`rebind<_Tp>::other`来获得传递进来的内存分配器，也就是前面提到的这些。\n\n```cpp\nstd::allocator<_Tp>\n__gnu_cxx::bitmap_allocator<_Tp>\n__gnu_cxx::bitmap_allocator<_Tp>\n__gnu_cxx::__mt_alloc<_Tp>\n__gnu_cxx::__pool_alloc<_Tp>\n__gnu_cxx::malloc_allocator<_Tp>\n```\n\n搞懂了这些，来测试一波：\n\n```cpp\nvoid print() {\n    cout<<sizeof(std::allocator<int>)<<\" \"<<sizeof(std::allocator<int>::rebind<int>::other)<<endl;\n    cout<<sizeof(__gnu_cxx::bitmap_allocator<int>)<<\" \"<<sizeof(__gnu_cxx::bitmap_allocator<int>::rebind<int>::other)<<endl;\n    cout<<sizeof(__gnu_cxx::new_allocator<int>)<<\" \"<<sizeof(__gnu_cxx::new_allocator<int>::rebind<int>::other)<<endl;\n    cout<<sizeof(__gnu_cxx::__mt_alloc<int>)<<\" \"<<sizeof(__gnu_cxx::__mt_alloc<int>::rebind<int>::other)<<endl;\n    cout<<sizeof(__gnu_cxx::__pool_alloc<int>)<<\" \"<<sizeof(__gnu_cxx::__pool_alloc<int>::rebind<int>::other)<<endl;\n    cout<<sizeof(__gnu_cxx::malloc_allocator<int>)<<\" \"<<sizeof(__gnu_cxx::malloc_allocator<int>::rebind<int>::other)<<endl;\n}\n```\n\n我们来测试这些sizeof是不是1，经过测试输出如下：\n\n```cpp\n1 1\n1 1\n1 1\n1 1\n1 1\n1 1\n```\n\n说明内存管理的实现就是通过采用继承的方式，使用空基类优化，来达到尽量降低容器所占的大小。\n\n## 4.利用EBO,手动实现一个简单的内存分配与释放\n\n首先定义一个sizeof(class)=1的类，同STL一样，里面使用allocate与deallocate来进行内存管理。\n\n```cpp\nclass MyAllocator {\npublic:\n    void *allocate(std::size_t size) {\n        return std::malloc(size);\n    }\n\n    void deallocate(void *ptr) {\n        std::free(ptr);\n    }\n};\n```\n\n第一种方式的内存管理：嵌入一个内存管理类\n\n```cpp\ntemplate<class T, class Allocator>\nclass MyContainerNotEBO {\n    T *data_ = nullptr;\n    std::size_t capacity_;\n    Allocator allocator_;   // 嵌入一个MyAllocator\npublic:\n    MyContainerNotEBO(std::size_t capacity)\n            : capacity_(capacity), allocator_(), data_(nullptr) {\n        std::cout << \"alloc malloc\" << std::endl;\n        data_ = reinterpret_cast<T *>(allocator_.allocate(capacity * sizeof(T))); // 分配内存\n    }\n\n    ~MyContainerNotEBO() {\n        std::cout << \"MyContainerNotEBO free malloc\" << std::endl;\n        allocator_.deallocate(data_);\n    }\n};\n```\n\n第二种方式：采用空基类优化，继承来获得内存管理功能\n\n```cpp\ntemplate<class T, class Allocator>\nclass MyContainerEBO\n        : public Allocator {    // 继承一个EBO\n    T *data_ = nullptr;\n    std::size_t capacity_;\npublic:\n    MyContainerEBO(std::size_t capacity)\n            : capacity_(capacity), data_(nullptr) {\n        std::cout << \"alloc malloc\" << std::endl;\n        data_ = reinterpret_cast<T *>(this->allocate(capacity * sizeof(T)));\n    }\n\n    ~MyContainerEBO() {\n        std::cout << \"MyContainerEBO free malloc\" << std::endl;\n        this->deallocate(data_);\n    }\n};\n```\n\n开始测试：\n\n```cpp\nint main() {\n    MyContainerNotEBO<int, MyAllocator> notEbo = MyContainerNotEBO<int, MyAllocator>(0);\n    std::cout << \"Using Not EBO Test sizeof is \" << sizeof(notEbo) << std::endl;\n    MyContainerEBO<int, MyAllocator> ebo = MyContainerEBO<int, MyAllocator>(0);\n    std::cout << \"Using EBO Test sizeof is \" << sizeof(ebo) << std::endl;\n\n    return 0;\n}\n```\n\n测试结果：\n\n```cpp\nalloc malloc\nUsing Not EBO Test sizeof is 24\nalloc malloc\nUsing EBO Test sizeof is 16\nMyContainerEBO free malloc\nMyContainerNotEBO free malloc\n```\n\n我们发现采用EBO的设计确实比嵌入设计好很多。至此，本节学习完毕。\n\n"
  },
  {
    "path": "tool/C++的Debug工具dbg-macro.md",
    "content": "# C++ 的 debug 工具：dbg-macro\n\n打日志是 C++ 开发中必不可少的一种 debug 方式，dbg-macro 受 Rust 语言中 的 `dbg` 启发，提供比 `printf` 和 `std::cout` 更好的宏函数。主要有如下特点：\n\n- 美观的彩色输出（当输出不是交互式终端时，颜色将自动禁用）\n- 兼容 C++11，并且是 header-only\n- 支持基础类型和 STL 容器类型的输出\n- 可以输出文件名、行号、函数名和原始表达式\n\n例如下面的代码：\n\n```cpp\n#include <vector>\n#include <dbg.h>\n\n// You can use \"dbg(..)\" in expressions:\nint factorial(int n) {\n  if (dbg(n <= 1)) {\n    return dbg(1);\n  } else {\n    return dbg(n * factorial(n - 1));\n  }\n}\n\nint main() {\n  std::string message = \"hello\";\n  dbg(message);  // [example.cpp:15 (main)] message = \"hello\" (std::string)\n\n  const int a = 2;\n  const int b = dbg(3 * a) + 1;  // [example.cpp:18 (main)] 3 * a = 6 (int)\n\n  std::vector<int> numbers{b, 13, 42};\n  dbg(numbers);  // [example.cpp:21 (main)] numbers = {7, 13, 42} (size: 3) (std::vector<int>)\n\n  dbg(\"this line is executed\");  // [example.cpp:23 (main)] this line is executed\n\n  factorial(4);\n\n  return 0;\n}\n```\n\n效果如下：\n\n![](https://camo.githubusercontent.com/3cde47c8db560b3ed42763f2ef306d78d4b19087/68747470733a2f2f692e696d6775722e636f6d2f4e4845596b39412e706e67)\n\n## 安装\n\n```bash\ngit clone https://github.com/sharkdp/dbg-macro\nsudo ln -s $(readlink -f dbg-macro/dbg.h) /usr/include/dbg.h\n```\n\n这样，你就可以在任何地方使用 `dbg.h` 了。\n\n### 在 Arch Linux 上\n\n```bash\nyay -S dbg-macro\n```\n\n### 使用 vcpkg\n\n```bash\nvcpkg install dbg-macro\n```\n\n更多内容详见 [官方文档](https://github.com/sharkdp/dbg-macro)。\n"
  },
  {
    "path": "tool/output/BUILD",
    "content": "# please run `bazel run //tool/output:container`\n\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\")\n\ncc_library(\n    name = \"output_container\",\n    hdrs = [\"output_container.h\"],\n    copts = [\"-std=c++17\"],\n)\n\ncc_binary(\n    name = \"container\",\n    srcs = [\"container.cpp\"],\n    deps = [\n        \":output_container\",\n    ],\n    copts = [\"-std=c++17\"],\n)"
  },
  {
    "path": "tool/output/container.cpp",
    "content": "//\n// Created by light on 19-12-16.\n//\n\n#include \"output_container.h\"\n#include <iostream>\n#include <map>\n#include <set>\n#include <vector>\n\nusing namespace std;\n\nint main() {\n  map<int, int> mp{{1, 1}, {2, 4}, {3, 9}};\n  cout << mp << endl;\n  vector<vector<int>> vv{{1, 1}, {2, 4}, {3, 9}};\n  cout << vv << endl;\n\n  pair<int, int> p{1, 2};\n  cout << p << endl;\n\n  set<int> s{1, 2, 3};\n  cout << s << endl;\n\n  vector<char> v{'a', 'b'};\n  cout << v << endl;\n  set<char *> vs{\"a\", \"b\"};\n  cout << vs << endl;\n\n  map<int, char *> mm{{1, \"23\"}, {2, \"234hi\"}};\n  cout << mm << endl;\n}\n"
  },
  {
    "path": "tool/output/output_container.h",
    "content": "//\n// Created by light on 20-1-8.\n//\n\n#ifndef MORDEN_C_OUTPUT_CONTAINER_H\n#define MORDEN_C_OUTPUT_CONTAINER_H\n\n#include <ostream>     // std::ostream\n#include <type_traits> // std::false_type/true_type/decay_t/is_same_v\n#include <utility>     // std::declval/pair\n\n// 检测是否是pair\ntemplate <typename T> struct is_pair : std::false_type {};\ntemplate <typename T, typename U>\nstruct is_pair<std::pair<T, U>> : std::true_type {};\ntemplate <typename T> inline constexpr bool is_pair_v = is_pair<T>::value;\n\n// 检测输出函数是否存在\ntemplate <typename T> struct has_output_function {\n  template <class U>\n  static auto output(U *ptr)\n      -> decltype(std::declval<std::ostream &>() << *ptr, std::true_type());\n\n  template <class U> static std::false_type output(...);\n\n  static constexpr bool value = decltype(output<T>(nullptr))::value;\n};\n\ntemplate <typename T>\ninline constexpr bool has_output_function_v = has_output_function<T>::value;\n\nenum CHARS {\n  ORD,   // 其他类型\n  CHAR,  // char 类型\n  STRING // string 类型\n};\n\ntemplate <typename T> int ischarOrString(T &elem) {\n  using std::decay_t;\n  using std::is_same_v;\n  using element_type = decay_t<decltype(elem)>;\n  constexpr bool is_char_v = is_same_v<element_type, char>;\n  constexpr bool is_string_v = is_same_v<element_type, char *> ||\n                               is_same_v<element_type, const char *> ||\n                               is_same_v<element_type, std::string>;\n  if (is_char_v)\n    return CHAR;\n  else if (is_string_v)\n    return STRING;\n  else\n    return ORD;\n}\n\ntemplate <typename T> void output(T &elem, int type, std::ostream &os) {\n  switch (type) {\n  case CHAR:\n    os << '\\'' << elem << '\\'';\n    break;\n  case STRING:\n    os << '\\\"' << elem << '\\\"';\n    break;\n  case ORD:\n    os << elem;\n    break;\n  }\n}\n\ntemplate <typename T, typename Cont>\nauto output_element(std::ostream &os, const T &element, const Cont &) ->\n    typename std::enable_if<is_pair<typename Cont::value_type>::value,\n                            bool>::type {\n  int ftype = ischarOrString(element.first);\n  int stype = ischarOrString(element.second);\n\n  output(element.first, ftype, os);\n  os << \" => \";\n  output(element.second, stype, os);\n  return true;\n}\n\ntemplate <typename T, typename Cont>\nauto output_element(std::ostream &os, const T &element, const Cont &) ->\n    typename std::enable_if<!is_pair<typename Cont::value_type>::value,\n                            bool>::type {\n  int etype = ischarOrString(element);\n  output(element, etype, os);\n  return false;\n}\n\ntemplate <typename T, typename U>\nstd::ostream &operator<<(std::ostream &os, const std::pair<T, U> &pr) {\n  os << '(' << pr.first << \", \" << pr.second << ')';\n  return os;\n}\n\n// template<typename T, typename Cont>\n// auto output_element(std::ostream& os, const T& element,\n//                     const Cont&, const std::true_type)\n//-> decltype(std::declval<typename Cont::key_type>(), os)\n//{\n//     os << element.first << \" => \" << element.second;\n//     return os;\n// }\n//\n// template <typename T, typename Cont>\n// auto output_element(std::ostream& os, const T& element,\n//                     const Cont&, ...)\n//-> decltype(os)\n//{\n//     os << element;\n//     return os;\n// }\n\n// 针对没有输出函数的容器处理\ntemplate <typename T, typename = std::enable_if_t<!has_output_function_v<T>>>\nauto operator<<(std::ostream &os, const T &container)\n    -> decltype(container.begin(), container.end(), os) {\n  os << \"{ \";\n  if (!container.empty()) {\n    bool on_first_element = true;\n    for (auto elem : container) {\n      if (!on_first_element) {\n        os << \", \";\n      } else {\n        on_first_element = false;\n      }\n      output_element(os, elem, container);\n    }\n  }\n  os << \" }\";\n  return os;\n}\n\n#endif // MORDEN_C_OUTPUT_CONTAINER_H\n"
  },
  {
    "path": "tool/像Python一样玩CC++.md",
    "content": "# 像Python一样玩C/C++\n\n在Python中我们可以使用`Jupyter Notebook`直接看到结果，例如：\n\n```c\nl = [1,2]\nl\n```\n\n直接输出：\n\n```\n[1,2]\n```\n\n那当使用C++的时候，例如：\n\n```cpp\nmap<string, int> mp{\n    {\"one\",   1},\n    {\"two\",   2},\n    {\"three\", 3},\n    {\"four\",  4}\n};\n```\n\n如果要输出，就得循环遍历，可否直接输出结果呢？\n\nso easy!!! `Jupyter Notebook`可以解决一切问题，哈哈～\n\n## 如何在Jupyter中玩C++？\n\n在github上有一个仓库，如下所示：\n\n> https://github.com/QuantStack/xeus-cling\n\n`xeus-cling` 是一个用于C++的Jupyter内核，基于C++解释器和Jupyter协议xeus的原生实现。\n\n目前，支持Mac与Linux，但不支持Windows。\n\n安装也是非常简单，首先安装好Anaconda，在里面创建一个虚拟环境：\n\n```\nconda create -n cling\n```\n\n切换进去：\n\n```\nconda activate cling\n```\n\n给新环境安装`jupyter`和`notebook`\n\n```\nconda install jupyter notebook\n```\n\n使用`conda-forge`安装`xeus-cling`\n\n```\nconda install xeus-cling -c conda-forge\n```\n\n为了加速安装，请记得给Anaconda配置源！\n\n检查是否安装好了内核(kernel)：\n\n```\njupyter kernelspec list\n```\n\n输出：\n\n```cpp\npython3    /home/xxx/anaconda3/envs/cling/share/jupyter/kernels/python3\nxcpp11     /home/xxx/anaconda3/envs/cling/share/jupyter/kernels/xcpp11\nxcpp14     /home/xxx/anaconda3/envs/cling/share/jupyter/kernels/xcpp14\nxcpp17     /home/xxx/anaconda3/envs/cling/share/jupyter/kernels/xcpp17\n```\n\n打开`Jupyter Notebook`，就可以看到看到kernel了。\n\n启动`Jupyter Notebook`：\n\n```\njupyter-notebook\n```\n\n## 如何在Jupyter中玩C？\n\n只需要安装c kernel即可！\n\n可以直接在当前环境中创建c kernel，也可以新开一个环境安装，下面是在当前环境中直接安装。\n\n```\npip install jupyter-c-kernel\ninstall_c_kernel\njupyter kernelspec list\n```\n\n此时，就输出：\n\n```cpp\nc          /home/light/anaconda3/envs/cling/share/jupyter/kernels/c\npython3    /home/light/anaconda3/envs/cling/share/jupyter/kernels/python3\nxcpp11     /home/light/anaconda3/envs/cling/share/jupyter/kernels/xcpp11\nxcpp14     /home/light/anaconda3/envs/cling/share/jupyter/kernels/xcpp14\nxcpp17     /home/light/anaconda3/envs/cling/share/jupyter/kernels/xcpp17\n```\n\n启动`Jupyter Notebook`：\n\n```\njupyter-notebook\n```\n\n"
  },
  {
    "path": "tool/用rr来进行debug.md",
    "content": "# 如何用rr来debug你的C/C++程序(Linux)\n\n想象一下如果你的程序某时会崩溃，但是不能稳定复现，你会如何debug它?\n\n用传统debugger面临的问题就是你不知道这次运行的时候能不能复现，你猜测可能某段代码出现了问题，所以进行了一番检查。但如果最后不能复现的话，刚刚所做的工作就是无用的。gdb通过一个反向调试的功能解决了这个问题。但是gdb的反向调试代价很大，在大型项目上运行很吃力。Bug出现的几率高还好，但如果出现的概率是1/100，甚至1/1000呢？\n\n要么就用大量printf，通过print出数据来进行debug，好处是重复运行程序的成本较低，可以写个脚本，反复可以多次尝试，直到程序崩溃，然后反回去看log。但也有一样的问题就是效率不高，哪怕复现成功了，也很有可能现有的printf并不能解决问题，需要加更多的printf，又要反复运行程序来复现。\n\nrr的出现就是来解决上面提到的问题。rr是一个debugger，通过记录程序运行时的状态，来提供一个可以反复反向调试的debug环境。\n\n### rr的优点\n- 比gdb的反向调试更成熟，并且有更少的消耗\n- 对多线程进程友好，可以只调试某个进程\n- 支持gdb的指令\n\n### 安装\n推荐按着[官方文档](https://github.com/mozilla/rr/wiki/Building-And-Installing)来进行本地编译，然后安装。请注意rr现在只能在Linux上运行并且对CPU有一定的要求，具体要求请看[官方文档](https://github.com/mozilla/rr#system-requirements)。\n\n### 基础使用方法\n使用rr总共分成两步\n  1. 第一步是`rr record` - 运行程序，并且记录下程序运行时的状态。\n  2. 第二部是`rr replay` - 回放记录好的程序。\n\n### 实例\n```cpp\n  #include <cstdio>\n  #include <thread>\n  #include <stdlib.h>\n\n  void inc(int& x, int id) {\n    id = id + 1; //这一行有点多余，主要是为了展示rr的reverse-continue 功能\n    if (x == 2 && id == 3) {\n      abort();\n    }\n    ++x;\n    printf(\"x=%d\\n\", x);\n  }\n\n  int main () {\n    int x = 0;\n    std::thread t1(inc, std::ref(x), 1);\n    std::thread t2(inc, std::ref(x), 2);\n    std::thread t3(inc, std::ref(x), 3);\n    std::thread t4(inc, std::ref(x), 4);\n\n    t1.join();\n    t2.join();\n    t3.join();\n    t4.join();\n  }\n```\n\n上面这个程序启动了多个线程，会在当`t2`这个线程运行时`x == 2 && id == 3`的时候崩溃。因为线程运行的顺序是不可控的，所以这个程序并不会总是崩溃。\n\n我们首先编译上面这个程序\n  - 运行`g++ -g -lpthread main.cpp -o main`。编译好后，我们得到了 `main` 这个二进制文件。\n\n然后我们来`record`\n  - 这里我们运行`rr record --chaos ./main`。正常情况我们 `rr record ../main` 就可以了，但我们这里用了`--chaos`让rr可以更加随机的进行调度，从而增加复现这个bug的概率。我们还可以自动化这个步骤通过`while rr record --chaos ./main; do :; done` 来反复运行这个程序，这个while循环会在`main`崩溃的时候自动结束。\n\n![rr_debugger_1](https://user-images.githubusercontent.com/8703745/96356858-4909a200-10c2-11eb-829d-f973716b4111.png)\n![rr_debugger_2](https://user-images.githubusercontent.com/8703745/96356872-853d0280-10c2-11eb-9adf-4b0165539b3d.png)\n\n我这次运行了32次才成功复现这个崩溃。\n\n`rr`　会把每次的记录存放在`~/.local/share/rr`里面，这时候我们`ls`看一下。\n\n![rr_debugger_3](https://user-images.githubusercontent.com/8703745/96356873-866e2f80-10c2-11eb-89f2-f3acd00fdc9d.png)\n\n每个`main-<数字>`就是rr存放的记录，`rr`支持我们回放每一个记录，`rr replay`\n默认会回放最新的那个，我们直接`rr replay`就好了。\n\n![rr_debugger_4](https://user-images.githubusercontent.com/8703745/96356875-8706c600-10c2-11eb-89e6-e481fa25e061.png)\n\n上图就是rr debugger的样子。因为rr使用的是gdb protocol，所以我们可以运行各种gdb的指令。\n\n我们接下来利用gdb的`continue`指令 (`continue`指令会让程序一直运行，直到程序结束，程序崩溃，或者命中断点)，让程序自动停在崩溃的地方。\n\n![rr_debugger_5](https://user-images.githubusercontent.com/8703745/96356876-8837f300-10c2-11eb-9735-679a3be7ebdd.png)\n\n我们看到rr的回放也print出了x=1 和 x=2，\n跟当时运行时是完全一样的。这里我们输入`bt`可以检查call stack的内容。可以看到`abort()`是让程序崩溃的原因。\n\n![rr_debugger_6](https://user-images.githubusercontent.com/8703745/96356877-89692000-10c2-11eb-8f4f-1142f71bffa9.png)\n\n输入`up 2`，来到`inc()`的这个frame。再配合`list`，就可以看到具体造成崩溃的代码。这里我们输入 `p id` 可以看到当前`id`的数据，确实是`id == 3`造成了崩溃。\n\n![rr_debugger_7](https://user-images.githubusercontent.com/8703745/96356892-b61d3780-10c2-11eb-8289-ddec50aea5a4.png)\n\n这时候我们需要寻找为什么`id`是3的原因，这段代码很明显是由`id = id + 1`造成的，但是假如我们不知道是哪里造成`id`变化，我们可以输入`watch -l id` 来观察这个变量，再通过`rc`来回到造成变化的代码。\n\n![rr_debugger_8](https://user-images.githubusercontent.com/8703745/96357406-6db54800-10c9-11eb-8f24-cebb0c0fc7f4.png)\n\n注意这里我们输入了`rc`两次，第一次`rc`的时候，`rr`收到了程序崩溃的信号，所以停止了，我们需要再输入一次让它继续。rr自动就帮我们回到了`id`变化的这一行代码，这时候我们再看`p id`，`id`就是2了。通过追逐`id`的变化，我们就可以对代码进行必要的修改了。\n\n这就是rr的能力，不单单我们返回到了这一行代码，连程序相应的状态都回到了这个时刻。rr的强大之处就是给我们的一种回到过去的能力。\n"
  }
]