{"id":13436415,"url":"https://github.com/huihut/interview","last_synced_at":"2025-05-15T01:03:54.033Z","repository":{"id":37294452,"uuid":"120887835","full_name":"huihut/interview","owner":"huihut","description":"📚 C/C++ 技术面试基础知识总结，包括语言、程序库、数据结构、算法、系统、网络、链接装载库等知识及面试经验、招聘、内推等信息。This repository is a summary of the basic knowledge of recruiting job seekers and beginners in the direction of C/C++ technology, including language, program library, data structure, algorithm, system, network, link loading library, interview experience, recruitment, recommendation, etc.","archived":false,"fork":false,"pushed_at":"2024-03-19T06:44:41.000Z","size":5367,"stargazers_count":35940,"open_issues_count":27,"forks_count":8041,"subscribers_count":864,"default_branch":"master","last_synced_at":"2025-04-22T07:00:56.941Z","etag":null,"topics":["algorithm","c","cpp","data-structures","database","interview","interview-practice","interview-preparation","interview-questions","interviews","leetcode","operating-system","stl"],"latest_commit_sha":null,"homepage":"https://interview.huihut.com","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/huihut.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2018-02-09T09:49:48.000Z","updated_at":"2025-04-22T06:19:47.000Z","dependencies_parsed_at":"2024-04-13T08:49:41.481Z","dependency_job_id":null,"html_url":"https://github.com/huihut/interview","commit_stats":{"total_commits":330,"total_committers":28,"mean_commits":"11.785714285714286","dds":"0.10303030303030303","last_synced_commit":"77b04416e2c7b60750ca3740c2c12e1ee6374500"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huihut%2Finterview","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huihut%2Finterview/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huihut%2Finterview/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/huihut%2Finterview/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/huihut","download_url":"https://codeload.github.com/huihut/interview/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250296202,"owners_count":21407037,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithm","c","cpp","data-structures","database","interview","interview-practice","interview-preparation","interview-questions","interviews","leetcode","operating-system","stl"],"created_at":"2024-07-31T03:00:47.913Z","updated_at":"2025-04-22T18:15:15.023Z","avatar_url":"https://github.com/huihut.png","language":"C++","funding_links":[],"categories":["HarmonyOS","六、网络优化","C++","Interview","编程","[Visual Basic](#visual-basic)","C/C++ 程序设计","C/C++","Programming Language Tutorials","笔记","Coding interview FAQ","语言"],"sub_categories":["Windows Manager","面试","网络服务_其他","3. Course and Code","C++/C","C++"],"readme":"\u003cdiv align=\"center\"\u003e\n📖 Github\n\u0026emsp;\u0026emsp; | \u0026emsp;\u0026emsp;\n\u003ca href=\"https://interview.huihut.com\"\u003e📚 Docsify\u003c/a\u003e\n\u003c/div\u003e \n\u003cbr\u003e\n\n\u003cdiv align=\"center\"\u003e\n简体中文\n\u0026emsp;\u0026emsp; | \u0026emsp;\u0026emsp;\n\u003ca href=\"https://github.com/huihut/interview/blob/master/README_en.md\"\u003eEnglish\u003c/a\u003e\n\u003c/div\u003e \n\u003cbr\u003e\n\n\u003cb\u003e\u003cdetails\u003e\u003csummary\u003e💡 关于\u003c/summary\u003e\u003c/b\u003e\n\n📚 本仓库是面向 C/C++ 技术方向校招求职者、初学者的基础知识总结，包括语言、程序库、数据结构、算法、系统、网络、链接装载库等知识及面试经验、招聘、内推等信息。\n\n💡 侧边目录支持方式：[📚 Docsify 文档](https://interview.huihut.com)、[Github + TOC 导航](https://github.com/jawil/GayHub)（[TOC预览.png](https://gitee.com/huihut/interview/raw/master/images/TOC预览.png)）\n\n📄 保存为 PDF 方式：使用 Chrome 浏览器打开 \u003ca href=\"https://interview.huihut.com\"\u003e📚 Docsify 文档\u003c/a\u003e 页面，缩起左侧目录-右键 - 打印 - 选择目标打印机是另存为PDF - 保存（[打印预览.png](https://gitee.com/huihut/interview/raw/master/images/打印预览.png)）\n\n🙏 仓库内容如有错误或改进欢迎 issue 或 pr，建议或讨论可在 [#12](https://github.com/huihut/interview/issues/12) 提出。由于本人水平有限，仓库中的知识点有来自本人原创、读书笔记、书籍、博文等，非原创均已标明出处，如有遗漏，请 issue 提出。本仓库遵循 [CC BY-NC-SA 4.0（署名 - 非商业性使用 - 相同方式共享）](https://github.com/huihut/interview/blob/master/LICENSE) 协议，转载请注明出处，不得用于商业目的。\n\n\u003c/details\u003e\n\n## 📑 目录\n\n* [➕ C/C++](#cc)\n* [⭐️ Effective](#effective)\n* [📦 STL](#stl)\n* [〽️ 数据结构](#data-structure)\n* [⚡️ 算法](#algorithm)\n* [❓ Problems](#problems)\n* [💻 操作系统](#os)\n* [☁️ 计算机网络](#computer-network)\n* [🌩 网络编程](#network-programming)\n* [💾 数据库](#database)\n* [📏 设计模式](#design-pattern)\n* [⚙️ 链接装载库](#link-loading-library)\n* [📚 书籍](#books)\n* [🔱 C/C++ 发展方向](#cc-development-direction)\n* [💯 复习刷题网站](#review-of-brush-questions-website)\n* [📝 面试题目经验](#interview-questions-experience)\n* [📆 招聘时间岗位](#recruitment-time-post)\n* [👍 内推](#recommend)\n* [👬 贡献者](#contributor)\n* [📜 License](#license)\n\n\n\u003ca id=\"cc\"\u003e\u003c/a\u003e\n\n## ➕ C/C++\n\n### const\n\n#### 作用\n\n1. 修饰变量，说明该变量不可以被改变；\n2. 修饰指针，分为指向常量的指针（pointer to const）和自身是常量的指针（常量指针，const pointer）；\n3. 修饰引用，指向常量的引用（reference to const），用于形参类型，即避免了拷贝，又避免了函数对值的修改；\n4. 修饰成员函数，说明该成员函数内不能修改成员变量。\n\n#### const 的指针与引用\n\n* 指针\n    * 指向常量的指针（pointer to const）\n    * 自身是常量的指针（常量指针，const pointer）\n* 引用\n    * 指向常量的引用（reference to const）\n    * 没有 const reference，因为引用只是对象的别名，引用不是对象，不能用 const 修饰\n\n\u003e （为了方便记忆可以想成）被 const 修饰（在 const 后面）的值不可改变，如下文使用例子中的 `p2`、`p3`\n\n#### 使用\n\nconst 使用\n\n```cpp\n// 类\nclass A\n{\nprivate:\n    const int a;                // 常对象成员，可以使用初始化列表或者类内初始化\n\npublic:\n    // 构造函数\n    A() : a(0) { };\n    A(int x) : a(x) { };        // 初始化列表\n\n    // const可用于对重载函数的区分\n    int getValue();             // 普通成员函数\n    int getValue() const;       // 常成员函数，不得修改类中的任何数据成员的值\n};\n\nvoid function()\n{\n    // 对象\n    A b;                        // 普通对象，可以调用全部成员函数\n    const A a;                  // 常对象，只能调用常成员函数\n    const A *p = \u0026a;            // 指针变量，指向常对象\n    const A \u0026q = a;             // 指向常对象的引用\n\n    // 指针\n    char greeting[] = \"Hello\";\n    char* p1 = greeting;                // 指针变量，指向字符数组变量\n    const char* p2 = greeting;          // 指针变量，指向字符数组常量（const 后面是 char，说明指向的字符（char）不可改变）\n    char* const p3 = greeting;          // 自身是常量的指针，指向字符数组变量（const 后面是 p3，说明 p3 指针自身不可改变）\n    const char* const p4 = greeting;    // 自身是常量的指针，指向字符数组常量\n}\n\n// 函数\nvoid function1(const int Var);           // 传递过来的参数在函数内不可变\nvoid function2(const char* Var);         // 参数指针所指内容为常量\nvoid function3(char* const Var);         // 参数指针为常量\nvoid function4(const int\u0026 Var);          // 引用参数在函数内为常量\n\n// 函数返回值\nconst int function5();      // 返回一个常数\nconst int* function6();     // 返回一个指向常量的指针变量，使用：const int *p = function6();\nint* const function7();     // 返回一个指向变量的常指针，使用：int* const p = function7();\n```\n\n#### 宏定义 #define 和 const 常量 \n\n宏定义 #define|const 常量\n---|---\n宏定义，相当于字符替换|常量声明\n预处理器处理|编译器处理\n无类型安全检查|有类型安全检查\n不分配内存|要分配内存\n存储在代码段|存储在数据段\n可通过 `#undef` 取消|不可取消\n\n### static\n\n#### 作用\n\n1. 修饰普通变量，修改变量的存储区域和生命周期，使变量存储在静态区，在 main 函数运行前就分配了空间，如果有初始值就用初始值初始化它，如果没有初始值系统用默认值初始化它。\n2. 修饰普通函数，表明函数的作用范围，仅在定义该函数的文件内才能使用。在多人开发项目时，为了防止与他人命名空间里的函数重名，可以将函数定位为 static。\n3. 修饰成员变量，修饰成员变量使所有的对象只保存一个该变量，而且不需要生成对象就可以访问该成员。\n4. 修饰成员函数，修饰成员函数使得不需要生成对象就可以访问该函数，但是在 static 函数内不能访问非静态成员。\n\n### this 指针\n\n1. `this` 指针是一个隐含于每一个非静态成员函数中的特殊指针。它指向调用该成员函数的那个对象。\n2. 当对一个对象调用成员函数时，编译程序先将对象的地址赋给 `this` 指针，然后调用成员函数，每次成员函数存取数据成员时，都隐式使用 `this` 指针。\n3. 当一个成员函数被调用时，自动向它传递一个隐含的参数，该参数是一个指向这个成员函数所在的对象的指针。\n4. `this` 指针被隐含地声明为: `ClassName *const this`，这意味着不能给 `this` 指针赋值；在 `ClassName` 类的 `const` 成员函数中，`this` 指针的类型为：`const ClassName* const`，这说明不能对 `this` 指针所指向的这种对象是不可修改的（即不能对这种对象的数据成员进行赋值操作）；\n5. `this` 并不是一个常规变量，而是个右值，所以不能取得 `this` 的地址（不能 `\u0026this`）。\n6. 在以下场景中，经常需要显式引用 `this` 指针：\n    1. 为实现对象的链式引用；\n    2. 为避免对同一对象进行赋值操作；\n    3. 在实现一些数据结构时，如 `list`。\n\n### inline 内联函数\n\n#### 特征\n\n* 相当于把内联函数里面的内容写在调用内联函数处；\n* 相当于不用执行进入函数的步骤，直接执行函数体；\n* 相当于宏，却比宏多了类型检查，真正具有函数特性；\n* 编译器一般不内联包含循环、递归、switch 等复杂操作的内联函数；\n* 在类声明中定义的函数，除了虚函数的其他函数都会自动隐式地当成内联函数。\n\n#### 使用\n\ninline 使用\n\n```cpp\n// 声明1（加 inline，建议使用）\ninline int functionName(int first, int second,...);\n\n// 声明2（不加 inline）\nint functionName(int first, int second,...);\n\n// 定义\ninline int functionName(int first, int second,...) {/****/};\n\n// 类内定义，隐式内联\nclass A {\n    int doA() { return 0; }         // 隐式内联\n}\n\n// 类外定义，需要显式内联\nclass A {\n    int doA();\n}\ninline int A::doA() { return 0; }   // 需要显式内联\n```\n\n#### 编译器对 inline 函数的处理步骤\n\n1. 将 inline 函数体复制到 inline 函数调用点处； \n2. 为所用 inline 函数中的局部变量分配内存空间； \n3. 将 inline 函数的的输入参数和返回值映射到调用方法的局部变量空间中； \n4. 如果 inline 函数有多个返回点，将其转变为 inline 函数代码块末尾的分支（使用 GOTO）。\n\n#### 优缺点\n\n优点\n\n1. 内联函数同宏函数一样将在被调用处进行代码展开，省去了参数压栈、栈帧开辟与回收，结果返回等，从而提高程序运行速度。\n2. 内联函数相比宏函数来说，在代码展开时，会做安全检查或自动类型转换（同普通函数），而宏定义则不会。 \n3. 在类中声明同时定义的成员函数，自动转化为内联函数，因此内联函数可以访问类的成员变量，宏定义则不能。\n4. 内联函数在运行时可调试，而宏定义不可以。\n\n缺点\n\n1. 代码膨胀。内联是以代码膨胀（复制）为代价，消除函数调用带来的开销。如果执行函数体内代码的时间，相比于函数调用的开销较大，那么效率的收获会很少。另一方面，每一处内联函数的调用都要复制代码，将使程序的总代码量增大，消耗更多的内存空间。\n2. inline 函数无法随着函数库升级而升级。inline函数的改变需要重新编译，不像 non-inline 可以直接链接。\n3. 是否内联，程序员不可控。内联函数只是对编译器的建议，是否对函数内联，决定权在于编译器。\n\n#### 虚函数（virtual）可以是内联函数（inline）吗？\n\n\u003e [Are \"inline virtual\" member functions ever actually \"inlined\"?](http://www.cs.technion.ac.il/users/yechiel/c++-faq/inline-virtuals.html)\n\n* 虚函数可以是内联函数，内联是可以修饰虚函数的，但是当虚函数表现多态性的时候不能内联。\n* 内联是在编译期建议编译器内联，而虚函数的多态性在运行期，编译器无法知道运行期调用哪个代码，因此虚函数表现为多态性时（运行期）不可以内联。\n* `inline virtual` 唯一可以内联的时候是：编译器知道所调用的对象是哪个类（如 `Base::who()`），这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。\n\n虚函数内联使用\n\n```cpp\n#include \u003ciostream\u003e  \nusing namespace std;\nclass Base\n{\npublic:\n\tinline virtual void who()\n\t{\n\t\tcout \u003c\u003c \"I am Base\\n\";\n\t}\n\tvirtual ~Base() {}\n};\nclass Derived : public Base\n{\npublic:\n\tinline void who()  // 不写inline时隐式内联\n\t{\n\t\tcout \u003c\u003c \"I am Derived\\n\";\n\t}\n};\n\nint main()\n{\n\t// 此处的虚函数 who()，是通过类（Base）的具体对象（b）来调用的，编译期间就能确定了，所以它可以是内联的，但最终是否内联取决于编译器。 \n\tBase b;\n\tb.who();\n\n\t// 此处的虚函数是通过指针调用的，呈现多态性，需要在运行时期间才能确定，所以不能为内联。  \n\tBase *ptr = new Derived();\n\tptr-\u003ewho();\n\n\t// 因为Base有虚析构函数（virtual ~Base() {}），所以 delete 时，会先调用派生类（Derived）析构函数，再调用基类（Base）析构函数，防止内存泄漏。\n\tdelete ptr;\n\tptr = nullptr;\n\n\tsystem(\"pause\");\n\treturn 0;\n} \n```\n\n### volatile\n\n```cpp\nvolatile int i = 10; \n```\n\n* volatile 关键字是一种类型修饰符，用它声明的类型变量表示可以被某些编译器未知的因素（操作系统、硬件、其它线程等）更改。所以使用 volatile 告诉编译器不应对这样的对象进行优化。\n* volatile 关键字声明的变量，每次访问时都必须从内存中取出值（没有被 volatile 修饰的变量，可能由于编译器的优化，从 CPU 寄存器中取值）\n* const 可以是 volatile （如只读的状态寄存器）\n* 指针可以是 volatile\n\n### assert()\n\n断言，是宏，而非函数。assert 宏的原型定义在 `\u003cassert.h\u003e`（C）、`\u003ccassert\u003e`（C++）中，其作用是如果它的条件返回错误，则终止程序执行。可以通过定义 `NDEBUG` 来关闭 assert，但是需要在源代码的开头，`include \u003cassert.h\u003e` 之前。\n\nassert() 使用\n\n```cpp\n#define NDEBUG          // 加上这行，则 assert 不可用\n#include \u003cassert.h\u003e\n\nassert( p != NULL );    // assert 不可用\n```\n\n### sizeof()\n\n* sizeof 对数组，得到整个数组所占空间大小。\n* sizeof 对指针，得到指针本身所占空间大小。\n\n### #pragma pack(n)\n\n设定结构体、联合以及类成员变量以 n 字节方式对齐\n\n#pragma pack(n) 使用\n\n```cpp\n#pragma pack(push)  // 保存对齐状态\n#pragma pack(4)     // 设定为 4 字节对齐\n\nstruct test\n{\n    char m1;\n    double m4;\n    int m3;\n};\n\n#pragma pack(pop)   // 恢复对齐状态\n```\n\n### 位域\n\n```cpp\nBit mode: 2;    // mode 占 2 位\n```\n\n类可以将其（非静态）数据成员定义为位域（bit-field），在一个位域中含有一定数量的二进制位。当一个程序需要向其他程序或硬件设备传递二进制数据时，通常会用到位域。\n\n* 位域在内存中的布局是与机器有关的\n* 位域的类型必须是整型或枚举类型，带符号类型中的位域的行为将因具体实现而定\n* 取地址运算符（\u0026）不能作用于位域，任何指针都无法指向类的位域\n\n### extern \"C\"\n\n* 被 extern 限定的函数或变量是 extern 类型的\n* 被 `extern \"C\"` 修饰的变量和函数是按照 C 语言方式编译和链接的\n\n`extern \"C\"` 的作用是让 C++ 编译器将 `extern \"C\"` 声明的代码当作 C 语言代码处理，可以避免 C++ 因符号修饰导致代码不能和C语言库中的符号进行链接的问题。\n\nextern \"C\" 使用\n\n```cpp\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid *memset(void *, int, size_t);\n\n#ifdef __cplusplus\n}\n#endif\n```\n\n### struct 和 typedef struct\n\n#### C 中\n\n```c\n// c\ntypedef struct Student {\n    int age; \n} S;\n```\n\n等价于\n\n```c\n// c\nstruct Student { \n    int age; \n};\n\ntypedef struct Student S;\n```\n\n此时 `S` 等价于 `struct Student`，但两个标识符名称空间不相同。\n\n另外还可以定义与 `struct Student` 不冲突的 `void Student() {}`。\n\n#### C++ 中\n\n由于编译器定位符号的规则（搜索规则）改变，导致不同于C语言。\n\n一、如果在类标识符空间定义了 `struct Student {...};`，使用 `Student me;` 时，编译器将搜索全局标识符表，`Student` 未找到，则在类标识符内搜索。\n\n即表现为可以使用 `Student` 也可以使用 `struct Student`，如下：\n\n```cpp\n// cpp\nstruct Student { \n    int age; \n};\n\nvoid f( Student me );       // 正确，\"struct\" 关键字可省略\n```\n\n二、若定义了与 `Student` 同名函数之后，则 `Student` 只代表函数，不代表结构体，如下：\n\n```cpp\ntypedef struct Student { \n    int age; \n} S;\n\nvoid Student() {}           // 正确，定义后 \"Student\" 只代表此函数\n\n//void S() {}               // 错误，符号 \"S\" 已经被定义为一个 \"struct Student\" 的别名\n\nint main() {\n    Student(); \n    struct Student me;      // 或者 \"S me\";\n    return 0;\n}\n```\n\n### C++ 中 struct 和 class\n\n总的来说，struct 更适合看成是一个数据结构的实现体，class 更适合看成是一个对象的实现体。\n\n#### 区别\n\n* 最本质的一个区别就是默认的访问控制\n    1. 默认的继承访问权限。struct 是 public 的，class 是 private 的。  \n    2. struct 作为数据结构的实现体，它默认的数据访问控制是 public 的，而 class 作为对象的实现体，它默认的成员变量访问控制是 private 的。\n\n### union 联合\n\n联合（union）是一种节省空间的特殊的类，一个 union 可以有多个数据成员，但是在任意时刻只有一个数据成员可以有值。当某个成员被赋值后其他成员变为未定义状态。联合有如下特点：\n\n* 默认访问控制符为 public\n* 可以含有构造函数、析构函数\n* 不能含有引用类型的成员\n* 不能继承自其他类，不能作为基类\n* 不能含有虚函数\n* 匿名 union 在定义所在作用域可直接访问 union 成员\n* 匿名 union 不能包含 protected 成员或 private 成员\n* 全局匿名联合必须是静态（static）的\n\nunion 使用\n\n```cpp\n#include\u003ciostream\u003e\n\nunion UnionTest {\n    UnionTest() : i(10) {};\n    int i;\n    double d;\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 \u003c\u003c u.i \u003c\u003c std::endl;  // 输出 UnionTest 联合的 10\n\n    ::i = 20;\n    std::cout \u003c\u003c ::i \u003c\u003c std::endl;  // 输出全局静态匿名联合的 20\n\n    i = 30;\n    std::cout \u003c\u003c i \u003c\u003c std::endl;    // 输出局部匿名联合的 30\n\n    return 0;\n}\n```\n\n### C 实现 C++ 类\n\nC 实现 C++ 的面向对象特性（封装、继承、多态）\n\n* 封装：使用函数指针把属性与方法封装到结构体中\n* 继承：结构体嵌套\n* 多态：父类与子类方法的函数指针不同\n\n\u003e [Can you write object-oriented code in C? [closed]](https://stackoverflow.com/a/351745)\n\n### explicit（显式）关键字\n\n* explicit 修饰构造函数时，可以防止隐式转换和复制初始化\n* explicit 修饰转换函数时，可以防止隐式转换，但 [按语境转换](https://zh.cppreference.com/w/cpp/language/implicit_conversion) 除外\n\nexplicit 使用\n\n```cpp\nstruct A\n{\n\tA(int) { }\n\toperator bool() const { return true; }\n};\n\nstruct B\n{\n\texplicit B(int) {}\n\texplicit operator bool() const { return true; }\n};\n\nvoid doA(A a) {}\n\nvoid doB(B b) {}\n\nint main()\n{\n\tA a1(1);\t\t// OK：直接初始化\n\tA a2 = 1;\t\t// OK：复制初始化\n\tA a3{ 1 };\t\t// OK：直接列表初始化\n\tA a4 = { 1 };\t\t// OK：复制列表初始化\n\tA a5 = (A)1;\t\t// OK：允许 static_cast 的显式转换 \n\tdoA(1);\t\t\t// OK：允许从 int 到 A 的隐式转换\n\tif (a1);\t\t// OK：使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换\n\tbool a6(a1);\t\t// OK：使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换\n\tbool a7 = a1;\t\t// OK：使用转换函数 A::operator bool() 的从 A 到 bool 的隐式转换\n\tbool a8 = static_cast\u003cbool\u003e(a1);  // OK ：static_cast 进行直接初始化\n\n\tB b1(1);\t\t// OK：直接初始化\n\tB b2 = 1;\t\t// 错误：被 explicit 修饰构造函数的对象不可以复制初始化\n\tB b3{ 1 };\t\t// OK：直接列表初始化\n\tB b4 = { 1 };\t\t// 错误：被 explicit 修饰构造函数的对象不可以复制列表初始化\n\tB b5 = (B)1;\t\t// OK：允许 static_cast 的显式转换\n\tdoB(1);\t\t\t// 错误：被 explicit 修饰构造函数的对象不可以从 int 到 B 的隐式转换\n\tif (b1);\t\t// OK：被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换\n\tbool b6(b1);\t\t// OK：被 explicit 修饰转换函数 B::operator bool() 的对象可以从 B 到 bool 的按语境转换\n\tbool b7 = b1;\t\t// 错误：被 explicit 修饰转换函数 B::operator bool() 的对象不可以隐式转换\n\tbool b8 = static_cast\u003cbool\u003e(b1);  // OK：static_cast 进行直接初始化\n\n\treturn 0;\n}\n```\n\n### friend 友元类和友元函数\n\n* 能访问私有成员  \n* 破坏封装性\n* 友元关系不可传递\n* 友元关系的单向性\n* 友元声明的形式及数量不受限制\n\n### using\n\n#### using 声明\n\n一条 `using 声明` 语句一次只引入命名空间的一个成员。它使得我们可以清楚知道程序中所引用的到底是哪个名字。如：\n\n```cpp\nusing namespace_name::name;\n```\n\n#### 构造函数的 using 声明\n\n在 C++11 中，派生类能够重用其直接基类定义的构造函数。\n\n```cpp\nclass Derived : Base {\npublic:\n    using Base::Base;\n    /* ... */\n};\n```\n\n如上 using 声明，对于基类的每个构造函数，编译器都生成一个与之对应（形参列表完全相同）的派生类构造函数。生成如下类型构造函数：\n\n```cpp\nDerived(parms) : Base(args) { }\n```\n\n#### using 指示\n\n`using 指示` 使得某个特定命名空间中所有名字都可见，这样我们就无需再为它们添加任何前缀限定符了。如：\n\n```cpp\nusing namespace_name name;\n```\n\n#### 尽量少使用 `using 指示` 污染命名空间\n\n\u003e 一般说来，使用 using 命令比使用 using 编译命令更安全，这是由于它**只导入了指定的名称**。如果该名称与局部名称发生冲突，编译器将**发出指示**。using编译命令导入所有的名称，包括可能并不需要的名称。如果与局部名称发生冲突，则**局部名称将覆盖名称空间版本**，而编译器**并不会发出警告**。另外，名称空间的开放性意味着名称空间的名称可能分散在多个地方，这使得难以准确知道添加了哪些名称。\n\nusing 使用\n\n尽量少使用 `using 指示`\n\n```cpp\nusing namespace std;\n```\n\n应该多使用 `using 声明`\n\n```cpp\nint x;\nstd::cin \u003e\u003e x ;\nstd::cout \u003c\u003c x \u003c\u003c std::endl;\n```\n\n或者\n\n```cpp\nusing std::cin;\nusing std::cout;\nusing std::endl;\nint x;\ncin \u003e\u003e x;\ncout \u003c\u003c x \u003c\u003c endl;\n```\n\n### :: 范围解析运算符\n\n#### 分类\n\n1. 全局作用域符（`::name`）：用于类型名称（类、类成员、成员函数、变量等）前，表示作用域为全局命名空间\n2. 类作用域符（`class::name`）：用于表示指定类型的作用域范围是具体某个类的\n3. 命名空间作用域符（`namespace::name`）:用于表示指定类型的作用域范围是具体某个命名空间的\n\n:: 使用\n\n```cpp\nint count = 11;         // 全局（::）的 count\n\nclass A {\npublic:\n\tstatic int count;   // 类 A 的 count（A::count）\n};\nint A::count = 21;\n\nvoid fun()\n{\n\tint count = 31;     // 初始化局部的 count 为 31\n\tcount = 32;         // 设置局部的 count 的值为 32\n}\n\nint main() {\n\t::count = 12;       // 测试 1：设置全局的 count 的值为 12\n\n\tA::count = 22;      // 测试 2：设置类 A 的 count 为 22\n\n\tfun();\t\t        // 测试 3\n\n\treturn 0;\n}\n```\n\n### enum 枚举类型\n\n#### 限定作用域的枚举类型\n\n```cpp\nenum class open_modes { input, output, append };\n```\n\n#### 不限定作用域的枚举类型\n\n```cpp\nenum color { red, yellow, green };\nenum { floatPrec = 6, doublePrec = 10 };\n```\n\n### decltype\n\ndecltype 关键字用于检查实体的声明类型或表达式的类型及值分类。语法：\n\n```cpp\ndecltype ( expression )\n```\n\ndecltype 使用\n\n```cpp\n// 尾置返回允许我们在参数列表之后声明返回类型\ntemplate \u003ctypename It\u003e\nauto fcn(It beg, It end) -\u003e decltype(*beg)\n{\n    // 处理序列\n    return *beg;    // 返回序列中一个元素的引用\n}\n// 为了使用模板参数成员，必须用 typename\ntemplate \u003ctypename It\u003e\nauto fcn2(It beg, It end) -\u003e typename remove_reference\u003cdecltype(*beg)\u003e::type\n{\n    // 处理序列\n    return *beg;    // 返回序列中一个元素的拷贝\n}\n```\n\n### 引用\n\n#### 左值引用\n\n常规引用，一般表示对象的身份。\n\n#### 右值引用\n\n右值引用就是必须绑定到右值（一个临时对象、将要销毁的对象）的引用，一般表示对象的值。\n\n右值引用可实现转移语义（Move Sementics）和精确传递（Perfect Forwarding），它的主要目的有两个方面：\n\n* 消除两个对象交互时不必要的对象拷贝，节省运算存储资源，提高效率。\n* 能够更简洁明确地定义泛型函数。\n\n#### 引用折叠\n\n* `X\u0026 \u0026`、`X\u0026 \u0026\u0026`、`X\u0026\u0026 \u0026` 可折叠成 `X\u0026`\n* `X\u0026\u0026 \u0026\u0026` 可折叠成 `X\u0026\u0026`\n\n### 宏\n\n* 宏定义可以实现类似于函数的功能，但是它终归不是函数，而宏定义中括弧中的“参数”也不是真的参数，在宏展开的时候对 “参数” 进行的是一对一的替换。\n\n### 成员初始化列表\n\n好处\n\n* 更高效：少了一次调用默认构造函数的过程。\n* 有些场合必须要用初始化列表：\n  1. 常量成员，因为常量只能初始化不能赋值，所以必须放在初始化列表里面\n  2. 引用类型，引用必须在定义的时候初始化，并且不能重新赋值，所以也要写在初始化列表里面\n  3. 没有默认构造函数的类类型，因为使用初始化列表可以不必调用默认构造函数来初始化\n\n### initializer_list 列表初始化\n\n用花括号初始化器列表初始化一个对象，其中对应构造函数接受一个 `std::initializer_list` 参数.\n\ninitializer_list 使用\n\n```cpp\n#include \u003ciostream\u003e\n#include \u003cvector\u003e\n#include \u003cinitializer_list\u003e\n \ntemplate \u003cclass T\u003e\nstruct S {\n    std::vector\u003cT\u003e v;\n    S(std::initializer_list\u003cT\u003e l) : v(l) {\n         std::cout \u003c\u003c \"constructed with a \" \u003c\u003c l.size() \u003c\u003c \"-element list\\n\";\n    }\n    void append(std::initializer_list\u003cT\u003e l) {\n        v.insert(v.end(), l.begin(), l.end());\n    }\n    std::pair\u003cconst T*, std::size_t\u003e c_arr() const {\n        return {\u0026v[0], v.size()};  // 在 return 语句中复制列表初始化\n                                   // 这不使用 std::initializer_list\n    }\n};\n \ntemplate \u003ctypename T\u003e\nvoid templated_fn(T) {}\n \nint main()\n{\n    S\u003cint\u003e s = {1, 2, 3, 4, 5}; // 复制初始化\n    s.append({6, 7, 8});      // 函数调用中的列表初始化\n \n    std::cout \u003c\u003c \"The vector size is now \" \u003c\u003c s.c_arr().second \u003c\u003c \" ints:\\n\";\n \n    for (auto n : s.v)\n        std::cout \u003c\u003c n \u003c\u003c ' ';\n    std::cout \u003c\u003c '\\n';\n \n    std::cout \u003c\u003c \"Range-for over brace-init-list: \\n\";\n \n    for (int x : {-1, -2, -3}) // auto 的规则令此带范围 for 工作\n        std::cout \u003c\u003c x \u003c\u003c ' ';\n    std::cout \u003c\u003c '\\n';\n \n    auto al = {10, 11, 12};   // auto 的特殊规则\n \n    std::cout \u003c\u003c \"The list bound to auto has size() = \" \u003c\u003c al.size() \u003c\u003c '\\n';\n \n//    templated_fn({1, 2, 3}); // 编译错误！“ {1, 2, 3} ”不是表达式，\n                             // 它无类型，故 T 无法推导\n    templated_fn\u003cstd::initializer_list\u003cint\u003e\u003e({1, 2, 3}); // OK\n    templated_fn\u003cstd::vector\u003cint\u003e\u003e({1, 2, 3});           // 也 OK\n}\n```\n\n### 面向对象\n\n面向对象程序设计（Object-oriented programming，OOP）是种具有对象概念的程序编程典范，同时也是一种程序开发的抽象方针。\n\n![面向对象特征](https://gitee.com/huihut/interview/raw/master/images/面向对象基本特征.png)\n\n面向对象三大特征 —— 封装、继承、多态\n\n### 封装\n\n把客观事物封装成抽象的类，并且类可以把自己的数据和方法只让可信的类或者对象操作，对不可信的进行信息隐藏。关键字：public, protected, private。不写默认为 private。\n\n* `public` 成员：可以被任意实体访问\n* `protected` 成员：只允许被子类及本类的成员函数访问\n* `private` 成员：只允许被本类的成员函数、友元类或友元函数访问\n\n### 继承\n\n* 基类（父类）——\u0026gt; 派生类（子类）\n\n### 多态\n\n* 多态，即多种状态（形态）。简单来说，我们可以将多态定义为消息以多种形式显示的能力。\n* 多态是以封装和继承为基础的。\n* C++ 多态分类及实现：\n    1. 重载多态（Ad-hoc Polymorphism，编译期）：函数重载、运算符重载\n    2. 子类型多态（Subtype Polymorphism，运行期）：虚函数\n    3. 参数多态性（Parametric Polymorphism，编译期）：类模板、函数模板\n    4. 强制多态（Coercion Polymorphism，编译期/运行期）：基本类型转换、自定义类型转换\n\n\u003e [The Four Polymorphisms in C++](https://catonmat.net/cpp-polymorphism)\n\n#### 静态多态（编译期/早绑定）\n\n函数重载\n\n```cpp\nclass A\n{\npublic:\n    void do(int a);\n    void do(int a, int b);\n};\n```\n\n#### 动态多态（运行期期/晚绑定）\n* 虚函数：用 virtual 修饰成员函数，使其成为虚函数\n* 动态绑定：当使用基类的引用或指针调用一个虚函数时将发生动态绑定\n\n**注意：**\n\n* 可以将派生类的对象赋值给基类的指针或引用，反之不可\n* 普通函数（非类成员函数）不能是虚函数\n* 静态函数（static）不能是虚函数\n* 构造函数不能是虚函数（因为在调用构造函数时，虚表指针并没有在对象的内存空间中，必须要构造函数调用完成后才会形成虚表指针）\n* 内联函数不能是表现多态性时的虚函数，解释见：[虚函数（virtual）可以是内联函数（inline）吗？](https://github.com/huihut/interview#%E8%99%9A%E5%87%BD%E6%95%B0virtual%E5%8F%AF%E4%BB%A5%E6%98%AF%E5%86%85%E8%81%94%E5%87%BD%E6%95%B0inline%E5%90%97)\n\n动态多态使用\n\n```cpp\nclass Shape                     // 形状类\n{\npublic:\n    virtual double calcArea()\n    {\n        ...\n    }\n    virtual ~Shape();\n};\nclass Circle : public Shape     // 圆形类\n{\npublic:\n    virtual double calcArea();\n    ...\n};\nclass Rect : public Shape       // 矩形类\n{\npublic:\n    virtual double calcArea();\n    ...\n};\nint main()\n{\n    Shape * shape1 = new Circle(4.0);\n    Shape * shape2 = new Rect(5.0, 6.0);\n    shape1-\u003ecalcArea();         // 调用圆形类里面的方法\n    shape2-\u003ecalcArea();         // 调用矩形类里面的方法\n    delete shape1;\n    shape1 = nullptr;\n    delete shape2;\n    shape2 = nullptr;\n    return 0;\n}\n```\n\n### 虚析构函数\n\n虚析构函数是为了解决基类的指针指向派生类对象，并用基类的指针删除派生类对象。\n\n虚析构函数使用\n\n```cpp\nclass Shape\n{\npublic:\n    Shape();                    // 构造函数不能是虚函数\n    virtual double calcArea();\n    virtual ~Shape();           // 虚析构函数\n};\nclass Circle : public Shape     // 圆形类\n{\npublic:\n    virtual double calcArea();\n    ...\n};\nint main()\n{\n    Shape * shape1 = new Circle(4.0);\n    shape1-\u003ecalcArea();    \n    delete shape1;  // 因为Shape有虚析构函数，所以delete释放内存时，先调用子类析构函数，再调用基类析构函数，防止内存泄漏。\n    shape1 = NULL;\n    return 0；\n}\n```\n\n### 纯虚函数\n\n纯虚函数是一种特殊的虚函数，在基类中不能对虚函数给出有意义的实现，而把它声明为纯虚函数，它的实现留给该基类的派生类去做。\n\n```cpp\nvirtual int A() = 0;\n```\n\n### 虚函数、纯虚函数\n\n* 类里如果声明了虚函数，这个函数是实现的，哪怕是空实现，它的作用就是为了能让这个函数在它的子类里面可以被覆盖（override），这样的话，编译器就可以使用后期绑定来达到多态了。纯虚函数只是一个接口，是个函数的声明而已，它要留到子类里去实现。 \n* 虚函数在子类里面可以不重写；但纯虚函数必须在子类实现才可以实例化子类。\n* 虚函数的类用于 “实作继承”，继承接口的同时也继承了父类的实现。纯虚函数关注的是接口的统一性，实现由子类完成。 \n* 带纯虚函数的类叫抽象类，这种类不能直接生成对象，而只有被继承，并重写其虚函数后，才能使用。抽象类被继承后，子类可以继续是抽象类，也可以是普通类。\n* 虚基类是虚继承中的基类，具体见下文虚继承。\n\n\u003e [CSDN . C++ 中的虚函数、纯虚函数区别和联系](https://blog.csdn.net/u012260238/article/details/53610462)\n\n### 虚函数指针、虚函数表\n\n* 虚函数指针：在含有虚函数类的对象中，指向虚函数表，在运行时确定。\n* 虚函数表：在程序只读数据段（`.rodata section`，见：[目标文件存储结构](#%E7%9B%AE%E6%A0%87%E6%96%87%E4%BB%B6%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84)），存放虚函数指针，如果派生类实现了基类的某个虚函数，则在虚表中覆盖原本基类的那个虚函数指针，在编译时根据类的声明创建。\n\n\u003e [C++中的虚函数(表)实现机制以及用C语言对其进行的模拟实现](https://blog.twofei.com/496/)\n\n### 虚继承\n\n虚继承用于解决多继承条件下的菱形继承问题（浪费存储空间、存在二义性）。\n\n底层实现原理与编译器相关，一般通过**虚基类指针**和**虚基类表**实现，每个虚继承的子类都有一个虚基类指针（占用一个指针的存储空间，4字节）和虚基类表（不占用类对象的存储空间）（需要强调的是，虚基类依旧会在子类里面存在拷贝，只是仅仅最多存在一份而已，并不是不在子类里面了）；当虚继承的子类被当做父类继承时，虚基类指针也会被继承。\n\n实际上，vbptr 指的是虚基类表指针（virtual base table pointer），该指针指向了一个虚基类表（virtual table），虚表中记录了虚基类与本类的偏移地址；通过偏移地址，这样就找到了虚基类成员，而虚继承也不用像普通多继承那样维持着公共基类（虚基类）的两份同样的拷贝，节省了存储空间。\n\n### 虚继承、虚函数\n\n* 相同之处：都利用了虚指针（均占用类的存储空间）和虚表（均不占用类的存储空间）\n* 不同之处：\n    * 虚继承\n        * 虚基类依旧存在继承类中，只占用存储空间\n        * 虚基类表存储的是虚基类相对直接继承类的偏移\n    * 虚函数\n        * 虚函数不占用存储空间\n        * 虚函数表存储的是虚函数地址\n\n### 类模板、成员模板、虚函数\n\n* 类模板中可以使用虚函数\n* 一个类（无论是普通类还是类模板）的成员模板（本身是模板的成员函数）不能是虚函数\n\n### 抽象类、接口类、聚合类\n\n* 抽象类：含有纯虚函数的类\n* 接口类：仅含有纯虚函数的抽象类\n* 聚合类：用户可以直接访问其成员，并且具有特殊的初始化语法形式。满足如下特点：\n    * 所有成员都是 public\n    * 没有定义任何构造函数\n    * 没有类内初始化\n    * 没有基类，也没有 virtual 函数\n\n### 内存分配和管理\n\n#### malloc、calloc、realloc、alloca\n\n1. malloc：申请指定字节数的内存。申请到的内存中的初始值不确定。\n2. calloc：为指定长度的对象，分配能容纳其指定个数的内存。申请到的内存的每一位（bit）都初始化为 0。\n3. realloc：更改以前分配的内存长度（增加或减少）。当增加长度时，可能需将以前分配区的内容移到另一个足够大的区域，而新增区域内的初始值则不确定。\n4. alloca：在栈上申请内存。程序在出栈的时候，会自动释放内存。但是需要注意的是，alloca 不具可移植性, 而且在没有传统堆栈的机器上很难实现。alloca 不宜使用在必须广泛移植的程序中。C99 中支持变长数组 (VLA)，可以用来替代 alloca。\n\n#### malloc、free\n\n用于分配、释放内存\n\nmalloc、free 使用\n\n申请内存，确认是否申请成功\n\n```cpp\nchar *str = (char*) malloc(100);\nassert(str != nullptr);\n```\n\n释放内存后指针置空\n\n```cpp\nfree(p); \np = nullptr;\n```\n\n#### new、delete\n\n1. new / new[]：完成两件事，先底层调用 malloc 分配了内存，然后调用构造函数（创建对象）。\n2. delete/delete[]：也完成两件事，先调用析构函数（清理资源），然后底层调用 free 释放空间。\n3. new 在申请内存时会自动计算所需字节数，而 malloc 则需我们自己输入申请内存空间的字节数。\n\nnew、delete 使用\n\n申请内存，确认是否申请成功\n\n```cpp\nint main()\n{\n    T* t = new T();     // 先内存分配 ，再构造函数\n    delete t;           // 先析构函数，再内存释放\n    return 0;\n}\n```\n\n#### 定位 new\n\n定位 new（placement new）允许我们向 new 传递额外的地址参数，从而在预先指定的内存区域创建对象。\n\n```cpp\nnew (place_address) type\nnew (place_address) type (initializers)\nnew (place_address) type [size]\nnew (place_address) type [size] { braced initializer list }\n```\n\n* `place_address` 是个指针\n* `initializers` 提供一个（可能为空的）以逗号分隔的初始值列表\n\n### delete this 合法吗？\n\n\u003e [Is it legal (and moral) for a member function to say delete this?](https://isocpp.org/wiki/faq/freestore-mgmt#delete-this)\n\n合法，但：\n\n1. 必须保证 this 对象是通过 `new`（不是 `new[]`、不是 placement new、不是栈上、不是全局、不是其他对象成员）分配的\n2. 必须保证调用 `delete this` 的成员函数是最后一个调用 this 的成员函数\n3. 必须保证成员函数的 `delete this ` 后面没有调用 this 了\n4. 必须保证 `delete this` 后没有人使用了\n\n### 如何定义一个只能在堆上（栈上）生成对象的类？\n\n\u003e [如何定义一个只能在堆上（栈上）生成对象的类?](https://www.nowcoder.com/questionTerminal/0a584aa13f804f3ea72b442a065a7618)\n\n#### 只能在堆上\n\n方法：将析构函数设置为私有\n\n原因：C++ 是静态绑定语言，编译器管理栈上对象的生命周期，编译器在为类对象分配栈空间时，会先检查类的析构函数的访问性。若析构函数不可访问，则不能在栈上创建对象。\n\n#### 只能在栈上\n\n方法：将 new 和 delete 重载为私有\n\n原因：在堆上生成对象，使用 new 关键词操作，其过程分为两阶段：第一阶段，使用 new 在堆上寻找可用内存，分配给对象；第二阶段，调用构造函数生成对象。将 new 操作设置为私有，那么第一阶段就无法完成，就不能够在堆上生成对象。\n\n### 智能指针\n\n#### C++ 标准库（STL）中\n\n头文件：`#include \u003cmemory\u003e`\n\n#### C++ 98\n\n```cpp\nstd::auto_ptr\u003cstd::string\u003e ps (new std::string(str))；\n```\n\n#### C++ 11\n\n1. shared_ptr\n2. unique_ptr\n3. weak_ptr\n4. auto_ptr（被 C++11 弃用）\n\n* Class shared_ptr 实现共享式拥有（shared ownership）概念。多个智能指针指向相同对象，该对象和其相关资源会在 “最后一个 reference 被销毁” 时被释放。为了在结构较复杂的情景中执行上述工作，标准库提供 weak_ptr、bad_weak_ptr 和 enable_shared_from_this 等辅助类。\n* Class unique_ptr 实现独占式拥有（exclusive ownership）或严格拥有（strict ownership）概念，保证同一时间内只有一个智能指针可以指向该对象。你可以移交拥有权。它对于避免内存泄漏（resource leak）——如 new 后忘记 delete ——特别有用。\n\n##### shared_ptr\n\n多个智能指针可以共享同一个对象，对象的最末一个拥有着有责任销毁对象，并清理与该对象相关的所有资源。\n\n* 支持定制型删除器（custom deleter），可防范 Cross-DLL 问题（对象在动态链接库（DLL）中被 new 创建，却在另一个 DLL 内被 delete 销毁）、自动解除互斥锁\n\n##### weak_ptr\n\nweak_ptr 允许你共享但不拥有某对象，一旦最末一个拥有该对象的智能指针失去了所有权，任何 weak_ptr 都会自动成空（empty）。因此，在 default 和 copy 构造函数之外，weak_ptr 只提供 “接受一个 shared_ptr” 的构造函数。\n\n* 可打破环状引用（cycles of references，两个其实已经没有被使用的对象彼此互指，使之看似还在 “被使用” 的状态）的问题\n\n##### unique_ptr\n\nunique_ptr 是 C++11 才开始提供的类型，是一种在异常时可以帮助避免资源泄漏的智能指针。采用独占式拥有，意味着可以确保一个对象和其相应的资源同一时间只被一个 pointer 拥有。一旦拥有着被销毁或编程 empty，或开始拥有另一个对象，先前拥有的那个对象就会被销毁，其任何相应资源亦会被释放。\n\n* unique_ptr 用于取代 auto_ptr\n\n##### auto_ptr\n\n被 c++11 弃用，原因是缺乏语言特性如 “针对构造和赋值” 的 `std::move` 语义，以及其他瑕疵。\n\n##### auto_ptr 与 unique_ptr 比较\n\n* auto_ptr 可以赋值拷贝，复制拷贝后所有权转移；unqiue_ptr 无拷贝赋值语义，但实现了`move` 语义；\n* auto_ptr 对象不能管理数组（析构调用 `delete`），unique_ptr 可以管理数组（析构调用 `delete[]` ）；\n\n### 强制类型转换运算符\n\n\u003e [MSDN . 强制转换运算符](https://msdn.microsoft.com/zh-CN/library/5f6c9f8h.aspx)\n\n#### static_cast\n\n* 用于非多态类型的转换\n* 不执行运行时类型检查（转换安全性不如 dynamic_cast）\n* 通常用于转换数值数据类型（如 float -\u003e int）\n* 可以在整个类层次结构中移动指针，子类转化为父类安全（向上转换），父类转化为子类不安全（因为子类可能有不在父类的字段或方法）\n\n\u003e 向上转换是一种隐式转换。\n\n#### dynamic_cast\n\n* 用于多态类型的转换\n* 执行行运行时类型检查\n* 只适用于指针或引用\n* 对不明确的指针的转换将失败（返回 nullptr），但不引发异常\n* 可以在整个类层次结构中移动指针，包括向上转换、向下转换\n\n#### const_cast \n\n* 用于删除 const、volatile 和 __unaligned 特性（如将 const int 类型转换为 int 类型 ）\n\n#### reinterpret_cast\n\n* 用于位的简单重新解释\n* 滥用 reinterpret_cast 运算符可能很容易带来风险。 除非所需转换本身是低级别的，否则应使用其他强制转换运算符之一。\n* 允许将任何指针转换为任何其他指针类型（如 `char*` 到 `int*` 或 `One_class*` 到 `Unrelated_class*` 之类的转换，但其本身并不安全）\n* 也允许将任何整数类型转换为任何指针类型以及反向转换。\n* reinterpret_cast 运算符不能丢掉 const、volatile 或 __unaligned 特性。 \n* reinterpret_cast 的一个实际用途是在哈希函数中，即，通过让两个不同的值几乎不以相同的索引结尾的方式将值映射到索引。\n\n#### bad_cast\n\n* 由于强制转换为引用类型失败，dynamic_cast 运算符引发 bad_cast 异常。\n\nbad_cast 使用\n\n```cpp\ntry {  \n    Circle\u0026 ref_circle = dynamic_cast\u003cCircle\u0026\u003e(ref_shape);   \n}  \ncatch (bad_cast b) {  \n    cout \u003c\u003c \"Caught: \" \u003c\u003c b.what();  \n} \n```\n\n### 运行时类型信息 (RTTI) \n\n#### dynamic_cast\n\n* 用于多态类型的转换\n\n#### typeid\n\n* typeid 运算符允许在运行时确定对象的类型\n* type\\_id 返回一个 type\\_info 对象的引用\n* 如果想通过基类的指针获得派生类的数据类型，基类必须带有虚函数\n* 只能获取对象的实际类型\n\n#### type_info\n\n* type_info 类描述编译器在程序中生成的类型信息。 此类的对象可以有效存储指向类型的名称的指针。 type_info 类还可存储适合比较两个类型是否相等或比较其排列顺序的编码值。 类型的编码规则和排列顺序是未指定的，并且可能因程序而异。\n* 头文件：`typeinfo`\n\ntypeid、type_info 使用\n\n```cpp\n#include \u003ciostream\u003e\nusing namespace std;\n\nclass Flyable                       // 能飞的\n{\npublic:\n    virtual void takeoff() = 0;     // 起飞\n    virtual void land() = 0;        // 降落\n};\nclass Bird : public Flyable         // 鸟\n{\npublic:\n    void foraging() {...}           // 觅食\n    virtual void takeoff() {...}\n    virtual void land() {...}\n    virtual ~Bird(){}\n};\nclass Plane : public Flyable        // 飞机\n{\npublic:\n    void carry() {...}              // 运输\n    virtual void takeoff() {...}\n    virtual void land() {...}\n};\n\nclass type_info\n{\npublic:\n    const char* name() const;\n    bool operator == (const type_info \u0026 rhs) const;\n    bool operator != (const type_info \u0026 rhs) const;\n    int before(const type_info \u0026 rhs) const;\n    virtual ~type_info();\nprivate:\n    ...\n};\n\nvoid doSomething(Flyable *obj)                 // 做些事情\n{\n    obj-\u003etakeoff();\n\n    cout \u003c\u003c typeid(*obj).name() \u003c\u003c endl;        // 输出传入对象类型（\"class Bird\" or \"class Plane\"）\n\n    if(typeid(*obj) == typeid(Bird))            // 判断对象类型\n    {\n        Bird *bird = dynamic_cast\u003cBird *\u003e(obj); // 对象转化\n        bird-\u003eforaging();\n    }\n\n    obj-\u003eland();\n}\n\nint main(){\n\tBird *b = new Bird();\n\tdoSomething(b);\n\tdelete b;\n\tb = nullptr;\n\treturn 0;\n}\n```\n\n\u003ca id=\"effective\"\u003e\u003c/a\u003e\n\n## ⭐️ Effective\n\n### Effective C++\n\n1. 视 C++ 为一个语言联邦（C、Object-Oriented C++、Template C++、STL）\n2. 宁可以编译器替换预处理器（尽量以 `const`、`enum`、`inline` 替换 `#define`）\n3. 尽可能使用 const\n4. 确定对象被使用前已先被初始化（构造时赋值（copy 构造函数）比 default 构造后赋值（copy assignment）效率高）\n5. 了解 C++ 默默编写并调用哪些函数（编译器暗自为 class 创建 default 构造函数、copy 构造函数、copy assignment 操作符、析构函数）\n6. 若不想使用编译器自动生成的函数，就应该明确拒绝（将不想使用的成员函数声明为 private，并且不予实现）\n7. 为多态基类声明 virtual 析构函数（如果 class 带有任何 virtual 函数，它就应该拥有一个 virtual 析构函数）\n8. 别让异常逃离析构函数（析构函数应该吞下不传播异常，或者结束程序，而不是吐出异常；如果要处理异常应该在非析构的普通函数处理）\n9. 绝不在构造和析构过程中调用 virtual 函数（因为这类调用从不下降至 derived class）\n10. 令 `operator=` 返回一个 `reference to *this` （用于连锁赋值）\n11. 在 `operator=` 中处理 “自我赋值”\n12. 赋值对象时应确保复制 “对象内的所有成员变量” 及 “所有 base class 成分”（调用基类复制构造函数）\n13. 以对象管理资源（资源在构造函数获得，在析构函数释放，建议使用智能指针，资源取得时机便是初始化时机（Resource Acquisition Is Initialization，RAII））\n14. 在资源管理类中小心 copying 行为（普遍的 RAII class copying 行为是：抑制 copying、引用计数、深度拷贝、转移底部资源拥有权（类似 auto_ptr））\n15. 在资源管理类中提供对原始资源（raw resources）的访问（对原始资源的访问可能经过显式转换或隐式转换，一般而言显示转换比较安全，隐式转换对客户比较方便）\n16. 成对使用 new 和 delete 时要采取相同形式（`new` 中使用 `[]` 则 `delete []`，`new` 中不使用 `[]` 则 `delete`）\n17. 以独立语句将 newed 对象存储于（置入）智能指针（如果不这样做，可能会因为编译器优化，导致难以察觉的资源泄漏）\n18. 让接口容易被正确使用，不易被误用（促进正常使用的办法：接口的一致性、内置类型的行为兼容；阻止误用的办法：建立新类型，限制类型上的操作，约束对象值、消除客户的资源管理责任）\n19. 设计 class 犹如设计 type，需要考虑对象创建、销毁、初始化、赋值、值传递、合法值、继承关系、转换、一般化等等。\n20. 宁以 pass-by-reference-to-const 替换 pass-by-value （前者通常更高效、避免切割问题（slicing problem），但不适用于内置类型、STL迭代器、函数对象）\n21. 必须返回对象时，别妄想返回其 reference（绝不返回 pointer 或 reference 指向一个 local stack 对象，或返回 reference 指向一个 heap-allocated 对象，或返回 pointer 或 reference 指向一个 local static 对象而有可能同时需要多个这样的对象。）\n22. 将成员变量声明为 private（为了封装、一致性、对其读写精确控制等）\n23. 宁以 non-member、non-friend 替换 member 函数（可增加封装性、包裹弹性（packaging flexibility）、机能扩充性）\n24. 若所有参数（包括被this指针所指的那个隐喻参数）皆须要类型转换，请为此采用 non-member 函数\n25. 考虑写一个不抛异常的 swap 函数\n26. 尽可能延后变量定义式的出现时间（可增加程序清晰度并改善程序效率）\n27. 尽量少做转型动作（旧式：`(T)expression`、`T(expression)`；新式：`const_cast\u003cT\u003e(expression)`、`dynamic_cast\u003cT\u003e(expression)`、`reinterpret_cast\u003cT\u003e(expression)`、`static_cast\u003cT\u003e(expression)`、；尽量避免转型、注重效率避免 dynamic_casts、尽量设计成无需转型、可把转型封装成函数、宁可用新式转型）\n28. 避免使用 handles（包括 引用、指针、迭代器）指向对象内部（以增加封装性、使 const 成员函数的行为更像 const、降低 “虚吊号码牌”（dangling handles，如悬空指针等）的可能性）\n29. 为 “异常安全” 而努力是值得的（异常安全函数（Exception-safe functions）即使发生异常也不会泄露资源或允许任何数据结构败坏，分为三种可能的保证：基本型、强列型、不抛异常型）\n30. 透彻了解 inlining 的里里外外（inlining 在大多数 C++ 程序中是编译期的行为；inline 函数是否真正 inline，取决于编译器；大部分编译器拒绝太过复杂（如带有循环或递归）的函数 inlining，而所有对 virtual 函数的调用（除非是最平淡无奇的）也都会使 inlining 落空；inline 造成的代码膨胀可能带来效率损失；inline 函数无法随着程序库的升级而升级）\n31. 将文件间的编译依存关系降至最低（如果使用 object references 或 object pointers 可以完成任务，就不要使用 objects；如果能够，尽量以 class 声明式替换 class 定义式；为声明式和定义式提供不同的头文件）\n32. 确定你的 public 继承塑模出 is-a（是一种）关系（适用于 base classes 身上的每一件事情一定适用于 derived classes 身上，因为每一个 derived class 对象也都是一个 base class 对象）\n33. 避免遮掩继承而来的名字（可使用 using 声明式或转交函数（forwarding functions）来让被遮掩的名字再见天日）\n34. 区分接口继承和实现继承（在 public 继承之下，derived classes 总是继承 base class 的接口；pure virtual 函数只具体指定接口继承；非纯 impure virtual 函数具体指定接口继承及缺省实现继承；non-virtual 函数具体指定接口继承以及强制性实现继承）\n35. 考虑 virtual 函数以外的其他选择（如 Template Method 设计模式的 non-virtual interface（NVI）手法，将 virtual 函数替换为 “函数指针成员变量”，以 `tr1::function` 成员变量替换 virtual 函数，将继承体系内的 virtual 函数替换为另一个继承体系内的 virtual 函数）\n36. 绝不重新定义继承而来的 non-virtual 函数\n37. 绝不重新定义继承而来的缺省参数值，因为缺省参数值是静态绑定（statically bound），而 virtual 函数却是动态绑定（dynamically bound）\n38. 通过复合塑模 has-a（有一个）或 “根据某物实现出”（在应用域（application domain），复合意味 has-a（有一个）；在实现域（implementation domain），复合意味着 is-implemented-in-terms-of（根据某物实现出））\n39. 明智而审慎地使用 private 继承（private 继承意味着 is-implemented-in-terms-of（根据某物实现出），尽可能使用复合，当 derived class 需要访问 protected base class 的成员，或需要重新定义继承而来的时候 virtual 函数，或需要 empty base 最优化时，才使用 private 继承）\n40. 明智而审慎地使用多重继承（多继承比单一继承复杂，可能导致新的歧义性，以及对 virtual 继承的需要，但确有正当用途，如 “public 继承某个 interface class” 和 “private 继承某个协助实现的 class”；virtual 继承可解决多继承下菱形继承的二义性问题，但会增加大小、速度、初始化及赋值的复杂度等等成本）\n41. 了解隐式接口和编译期多态（class 和 templates 都支持接口（interfaces）和多态（polymorphism）；class 的接口是以签名为中心的显式的（explicit），多态则是通过 virtual 函数发生于运行期；template 的接口是奠基于有效表达式的隐式的（implicit），多态则是通过 template 具现化和函数重载解析（function overloading resolution）发生于编译期）\n42. 了解 typename 的双重意义（声明 template 类型参数是，前缀关键字 class 和 typename 的意义完全相同；请使用关键字 typename 标识嵌套从属类型名称，但不得在基类列（base class lists）或成员初值列（member initialization list）内以它作为 base class 修饰符）\n43. 学习处理模板化基类内的名称（可在 derived class templates 内通过 `this-\u003e` 指涉 base class templates 内的成员名称，或藉由一个明白写出的 “base class 资格修饰符” 完成）\n44. 将与参数无关的代码抽离 templates（因类型模板参数（non-type template parameters）而造成代码膨胀往往可以通过函数参数或 class 成员变量替换 template 参数来消除；因类型参数（type parameters）而造成的代码膨胀往往可以通过让带有完全相同二进制表述（binary representations）的实现类型（instantiation types）共享实现码）\n45. 运用成员函数模板接受所有兼容类型（请使用成员函数模板（member function templates）生成 “可接受所有兼容类型” 的函数；声明 member templates 用于 “泛化 copy 构造” 或 “泛化 assignment 操作” 时还需要声明正常的 copy 构造函数和 copy assignment 操作符）\n46. 需要类型转换时请为模板定义非成员函数（当我们编写一个 class template，而它所提供之 “与此 template 相关的” 函数支持 “所有参数之隐式类型转换” 时，请将那些函数定义为 “class template 内部的 friend 函数”）\n47. 请使用 traits classes 表现类型信息（traits classes 通过 templates 和 “templates 特化” 使得 “类型相关信息” 在编译期可用，通过重载技术（overloading）实现在编译期对类型执行 if...else 测试）\n48. 认识 template 元编程（模板元编程（TMP，template metaprogramming）可将工作由运行期移往编译期，因此得以实现早期错误侦测和更高的执行效率；TMP 可被用来生成 “给予政策选择组合”（based on combinations of policy choices）的客户定制代码，也可用来避免生成对某些特殊类型并不适合的代码）\n49. 了解 new-handler 的行为（set\\_new\\_handler 允许客户指定一个在内存分配无法获得满足时被调用的函数；nothrow new 是一个颇具局限的工具，因为它只适用于内存分配（operator new），后继的构造函数调用还是可能抛出异常）\n50. 了解 new 和 delete 的合理替换时机（为了检测运用错误、收集动态分配内存之使用统计信息、增加分配和归还速度、降低缺省内存管理器带来的空间额外开销、弥补缺省分配器中的非最佳齐位、将相关对象成簇集中、获得非传统的行为）\n51. 编写 new 和 delete 时需固守常规（operator new 应该内涵一个无穷循环，并在其中尝试分配内存，如果它无法满足内存需求，就应该调用 new-handler，它也应该有能力处理 0 bytes 申请，class 专属版本则还应该处理 “比正确大小更大的（错误）申请”；operator delete 应该在收到 null 指针时不做任何事，class 专属版本则还应该处理 “比正确大小更大的（错误）申请”）\n52. 写了 placement new 也要写 placement delete（当你写一个 placement operator new，请确定也写出了对应的 placement operator delete，否则可能会发生隐微而时断时续的内存泄漏；当你声明 placement new 和 placement delete，请确定不要无意识（非故意）地遮掩了它们地正常版本）\n53. 不要轻忽编译器的警告\n54. 让自己熟悉包括 TR1 在内的标准程序库（TR1，C++ Technical Report 1，C++11 标准的草稿文件）\n55. 让自己熟悉 Boost（准标准库）\n\n### More Effective c++\n\n1. 仔细区别 pointers 和 references（当你知道你需要指向某个东西，而且绝不会改变指向其他东西，或是当你实现一个操作符而其语法需求无法由 pointers 达成，你就应该选择 references；任何其他时候，请采用 pointers）\n2. 最好使用 C++ 转型操作符（`static_cast`、`const_cast`、`dynamic_cast`、`reinterpret_cast`）\n3. 绝不要以多态（polymorphically）方式处理数组（多态（polymorphism）和指针算术不能混用；数组对象几乎总是会涉及指针的算术运算，所以数组和多态不要混用）\n4. 非必要不提供 default constructor（避免对象中的字段被无意义地初始化）\n5. 对定制的 “类型转换函数” 保持警觉（单自变量 constructors 可通过简易法（explicit 关键字）或代理类（proxy classes）来避免编译器误用；隐式类型转换操作符可改为显式的 member function 来避免非预期行为）\n6. 区别 increment/decrement 操作符的前置（prefix）和后置（postfix）形式（前置式累加后取出，返回一个 reference；后置式取出后累加，返回一个 const 对象；处理用户定制类型时，应该尽可能使用前置式 increment；后置式的实现应以其前置式兄弟为基础）\n7. 千万不要重载 `\u0026\u0026`，`||` 和 `,` 操作符（`\u0026\u0026` 与 `||` 的重载会用 “函数调用语义” 取代 “骤死式语义”；`,` 的重载导致不能保证左侧表达式一定比右侧表达式更早被评估）\n8. 了解各种不同意义的 new 和 delete（`new operator`、`operator new`、`placement new`、`operator new[]`；`delete operator`、`operator delete`、`destructor`、`operator delete[]`）\n9. 利用 destructors 避免泄漏资源（在 destructors 释放资源可以避免异常时的资源泄漏）\n10. 在 constructors 内阻止资源泄漏（由于 C++ 只会析构已构造完成的对象，因此在构造函数可以使用 try...catch 或者 auto_ptr（以及与之相似的 classes） 处理异常时资源泄露问题）\n11. 禁止异常流出 destructors 之外（原因：一、避免 terminate 函数在 exception 传播过程的栈展开（stack-unwinding）机制种被调用；二、协助确保 destructors 完成其应该完成的所有事情）\n12. 了解 “抛出一个 exception” 与 “传递一个参数” 或 “调用一个虚函数” 之间的差异（第一，exception objects 总是会被复制（by pointer 除外），如果以 by value 方式捕捉甚至被复制两次，而传递给函数参数的对象则不一定得复制；第二，“被抛出成为 exceptions” 的对象，其被允许的类型转换动作比 “被传递到函数去” 的对象少；第三，catch 子句以其 “出现于源代码的顺序” 被编译器检验对比，其中第一个匹配成功者便执行，而调用一个虚函数，被选中执行的是那个 “与对象类型最佳吻合” 的函数）\n13. 以 by reference 方式捕获 exceptions（可避免对象删除问题、exception objects 的切割问题，可保留捕捉标准 exceptions 的能力，可约束 exception object 需要复制的次数）\n14. 明智运用 exception specifications（exception specifications 对 “函数希望抛出什么样的 exceptions” 提供了卓越的说明；也有一些缺点，包括编译器只对它们做局部性检验而很容易不经意地违反，与可能会妨碍更上层的 exception 处理函数处理未预期的 exceptions）\n15. 了解异常处理的成本（粗略估计，如果使用 try 语句块，代码大约整体膨胀 5%-10%，执行速度亦大约下降这个数；因此请将你对 try 语句块和 exception specifications 的使用限制于非用不可的地点，并且在真正异常的情况下才抛出 exceptions）\n16. 谨记 80-20 法则（软件的整体性能几乎总是由其构成要素（代码）的一小部分决定的，可使用程序分析器（program profiler）识别出消耗资源的代码）\n17. 考虑使用 lazy evaluation（缓式评估）（可应用于：Reference Counting（引用计数）来避免非必要的对象复制、区分 operator[] 的读和写动作来做不同的事情、Lazy Fetching（缓式取出）来避免非必要的数据库读取动作、Lazy Expression Evaluation（表达式缓评估）来避免非必要的数值计算动作）\n18. 分期摊还预期的计算成本（当你必须支持某些运算而其结构几乎总是被需要，或其结果常常被多次需要的时候，over-eager evaluation（超急评估）可以改善程序效率）\n\n### Google C++ Style Guide\n\n* 英文：[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)\n* 中文：[C++ 风格指南](https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/contents/)\n\n### 其他\n\n* [Bjarne Stroustrup 的常见问题](http://www.stroustrup.com/bs_faq.html)\n* [Bjarne Stroustrup 的 C++ 风格和技巧常见问题](http://www.stroustrup.com/bs_faq2.html)\n\n\u003ca id=\"stl\"\u003e\u003c/a\u003e\n\n## 📦 STL\n\n### STL 索引\n\n[STL 方法含义索引](https://github.com/huihut/interview/tree/master/STL)\n\n### STL 容器\n\n容器 | 底层数据结构 | 时间复杂度 | 有无序 | 可不可重复 | 其他\n---|---|---|---|---|---\n[array](https://github.com/huihut/interview/tree/master/STL#array)|数组|随机读改 O(1)|无序|可重复|支持随机访问\n[vector](https://github.com/huihut/interview/tree/master/STL#vector)|数组|随机读改、尾部插入、尾部删除 O(1)\u003cbr/\u003e头部插入、头部删除 O(n)|无序|可重复|支持随机访问\n[deque](https://github.com/huihut/interview/tree/master/STL#deque)|双端队列|头尾插入、头尾删除 O(1)|无序|可重复|一个中央控制器 + 多个缓冲区，支持首尾快速增删，支持随机访问\n[forward_list](https://github.com/huihut/interview/tree/master/STL#forward_list)|单向链表|插入、删除 O(1)|无序|可重复|不支持随机访问\n[list](https://github.com/huihut/interview/tree/master/STL#list)|双向链表|插入、删除 O(1)|无序|可重复|不支持随机访问\n[stack](https://github.com/huihut/interview/tree/master/STL#stack)|deque / list|顶部插入、顶部删除 O(1)|无序|可重复|deque 或 list 封闭头端开口，不用 vector 的原因应该是容量大小有限制，扩容耗时\n[queue](https://github.com/huihut/interview/tree/master/STL#queue)|deque / list|尾部插入、头部删除 O(1)|无序|可重复|deque 或 list 封闭头端开口，不用 vector 的原因应该是容量大小有限制，扩容耗时\n[priority_queue](https://github.com/huihut/interview/tree/master/STL#priority_queue)|vector + max-heap|插入、删除 O(log\u003csub\u003e2\u003c/sub\u003en)|有序|可重复|vector容器+heap处理规则\n[set](https://github.com/huihut/interview/tree/master/STL#set)|红黑树|插入、删除、查找 O(log\u003csub\u003e2\u003c/sub\u003en)|有序|不可重复|\n[multiset](https://github.com/huihut/interview/tree/master/STL#multiset)|红黑树|插入、删除、查找 O(log\u003csub\u003e2\u003c/sub\u003en)|有序|可重复|\n[map](https://github.com/huihut/interview/tree/master/STL#map)|红黑树|插入、删除、查找 O(log\u003csub\u003e2\u003c/sub\u003en)|有序|不可重复|\n[multimap](https://github.com/huihut/interview/tree/master/STL#multimap)|红黑树|插入、删除、查找 O(log\u003csub\u003e2\u003c/sub\u003en)|有序|可重复|\n[unordered_set](https://github.com/huihut/interview/tree/master/STL#unordered_set)|哈希表|插入、删除、查找 O(1) 最差 O(n)|无序|不可重复|\n[unordered_multiset](https://github.com/huihut/interview/tree/master/STL#unordered_multiset)|哈希表|插入、删除、查找 O(1) 最差 O(n)|无序|可重复|\n[unordered_map](https://github.com/huihut/interview/tree/master/STL#unordered_map)|哈希表|插入、删除、查找 O(1) 最差 O(n)|无序|不可重复|\n[unordered_multimap](https://github.com/huihut/interview/tree/master/STL#unordered_multimap)|哈希表|插入、删除、查找 O(1) 最差 O(n)|无序|可重复|\n\n### STL 算法\n\n算法 | 底层算法 | 时间复杂度 | 可不可重复\n---|---|---|---\n[find](http://www.cplusplus.com/reference/algorithm/find/)|顺序查找|O(n)|可重复\n[sort](https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/bits/stl_algo.h#L4808)|[内省排序](https://en.wikipedia.org/wiki/Introsort)|O(n*log\u003csub\u003e2\u003c/sub\u003en)|可重复\n\n\n\u003ca id=\"data-structure\"\u003e\u003c/a\u003e\n\n## 〽️ 数据结构\n\n### 顺序结构\n\n#### 顺序栈（Sequence Stack）\n\n[SqStack.cpp](DataStructure/SqStack.cpp)\n\n顺序栈数据结构和图片\n\n```cpp\ntypedef struct {\n\tElemType *elem;\n\tint top;\n\tint size;\n\tint increment;\n} SqStack;\n```\n\n![](https://gitee.com/huihut/interview/raw/master/images/SqStack.png)\n\n#### 队列（Sequence Queue）\n\n队列数据结构\n\n```cpp\ntypedef struct {\n\tElemType * elem;\n\tint front;\n\tint rear;\n\tint maxSize;\n}SqQueue;\n```\n\n##### 非循环队列\n\n非循环队列图片\n\n![](https://gitee.com/huihut/interview/raw/master/images/SqQueue.png)\n\n`SqQueue.rear++`\n\n##### 循环队列\n\n循环队列图片\n\n![](https://gitee.com/huihut/interview/raw/master/images/SqLoopStack.png)\n\n`SqQueue.rear = (SqQueue.rear + 1) % SqQueue.maxSize`\n\n#### 顺序表（Sequence List）\n\n[SqList.cpp](DataStructure/SqList.cpp)\n\n顺序表数据结构和图片\n\n```cpp\ntypedef struct {\n\tElemType *elem;\n\tint length;\n\tint size;\n\tint increment;\n} SqList;\n```\n\n![](https://gitee.com/huihut/interview/raw/master/images/SqList.png)\n\n\n### 链式结构\n\n[LinkList.cpp](DataStructure/LinkList.cpp)\n\n[LinkList_with_head.cpp](DataStructure/LinkList_with_head.cpp)\n\n链式数据结构\n\n```cpp\ntypedef struct LNode {\n    ElemType data;\n    struct LNode *next;\n} LNode, *LinkList; \n```\n\n#### 链队列（Link Queue）\n\n链队列图片\n\n![](https://gitee.com/huihut/interview/raw/master/images/LinkQueue.png)\n\n#### 线性表的链式表示\n\n##### 单链表（Link List）\n\n单链表图片\n\n![](https://gitee.com/huihut/interview/raw/master/images/LinkList.png)\n\n##### 双向链表（Du-Link-List）\n\n双向链表图片\n\n![](https://gitee.com/huihut/interview/raw/master/images/DuLinkList.png)\n\n##### 循环链表（Cir-Link-List）\n\n循环链表图片\n\n![](https://gitee.com/huihut/interview/raw/master/images/CirLinkList.png)\n\n### 哈希表\n\n[HashTable.cpp](DataStructure/HashTable.cpp)\n\n#### 概念\n\n哈希函数：`H(key): K -\u003e D , key ∈ K`\n\n#### 构造方法\n\n* 直接定址法\n* 除留余数法\n* 数字分析法\n* 折叠法\n* 平方取中法\n\n#### 冲突处理方法\n\n* 链地址法：key 相同的用单链表链接\n* 开放定址法\n    * 线性探测法：key 相同 -\u003e 放到 key 的下一个位置，`Hi = (H(key) + i) % m`\n    * 二次探测法：key 相同 -\u003e 放到 `Di = 1^2, -1^2, ..., ±（k)^2,(k\u003c=m/2）`\n    * 随机探测法：`H = (H(key) + 伪随机数) % m`\n\n#### 线性探测的哈希表数据结构\n\n线性探测的哈希表数据结构和图片\n\n```cpp\ntypedef char KeyType;\n\ntypedef struct {\n\tKeyType key;\n}RcdType;\n\ntypedef struct {\n\tRcdType *rcd;\n\tint size;\n\tint count;\n\tbool *tag;\n}HashTable;\n```\n\n![](https://gitee.com/huihut/interview/raw/master/images/HashTable.png)\n\n### 递归\n\n#### 概念\n\n函数直接或间接地调用自身\n\n#### 递归与分治\n\n* 分治法\n    * 问题的分解\n    * 问题规模的分解\n* 折半查找（递归）\n* 归并排序（递归）\n* 快速排序（递归）\n\n#### 递归与迭代\n\n* 迭代：反复利用变量旧值推出新值\n* 折半查找（迭代）\n* 归并排序（迭代）\n\n#### 广义表\n\n##### 头尾链表存储表示\n\n广义表的头尾链表存储表示和图片\n\n```cpp\n// 广义表的头尾链表存储表示\ntypedef enum {ATOM, LIST} ElemTag;\n// ATOM==0：原子，LIST==1：子表\ntypedef struct GLNode {\n    ElemTag tag;\n    // 公共部分，用于区分原子结点和表结点\n    union {\n        // 原子结点和表结点的联合部分\n        AtomType atom;\n        // atom 是原子结点的值域，AtomType 由用户定义\n        struct {\n            struct GLNode *hp, *tp;\n        } ptr;\n        // ptr 是表结点的指针域，prt.hp 和 ptr.tp 分别指向表头和表尾\n    } a;\n} *GList, GLNode;\n```\n\n![](https://gitee.com/huihut/interview/raw/master/images/GeneralizedList1.png)\n\n##### 扩展线性链表存储表示\n\n扩展线性链表存储表示和图片\n\n```cpp\n// 广义表的扩展线性链表存储表示\ntypedef enum {ATOM, LIST} ElemTag;\n// ATOM==0：原子，LIST==1：子表\ntypedef struct GLNode1 {\n    ElemTag tag;\n    // 公共部分，用于区分原子结点和表结点\n    union {\n        // 原子结点和表结点的联合部分\n        AtomType atom; // 原子结点的值域\n        struct GLNode1 *hp; // 表结点的表头指针\n    } a;\n    struct GLNode1 *tp;\n    // 相当于线性链表的 next，指向下一个元素结点\n} *GList1, GLNode1;\n```\n\n![](https://gitee.com/huihut/interview/raw/master/images/GeneralizedList2.png)\n\n### 二叉树\n\n[BinaryTree.cpp](DataStructure/BinaryTree.cpp)\n\n#### 性质\n\n1. 非空二叉树第 i 层最多 2\u003csup\u003e(i-1)\u003c/sup\u003e 个结点 （i \u003e= 1）\n2. 深度为 k 的二叉树最多 2\u003csup\u003ek\u003c/sup\u003e - 1 个结点 （k \u003e= 1）\n3. 度为 0 的结点数为 n\u003csub\u003e0\u003c/sub\u003e，度为 2 的结点数为 n\u003csub\u003e2\u003c/sub\u003e，则 n\u003csub\u003e0\u003c/sub\u003e = n\u003csub\u003e2\u003c/sub\u003e + 1\n4. 有 n 个结点的完全二叉树深度 k = ⌊ log\u003csub\u003e2\u003c/sub\u003e(n) ⌋ + 1 \n5. 对于含 n 个结点的完全二叉树中编号为 i （1 \u003c= i \u003c= n） 的结点\n    1. 若 i = 1，为根，否则双亲为 ⌊ i / 2 ⌋\n    2. 若 2i \u003e n，则 i 结点没有左孩子，否则孩子编号为 2i\n    3. 若 2i + 1 \u003e n，则 i 结点没有右孩子，否则孩子编号为 2i + 1\n\n#### 存储结构\n\n二叉树数据结构\n\n```cpp\ntypedef struct BiTNode\n{\n    TElemType data;\n    struct BiTNode *lchild, *rchild;\n}BiTNode, *BiTree;\n```\n\n##### 顺序存储\n\n二叉树顺序存储图片\n\n![](https://gitee.com/huihut/interview/raw/master/images/SqBinaryTree.png)\n\n##### 链式存储\n\n二叉树链式存储图片\n\n![](https://gitee.com/huihut/interview/raw/master/images/LinkBinaryTree.png)\n\n#### 遍历方式\n\n* 先序遍历\n* 中序遍历\n* 后续遍历\n* 层次遍历\n\n#### 分类\n\n* 满二叉树\n* 完全二叉树（堆）\n    * 大顶堆：根 \u003e= 左 \u0026\u0026 根 \u003e= 右\n    * 小顶堆：根 \u003c= 左 \u0026\u0026 根 \u003c= 右\n* 二叉查找树（二叉排序树）：左 \u003c 根 \u003c 右\n* 平衡二叉树（AVL树）：| 左子树树高 - 右子树树高 | \u003c= 1\n* 最小失衡树：平衡二叉树插入新结点导致失衡的子树：调整：\n    * LL型：根的左孩子右旋\n    * RR型：根的右孩子左旋\n    * LR型：根的左孩子左旋，再右旋\n    * RL型：右孩子的左子树，先右旋，再左旋\n\n### 其他树及森林\n\n#### 树的存储结构\n\n* 双亲表示法\n* 双亲孩子表示法\n* 孩子兄弟表示法\n\n#### 并查集\n\n一种不相交的子集所构成的集合 S = {S1, S2, ..., Sn}\n\n#### 平衡二叉树（AVL树）\n\n##### 性质\n\n* | 左子树树高 - 右子树树高 | \u003c= 1\n* 平衡二叉树必定是二叉搜索树，反之则不一定\n* 最小二叉平衡树的节点的公式：`F(n)=F(n-1)+F(n-2)+1` （1 是根节点，F(n-1) 是左子树的节点数量，F(n-2) 是右子树的节点数量）\n\n平衡二叉树图片\n\n![](https://gitee.com/huihut/interview/raw/master/images/Self-balancingBinarySearchTree.png)\n\n##### 最小失衡树\n\n平衡二叉树插入新结点导致失衡的子树\n\n调整：\n\n* LL 型：根的左孩子右旋\n* RR 型：根的右孩子左旋\n* LR 型：根的左孩子左旋，再右旋\n* RL 型：右孩子的左子树，先右旋，再左旋\n\n#### 红黑树\n\n[RedBlackTree.cpp](DataStructure/RedBlackTree.cpp)\n\n##### 红黑树的特征是什么？\n\n1. 节点是红色或黑色。\n2. 根是黑色。\n3. 所有叶子都是黑色（叶子是 NIL 节点）。\n4. 每个红色节点必须有两个黑色的子节点。（从每个叶子到根的所有路径上不能有两个连续的红色节点。）（新增节点的父节点必须相同）\n5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。（新增节点必须为红）\n\n##### 调整\n\n1. 变色\n2. 左旋\n3. 右旋\n\n##### 应用\n\n* 关联数组：如 STL 中的 map、set\n\n##### 红黑树、B 树、B+ 树的区别？\n\n* 红黑树的深度比较大，而 B 树和 B+ 树的深度则相对要小一些\n* B+ 树则将数据都保存在叶子节点，同时通过链表的形式将他们连接在一起。\n\n#### B 树（B-tree）、B+ 树（B+-tree）\n\nB 树、B+ 树图片\n\n![B 树（B-tree）、B+ 树（B+-tree）](https://i.stack.imgur.com/l6UyF.png)\n\n##### 特点\n\n* 一般化的二叉查找树（binary search tree）\n* “矮胖”，内部（非叶子）节点可以拥有可变数量的子节点（数量范围预先定义好）\n\n##### 应用\n\n* 大部分文件系统、数据库系统都采用B树、B+树作为索引结构\n\n##### 区别\n\n* B+树中只有叶子节点会带有指向记录的指针（ROWID），而B树则所有节点都带有，在内部节点出现的索引项不会再出现在叶子节点中。\n* B+树中所有叶子节点都是通过指针连接在一起，而B树不会。\n\n##### B树的优点\n\n对于在内部节点的数据，可直接得到，不必根据叶子节点来定位。\n\n##### B+树的优点\n\n* 非叶子节点不会带上 ROWID，这样，一个块中可以容纳更多的索引项，一是可以降低树的高度。二是一个内部节点可以定位更多的叶子节点。\n* 叶子节点之间通过指针来连接，范围扫描将十分简单，而对于B树来说，则需要在叶子节点和内部节点不停的往返移动。\n\n\u003e B 树、B+ 树区别来自：[differences-between-b-trees-and-b-trees](https://stackoverflow.com/questions/870218/differences-between-b-trees-and-b-trees)、[B树和B+树的区别](https://www.cnblogs.com/ivictor/p/5849061.html)\n\n#### 八叉树\n\n八叉树图片\n\n![](https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/Octree2.png/400px-Octree2.png)\n\n八叉树（octree），或称八元树，是一种用于描述三维空间（划分空间）的树状数据结构。八叉树的每个节点表示一个正方体的体积元素，每个节点有八个子节点，这八个子节点所表示的体积元素加在一起就等于父节点的体积。一般中心点作为节点的分叉中心。\n\n##### 用途\n\n* 三维计算机图形\n* 最邻近搜索\n\n\u003ca id=\"algorithm\"\u003e\u003c/a\u003e\n\n## ⚡️ 算法\n\n### 排序\n\n排序算法 | 平均时间复杂度 | 最差时间复杂度 | 空间复杂度 | 数据对象稳定性\n---|---|---|---|---\n[冒泡排序](Algorithm/BubbleSort.h) | O(n\u003csup\u003e2\u003c/sup\u003e)|O(n\u003csup\u003e2\u003c/sup\u003e)|O(1)|稳定\n[选择排序](Algorithm/SelectionSort.h) | O(n\u003csup\u003e2\u003c/sup\u003e)|O(n\u003csup\u003e2\u003c/sup\u003e)|O(1)|数组不稳定、链表稳定\n[插入排序](Algorithm/InsertSort.h) | O(n\u003csup\u003e2\u003c/sup\u003e)|O(n\u003csup\u003e2\u003c/sup\u003e)|O(1)|稳定\n[快速排序](Algorithm/QuickSort.h) | O(n*log\u003csub\u003e2\u003c/sub\u003en) |  O(n\u003csup\u003e2\u003c/sup\u003e) | O(log\u003csub\u003e2\u003c/sub\u003en) | 不稳定\n[堆排序](Algorithm/HeapSort.cpp) | O(n*log\u003csub\u003e2\u003c/sub\u003en)|O(n*log\u003csub\u003e2\u003c/sub\u003en)|O(1)|不稳定\n[归并排序](Algorithm/MergeSort.h) | O(n*log\u003csub\u003e2\u003c/sub\u003en) | O(n*log\u003csub\u003e2\u003c/sub\u003en)|O(n)|稳定\n[希尔排序](Algorithm/ShellSort.h) | O(n*log\u003csup\u003e2\u003c/sup\u003en)|O(n\u003csup\u003e2\u003c/sup\u003e)|O(1)|不稳定\n[计数排序](Algorithm/CountSort.cpp) | O(n+m)|O(n+m)|O(n+m)|稳定\n[桶排序](Algorithm/BucketSort.cpp) | O(n)|O(n)|O(m)|稳定\n[基数排序](Algorithm/RadixSort.h) | O(k*n)|O(n\u003csup\u003e2\u003c/sup\u003e)| |稳定\n\n\u003e * 均按从小到大排列\n\u003e * k：代表数值中的 “数位” 个数\n\u003e * n：代表数据规模\n\u003e * m：代表数据的最大值减最小值\n\u003e * 来自：[wikipedia . 排序算法](https://zh.wikipedia.org/wiki/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95)\n\n### 查找\n\n查找算法 | 平均时间复杂度 | 空间复杂度 | 查找条件\n---|---|---|---\n[顺序查找](Algorithm/SequentialSearch.h) | O(n) | O(1) | 无序或有序\n[二分查找（折半查找）](Algorithm/BinarySearch.h) | O(log\u003csub\u003e2\u003c/sub\u003en)| O(1) | 有序\n[插值查找](Algorithm/InsertionSearch.h) | O(log\u003csub\u003e2\u003c/sub\u003e(log\u003csub\u003e2\u003c/sub\u003en)) | O(1) | 有序\n[斐波那契查找](Algorithm/FibonacciSearch.cpp) | O(log\u003csub\u003e2\u003c/sub\u003en) | O(1) | 有序\n[哈希查找](DataStructure/HashTable.cpp) | O(1) | O(n) | 无序或有序\n[二叉查找树（二叉搜索树查找）](Algorithm/BSTSearch.h) |O(log\u003csub\u003e2\u003c/sub\u003en) |   | \n[红黑树](DataStructure/RedBlackTree.cpp) |O(log\u003csub\u003e2\u003c/sub\u003en) | |\n2-3树 | O(log\u003csub\u003e2\u003c/sub\u003en - log\u003csub\u003e3\u003c/sub\u003en) |   | \nB树/B+树 |O(log\u003csub\u003e2\u003c/sub\u003en) |   | \n\n### 图搜索算法\n\n图搜索算法 |数据结构| 遍历时间复杂度 | 空间复杂度\n---|---|---|---\n[BFS广度优先搜索](https://zh.wikipedia.org/wiki/%E5%B9%BF%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2)|邻接矩阵\u003cbr/\u003e邻接链表|O(\\|v\\|\u003csup\u003e2\u003c/sup\u003e)\u003cbr/\u003eO(\\|v\\|+\\|E\\|)|O(\\|v\\|\u003csup\u003e2\u003c/sup\u003e)\u003cbr/\u003eO(\\|v\\|+\\|E\\|)\n[DFS深度优先搜索](https://zh.wikipedia.org/wiki/%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2)|邻接矩阵\u003cbr/\u003e邻接链表|O(\\|v\\|\u003csup\u003e2\u003c/sup\u003e)\u003cbr/\u003eO(\\|v\\|+\\|E\\|)|O(\\|v\\|\u003csup\u003e2\u003c/sup\u003e)\u003cbr/\u003eO(\\|v\\|+\\|E\\|)\n\n### 其他算法\n\n算法 |思想| 应用\n---|---|---\n[分治法](https://zh.wikipedia.org/wiki/%E5%88%86%E6%B2%BB%E6%B3%95)|把一个复杂的问题分成两个或更多的相同或相似的子问题，直到最后子问题可以简单的直接求解，原问题的解即子问题的解的合并|[循环赛日程安排问题](https://github.com/huihut/interview/tree/master/Problems/RoundRobinProblem)、排序算法（快速排序、归并排序）\n[动态规划](https://zh.wikipedia.org/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92)|通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法，适用于有重叠子问题和最优子结构性质的问题|[背包问题](https://github.com/huihut/interview/tree/master/Problems/KnapsackProblem)、斐波那契数列\n[贪心法](https://zh.wikipedia.org/wiki/%E8%B4%AA%E5%BF%83%E6%B3%95)|一种在每一步选择中都采取在当前状态下最好或最优（即最有利）的选择，从而希望导致结果是最好或最优的算法|旅行推销员问题（最短路径问题）、最小生成树、哈夫曼编码\n\n\u003ca id=\"problems\"\u003e\u003c/a\u003e\n\n## ❓ Problems\n\n### Single Problem\n\n* [Chessboard Coverage Problem（棋盘覆盖问题）](Problems/ChessboardCoverageProblem)\n* [Knapsack Problem（背包问题）](Problems/KnapsackProblem)\n* [Neumann Neighbor Problem（冯诺依曼邻居问题）](Problems/NeumannNeighborProblem)\n* [Round Robin Problem（循环赛日程安排问题）](Problems/RoundRobinProblem)\n* [Tubing Problem（输油管道问题）](Problems/TubingProblem)\n\n### Leetcode Problems\n\n* [Github . haoel/leetcode](https://github.com/haoel/leetcode)\n* [Github . pezy/LeetCode](https://github.com/pezy/LeetCode)\n\n### 剑指 Offer\n\n* [Github . zhedahht/CodingInterviewChinese2](https://github.com/zhedahht/CodingInterviewChinese2)\n* [Github . gatieme/CodingInterviews](https://github.com/gatieme/CodingInterviews)\n\n### Cracking the Coding Interview 程序员面试金典\n\n* [Github . careercup/ctci](https://github.com/careercup/ctci)\n* [牛客网 . 程序员面试金典](https://www.nowcoder.com/ta/cracking-the-coding-interview)\n\n### 牛客网\n\n* [牛客网 . 在线编程专题](https://www.nowcoder.com/activity/oj)\n\n\u003ca id=\"os\"\u003e\u003c/a\u003e\n\n## 💻 操作系统\n\n### 进程与线程\n\n对于有线程系统：\n* 进程是资源分配的独立单位\n* 线程是资源调度的独立单位\n\n对于无线程系统：\n* 进程是资源调度、分配的独立单位\n\n#### 进程之间的通信方式以及优缺点\n\n* 管道（PIPE）\n    * 有名管道：一种半双工的通信方式，它允许无亲缘关系进程间的通信\n        * 优点：可以实现任意关系的进程间的通信\n        * 缺点：\n            1. 长期存于系统中，使用不当容易出错\n            2. 缓冲区有限\n    * 无名管道：一种半双工的通信方式，只能在具有亲缘关系的进程间使用（父子进程）\n        * 优点：简单方便\n        * 缺点：\n            1. 局限于单向通信 \n            2. 只能创建在它的进程以及其有亲缘关系的进程之间\n            3. 缓冲区有限\n* 信号量（Semaphore）：一个计数器，可以用来控制多个线程对共享资源的访问\n    * 优点：可以同步进程\n    * 缺点：信号量有限\n* 信号（Signal）：一种比较复杂的通信方式，用于通知接收进程某个事件已经发生\n* 消息队列（Message Queue）：是消息的链表，存放在内核中并由消息队列标识符标识\n    * 优点：可以实现任意进程间的通信，并通过系统调用函数来实现消息发送和接收之间的同步，无需考虑同步问题，方便\n    * 缺点：信息的复制需要额外消耗 CPU 的时间，不适宜于信息量大或操作频繁的场合\n* 共享内存（Shared Memory）：映射一段能被其他进程所访问的内存，这段共享内存由一个进程创建，但多个进程都可以访问\n    * 优点：无须复制，快捷，信息量大\n    * 缺点：\n        1. 通信是通过将共享空间缓冲区直接附加到进程的虚拟地址空间中来实现的，因此进程间的读写操作的同步问题\n        2. 利用内存缓冲区直接交换信息，内存的实体存在于计算机中，只能同一个计算机系统中的诸多进程共享，不方便网络通信\n* 套接字（Socket）：可用于不同计算机间的进程通信\n    * 优点：\n        1. 传输数据为字节级，传输数据可自定义，数据量小效率高\n        2. 传输数据时间短，性能高\n        3. 适合于客户端和服务器端之间信息实时交互\n        4. 可以加密,数据安全性强\n    * 缺点：需对传输的数据进行解析，转化成应用级的数据。\n\n#### 线程之间的通信方式\n\n* 锁机制：包括互斥锁/量（mutex）、读写锁（reader-writer lock）、自旋锁（spin lock）、条件变量（condition）\n    * 互斥锁/量（mutex）：提供了以排他方式防止数据结构被并发修改的方法。\n    * 读写锁（reader-writer lock）：允许多个线程同时读共享数据，而对写操作是互斥的。\n    * 自旋锁（spin lock）与互斥锁类似，都是为了保护共享资源。互斥锁是当资源被占用，申请者进入睡眠状态；而自旋锁则循环检测保持者是否已经释放锁。\n    * 条件变量（condition）：可以以原子的方式阻塞进程，直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。\n* 信号量机制(Semaphore)\n    * 无名线程信号量\n    * 命名线程信号量\n* 信号机制(Signal)：类似进程间的信号处理\n* 屏障（barrier）：屏障允许每个线程等待，直到所有的合作线程都达到某一点，然后从该点继续执行。\n\n线程间的通信目的主要是用于线程同步，所以线程没有像进程通信中的用于数据交换的通信机制  \n\n\u003e 进程之间的通信方式以及优缺点来源于：[进程线程面试题总结](http://blog.csdn.net/wujiafei_njgcxy/article/details/77098977)\n\n#### 进程之间私有和共享的资源\n\n* 私有：地址空间、堆、全局变量、栈、寄存器\n* 共享：代码段，公共数据，进程目录，进程 ID\n\n#### 线程之间私有和共享的资源\n\n* 私有：线程栈，寄存器，程序计数器\n* 共享：堆，地址空间，全局变量，静态变量\n\n#### 多进程与多线程间的对比、优劣与选择\n\n##### 对比\n\n对比维度 | 多进程 | 多线程 | 总结\n---|---|---|---\n数据共享、同步|数据共享复杂，需要用 IPC；数据是分开的，同步简单|因为共享进程数据，数据共享简单，但也是因为这个原因导致同步复杂|各有优势\n内存、CPU|占用内存多，切换复杂，CPU 利用率低|占用内存少，切换简单，CPU 利用率高|线程占优\n创建销毁、切换|创建销毁、切换复杂，速度慢|创建销毁、切换简单，速度很快|线程占优\n编程、调试|编程简单，调试简单|编程复杂，调试复杂|进程占优\n可靠性|进程间不会互相影响|一个线程挂掉将导致整个进程挂掉|进程占优\n分布式|适应于多核、多机分布式；如果一台机器不够，扩展到多台机器比较简单|适应于多核分布式|进程占优\n\n##### 优劣\n\n优劣|多进程|多线程\n---|---|---\n优点|编程、调试简单，可靠性较高|创建、销毁、切换速度快，内存、资源占用小\n缺点|创建、销毁、切换速度慢，内存、资源占用大|编程、调试复杂，可靠性较差\n\n##### 选择\n\n* 需要频繁创建销毁的优先用线程\n* 需要进行大量计算的优先使用线程\n* 强相关的处理用线程，弱相关的处理用进程\n* 可能要扩展到多机分布的用进程，多核分布的用线程\n* 都满足需求的情况下，用你最熟悉、最拿手的方式\n\n\u003e 多进程与多线程间的对比、优劣与选择来自：[多线程还是多进程的选择及区别](https://blog.csdn.net/lishenglong666/article/details/8557215)\n\n### Linux 内核的同步方式\n\n#### 原因\n\n在现代操作系统里，同一时间可能有多个内核执行流在执行，因此内核其实像多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问。尤其是在多处理器系统上，更需要一些同步机制来同步不同处理器上的执行单元对共享的数据的访问。\n\n#### 同步方式\n\n* 原子操作\n* 信号量（semaphore）\n* 读写信号量（rw_semaphore）\n* 自旋锁（spinlock）\n* 大内核锁（BKL，Big Kernel Lock）\n* 读写锁（rwlock）\n* 大读者锁（brlock-Big Reader Lock）\n* 读-拷贝修改(RCU，Read-Copy Update)\n* 顺序锁（seqlock）\n\n\u003e 来自：[Linux 内核的同步机制，第 1 部分](https://www.ibm.com/developerworks/cn/linux/l-synch/part1/)、[Linux 内核的同步机制，第 2 部分](https://www.ibm.com/developerworks/cn/linux/l-synch/part2/)\n\n### 死锁\n\n#### 原因\n\n* 系统资源不足\n* 资源分配不当\n* 进程运行推进顺序不合适\n\n#### 产生条件\n\n* 互斥\n* 请求和保持\n* 不剥夺\n* 环路\n\n#### 预防\n\n* 打破互斥条件：改造独占性资源为虚拟资源，大部分资源已无法改造。\n* 打破不可抢占条件：当一进程占有一独占性资源后又申请一独占性资源而无法满足，则退出原占有的资源。\n* 打破占有且申请条件：采用资源预先分配策略，即进程运行前申请全部资源，满足则运行，不然就等待，这样就不会占有且申请。\n* 打破循环等待条件：实现资源有序分配策略，对所有设备实现分类编号，所有进程只能采用按序号递增的形式申请资源。\n* 有序资源分配法\n* 银行家算法\n\n### 文件系统\n\n* Windows：FCB 表 + FAT + 位图\n* Unix：inode + 混合索引 + 成组链接\n\n### 主机字节序与网络字节序\n\n#### 主机字节序（CPU 字节序）\n\n##### 概念\n\n主机字节序又叫 CPU 字节序，其不是由操作系统决定的，而是由 CPU 指令集架构决定的。主机字节序分为两种：\n\n* 大端字节序（Big Endian）：高序字节存储在低位地址，低序字节存储在高位地址\n* 小端字节序（Little Endian）：高序字节存储在高位地址，低序字节存储在低位地址\n\n##### 存储方式\n\n32 位整数 `0x12345678` 是从起始位置为 `0x00` 的地址开始存放，则：\n\n内存地址 | 0x00 | 0x01 | 0x02 | 0x03\n---|---|---|---|---\n大端|12|34|56|78\n小端|78|56|34|12\n\n大端小端图片\n\n![大端序](https://gitee.com/huihut/interview/raw/master/images/CPU-Big-Endian.svg.png)\n![小端序](https://gitee.com/huihut/interview/raw/master/images/CPU-Little-Endian.svg.png)\n\n##### 判断大端小端\n\n判断大端小端\n\n可以这样判断自己 CPU 字节序是大端还是小端：\n\n```cpp\n#include \u003ciostream\u003e\nusing namespace std;\n\nint main()\n{\n\tint i = 0x12345678;\n\n\tif (*((char*)\u0026i) == 0x12)\n\t\tcout \u003c\u003c \"大端\" \u003c\u003c endl;\n\telse\t\n\t\tcout \u003c\u003c \"小端\" \u003c\u003c endl;\n\n\treturn 0;\n}\n```\n\n##### 各架构处理器的字节序\n\n* x86（Intel、AMD）、MOS Technology 6502、Z80、VAX、PDP-11 等处理器为小端序；\n* Motorola 6800、Motorola 68000、PowerPC 970、System/370、SPARC（除 V9 外）等处理器为大端序；\n* ARM（默认小端序）、PowerPC（除 PowerPC 970 外）、DEC Alpha、SPARC V9、MIPS、PA-RISC 及 IA64 的字节序是可配置的。\n\n#### 网络字节序\n\n网络字节顺序是 TCP/IP 中规定好的一种数据表示格式，它与具体的 CPU 类型、操作系统等无关，从而可以保证数据在不同主机之间传输时能够被正确解释。\n\n网络字节顺序采用：大端（Big Endian）排列方式。\n\n### 页面置换算法\n\n在地址映射过程中，若在页面中发现所要访问的页面不在内存中，则产生缺页中断。当发生缺页中断时，如果操作系统内存中没有空闲页面，则操作系统必须在内存选择一个页面将其移出内存，以便为即将调入的页面让出空间。而用来选择淘汰哪一页的规则叫做页面置换算法。\n\n#### 分类\n\n* 全局置换：在整个内存空间置换\n* 局部置换：在本进程中进行置换\n\n#### 算法\n\n全局：\n* 工作集算法\n* 缺页率置换算法\n\n局部：\n* 最佳置换算法（OPT）\n* 先进先出置换算法（FIFO）\n* 最近最久未使用（LRU）算法\n* 时钟（Clock）置换算法\n\n\u003ca id=\"computer-network\"\u003e\u003c/a\u003e\n\n## ☁️ 计算机网络\n\n\u003e 本节部分知识点来自《计算机网络（第 7 版）》\n\n计算机网络体系结构：\n\n![计算机网络体系结构](https://gitee.com/huihut/interview/raw/master/images/计算机网络体系结构.png)\n\n### 各层作用及协议\n\n分层 | 作用 | 协议\n---|---|---\n物理层 | 通过媒介传输比特，确定机械及电气规范（比特 Bit） | RJ45、CLOCK、IEEE802.3（中继器，集线器）\n数据链路层|将比特组装成帧和点到点的传递（帧 Frame）| PPP、FR、HDLC、VLAN、MAC（网桥，交换机）\n网络层|负责数据包从源到宿的传递和网际互连（包 Packet）|IP、ICMP、ARP、RARP、OSPF、IPX、RIP、IGRP（路由器）\n运输层|提供端到端的可靠报文传递和错误恢复（ 段Segment）|TCP、UDP、SPX\n会话层|建立、管理和终止会话（会话协议数据单元 SPDU）|NFS、SQL、NETBIOS、RPC\n表示层|对数据进行翻译、加密和压缩（表示协议数据单元 PPDU）|JPEG、MPEG、ASII\n应用层|允许访问OSI环境的手段（应用协议数据单元 APDU）|FTP、DNS、Telnet、SMTP、HTTP、WWW、NFS\n\n\n### 物理层\n\n* 传输数据的单位：比特\n* 数据传输系统：源系统（源点、发送器） --\u003e 传输系统 --\u003e 目的系统（接收器、终点）\n\n通道：\n* 单向通道（单工通道）：只有一个方向通信，没有反方向交互，如广播\n* 双向交替通信（半双工通信）：通信双方都可发消息，但不能同时发送或接收\n* 双向同时通信（全双工通信）：通信双方可以同时发送和接收信息\n\n通道复用技术：\n* 频分复用（FDM，Frequency Division Multiplexing）：不同用户在不同频带，所用用户在同样时间占用不同带宽资源\n* 时分复用（TDM，Time Division Multiplexing）：不同用户在同一时间段的不同时间片，所有用户在不同时间占用同样的频带宽度\n* 波分复用（WDM，Wavelength Division Multiplexing）：光的频分复用\n* 码分复用（CDM，Code Division Multiplexing）：不同用户使用不同的码，可以在同样时间使用同样频带通信\n\n### 数据链路层\n\n主要信道：\n* 点对点信道\n* 广播信道\n\n#### 点对点信道\n\n* 数据单元：帧\n\n三个基本问题：\n* 封装成帧：把网络层的 IP 数据报封装成帧，`SOH - 数据部分 - EOT`\n* 透明传输：不管数据部分什么字符，都能传输出去；可以通过字节填充方法解决（冲突字符前加转义字符）\n* 差错检测：降低误码率（BER，Bit Error Rate），广泛使用循环冗余检测（CRC，Cyclic Redundancy Check）\n\n点对点协议（Point-to-Point Protocol）：\n* 点对点协议（Point-to-Point Protocol）：用户计算机和 ISP 通信时所使用的协议\n\n#### 广播信道\n\n广播通信：\n* 硬件地址（物理地址、MAC 地址）\n* 单播（unicast）帧（一对一）：收到的帧的 MAC 地址与本站的硬件地址相同\n* 广播（broadcast）帧（一对全体）：发送给本局域网上所有站点的帧\n* 多播（multicast）帧（一对多）：发送给本局域网上一部分站点的帧\n\n### 网络层\n\n* IP（Internet Protocol，网际协议）是为计算机网络相互连接进行通信而设计的协议。\n* ARP（Address Resolution Protocol，地址解析协议）\n* ICMP（Internet Control Message Protocol，网际控制报文协议）\n* IGMP（Internet Group Management Protocol，网际组管理协议）\n\n#### IP 网际协议\n\nIP 地址分类：\n* `IP 地址 ::= {\u003c网络号\u003e,\u003c主机号\u003e}`\n\nIP 地址类别 | 网络号 | 网络范围 | 主机号 | IP 地址范围\n---|---|---|---|---\nA 类 | 8bit，第一位固定为 0 | 0 —— 127 | 24bit | 1.0.0.0 —— 127.255.255.255\nB 类 | 16bit，前两位固定为  10 | 128.0 —— 191.255 | 16bit | 128.0.0.0 —— 191.255.255.255\nC  类 | 24bit，前三位固定为  110 | 192.0.0 —— 223.255.255 | 8bit | 192.0.0.0 —— 223.255.255.255\nD  类 | 前四位固定为 1110，后面为多播地址\nE  类 | 前五位固定为 11110，后面保留为今后所用\n\nIP 数据报格式：\n\n![IP 数据报格式](https://gitee.com/huihut/interview/raw/master/images/IP数据报格式.png)\n\n#### ICMP 网际控制报文协议\n\nICMP 报文格式：\n\n![ICMP 报文格式](https://gitee.com/huihut/interview/raw/master/images/ICMP报文格式.png)\n\n应用：\n* PING（Packet InterNet Groper，分组网间探测）测试两个主机之间的连通性\n* TTL（Time To Live，生存时间）该字段指定 IP 包被路由器丢弃之前允许通过的最大网段数量\n\n#### 内部网关协议\n\n* RIP（Routing Information Protocol，路由信息协议）\n* OSPF（Open Sortest Path First，开放最短路径优先）\n\n#### 外部网关协议\n\n* BGP（Border Gateway Protocol，边界网关协议）\n\n#### IP多播\n\n* IGMP（Internet Group Management Protocol，网际组管理协议）\n* 多播路由选择协议\n\n#### VPN 和 NAT\n\n* VPN（Virtual Private Network，虚拟专用网）\n* NAT（Network Address Translation，网络地址转换）\n\n#### 路由表包含什么？\n\n1. 网络 ID（Network ID, Network number）：就是目标地址的网络 ID。\n2. 子网掩码（subnet mask）：用来判断 IP 所属网络\n3. 下一跳地址/接口（Next hop / interface）：就是数据在发送到目标地址的旅途中下一站的地址。其中 interface 指向 next hop（即为下一个 route）。一个自治系统（AS, Autonomous system）中的 route 应该包含区域内所有的子网络，而默认网关（Network id: `0.0.0.0`, Netmask: `0.0.0.0`）指向自治系统的出口。\n\n根据应用和执行的不同，路由表可能含有如下附加信息：\n\n1. 花费（Cost）：就是数据发送过程中通过路径所需要的花费。\n2. 路由的服务质量\n3. 路由中需要过滤的出/入连接列表\n\n### 运输层\n\n协议：\n\n* TCP（Transmission Control Protocol，传输控制协议）\n* UDP（User Datagram Protocol，用户数据报协议）\n\n端口：\n\n应用程序 | FTP | TELNET | SMTP | DNS | TFTP | HTTP | HTTPS | SNMP  \n--- | --- | --- |--- |--- |--- |--- |--- |---   \n端口号 | 21 | 23 | 25 | 53 | 69 | 80 | 443 | 161  \n\n#### TCP\n\n* TCP（Transmission Control Protocol，传输控制协议）是一种面向连接的、可靠的、基于字节流的传输层通信协议，其传输的单位是报文段。\n\n特征：\n* 面向连接\n* 只能点对点（一对一）通信\n* 可靠交互\n* 全双工通信\n* 面向字节流\n\nTCP 如何保证可靠传输：\n* 确认和超时重传\n* 数据合理分片和排序\n* 流量控制\n* 拥塞控制\n* 数据校验\n\nTCP 报文结构\n\n![TCP 报文](https://gitee.com/huihut/interview/raw/master/images/TCP报文.png)\n\nTCP 首部\n\n![TCP 首部](https://gitee.com/huihut/interview/raw/master/images/TCP首部.png)\n\nTCP：状态控制码（Code，Control Flag），占 6 比特，含义如下：\n* URG：紧急比特（urgent），当 `URG＝1` 时，表明紧急指针字段有效，代表该封包为紧急封包。它告诉系统此报文段中有紧急数据，应尽快传送(相当于高优先级的数据)， 且上图中的 Urgent Pointer 字段也会被启用。\n* ACK：确认比特（Acknowledge）。只有当 `ACK＝1` 时确认号字段才有效，代表这个封包为确认封包。当 `ACK＝0` 时，确认号无效。\n* PSH：（Push function）若为 1 时，代表要求对方立即传送缓冲区内的其他对应封包，而无需等缓冲满了才送。\n* RST：复位比特(Reset)，当 `RST＝1` 时，表明 TCP 连接中出现严重差错（如由于主机崩溃或其他原因），必须释放连接，然后再重新建立运输连接。\n* SYN：同步比特(Synchronous)，SYN 置为 1，就表示这是一个连接请求或连接接受报文，通常带有 SYN 标志的封包表示『主动』要连接到对方的意思。\n* FIN：终止比特(Final)，用来释放一个连接。当 `FIN＝1` 时，表明此报文段的发送端的数据已发送完毕，并要求释放运输连接。\n\n#### UDP\n\n* UDP（User Datagram Protocol，用户数据报协议）是 OSI（Open System Interconnection 开放式系统互联） 参考模型中一种无连接的传输层协议，提供面向事务的简单不可靠信息传送服务，其传输的单位是用户数据报。\n\n特征：\n* 无连接\n* 尽最大努力交付\n* 面向报文\n* 没有拥塞控制\n* 支持一对一、一对多、多对一、多对多的交互通信\n* 首部开销小\n\nUDP 报文结构\n\n![UDP 报文](https://gitee.com/huihut/interview/raw/master/images/UDP报文.png)\n\nUDP 首部\n\n![UDP 首部](https://gitee.com/huihut/interview/raw/master/images/UDP首部.png)\n\n\u003e TCP/UDP 图片来源于：\u003chttps://github.com/JerryC8080/understand-tcp-udp\u003e\n\n#### TCP 与 UDP 的区别\n\n1. TCP 面向连接，UDP 是无连接的；\n2. TCP 提供可靠的服务，也就是说，通过 TCP 连接传送的数据，无差错，不丢失，不重复，且按序到达；UDP 尽最大努力交付，即不保证可靠交付\n3. TCP 的逻辑通信信道是全双工的可靠信道；UDP 则是不可靠信道\n5. 每一条 TCP 连接只能是点到点的；UDP 支持一对一，一对多，多对一和多对多的交互通信\n6. TCP 面向字节流（可能出现黏包问题），实际上是 TCP 把数据看成一连串无结构的字节流；UDP 是面向报文的（不会出现黏包问题）\n7. UDP 没有拥塞控制，因此网络出现拥塞不会使源主机的发送速率降低（对实时应用很有用，如 IP 电话，实时视频会议等）\n8. TCP 首部开销20字节；UDP 的首部开销小，只有 8 个字节\n\n#### TCP 黏包问题\n\n##### 原因\n\nTCP 是一个基于字节流的传输服务（UDP 基于报文的），“流” 意味着 TCP 所传输的数据是没有边界的。所以可能会出现两个数据包黏在一起的情况。\n\n##### 解决\n\n* 发送定长包。如果每个消息的大小都是一样的，那么在接收对等方只要累计接收数据，直到数据等于一个定长的数值就将它作为一个消息。\n* 包头加上包体长度。包头是定长的 4 个字节，说明了包体的长度。接收对等方先接收包头长度，依据包头长度来接收包体。\n* 在数据包之间设置边界�","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhuihut%2Finterview","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhuihut%2Finterview","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhuihut%2Finterview/lists"}