<?xml version="1.0" encoding="utf-8"?>
<search> 
  
  
    
    <entry>
      <title>对于 C++ 这门语言的一点思考</title>
      <link href="/posts/dui-yu-c-zhe-men-yu-yan-de-yi-dian-si-kao/"/>
      <url>/posts/dui-yu-c-zhe-men-yu-yan-de-yi-dian-si-kao/</url>
      
        <content type="html"><![CDATA[<blockquote><p>注：本文部分内容可能引起争议，如有错误，敬请指正</p></blockquote><p>第一次正儿八经的学习 C++ 是在 20 年的 3 月，现在已经是 22 年的 12月了，差不多快三年的时间，中间来回读了几遍的 <em>C++ Primer</em> 及<em>Effective C++</em>，Leetcode 上用 C++ 写的题目也有 500多道，回想过去的时光，不敢说是精通 C++，但也算是有所熟悉，对于 C++这门语言也有不少牢骚，干脆就记录一下</p><p>我帮各位回顾一下 C++ 的发展历程</p><blockquote><p>C++ 是由 Bjarne Stroustrup 于 1979年在贝尔实验室开发的一种编程语言。当时，Stroustrup 正在研究一种名为Simula 67 的面向对象编程语言。他发现 Simula 67虽然优秀，但由于它的运行速度较慢，并不适合于开发大型系统。因此，他开始着手设计一种新的编程语言，该语言能够兼具Simula 67 的面向对象特性和 C 语言的运行速度。<br />C++ 的最初版本被称为 “C with Classes”。它于 1983年正式推出，并在接下来的几年里经历了许多修改和更新。1985 年，C++正式更名为 C++，并于 1989 年推出了最终版本——C++ 2.0。<br />C++在推出后迅速流行起来，并成为许多大型系统的首选开发语言。它的优秀性能和灵活的语法使它成为许多领域的标准编程语言，例如游戏开发、操作系统开发、网络编程等。<br />随着时间的推移，C++ 也不断演进和发展。例如，1998 年推出了 C++98标准，2003 年推出了 C++03 标准，2011 年推出了 C++11标准，并在接下来的几年中又推出了 C++14 和 C++17 等</p></blockquote><p>详细可见 <ahref="https://zh.m.wikipedia.org/zh-sg/C%2B%2B">wiki</a></p><p>可以说已经有了 40 多年的历史了，同一个历史舞台的很多语言都没落了，但C++仍然流行，在浏览器，游戏，操作系统等对性能要求较高的领域，都能看见它的身影，这当然要得益于其与时俱进的标准，能够吸收很多优秀的特性，如：模板，异常处理，自动类型推导，还引入了方便的STL 库，可谓是算法党的福音</p><p>C++ 又可以认为是 C 语言的超集，除了某些方面，如：</p><blockquote><p>最常见的差异之一是，C 允许从 void* 隐式转换到其它的指标类型，但 C++不允许。</p></blockquote><p>C++ 的效率上差约 5% 多一些</p><p>但是 40 多年的历史新特性不断增增改改，再加上其继承于 C 语言又拥有 C语言的很多特性，在拥有很强兼容性的同时，又让 C++变成了一个<strong>怪物语言</strong></p><p>参差不齐的输出方式</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">// C</span><span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"Hello World!\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">puts</span><span class="token punctuation">(</span><span class="token string">"Hello World!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// C++</span>cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Hello World!\n"</span><span class="token punctuation">;</span>cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Hello World!"</span> <span class="token operator">&lt;&lt;</span> endl<span class="token punctuation">;</span> <span class="token comment">// 清空缓冲区</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>C++ 拥有复杂的输出方式，甚至不同的输出方式混用也会出现问题熟悉算法竞赛的一定知道<code>ios_base::sync_with_stdio(false); cin.tie(NULL);</code></p><p>第一个语句将 stdio 与 iostream 的缓冲区同步设置为关，默认是开的</p><p>如果为开，C 风格的 IO 和 C++ 风格的 IO 就可以一起用了，但是缺点在与C++ 风格的 IO 会变得慢</p><p>所以我们设置为 <code>false</code>，这样能够加快 C++ 风格 IO的速度</p><p>缺点在于混用 stdio 和 iostream IO的先后顺序没有同步缓冲区的保护，可能出现异常</p><p>所以如果你只使用 C++ 的 IO，建议使用<code>ios_base::sync_with_stdio(false);</code> 来加快程序运行的速度</p><p><code>cin.tie(NULL);</code> 则将 cin 与 cout 解耦，通常 cin前都要刷新缓冲区，即 <code>cout &lt;&lt; flush;</code>，导致效率很低</p><p>当然解耦后，如</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">cout <span class="token operator">&lt;&lt;</span> <span class="token string">"input your name: "</span><span class="token punctuation">;</span>string name<span class="token punctuation">;</span>cin <span class="token operator">>></span> name<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>在屏幕上可能不会正常工作，可能先输入才有输出。</p><p>不过在算法竞赛中，检验结果看的是 stdout 这个文件描述符</p><!-- 输入方面，我们一般使用 `cin` 而不是 `scanf`，因为后者需要格式化输入字符串，很麻烦 --><!-- 输出方面，使用 `printf` 而不是 `cout`，后者不可以格式化字符串很麻烦 --><p>说起 IO，就不得不吐槽下 C++ 反人类的 cout，格式化字符串直到 C++20才得到解决，但这个标准还不知道要多少年才能成为主流</p><p>e.g.</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> a<span class="token punctuation">,</span> b<span class="token punctuation">;</span><span class="token function">scanf</span><span class="token punctuation">(</span><span class="token string">"%d %d"</span><span class="token punctuation">,</span> <span class="token operator">%</span>a<span class="token punctuation">,</span> <span class="token operator">%</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// hard</span>cin <span class="token operator">>></span> a <span class="token operator">>></span> b<span class="token punctuation">;</span> <span class="token comment">// easy</span><span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"result: %d+%d=%d\n"</span><span class="token punctuation">,</span> a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> a<span class="token operator">+</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// easy</span>cout <span class="token operator">&lt;&lt;</span> <span class="token string">"result: "</span> <span class="token operator">&lt;&lt;</span> a <span class="token operator">&lt;&lt;</span> <span class="token string">"+"</span> <span class="token operator">&lt;&lt;</span> b <span class="token operator">&lt;&lt;</span> <span class="token string">"="</span> <span class="token operator">&lt;&lt;</span> a<span class="token operator">+</span>b <span class="token operator">&lt;&lt;</span> <span class="token string">"\n"</span><span class="token punctuation">;</span> <span class="token comment">// hard</span>cout <span class="token operator">&lt;&lt;</span> <span class="token function">format</span><span class="token punctuation">(</span><span class="token string">"result: &#123;&#125;+&#123;&#125;=&#123;&#125;\n"</span><span class="token punctuation">,</span> a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> a<span class="token operator">+</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>即便有了 format，相比较其他语言依然不够方便</p><pre class="line-numbers language-python" data-language="python"><code class="language-python">a<span class="token punctuation">,</span> b<span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f'result: </span><span class="token interpolation"><span class="token punctuation">&#123;</span>a<span class="token punctuation">&#125;</span></span><span class="token string">+</span><span class="token interpolation"><span class="token punctuation">&#123;</span>b<span class="token punctuation">&#125;</span></span><span class="token string">=</span><span class="token interpolation"><span class="token punctuation">&#123;</span>a<span class="token operator">+</span>b<span class="token punctuation">&#125;</span></span><span class="token string">'</span></span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><pre class="line-numbers language-swift" data-language="swift"><code class="language-swift"><span class="token keyword">let</span> a <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">,</span> b <span class="token operator">=</span> <span class="token number">5</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"result: </span><span class="token interpolation-punctuation punctuation">\(</span><span class="token interpolation">a</span><span class="token interpolation-punctuation punctuation">)</span><span class="token string">+</span><span class="token interpolation-punctuation punctuation">\(</span><span class="token interpolation">b</span><span class="token interpolation-punctuation punctuation">)</span><span class="token string">=</span><span class="token interpolation-punctuation punctuation">\(</span><span class="token interpolation">a<span class="token operator">+</span>b</span><span class="token interpolation-punctuation punctuation">)</span><span class="token string">"</span></span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>在其他设计方面，如非要在每句结尾加<code>;</code>，本来是设计上就可以避免的事</p><p>C++还有很多问题是由于太自由引起的，它给予程序员的自由度太高了，不同水准的程序员写出的代码质量天差地别，而其他语言，如Java 就没有这种问题</p><p>最典型的例子，莫过于指针，C++把动态内存分配的权利交给了程序员，这让我们能够更好的利用内存，但给程序员带来了很大的挑战，一不小心就会导致程序崩溃，而在指针中我们总会遇到各种各样的问题</p><p>以下的各种指针你能认识哪些</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token operator">*</span>p1<span class="token punctuation">;</span> <span class="token comment">// 是一个指向整型的指针。它可以指向任何整型变量。</span><span class="token keyword">const</span> <span class="token keyword">int</span> <span class="token operator">*</span>p2<span class="token punctuation">;</span> <span class="token comment">// 是一个指向常整型的指针。它可以指向任何常整型变量，但不能通过这个指针去更改它所指向的变量。</span><span class="token keyword">int</span> <span class="token keyword">const</span> <span class="token operator">*</span>p3<span class="token punctuation">;</span> <span class="token comment">// 是一个指向常整型的指针。这与 const int *p2 是等价的，它可以指向任何常整型变量，但不能通过这个指针去更改它所指向的变量。</span><span class="token keyword">int</span> <span class="token operator">*</span> <span class="token keyword">const</span> p4<span class="token punctuation">;</span> <span class="token comment">// 是一个常指针。它指向一个整型变量，并且不能更改它所指向的变量。</span><span class="token keyword">const</span> <span class="token keyword">int</span> <span class="token operator">*</span> <span class="token keyword">const</span> p5<span class="token punctuation">;</span> <span class="token comment">// 是一个常指针，指向一个常整型变量。它不能更改它所指向的变量。</span><span class="token keyword">int</span> <span class="token operator">*</span>p6<span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// 是一个整型指针数组。它有 10 个元素，每个元素都是一个指向整型的指针。</span><span class="token keyword">int</span> <span class="token punctuation">(</span><span class="token operator">*</span>p7<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// 是一个指向包含 10 个整型元素的数组的指针。</span><span class="token keyword">void</span> <span class="token punctuation">(</span><span class="token operator">*</span>func<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token keyword">int</span> <span class="token operator">*</span><span class="token punctuation">,</span> <span class="token keyword">double</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 是一个指向函数的指针。这个函数接受两个参数：一个指向整型的指针和一个指向浮点型的指针。它没有返回值，因为它的返回值类型是 void。</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>当然 C++ 的引用也能够缓解指针存在的问题，但无疑增加了学习成本</p><p>而很多语言都存在的垃圾回收机制，C++ 直到 C++11才引入了智能指针的库（auto_ptr那种不算），库出的太晚，且并没有作为特性而存在</p><p>并且支持 new 和 make_shared 两种方法赋值，只推荐后一种</p><p>C++ 为了极致的性能，在 C++11 又提出了移动语义这种复杂的概念</p><p>再深入还有元编程这座大山，C++ 论复杂说第二，真没有语言敢称第一</p><p>再谈面向对象的方面，C++ 有着复杂的多继承</p><p>对比之下，Swift 使用单继承，但可以遵循多个协议</p><p>若要细细对比 C++ 和 Swift，可以看出 Swift 作为一个新兴的语言修改了C++ 很多使用不舒服的痛点</p><ol type="1"><li>牺牲了自由性，带来了非常强大的编译前运行检查</li><li>使用垃圾回收机制避免了复杂的内存管理</li><li>去除了分号，但同时也在同一行有多个语句时也可以用分号，如：<code>var a=3; a=5</code></li><li>引入了可选值，当没有结果时返回 nil，而 C++ 通常我们可能会通过返回-1，nullptr 的方式解决</li><li>去除了繁琐的头文件机制，同一个项目不需要头文件也可直接导入（通过新的访问控制状态internal 实现），不同项目使用 import 导入，语法上也比 C++ 简单</li><li>方便的范围控制，如 <code>for i in 0...3</code> 代表 [0,3],<code>0..&lt;3</code> 代表 [0,3)</li><li><code>for (index, num) in nums.enumerated()</code>能够同时获取索引及相关值</li><li>方便的元组解包，<code>let (a, b) = (2, 3)</code>，而 C++ 既存在 pair又存在 tuple，语法上存在很大冗余，也很丑陋</li><li>if 后必须加 {}，统一了风格</li><li>if，while 后省略了无意义的 ()，使得语法更加简洁</li></ol><p>C++ 这门语言学的越多，越发现这门语言包袱之重，兼容 C 及 40年来的各种标准的整改，让这门语言拥有了更多的特性，让其很“自由”，代价就是写起来有各种的实现方法，可能导致各种问题，很多问题难以定位，更别提修改了</p><p>但尽管 C++ 有着各种问题，人们却不得不用它，一方面过去的很多项目都是C++ 开发的，再使用别的语言重新开发代价太大，另一方面 C++的性能仍然首屈一指，很多高性能要求领域还是得用</p><p>目前看来指望标准组丢弃历史包袱是不现实的，只能等待一门新兴的语言慢慢取代C++ 在各方面的地位</p>]]></content>
      
      
      <categories>
          
          <category> C++ 修炼之路 </category>
          
      </categories>
      
      
        <tags>
            
            <tag> C++ </tag>
            
        </tags>
      
    </entry>
    
    
    
    <entry>
      <title>GET vs POST</title>
      <link href="/posts/get-vs-post/"/>
      <url>/posts/get-vs-post/</url>
      
        <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>GET 和 POST 都是 HTTP最常用的两种方法，两者在计算机网络中都有着举足轻重的地位，也有很多关于安全性，便利性方面的讨论</p><p>本篇文章，笔者会详尽的讨论两者的方方面面</p><h2 id="get">GET</h2><p>GET 用来获取资源</p><p>GET 方法简约版报文是这样的</p><pre class="line-numbers language-http" data-language="http"><code class="language-http"><span class="token request-line"><span class="token method property">GET</span> <span class="token request-target url">/index.html?name=qiming.c&amp;age=22</span> <span class="token http-version property">HTTP/1.1</span></span><span class="token header"><span class="token header-name keyword">Host</span><span class="token punctuation">:</span> <span class="token header-value">localhost</span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h2 id="post">POST</h2><p>POST 用来传递数据给服务器处理</p><p>POST 方法简约版报文是这样的</p><pre class="line-numbers language-http" data-language="http"><code class="language-http"><span class="token request-line"><span class="token method property">POST</span> <span class="token request-target url">/index.html</span> <span class="token http-version property">HTTP/1.1</span></span><span class="token header"><span class="token header-name keyword">Host</span><span class="token punctuation">:</span> <span class="token header-value">localhost</span></span><span class="token header"><span class="token header-name keyword">Content-Type</span><span class="token punctuation">:</span> <span class="token header-value">application/x-www-form-urlencoded</span></span>name=qiming.c&amp;age=22<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="区别">区别</h2><table><colgroup><col style="width: 33%" /><col style="width: 33%" /><col style="width: 33%" /></colgroup><thead><tr class="header"><th></th><th>GET</th><th>POST</th></tr></thead><tbody><tr class="odd"><td>后退按钮/刷新</td><td>无害</td><td>数据会被重新提交（浏览器应该告知用户数据会被重新提交）</td></tr><tr class="even"><td>书签</td><td>可收藏为书签</td><td>不可收藏为书签</td></tr><tr class="odd"><td>缓存</td><td>能被缓存</td><td>不能缓存</td></tr><tr class="even"><td>编码类型</td><td>application/x-www-form-urlencoded</td><td>application/x-www-form-urlencoded ormultipart/form-data。为二进制数据使用多重编码</td></tr><tr class="odd"><td>历史</td><td>参数保留在浏览器历史中</td><td>参数不会保存在浏览器历史中</td></tr><tr class="even"><td>对数据长度的限制</td><td>是的，当发送数据时，GET 方法向 URL 添加数据；URL的长度是受限制的（URL 的最大长度是 2048 个字符）</td><td>无限制</td></tr><tr class="odd"><td>对数据类型的限制</td><td>只允许 ASCII 字符</td><td>没有限制。也允许二进制数据</td></tr><tr class="even"><td>安全性</td><td>与 POST 相比，GET 的安全性较差，因为所发送的数据是 URL的一部分。在发送密码或其他敏感信息时绝不要使用 GET ！</td><td>POST 比 GET 更安全，因为参数不会被保存在浏览器历史或 web服务器日志中</td></tr><tr class="odd"><td>可见性</td><td>数据在 URL 中对所有人都是可见的</td><td>数据不会显示在 URL 中</td></tr></tbody></table><h2 id="安全性">安全性</h2><p>POST 比 GET 安全，因为数据在 URL 上不可见，不用担心被缓存，存储在本地history 中...</p><p>但从传输的角度来说，他们都不安全，因为 HTTP明文的特性，只要抓包就能知道所有信息</p><p>只有使用 HTTPS 才安全</p><h2 id="便携性">便携性</h2><p>GET 可以作为书签，可以保存在 history 中便于下一次输入</p><p>GET 回退没有任何问题，POST 回退会再次提交</p><p>GET 会被缓存，POST 不会</p><h2 id="发送方式">发送方式</h2><p>对于 GET 方式的请求，浏览器会把 http header 和 data一并发送出去，服务器响应 200（返回数据）</p><p>对于 POST，浏览器先发送 header，服务器响应 100 continue，浏览器再发送data，服务器响应 200 ok</p><blockquote><p>实际测试很多浏览器 POST 也只发一次</p></blockquote><h2 id="幂等性idempotent">幂等性（Idempotent）</h2><p>幂等的概念是指同一个请求方法执行多次和仅执行一次的效果完全相同。</p><p>按照RFC规范，PUT，DELETE和安全方法都是幂等的。同样，这也仅仅是规范，服务端实现是否幂等是无法确保的。引入幂等主要是为了处理同一个请求重复发送的情况，比如在请求响应前失去连接，如果方法是幂等的，就可以放心地重发一次请求。这也是浏览器在后退/刷新时遇到POST会给用户提示的原因：POST语义不是幂等的，重复请求可能会带来意想不到的后果。</p><h2 id="参考">参考</h2><ul><li><ahref="https://www.w3schools.com/tags/ref_httpmethods.asp">w3schools httpmethods</a></li><li><ahref="https://github.com/febobo/web-interview/issues/145">面试官：说一下GET 和 POST 的区别？</a></li><li><ahref="https://www.zhihu.com/question/28586791/answer/767316172">GET 和POST 到底有什么区别？ - 大宽宽的回答 - 知乎</a></li></ul>]]></content>
      
      
      <categories>
          
          <category> Network </category>
          
      </categories>
      
      
        <tags>
            
            <tag> GET </tag>
            
            <tag> POST </tag>
            
            <tag> HTTP </tag>
            
        </tags>
      
    </entry>
    
    
    
    <entry>
      <title>C++ 为何构造函数和析构函数中不能调用虚函数？</title>
      <link href="/posts/c-wei-he-gou-zao-han-shu-he-xi-gou-han-shu-zhong-bu-neng-diao-yong-xu-han-shu/"/>
      <url>/posts/c-wei-he-gou-zao-han-shu-he-xi-gou-han-shu-zhong-bu-neng-diao-yong-xu-han-shu/</url>
      
        <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>之前看过很多书都提到 C++在构造函数和析构函数中不能调用虚函数，但当时的原因记得是<strong><em>构造时没有虚函数指针，析构时虚函数指针已经析构了，所以不能调用</em></strong>，现在想来似乎有些问题，毕竟虚函数指针初始化可以在调用语句前，析构可以先调用，最后才析构</p><blockquote><p>以下用 ctor(constructor) 代替构造函数，dtor(destructor)代替析构函数</p></blockquote><p>接下来将详细的介绍具体的原因，顺便介绍下是否可以将 ctor 和 dtor声明为虚函数</p><h2 id="例子">例子</h2><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;bits/stdc++.h></span></span><span class="token keyword">using</span> <span class="token keyword">namespace</span> std<span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">Base</span> <span class="token punctuation">&#123;</span><span class="token keyword">public</span><span class="token operator">:</span>    <span class="token function">Base</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">&#125;</span>    <span class="token operator">~</span><span class="token function">Base</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">&#125;</span>    <span class="token keyword">virtual</span> <span class="token keyword">void</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> std<span class="token double-colon punctuation">::</span>cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Base"</span> <span class="token operator">&lt;&lt;</span> std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span> <span class="token punctuation">&#125;</span> <span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">Derived</span> <span class="token operator">:</span> <span class="token base-clause"><span class="token keyword">public</span> <span class="token class-name">Base</span></span><span class="token punctuation">&#123;</span><span class="token keyword">public</span><span class="token operator">:</span>    <span class="token function">Derived</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">Base</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span>    <span class="token keyword">virtual</span> <span class="token keyword">void</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> std<span class="token double-colon punctuation">::</span>cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Derived"</span> <span class="token operator">&lt;&lt;</span> std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span> <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    Derived d<span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token comment">// outputs:</span><span class="token comment">// Base</span><span class="token comment">// Base</span><span class="token comment">// outputs as the vtable still points to Base::f() when Base::Base() is run</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>可以将 C++ 的构造顺序看成搭积木一样（先搭里面再外面），在 Base中调用多态函数，只会调用 Base 自己的方法而不是派生类的方法</p><p>可以将 C++ 的析构顺序看成剥洋葱一样（先剥外面再里面），在 Base中调用多态函数，只会调用 Base 自己的方法而不是派生类的方法</p><p>之所以这样做可以解释为 Base 构造先于 Derived，如果在 Base 中调用的virtual 函数用的 Derived 的实现，而 Derived 中的变量还没有实例化，此时若virtual 函数调用 Derived 的变量就会出问题，而且还有另一个理由，在构造Base 的对象时就该把它当成 Base 来构造，而不该还牵扯到 Derived</p><p>同理也对应于析构函数，Base 析构前 Derived 就已经析构了</p><p>构造析构顺序可以看<ahref="https://cs-moushuai.github.io/posts/c-gou-zao-he-xi-gou-de-shun-xu/">这里</a></p><h2 id="总结">总结</h2><ul><li>根据《Effective C++》的条款09：绝不在构造和析构过程中调用虚函数可知，在构造函数中虽然可以调用虚函数，但是强烈建议不要这样做。因为基类的构造的过程中，虚函数不能算作是虚函数。若构造函数中调用虚函数，可能会导致不确定行为的发生。</li></ul><p>ctor：</p><ul><li>虚函数对应一个 vtable(虚函数表)，类中存储一个 vptr 指向这个vtable。如果 ctor 是虚函数，就需要通过 vtable调用，可是对象没有初始化就没有 vptr，无法找到 vtable，所以 ctor不能是虚函数。</li></ul><p>dtor：</p><ul><li>dtor 为虚函数，并且一般情况下基类 dtor要定义为虚函数，非基类没有必要，因为会降低性能。</li><li>只有在基类 dtor 定义为虚函数时，调用操作符 delete销毁指向对象的基类指针时，才能准确调用派生类的析构函数（从该级向上按序调用虚函数），才能准确销毁数据，否则只会析构基类的dtor</li><li>dtor可以是纯虚函数，含有纯虚函数的类是抽象类，此时不能被实例化。但派生类中可以根据自身需求重新改写基类中的纯虚函数。</li></ul><h2 id="参考">参考</h2><ul><li>《Effective C++》条款 9：绝不在构造和析构过程中调用 virtual函数</li><li><ahref="https://stackoverflow.com/questions/962132/calling-virtual-functions-inside-constructors">Callingvirtual functions inside constructors -- stackoverflow</a></li><li><ahref="https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctors">Whenmy base class’s constructor calls a virtual function on its this object,why doesn’t my derived class’s override of that virtual function getinvoked?</a></li><li><ahref="https://interviewguide.cn/notes/03-hunting_job/02-interview/01-05-01-other.html#_3%E3%80%81%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E8%83%BD%E5%90%A6%E5%A3%B0%E6%98%8E%E4%B8%BA%E8%99%9A%E5%87%BD%E6%95%B0%E6%88%96%E8%80%85%E7%BA%AF%E8%99%9A%E5%87%BD%E6%95%B0-%E6%9E%90%E6%9E%84%E5%87%BD%E6%95%B0%E5%91%A2">阿秀的笔记</a></li></ul>]]></content>
      
      
      <categories>
          
          <category> C++ 修炼之路 </category>
          
      </categories>
      
      
        <tags>
            
            <tag> C++ </tag>
            
            <tag> 构造函数 </tag>
            
            <tag> 析构函数 </tag>
            
            <tag> 虚函数 </tag>
            
        </tags>
      
    </entry>
    
    
    
    <entry>
      <title>数据库锁详解</title>
      <link href="/posts/shu-ju-ku-suo-xiang-jie/"/>
      <url>/posts/shu-ju-ku-suo-xiang-jie/</url>
      
        <content type="html"><![CDATA[<p><img src="1.awebp" alt="大纲" /></p><p><img src="2.png" alt="层级图" /></p><h2 id="前言">前言</h2><p>锁是并发编程中访问同一资源的同步机制，保证了数据的一致性和有效性</p><p>前面的<ahref="https://cs-moushuai.github.io/posts/shu-ju-ku-shi-wu-guan-li-xiang-jie/">文章</a>提到数据库事务的ACID 特性和 4 个隔离级别，这次来看看如何用锁实现事务的隔离性</p><h2 id="按类型分">按类型分</h2><p><img src="4.png!large" alt="乐观锁和悲观锁对比" /></p><h3 id="乐观锁">乐观锁</h3><h4 id="概念">1. 概念</h4><p>假设一般数据不会发生冲突，只有在提交时才会检测，如发现冲突会通知用户</p><h4 id="应用场景">2. 应用场景</h4><p>读多写少的情况，如有大量的读操作，冲突的可能性会大大提高，效率会大大降低</p><h4 id="实现方式">3. 实现方式</h4><p>一般使用数据版本记录机制实现，在表中添加一个 version 字段，读数据时将version 一并读出，数据每更新一次，version + 1。 提交数据时和第一次取出的version 对比，不同则说明冲突</p><h3 id="悲观锁">悲观锁</h3><h4 id="概念-1">1. 概念</h4><p>悲观锁，正如其名，具有强烈的独占和排他特性，每次去拿数据的时候都认为别人会修改，对数据被外界（包括本系统当前的其他事务，以及来自外部系统的事务处理）修改持保守态度，因此，在整个数据处理过程中，将数据处于锁定状态。</p><h4 id="应用场景-1">2. 应用场景</h4><p>适用于并发量不大，写操作频繁的场景</p><h4 id="实现方式-1">3. 实现方式</h4><p>在MySQL中使用悲观锁，必须关闭MySQL的自动提交，<code>set autocommit=0</code>。共享锁和排它锁是悲观锁的不同的实现，它俩都属于悲观锁的范畴。</p><h2 id="按粒度分">按粒度分</h2><p>锁粒度<span class="math inline">\(\uparrow\)</span>，锁冲突概率<spanclass="math inline">\(\uparrow\)</span>，死锁概率<spanclass="math inline">\(\downarrow\)</span>，加锁的开销<spanclass="math inline">\(\downarrow\)</span>（需要维护的锁少），但并发性能<spanclass="math inline">\(\downarrow\)</span></p><p><strong>以下按照锁粒度从高到低的顺序介绍</strong></p><h3 id="全局锁">全局锁</h3><h4 id="概念-2">1. 概念</h4><p>对整个数据库进行加锁</p><h4 id="应用场景-2">2. 应用场景</h4><p>全库逻辑备份（mysqldump）</p><h4 id="实现方式-2">3. 实现方式</h4><p>MySQL提供了一个加全局读锁的方法，命令是<code>Flush tables with read lock</code>(FTWRL)。</p><p>此时其他操作会被堵塞</p><h4 id="问题">4. 问题</h4><p>如果在主库上更新：备份期间业务基本都暂停</p><p>如果在从库上更新：备份期间不能执行主库同步来的binlog，会导致主从延迟</p><h4 id="解决方案">5. 解决方案</h4><p>mysqldump使用参数--single-transaction，启动一个事务，确保拿到一致性视图。而由于MVCC的支持，这个过程中数据是可以正常更新的。</p><h3 id="表级锁">表级锁</h3><h4 id="概念-3">1. 概念</h4><p>对整张数据表进行加锁</p><h4 id="应用场景-3">2. 应用场景</h4><p>MyISAM 使用表级锁，InnoDB 如果不走索引的话也是用表级锁</p><h4 id="实现方式-3">3. 实现方式</h4><p><code>lock tables … read/write</code></p><h3 id="页级锁">页级锁</h3><h4 id="概念-4">1. 概念</h4><p>页级锁是 MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快，但冲突多，行级冲突少，但速度慢。因此，采取了折衷的页级锁，一次锁定相邻的一组记录。</p><h4 id="应用场景-4">2. 应用场景</h4><p>BDB 引擎支持页级锁。</p><h3 id="行级锁">行级锁</h3><h4 id="概念-5">1. 概念</h4><p>行级锁是粒度最低的锁，发生锁冲突的概率也最低、并发度最高。但是加锁慢、开销大，容易发生死锁现象。</p><h4 id="应用场景-5">2. 应用场景</h4><p>MySQL中只有 InnoDB 支持行级锁，其在走索引的情况下都是用行级锁</p><h4 id="实现方式-4">3. 实现方式</h4><p>在MySQL中，行级锁并不是直接锁记录，而是锁索引。索引分为主键索引和非主键索引两种，如果一条sql语句操作了主键索引，MySQL就会锁定这条主键索引；如果一条语句操作了非主键索引，MySQL会先锁定该非主键索引，再锁定相关的主键索引。在UPDATE、DELETE操作时，MySQL不仅锁定WHERE条件扫描过的所有索引记录，而且会锁定相邻的键值，即所谓的next-keylocking。</p><h2 id="按属性分">按属性分</h2><h3 id="共享锁s-锁">共享锁（S 锁）</h3><h4 id="概念-6">1. 概念</h4><p>共享锁（Shared lock），右称读锁，当事务 A对数据加上读锁后，其他事务就只能再加读锁，不能修改数据，只有读锁都释放后，才可以加写锁</p><h4 id="应用场景-6">2. 应用场景</h4><p>共享锁主要是为了支持并发的读取数据而出现的，读取数据时，不允许其他事务对当前数据进行修改操作，从而避免“不可重读”的问题的出现。</p><h4 id="实现方式-5">3. 实现方式</h4><p><code>select … lock in share mode</code></p><h3 id="排它锁x-锁">排它锁（X 锁）</h3><h4 id="概念-7">1. 概念</h4><p>排他锁（Exclusivelock），又称写锁，事务加上写锁后，其他事务既不能读也不能写，也不可以加任何锁，只有当前锁被释放了才可以</p><blockquote><p>注：MySQL InnoDB 引擎默认 <code>update,delete,insert</code>都会自动给涉及到的数据加上排他锁，<code>select</code>语句默认不会加任何锁类型。</p></blockquote><h4 id="应用场景-7">2. 应用场景</h4><p>写锁主要是为了解决在修改数据时，不允许其他事务对当前数据进行修改和读取操作，从而可以有效避免“脏读”问题的产生。</p><h4 id="实现方式-6">3. 实现方式</h4><p><code>select …for update</code></p><h2 id="按状态分">按状态分</h2><p>需要强调一下，意向锁是一种<strong>不与行级锁冲突表级锁</strong>，可以实现行级锁和表级锁共存</p><p>当事务 A 有行锁时，MySQL 会自动添加意向锁，事务 B如果要申请整个表的锁，不需要一行行看，而是直接判断有无意向锁，增强性能</p><ul><li>意向共享锁（intention shared lock,IS）：事务有意向对表中的某些行加共享锁（S锁）</li></ul><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">-- 事务要获取某些行的 S 锁，必须先获得表的 IS 锁。</span><span class="token keyword">SELECT</span> <span class="token keyword">column</span> <span class="token keyword">FROM</span> <span class="token keyword">table</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token keyword">LOCK</span> <span class="token operator">IN</span> <span class="token keyword">SHARE</span> <span class="token keyword">MODE</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><ul><li>意向排他锁（intention exclusive lock,IX）：事务有意向对表中的某些行加排他锁（X锁）</li></ul><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token comment">-- 事务要获取某些行的 X 锁，必须先获得表的 IX 锁。</span><span class="token keyword">SELECT</span> <span class="token keyword">column</span> <span class="token keyword">FROM</span> <span class="token keyword">table</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token keyword">FOR</span> <span class="token keyword">UPDATE</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><blockquote><p>即：意向锁是有数据引擎自己维护的，用户无法手动操作意向锁，在为数据行加共享/ 排他锁之前，InnoDB 会先获取该数据行所在在数据表的对应意向锁。</p></blockquote><table><thead><tr class="header"><th></th><th>意向共享锁（IS）</th><th>意向排他锁（IX）</th></tr></thead><tbody><tr class="odd"><td>意向共享锁（IS）</td><td>兼容</td><td>兼容</td></tr><tr class="even"><td>意向排他锁（IX）</td><td>兼容</td><td>兼容</td></tr></tbody></table><p>意向锁之间时互相兼容的，但是它会与普通的排他 / 共享锁互斥</p><table><thead><tr class="header"><th></th><th>意向共享锁（IS）</th><th>意向排他锁（IX）</th></tr></thead><tbody><tr class="odd"><td>共享锁（S）</td><td>兼容</td><td>互斥</td></tr><tr class="even"><td>排他锁（X）</td><td>互斥</td><td>互斥</td></tr></tbody></table><blockquote><p>注：这里的排他/共享锁指的都是行锁！！！意向锁不会与行级的共享 /排他锁互斥！！！</p></blockquote><p>例子：</p><p>事务 A 先获取了某一行的排他锁，并未提交：</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> users <span class="token keyword">WHERE</span> id <span class="token operator">=</span> <span class="token number">6</span> <span class="token keyword">FOR</span> <span class="token keyword">UPDATE</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>事务 A 获取了 users 表上的意向排他锁。</p><p>事务 A 获取了 id 为 6 的数据行上的排他锁。</p><p>之后事务 B 想要获取 users 表的共享锁：</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">LOCK</span> <span class="token keyword">TABLES</span> users <span class="token keyword">READ</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>事务 B 检测到事务 A 持有 users 表的意向排他锁。</p><p>事务 B 对 users 表的加锁请求被阻塞（排斥）。</p><p>最后事务 C 也想获取 users 表中某一行的排他锁：</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> users <span class="token keyword">WHERE</span> id <span class="token operator">=</span> <span class="token number">5</span> <span class="token keyword">FOR</span> <span class="token keyword">UPDATE</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>事务 C 申请 users 表的意向排他锁。</p><p>事务 C 检测到事务 A 持有 users 表的意向排他锁。</p><p>因为意向锁之间并不互斥，所以事务 C 获取到了 users表的意向排他锁。</p><p>因为id 为 5 的数据行上不存在任何排他锁，最终事务 C成功获取到了该数据行上的排他锁。</p><h2 id="按算法分">按算法分</h2><p>记录锁、间隙锁、临键锁都是排它锁，而记录锁的使用方法跟排它锁介绍一致。</p><h3 id="记录锁">记录锁</h3><p>记录锁是 封锁记录，记录锁也叫行锁，例如：</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> <span class="token identifier"><span class="token punctuation">`</span>test<span class="token punctuation">`</span></span> <span class="token keyword">WHERE</span> <span class="token identifier"><span class="token punctuation">`</span>id<span class="token punctuation">`</span></span><span class="token operator">=</span><span class="token number">1</span> <span class="token keyword">FOR</span> <span class="token keyword">UPDATE</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>它会在 id=1 的记录上加上记录锁，以阻止其他事务插入，更新，删除 id=1这一行。</p><h3 id="间隙锁">间隙锁</h3><p>间隙锁 是 Innodb 在 RR（可重复读）隔离级别下为了解决幻读问题时引入的锁机制。间隙锁是 Innodb 中行锁的一种。</p><p>请务必牢记：使用间隙锁锁住的是一个区间，而不仅仅是这个区间中的每一条数据。</p><p>举例来说，假如 emp 表中只有 101 条记录，其 empid 的值分别是1,2,...,100,101，下面的 SQL：</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> emp <span class="token keyword">WHERE</span> empid <span class="token operator">></span> <span class="token number">100</span> <span class="token keyword">FOR</span> <span class="token keyword">UPDATE</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>当我们用条件检索数据，并请求共享或排他锁时，InnoDB 不仅会对符合条件的empid 值为 101 的记录加锁，也会对 empid 大于101（这些记录并不存在）的“间隙”加锁。</p><p>这个时候如果你插入 empid 等于 102的数据的，如果那边事物还没有提交，那你就会处于等待状态，无法插入数据。</p><h3 id="临键锁">临键锁</h3><p>临键锁，是记录锁与间隙锁的组合，它的封锁范围，既包含索引记录，又包含索引区间。</p><blockquote><p>注：临键锁的主要目的，也是为了避免幻读（PhantomRead）。如果把事务的隔离级别降级为RC，临键锁则也会失效。</p></blockquote><h2 id="总结">总结</h2><p><img src="3.png!large" alt="总结" /></p><h2 id="参考">参考</h2><p><ahref="https://juejin.cn/post/6931752749545553933">史上最全MySQL各种锁详解</a></p><p><a href="https://juejin.cn/post/6844903666332368909">详解 MySqlInnoDB 中意向锁的作用</a></p><p><a href="https://www.sqlshack.com/locking-sql-server/">All aboutlocking in SQL Server</a></p><p><ahref="https://learnku.com/articles/39212?order_by=vote_count&amp;">一张图彻底搞懂MySQL 的锁机制</a></p>]]></content>
      
      
      <categories>
          
          <category> Database </category>
          
      </categories>
      
      
        <tags>
            
            <tag> 并发编程 </tag>
            
            <tag> 锁 </tag>
            
        </tags>
      
    </entry>
    
    
    
    <entry>
      <title>数据库事务管理详解</title>
      <link href="/posts/shu-ju-ku-shi-wu-guan-li-xiang-jie/"/>
      <url>/posts/shu-ju-ku-shi-wu-guan-li-xiang-jie/</url>
      
        <content type="html"><![CDATA[<h2 id="基本概念">基本概念</h2><ul><li>事务（transaction）：是一组 SQL语句，是保证逻辑数据完整性和可恢复性的重要利器</li><li>回退（rollback）：撤销指定 SQL 语句</li><li>提交（commit）：将未存储的 SQL 语句写入数据库表</li><li>保留点（savepoint）：事务处理中临时占位符（placeholder），可以对其回退（不需要回退整个事务）</li></ul><h2 id="事务状态">事务状态</h2><p><img src="1.jpeg" alt="状态转换图" /></p><ul><li>活跃状态：任何正在执行的事务都在此，操作进入部分提交状态</li><li>部分提交状态：执行上次操作后进入，更改存储在内存中</li><li>提交状态：所有操作执行完后进入此状态，此时不能回滚</li><li>失败状态：发生错误，事务进入终止状态</li><li>终止状态：进行回滚</li></ul><h2 id="事务的特征acid">事务的特征（ACID）</h2><ol type="1"><li>原子性（Atomiocity）：事务的操作要么全都执行要么都不执行</li><li>一致性（Consistency）：数据库从一个一致状态转变为另一个一致状态</li><li>隔离性（Isolation）：并发的事务间相不干扰，相互隔离</li><li>持久性（Durability）：事务一旦提交，影响是持久的</li></ol><h2 id="事务间相互影响">事务间相互影响</h2><p>事务A和事务B操作同一个银行账号，初始余额为0。</p><ul><li>脏读：事务B存入100元，此时事务A查余额，为100，但事务B发生了错误，回滚了，余额又重新变成了0，出现了不一致的情况，此时便说事务A读到了脏数据。</li><li>不可重复读：事务A查余额，为0，事务B存入100元并成功提交，此时事务A再查余额，变成了100，同样的查询得到不同的结果，这就是不可重复读。</li><li>幻读：与不可重复读有点像，但幻读强调的是查询结果为多条数据的场景。A查余额明细，有20条，B存入100元，使得余额明细多了一条，然后提交成功，此时A再查余额明细，得到了21条，这就是幻读。</li><li>丢失更新：事务A和B同时开始，A存入100元，B存入50元，A先于B提交成功，此时余额为100，但当B提交成功后，余额变成了50，A对余额的更新就不见了，这就是丢失更新。</li></ul><h2 id="事务的隔离级别">事务的隔离级别</h2><table><colgroup><col style="width: 20%" /><col style="width: 20%" /><col style="width: 20%" /><col style="width: 20%" /><col style="width: 20%" /></colgroup><thead><tr class="header"><th><strong>隔离级别</strong></th><th><strong>读数据一致性</strong></th><th><strong>脏读</strong></th><th><strong>不可重复读</strong></th><th><strong>幻读</strong></th></tr></thead><tbody><tr class="odd"><td><strong>未提交(RU)</strong></td><td>只能保证不读取物理上的损坏</td><td>√</td><td>√</td><td>√</td></tr><tr class="even"><td><strong>已提交(RC)</strong></td><td>语句级</td><td>×</td><td>√</td><td>√</td></tr><tr class="odd"><td><strong>可重复读(RR)</strong></td><td>事务级</td><td>×</td><td>×</td><td>√</td></tr><tr class="even"><td><strong>串行化(SR)</strong></td><td>最高级别，事务级</td><td>×</td><td>×</td><td>×</td></tr></tbody></table><hr /><p>事务A与事务B并发访问数据库：</p><ol type="1"><li>读未提交<br />A可以看到B正在修改而未commit的数据，A读的是实时数据，所以会出现脏读现象。</li><li>读已提交<br />A只能看到B提交后的数据，但是B若多次提交，A读的数据不相同，所以出现幻读现象。</li><li>可重复读（MySQL 默认隔离级别）<br />A只能读到A事务开启前的数据，所以B无论提交多少次，A看到的数据都是不变的，故可以重复读。</li><li>序列化<br />A与B不能并发，只能顺序执行</li></ol><h2 id="代码">代码</h2><h3 id="正常流程">正常流程</h3><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">START</span> <span class="token keyword">TRANSACTION</span><span class="token punctuation">;</span><span class="token keyword">DELETE</span> <span class="token keyword">FROM</span> orderitems <span class="token keyword">WHERE</span> order_num <span class="token operator">=</span> <span class="token number">20010</span><span class="token punctuation">;</span><span class="token keyword">DELETE</span> <span class="token keyword">FROM</span> orders <span class="token keyword">WHERE</span> order_num <span class="token operator">=</span> <span class="token number">20010</span><span class="token punctuation">;</span><span class="token keyword">COMMIT</span><span class="token punctuation">;</span><span class="token comment"># 或</span><span class="token keyword">ROLLBACK</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>注：当 <code>COMMIT</code> 或 <code>ROLLBACK</code>后，事务会自动关闭</p></blockquote><h3 id="保留点">保留点</h3><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SAVEPOINT</span> delete1<span class="token punctuation">;</span><span class="token keyword">ROLLBACK</span> <span class="token keyword">TO</span> delete1<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h3 id="自动提交">自动提交</h3><p><img src="2.png" alt="自动提交" /></p><p><code>SET autocommit=0;</code> 来取消自动提交，所谓自动提交就是不使用<code>COMMIT</code> 就直接将数据更改到数据库，而不通过内存</p><h2 id="哪些语句可以回退">哪些语句可以回退？</h2><p>DML（Data Manipulation Language） 语句： INSERT, UPDATE, DELETE</p><blockquote><p>注：回退 SELECT 没有意义，而 CREATE, DROP, TRUNCATE 属于 DDL都不能回退</p></blockquote><h2 id="如何实现事务的-acid-特性">如何实现事务的 ACID 特性</h2><ul><li>归档日志 bin log</li><li>回滚日志 undo log</li><li>重做日志 redo log</li></ul><hr /><ul><li>原子性：通过 undo log 记录 sql 操作，当发生回滚，逆序逆操作</li><li>持久性：bin log &amp; redo log</li><li>一致性：bin log</li><li>隔离性：事务的隔离级别，通过锁机制及 MVCC 来实现</li></ul><h2 id="参考">参考</h2><p><ahref="https://www.cnblogs.com/kismetv/p/10331633.html">深入学习MySQL事务：ACID特性的实现原理</a></p>]]></content>
      
      
      <categories>
          
          <category> Database </category>
          
      </categories>
      
      
        <tags>
            
            <tag> 事务处理 </tag>
            
            <tag> 隔离级别 </tag>
            
        </tags>
      
    </entry>
    
    
    
    <entry>
      <title>数据库存储引擎对比</title>
      <link href="/posts/shu-ju-ku-cun-chu-yin-qing-dui-bi/"/>
      <url>/posts/shu-ju-ku-cun-chu-yin-qing-dui-bi/</url>
      
        <content type="html"><![CDATA[<h2 id="存储引擎是什么">存储引擎是什么？</h2><p>存储引擎是数据库的核心</p><blockquote><p><strong>百度百科</strong><br />MySQL中的数据用各种不同的技术存储在文件（或者内存）中。这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力。通过选择不同的技术，你能够获得额外的速度或者功能，从而改善你的应用的整体功能。</p></blockquote><h2 id="存储引擎有哪些">存储引擎有哪些</h2><p>使用 <code>SHOW ENGINES;</code> 查看</p><p><img src="1.png" alt="存储引擎一览" /></p><table style="width:100%;"><colgroup><col style="width: 14%" /><col style="width: 14%" /><col style="width: 14%" /><col style="width: 14%" /><col style="width: 14%" /><col style="width: 14%" /><col style="width: 14%" /></colgroup><thead><tr class="header"><th style="text-align: center;"><strong>存储引擎</strong></th><th style="text-align: center;"><strong>MyISAM</strong></th><th style="text-align: center;"><strong>InnoDB</strong></th><th style="text-align: center;"><strong>BDB</strong></th><th style="text-align: center;"><strong>Memory</strong></th><th style="text-align: center;"><strong>Archive</strong></th><th style="text-align: center;"><strong>NDB</strong></th></tr></thead><tbody><tr class="odd"><td style="text-align: center;"><strong>存储限制</strong></td><td style="text-align: center;">无</td><td style="text-align: center;">64TB</td><td style="text-align: center;">无</td><td style="text-align: center;">有</td><td style="text-align: center;">无</td><td style="text-align: center;">有</td></tr><tr class="even"><td style="text-align: center;"><strong>事务</strong></td><td style="text-align: center;"></td><td style="text-align: center;">支持</td><td style="text-align: center;">支持</td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;"></td></tr><tr class="odd"><td style="text-align: center;"><strong>锁级别</strong></td><td style="text-align: center;">表锁</td><td style="text-align: center;">行锁</td><td style="text-align: center;">page</td><td style="text-align: center;">表</td><td style="text-align: center;">行</td><td style="text-align: center;">行</td></tr><tr class="even"><td style="text-align: center;"><strong>MVCC（并发控制）</strong></td><td style="text-align: center;"></td><td style="text-align: center;">支持</td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;">支持</td><td style="text-align: center;">支持</td></tr><tr class="odd"><td style="text-align: center;"><strong>全文索引</strong></td><td style="text-align: center;">支持</td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;"></td></tr><tr class="even"><td style="text-align: center;"><strong>集群索引</strong></td><td style="text-align: center;"></td><td style="text-align: center;">支持</td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;"></td></tr><tr class="odd"><td style="text-align: center;"><strong>数据缓存和索引缓存</strong></td><td style="text-align: center;"></td><td style="text-align: center;">支持</td><td style="text-align: center;"></td><td style="text-align: center;">支持</td><td style="text-align: center;"></td><td style="text-align: center;">支持</td></tr><tr class="even"><td style="text-align: center;"><strong>数据压缩</strong></td><td style="text-align: center;">支持</td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;">支持</td><td style="text-align: center;"></td></tr><tr class="odd"><td style="text-align: center;"><strong>批量插入速度</strong></td><td style="text-align: center;">高</td><td style="text-align: center;">低</td><td style="text-align: center;">高</td><td style="text-align: center;">高</td><td style="text-align: center;">很高</td><td style="text-align: center;">高</td></tr><tr class="even"><td style="text-align: center;"><strong>集群数据库支持</strong></td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;">支持</td></tr><tr class="odd"><td style="text-align: center;"><strong>外键支持</strong></td><td style="text-align: center;"></td><td style="text-align: center;">支持</td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;"></td><td style="text-align: center;"></td></tr><tr class="even"><td style="text-align: center;"><strong>适用场景</strong></td><tdstyle="text-align: center;">不需要事务的操作；插入、更新少，读取频繁；频繁的统计计算。</td><tdstyle="text-align: center;">需要事务的操作；更新数据需要使用行级锁；大数据量读写；大型互联网应用。</td><td style="text-align: center;">类似 InnoDB</td><tdstyle="text-align: center;">数据量不大，需要被频繁的访问，而且数据丢失不会对业务产生比较严重的影响。</td><tdstyle="text-align: center;">存储引擎基本上用于数据归档，作为日志表</td><td style="text-align: center;">集群</td></tr><tr class="odd"><td style="text-align: center;"><strong>特点</strong></td><td style="text-align: center;"></td><td style="text-align: center;"></td><tdstyle="text-align: center;">可替代InnoDB的事务引擎，支持COMMIT、ROLLBACK和其他事务特性</td><tdstyle="text-align: center;">数据存储在内存中，重启或崩溃，数据消失，使用哈希索引</td><tdstyle="text-align: center;">只支持Insert和Select操作，支持索引，非常适合存储归档数据，目标：高速插入和压缩功能</td><tdstyle="text-align: center;">集群存储引擎，数据全部放在内存中，高可用、高性能的集群系统</td></tr></tbody></table><p><img src="2.png" alt="适用场景" /></p><blockquote><p>注：新 MySQL 的存储引擎默认是 InnoDB</p></blockquote><p>以下内容来自 <ahref="https://www.bilibili.com/video/BV1zr4y1x7o7/?spm_id_from=pageDriver&amp;vd_source=792f3008384e88bd777ae4c2be9b658b">bilibili视频笔记</a></p><p>MySQL中常用的四种存储引擎分别是：MyISAM、InnoDB、MEMORY、ARCHIVE。MySQL5.5版本后默认的存储引擎为InnoDB。</p><h3 id="innodb-存储引擎">InnoDB 存储引擎</h3><p>InnoDB 是 MySQL默认的事务型存储引擎，使用最广泛，基于聚簇索引建立的。InnoDB内部做了很多优化，如能够自动在内存中创建自适应 hash索引，以加速读操作。</p><p>优点：支持事务和崩溃修复能力；引入了行级锁和外键约束。</p><p>缺点：占用的数据空间相对较大。</p><p>适用场景：需要事务支持，并且有较高的并发读写频率。</p><h3 id="myisam存储引擎">MyISAM存储引擎</h3><p>数据以紧密格式存储。对于只读数据，或者表比较小、可以容忍修复操作，可以使用MyISAM 引擎。MyISAM 会将表存储在两个文件中，数据文件 .MYD 和索引文件.MYI。</p><p>优点：访问速度快。</p><p>缺点：MyISAM不支持事务和行级锁，不支持崩溃后的安全恢复，也不支持外键。</p><p>适用场景：对事务完整性没有要求；表的数据都会只读的。</p><h3 id="memory-存储引擎">MEMORY 存储引擎</h3><p>MEMORY引擎将数据全部放在内存中，访问速度较快，但是一旦系统奔溃的话，数据都会丢失。</p><p>MEMORY引擎默认使用哈希索引，将键的哈希值和指向数据行的指针保存在哈希索引中。</p><p>优点：访问速度较快。</p><p>缺点：</p><p>哈希索引数据不是按照索引值顺序存储，无法用于排序。不支持部分索引匹配查找，因为哈希索引是使用索引列的全部内容来计算哈希值的。只支持等值比较，不支持范围查询。当出现哈希冲突时，存储引擎需要遍历链表中所有的行指针，逐行进行比较，直到找到符合条件的行。</p><h3 id="archive-存储引擎">ARCHIVE 存储引擎</h3><p>ARCHIVE 存储引擎非常适合存储大量独立的、作为历史记录的数据。ARCHIVE提供了压缩功能，拥有高效的插入速度，但是这种引擎不支持索引，所以查询性能较差。</p><h2 id="参考">参考</h2><p><ahref="https://segmentfault.com/a/1190000020383024">存储引擎对比</a></p><p><a href="https://segmentfault.com/a/1190000012588602">MySQL -常见的三种存储引擎</a></p>]]></content>
      
      
      <categories>
          
          <category> Database </category>
          
      </categories>
      
      
        <tags>
            
            <tag> 存储引擎 </tag>
            
        </tags>
      
    </entry>
    
    
    
    <entry>
      <title>数据库索引详解</title>
      <link href="/posts/shu-ju-ku-suo-yin-xiang-jie/"/>
      <url>/posts/shu-ju-ku-suo-yin-xiang-jie/</url>
      
        <content type="html"><![CDATA[<p><img src="1.png" alt="总纲" /></p><h2 id="什么是索引">什么是索引？</h2><p>正常使用 <code>select</code>需要对表中所有元素进行遍历，效率极低，而索引是对数据库中列进行有序化的数据结构，使用其可以快速访问特定信息</p><h2 id="索引优点">索引优点</h2><ul><li>通过创建<strong>唯一性索引</strong>，可以保证数据库表中每一行数据的唯一性；</li><li>可以加快数据的<strong>检索速度</strong>，这也是创建索引的主要原因；</li><li>可以加速表和表之间的连接，特别是在实现<strong>数据的参考完整性</strong>方面特别有意义；</li><li>通过使用索引，可以在查询的过程中，使用<strong>优化隐藏器</strong>，提高系统性能。</li></ul><h2 id="索引缺点">索引缺点</h2><ul><li>时间上，创建和维护索引都要耗费时间，这种时间随着数据量的增加而增加，具体地，当对表中的数据进行增加、删除和修改的时候，索引也要动态的维护，这样就降低了数据的维护速度；</li><li>空间上，索引需要占<strong>物理空间</strong>，除了数据表占数据空间之外，每一个索引还要占一定的物理空间，如果要建立聚簇索引，那么需要的空间就会更大。</li></ul><h2 id="按数据结构分类">按数据结构分类</h2><p><img src="2.png" alt="存储引擎支持索引类别" /></p><blockquote><p>注：InnoDB实际上也支持Hash索引，但是InnoDB中Hash索引的创建由存储引擎引擎自动优化创建，不能人为干预是否为表创建Hash索引</p></blockquote><h3 id="btree-索引">BTREE 索引</h3><p>B 树也称 B-树，它是一颗多路平衡查找树，相较于红黑树这种平衡树，其树的高度更低，这也意味着磁盘IO 的次数更少</p><p>B+ 树是其的 plus 版本，常用于数据库索引（InnoDB 默认），具有 B树的平衡性，同时节点间通过双向链表提高了区间查询的性能</p><p>B+tree 非叶子节点只存储键值信息，数据记录都存放在叶子节点中。而B-tree的非叶子节点也存储数据。所以 B+tree单个节点的数据量更小，在相同的磁盘I/O次数下，能查询更多的节点。</p><h3 id="位图索引">位图索引</h3><p>适用于字段取值范围小的情况，如：性别，婚姻状况</p><ul><li>不使用索引<br />不使用索引时，数据库只能一行行扫描所有记录，然后判断该记录是否满足查询条件。</li><li>BTREE 索引<br />对于性别，可取值的范围只有'男','女'，并且男和女可能各站该表的50%的数据，这时添加B树索引还是需要取出一半的数据，因此完全没有必要。相反，如果某个字段的取值范围很广，几乎没有重复，比如身份证号，此时使用B树索引较为合适。事实上，当取出的行数据占用表中大部分的数据时，即使添加了B树索引，数据库如oracle、mysql也不会使用B树索引，很有可能还是一行行全部扫描。</li></ul><p>那位图索引呢，通过位向量形式表示</p><p>性别：</p><p><img src="3.png" alt="性别位图" /></p><p>其中 1 表示为男，否则为女，则位图索引生成两个向量</p><p>男向量：10100</p><p>女向量：01011</p><p>对于婚姻状况这一列，位图索引生成三个向量，已婚为11000...，未婚为00100...，离婚为00010...</p><p><img src="4.png" alt="婚姻状况" /></p><p><code>select * from &lt;table&gt; where gender="男" and marriage="未婚"</code></p><p>取出男向量 10100，未婚向量 00100 进行 AND 操作，结果为00100，第三行为结果</p><h3 id="hash-索引">hash 索引</h3><p>哈希索引通过 hash算法（直接定址法，平方取中法，折叠法，除数取余法，随机数法）将数据转换为hash 的 key，对应的 value 为数据，MySQL 中只有 Memory引擎显式的支持哈希索引，这也是Memory引擎表的默认索引结构，Memory同时也支持B-Tree索引。并且，Memory引擎支持非唯一哈希索引，当发生hash 冲突时，使用拉链法解决</p><p>InnoBD引擎有一个特殊的功能叫“自适应哈希索引”。当InnoDB注意到某些索引值被使用得非常频繁时，它会在内存中基于B-Tree索引之上再创建一个哈希索引，这样就让B-Tree索引也具有哈希索引的一些优点，比如快速的哈希查找。但这是一个完全自动的、内部的行为，用于无法控制，但用户可以选择完全关闭该动能。</p><p>在使用InnoBD引擎时，我们可以在B-Tree的基础上创建一个伪哈希索引。当然这不是真正的哈希索引，因为还是使用的B-Tree进行查找，但它使用哈希值而不是键本身进行索引查找。</p><p>例如，如果需要存储大量的URL并且需要根据URL进行查找，如果使用B-Tree来存储URL，则存储的内容就会很大，因为URL本身就很长。为此，我们可以单独指定一个哈希列并为该列创建索引，并选择一个哈希函数！每次存储、变更URL时，对该URL应用一个函数计算出一个哈希值，存入对应的哈希列中。</p><p>在查询时，如果采用体积很小的基于哈希值的索引来查找，则性能会提升很多，唯一的缺点就是需要调用一个哈希函数，为此我们可以使用触发器来实现。</p><p>如果出现了哈希冲突，则查询会返回多行数据，为此在查询时还必须带上真正的URL常量值。正确的查询语句为：</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">select</span> xx <span class="token keyword">from</span> url<span class="token keyword">where</span> url_hash <span class="token operator">=</span> <span class="token keyword">hash</span><span class="token punctuation">(</span><span class="token string">'https://www.baidu.com/'</span><span class="token punctuation">)</span> <span class="token operator">AND</span> url <span class="token operator">=</span> <span class="token string">'https://www.baidu.com/'</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h4 id="优点">优点</h4><p>查询速度极快，平均为 <spanclass="math inline">\(\mathcal{O}(1)\)</span></p><h4 id="缺点">缺点</h4><ol type="1"><li>只能用于等值查询，如 =, !=,IN，由于本身的数据结构，像范围查询（BETWEEN），模糊查询（LIKE）都是用不了的</li><li>不能排序，哈希表是无序的</li><li>不支持部分索引列查询，如对（A，B）建立索引，若查询只有A，则无法用索引</li><li>仍需要回表查询<br />由于可能发生哈希冲突，需要将数据与数据库中的数据比较</li><li>哈希表负载因子高了，性能会降低</li></ol><p>所以其使用场景很小</p><h2 id="按物理存储分类">按物理存储分类</h2><p><strong>区别：</strong>聚簇索引节点存放行记录数据，而非聚簇索引存放指针</p><h3 id="聚簇索引">聚簇索引</h3><p>也称聚集索引，根据每张表的主键构造一颗 B+树，叶子结点存放行记录数据<br />InnoDB表要求必须有聚簇索引，默认在主键字段上建立聚簇索引，在没有主键字段的情况下，表的第一个非空的唯一索引将被建立为聚簇索引，在前两者都没有的情况下，InnoDB将自动生成一个隐式的自增 id 列，并在此列上建立聚簇索引。 以 MyISAM为存储引擎的表不存在聚簇索引。</p><p><strong>优点：</strong></p><ol type="1"><li>速度快，因为其将数据直接存在结点上，少了一次查询</li><li>数据逻辑上连续（物理上不连续），区间查询很快<br />页间使用双向链表，页内使用单向链表来实现顺序性</li></ol><h3 id="非聚簇索引">非聚簇索引</h3><p>也称二级索引，辅助索引，叶子结点存放的是数据的指针</p><h2 id="按字段特性分类">按字段特性分类</h2><h3 id="普通索引">普通索引</h3><p>普通索引就是最最基础的索引，这种索引没有任何的约束作用，它存在的主要意义就是提高查询效率。</p><h3 id="唯一性索引">唯一性索引</h3><p>在普通索引的基础上加上唯一性约束</p><h3 id="主键索引">主键索引</h3><p>在唯一性索引的基础上加上非空约束</p><h3 id="全文索引">全文索引</h3><p>对内容进行分词， MySQL 中少用（5.6 版本后支持）</p><h3 id="前缀索引">前缀索引</h3><p>对字符类型字段的前几个字符或 bytes建立索引，而不是整个字段，大大减少索引占用空间，也提高了查找效率，关键在于建立多长的前缀使得查到的数据冲突少，但同时也会带来空间的开销</p><h2 id="按字段个数分类">按字段个数分类</h2><h3 id="单列索引">单列索引</h3><p>对单个列进行索引</p><h3 id="联合索引">联合索引</h3><p>对多个列进行索引，又称复合索引，组合索引，使用最左前缀原则匹配</p><p><strong>最左前缀原则</strong>：</p><p>进行匹配，必须按照组合索引从左往右添加 <code>where</code>条件，当遇到范围查询，如：&lt;, &gt;, between, like 等就会停止匹配</p><ol type="1"><li>像对 (a, b, c) 建立索引，查询条件 a / ab / abc 会走索引，使用 bc不会索引<br />因为 <code>a</code> 全局有序，<code>b</code> 是局部有序，只有<code>a</code> 确定了，<code>b</code> 才能有序</li><li>如 <code>where a=1 and b&gt;2 and c=3</code> 时，<code>b</code>进行了范围查找，此时 <code>c</code> 用不了索引</li></ol><h2 id="覆盖索引的情况">覆盖索引的情况</h2><p>当数据在二级索引且为联合索引中找到时，不需要回表查询，可以用Explain命令查看SQL语句的执行计划，执行计划的Extra字段中若出现Usingindex，表示查询触发了索引覆盖。</p><p>如：</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> <span class="token identifier"><span class="token punctuation">`</span>student<span class="token punctuation">`</span></span> <span class="token punctuation">(</span>  <span class="token identifier"><span class="token punctuation">`</span>id<span class="token punctuation">`</span></span> <span class="token keyword">int</span><span class="token punctuation">(</span><span class="token number">11</span><span class="token punctuation">)</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span> <span class="token keyword">AUTO_INCREMENT</span> <span class="token keyword">COMMENT</span> <span class="token string">'自增主键'</span><span class="token punctuation">,</span>  <span class="token identifier"><span class="token punctuation">`</span>name<span class="token punctuation">`</span></span> <span class="token keyword">varchar</span><span class="token punctuation">(</span><span class="token number">32</span><span class="token punctuation">)</span> <span class="token keyword">COLLATE</span> utf8_bin <span class="token operator">NOT</span> <span class="token boolean">NULL</span> <span class="token keyword">COMMENT</span> <span class="token string">'名称'</span><span class="token punctuation">,</span>  <span class="token identifier"><span class="token punctuation">`</span>age<span class="token punctuation">`</span></span> <span class="token keyword">int</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span> <span class="token keyword">unsigned</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span> <span class="token keyword">DEFAULT</span> <span class="token string">'1'</span> <span class="token keyword">COMMENT</span> <span class="token string">'年龄'</span><span class="token punctuation">,</span>  <span class="token keyword">PRIMARY</span> <span class="token keyword">KEY</span> <span class="token punctuation">(</span><span class="token identifier"><span class="token punctuation">`</span>id<span class="token punctuation">`</span></span><span class="token punctuation">)</span><span class="token punctuation">,</span>  <span class="token keyword">KEY</span> <span class="token identifier"><span class="token punctuation">`</span>I_name<span class="token punctuation">`</span></span> <span class="token punctuation">(</span><span class="token identifier"><span class="token punctuation">`</span>name<span class="token punctuation">`</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">ENGINE</span><span class="token operator">=</span><span class="token keyword">InnoDB</span><span class="token punctuation">;</span><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> student <span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">)</span> <span class="token keyword">VALUES</span><span class="token punctuation">(</span><span class="token string">"小赵"</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token string">"小王"</span><span class="token punctuation">,</span> <span class="token number">11</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token string">"小李"</span><span class="token punctuation">,</span> <span class="token number">12</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token string">"小陈"</span><span class="token punctuation">,</span> <span class="token number">13</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">ALTER</span> <span class="token keyword">TABLE</span> student <span class="token keyword">ADD</span> <span class="token keyword">INDEX</span> I_name_age<span class="token punctuation">(</span>name<span class="token punctuation">,</span> age<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">SELECT</span> age <span class="token keyword">FROM</span> student <span class="token keyword">WHERE</span> name <span class="token operator">=</span> <span class="token string">'小李'</span>；<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>name, age</code> 组成联合索引，<code>where</code> 中对<code>name</code> 进行查找，<code>select</code> 返回<code>age</code>，其存在于联合索引，不需要回表，故为覆盖索引</p><h2 id="什么时候不该建立索引">什么时候不该建立索引？</h2><p>索引虽好，但由于本身会带来空间上的开销，增删改操作也会变慢，所以有一些情况不适合建立索引</p><ol type="1"><li>where 条件中用不到的字段不适合</li><li>表记录很少只有几百条</li><li>需要经常增删改，需要评估是否适合加索引</li><li>参与列计算的列不适合</li><li>区分度不高的字段不适合，如：性别（男，女，未知三个值），加了索引效率也不会太高，<code>select</code>会有多个值<br />ps：可以使用位图索引</li></ol><h2 id="参考">参考</h2><p><ahref="https://blog.csdn.net/weixin_43767015/article/details/119346481">MySQL哈希索引的数据结构以及索引的优缺点</a></p><p><ahref="https://segmentfault.com/a/1190000037683781">MySQL索引有哪些分类，你真的清楚吗？</a></p><p><a href="https://segmentfault.com/a/1190000020416577">B 树，B+树详解</a></p><p><ahref="https://juejin.cn/post/7078512620289916964">主键索引就是聚集索引？MySQL索引类型大梳理</a></p><p><ahref="https://www.cnblogs.com/lbser/p/3322630.html">位图（BitMap）索引</a></p>]]></content>
      
      
      <categories>
          
          <category> Database </category>
          
      </categories>
      
      
        <tags>
            
            <tag> 索引 </tag>
            
        </tags>
      
    </entry>
    
    
    
    <entry>
      <title>HTTP 详解</title>
      <link href="/posts/http-xiang-jie/"/>
      <url>/posts/http-xiang-jie/</url>
      
        <content type="html"><![CDATA[<p><img src="1.png" alt="总纲" /></p><h2 id="一基本概念">一、基本概念</h2><p>超文本传输协议（Hyber Text TransferProtocol，HTTP）是万维网的基础，用于浏览器与服务器通信，通过超文本链接加载网页。HTTP属于应用层，基于 TCP/IP 通信，端口默认为 TCP 80</p><p>HTTP 特性：</p><ol type="1"><li>无状态：服务器无法通过 HTTP 记录用户是谁，目前可以通过 <ahref="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies">cookie</a>或者 <a href="https://www.woshipm.com/pd/877760.html">token</a>来保持状态</li><li>灵活可扩展：三十年时间里其新增了请求方法、版本号、状态码、头字段等特性</li><li>可靠：基于 TCP 协议</li><li>request - reponse 通信模式：一问一答，符合 RPC（Remote ProcedureCall），将 HTTP request 封装成远程函数调用</li></ol><h2 id="二http-报文解析">二、HTTP 报文解析</h2><h2 id="三http-发展历程">三、HTTP 发展历程</h2><ol type="1"><li>HTTP 协议始于三十年前蒂姆·伯纳斯 - 李的一篇论文；</li><li>HTTP/0.9 是个简单的文本协议，只能获取文本资源；</li><li>HTTP/1.0 确立了大部分现在使用的技术，但它不是正式标准；</li><li>HTTP/1.1 是目前互联网上使用最广泛的协议，功能也非常完善；</li><li>HTTP/2 基于 Google 的 SPDY 协议，注重性能改善，但还未普及；</li><li>HTTP/3 基于 Google 的 QUIC 协议，是将来的发展方向。</li></ol><h2 id="参考">参考</h2><p><a href="https://zq99299.github.io/note-book2/http-protocol/">透视HTTP 协议</a> <ahref="https://www.cnblogs.com/rickiyang/p/13138574.html#:~:text=%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE%EF%BC%88HTTP,%E4%B9%9F%E5%8F%AF%E4%BB%A5%E4%BD%BF%E7%94%A8%E5%85%B6%E4%BB%96%E7%AB%AF%E5%8F%A3%E3%80%82">HTTP协议详解</a> <ahref="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Overview">MDN</a></p>]]></content>
      
      
      
        <tags>
            
            <tag> 网络 </tag>
            
        </tags>
      
    </entry>
    
    
    
    <entry>
      <title>C++ 构造和析构的顺序</title>
      <link href="/posts/c-gou-zao-he-xi-gou-de-shun-xu/"/>
      <url>/posts/c-gou-zao-he-xi-gou-de-shun-xu/</url>
      
        <content type="html"><![CDATA[<h2 id="构造函数调用顺序">构造函数调用顺序</h2><ol type="1"><li>调用虚基类构造函数（从左到右）</li><li>调用非虚基类构造函数（从左到右）</li><li>调用成员变量构造函数（按声明顺序从前往后，而非初始化列表顺序）</li><li>调用类自身构造函数的语句</li></ol><h2 id="析构函数调用顺序">析构函数调用顺序</h2><p>与构造函数相反</p><ol type="1"><li>执行自身析构函数语句</li><li>析构成员变量（从后往前）</li><li>调用非虚基类构造函数（从右到左）</li><li>调用虚基类构造函数（从右到左）</li></ol><h2 id="例子">例子</h2><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;bits/stdc++.h></span></span><span class="token keyword">using</span> <span class="token keyword">namespace</span> std<span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">Base1</span> <span class="token punctuation">&#123;</span><span class="token keyword">public</span><span class="token operator">:</span>    <span class="token function">Base1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Base1 ctor"</span> <span class="token operator">&lt;&lt;</span> endl<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token operator">~</span><span class="token function">Base1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Base1 dtor"</span> <span class="token operator">&lt;&lt;</span> endl<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">Base2</span> <span class="token punctuation">&#123;</span><span class="token keyword">public</span><span class="token operator">:</span>    <span class="token function">Base2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Base2 ctor"</span> <span class="token operator">&lt;&lt;</span> endl<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token operator">~</span><span class="token function">Base2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Base2 dtor"</span> <span class="token operator">&lt;&lt;</span> endl<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">VBase</span> <span class="token punctuation">&#123;</span><span class="token keyword">public</span><span class="token operator">:</span>    <span class="token function">VBase</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        cout <span class="token operator">&lt;&lt;</span> <span class="token string">"VBase ctor"</span> <span class="token operator">&lt;&lt;</span> endl<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token operator">~</span><span class="token function">VBase</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        cout <span class="token operator">&lt;&lt;</span> <span class="token string">"VBase dtor"</span> <span class="token operator">&lt;&lt;</span> endl<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">A</span><span class="token punctuation">&#123;</span><span class="token keyword">public</span><span class="token operator">:</span>    <span class="token function">A</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        cout <span class="token operator">&lt;&lt;</span> <span class="token string">"A ctor"</span> <span class="token operator">&lt;&lt;</span> endl<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token operator">~</span><span class="token function">A</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        cout <span class="token operator">&lt;&lt;</span> <span class="token string">"A dtor"</span> <span class="token operator">&lt;&lt;</span> endl<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">B</span><span class="token punctuation">&#123;</span><span class="token keyword">public</span><span class="token operator">:</span>    <span class="token function">B</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        cout <span class="token operator">&lt;&lt;</span> <span class="token string">"B ctor"</span> <span class="token operator">&lt;&lt;</span> endl<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token operator">~</span><span class="token function">B</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        cout <span class="token operator">&lt;&lt;</span> <span class="token string">"B dtor"</span> <span class="token operator">&lt;&lt;</span> endl<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span class="token keyword">class</span> <span class="token class-name">Derived</span><span class="token operator">:</span> <span class="token base-clause"><span class="token keyword">public</span> <span class="token class-name">Base1</span><span class="token punctuation">,</span> <span class="token keyword">public</span> <span class="token class-name">Base2</span><span class="token punctuation">,</span> <span class="token keyword">virtual</span> <span class="token keyword">public</span> <span class="token class-name">VBase</span></span> <span class="token punctuation">&#123;</span><span class="token keyword">private</span><span class="token operator">:</span>    A a<span class="token punctuation">;</span>    B b<span class="token punctuation">;</span><span class="token keyword">public</span><span class="token operator">:</span>    <span class="token function">Derived</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token function">b</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Derived ctor"</span> <span class="token operator">&lt;&lt;</span> endl<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token operator">~</span><span class="token function">Derived</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Derived dtor"</span> <span class="token operator">&lt;&lt;</span> endl<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    Derived der<span class="token punctuation">;</span>    <span class="token comment">// Base1 *base = new Derived;</span>    <span class="token comment">// delete base;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-text" data-language="text"><code class="language-text">VBase ctorBase1 ctorBase2 ctorA ctorB ctorDerived ctorDerived dtorB dtorA dtorBase2 dtorBase1 dtorVBase dtor<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>完全符合规则</p><h2 id="多态的基类析构函数要声明为-virtual">多态的基类析构函数要声明为virtual</h2><p>下面如果使用 <code>Base1 *base = new Derived; delete base;</code>呢</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">VBase ctorBase1 ctorBase2 ctorA ctorB ctorDerived ctorBase1 dtor<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>思考哪出了问题？</p><p>其实是为了实现多态，则必须声明基类的析构函数为<code>virtual</code>，本题可以将 <code>Base1</code> 的析构函数声明为<code>virtual</code></p><p>结果：</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">VBase ctorBase1 ctorBase2 ctorA ctorB ctorDerived ctorDerived dtorB dtorA dtorBase2 dtorBase1 dtorVBase dtor<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
      
      
      <categories>
          
          <category> C++ 修炼之路 </category>
          
      </categories>
      
      
        <tags>
            
            <tag> C++ </tag>
            
            <tag> 构造 </tag>
            
            <tag> 析构 </tag>
            
        </tags>
      
    </entry>
    
    
    
    <entry>
      <title>僵尸进程与孤儿进程</title>
      <link href="/posts/jiang-shi-jin-cheng-yu-gu-er-jin-cheng/"/>
      <url>/posts/jiang-shi-jin-cheng-yu-gu-er-jin-cheng/</url>
      
        <content type="html"><![CDATA[<p>什么是僵尸进程，什么是孤儿进程，会带来什么问题，如何解决？</p><h2 id="基本概念">基本概念</h2><p>子进程由父进程创建，而子进程和父进程的运行是异步的，谁也不确定谁先运行，当子进程完成工作时，父进程需要使用<code>wait</code> 或 <code>waitpid</code> 来获取子进程终止状态</p><h2 id="孤儿进程">孤儿进程 👶</h2><h3 id="产生原因">产生原因</h3><p>父进程先于子进程退出（没有使用 <code>wait</code> 或<code>waitpid</code>），子进程还在运行，则这些子进程会变成孤儿进程，将被init 进程（pid 为 1）收养，并由 init 对其进行状态收集</p><h3 id="危害">危害</h3><p>孤儿进程由 init 进程收养，并不会有什么危害</p><h3 id="代码">代码</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;unistd.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token comment">// 对于 fork，父进程返回子进程 pid，子进程返回 0，失败返回 -1</span>    pid_t pid <span class="token operator">=</span> <span class="token function">fork</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span>pid <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        <span class="token function">perror</span><span class="token punctuation">(</span><span class="token string">"Fork Error"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pid <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> <span class="token comment">// 子进程</span>        <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"I'm the child process\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"pid: %d\tppid: %d\n"</span><span class="token punctuation">,</span> <span class="token function">getpid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">getppid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 确保此时父进程已退出，被 init 进程收养</span>        <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"pid: %d\tppid: %d\n"</span><span class="token punctuation">,</span> <span class="token function">getpid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">getppid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"child process is exited\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span> <span class="token comment">// 父进程</span>        <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"I'm the father process\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 确保子进程先输出当前的 ppid</span>        <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"father process is exited\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-text" data-language="text"><code class="language-text">I'm the father processI'm the child processpid: 27788      ppid: 27787father process is exitedpid: 27788      ppid: 1child process is exited<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="僵尸进程">僵尸进程 🧟‍♀️</h2><h3 id="产生原因-1">产生原因</h3><p>子进程退出，父进程没有调用 <code>wait</code> 或 <code>waitpid</code>获取子进程状态，则子进程的进程描述符会一直占用，被称为僵尸进程</p><h3 id="危害-1">危害</h3><p>僵尸进程的进程描述符会一直占用，如产生大量僵尸进程会导致可用的 pid大量减少，甚至不能产生新进程，且会占用空间</p><h3 id="解决方案">解决方案</h3><p>僵尸进程的罪魁祸首是不回收子进程的父进程，可以 <code>kill</code> 发送<code>SIGKILL</code> 或 <code>SIGTERM</code> 来枪毙他，让 init进程回收这些孤儿进程，使这些僵死的孤儿进程瞑目</p><h3 id="代码-1">代码</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;unistd.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token comment">// 对于 fork，父进程返回子进程 pid，子进程返回 0，失败返回 -1</span>    pid_t pid <span class="token operator">=</span> <span class="token function">fork</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span>pid <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        <span class="token function">perror</span><span class="token punctuation">(</span><span class="token string">"Fork Error"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pid <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> <span class="token comment">// 子进程</span>        <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"I'm the child process\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"pid: %d\tppid: %d\n"</span><span class="token punctuation">,</span> <span class="token function">getpid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">getppid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"child process is exited\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"I'm the father process\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 确保子进程先输出当前的 ppid</span>    <span class="token function">system</span><span class="token punctuation">(</span><span class="token string">"ps -o pid,ppid,state,tty,command | grep zombie | grep -v grep"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"father process is exited\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-text" data-language="text"><code class="language-text">I'm the father processI'm the child processpid: 30196      ppid: 30195child process is exited30195 11875 S+   ttys008  /Users/world/code/tmp/zombie30196 30195 Z+   ttys008  (zombie)father process is exited<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>大量产生僵尸进程</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdio.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;stdlib.h></span></span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string">&lt;unistd.h></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        <span class="token comment">// 对于 fork，父进程返回子进程 pid，子进程返回 0，失败返回 -1</span>        pid_t pid <span class="token operator">=</span> <span class="token function">fork</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">if</span> <span class="token punctuation">(</span>pid <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>            <span class="token function">perror</span><span class="token punctuation">(</span><span class="token string">"Fork Error"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pid <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> <span class="token comment">// 子进程</span>            <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"I am a child process.\nI am exiting.\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            <span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span>        <span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 确保子进程先输出当前的 ppid</span>        <span class="token function">system</span><span class="token punctuation">(</span><span class="token string">"ps -o pid,ppid,state,tty,command | grep zombie | grep -v grep"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-text" data-language="text"><code class="language-text">I am a child process.I am exiting.30960 11875 S+   ttys008  /Users/world/code/tmp/zombie230965 30960 Z+   ttys008  (zombie2)I am a child process.I am exiting.30960 11875 S+   ttys008  /Users/world/code/tmp/zombie230965 30960 Z+   ttys008  (zombie2)30974 30960 Z+   ttys008  (zombie2)I am a child process.I am exiting.30960 11875 S+   ttys008  /Users/world/code/tmp/zombie230965 30960 Z+   ttys008  (zombie2)30974 30960 Z+   ttys008  (zombie2)30983 30960 Z+   ttys008  (zombie2)I am a child process.I am exiting.30960 11875 S+   ttys008  /Users/world/code/tmp/zombie230965 30960 Z+   ttys008  (zombie2)30974 30960 Z+   ttys008  (zombie2)30983 30960 Z+   ttys008  (zombie2)30992 30960 Z+   ttys008  (zombie2)I am a child process.I am exiting.30960 11875 S+   ttys008  /Users/world/code/tmp/zombie230965 30960 Z+   ttys008  (zombie2)30974 30960 Z+   ttys008  (zombie2)30983 30960 Z+   ttys008  (zombie2)30992 30960 Z+   ttys008  (zombie2)31001 30960 Z+   ttys008  (zombie2)I am a child process.I am exiting.30960 11875 S+   ttys008  /Users/world/code/tmp/zombie230965 30960 Z+   ttys008  (zombie2)30974 30960 Z+   ttys008  (zombie2)30983 30960 Z+   ttys008  (zombie2)30992 30960 Z+   ttys008  (zombie2)31001 30960 Z+   ttys008  (zombie2)31014 30960 Z+   ttys008  (zombie2)I am a child process.I am exiting.30960 11875 S+   ttys008  /Users/world/code/tmp/zombie230965 30960 Z+   ttys008  (zombie2)30974 30960 Z+   ttys008  (zombie2)30983 30960 Z+   ttys008  (zombie2)30992 30960 Z+   ttys008  (zombie2)31001 30960 Z+   ttys008  (zombie2)31014 30960 Z+   ttys008  (zombie2)31023 30960 Z+   ttys008  (zombie2)I am a child process.I am exiting.^C<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>最后使用 Ctrl-C 退出，僵尸进程被 init 收养释放，也可以使用<code>kill -9 &lt;pid&gt;</code></p><h2 id="总结">总结</h2><p>无论是孤儿进程还是僵尸进程产生的原因都是父进程没有 <code>wait</code>或<code>waitpid</code>，区别在于孤儿进程是父进程先走，僵尸进程是子进程先走</p><h2 id="参考">参考</h2><p><ahref="https://www.cnblogs.com/anker/p/3271773.html">孤儿进程与僵尸进程[总结]</a></p>]]></content>
      
      
      <categories>
          
          <category> OS </category>
          
      </categories>
      
      
        <tags>
            
            <tag> C++ </tag>
            
            <tag> 僵尸进程 </tag>
            
            <tag> 孤儿进程 </tag>
            
        </tags>
      
    </entry>
    
    
    
    <entry>
      <title>浅谈网络 IO</title>
      <link href="/posts/qian-tan-wang-luo-io/"/>
      <url>/posts/qian-tan-wang-luo-io/</url>
      
        <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>网络 IO 是后端开发中经常需要接触的东西，也是面试中问到网络或者 Redis时的常考题，笔者也常为其烦恼</p><p>所以有了这篇文章，一是解决之前的一些疑惑，二是方便以后查阅</p><p>接下来将介绍网络 IO 的基础及五种 IO 模型方面的知识</p><h2 id="基本概念">基本概念</h2><h3 id="socket">Socket</h3><p>套接字，用于网络中不同主机上应用程序间双向通信的端点的抽象，他将复杂的TCP/IP 隐藏在 Socket 接口后面</p><ul><li>Socket 地址 = IP 地址:端口号</li><li>不同的协议，如 TCP，UDP 可同时使用相同端口号</li></ul><p><img src="1.jpeg" alt="什么是 Socket" /></p><p><img src="2.jpeg" alt="Socket 通信流程" /></p><p>对具体使用感兴趣的可以做下这个实验 <ahref="https://gaia.cs.umass.edu/kurose_ross/programming.php">计算机网络自顶向下Lab</a>，很有意思</p><h3 id="fdfile-descriptor">FD(File Descriptor)</h3><p>文件描述符，为非负整数，Linux中一切资源都可以通过文件来访问和管理，FD 用于指向某一资源</p><h3 id="阻塞与非阻塞">阻塞与非阻塞</h3><p>阻塞与非阻塞，描述调用者在等待返回结果的过程</p><ul><li>阻塞：调用者发送请求后，会一直等待返回结果，当前线程被阻塞</li><li>非阻塞：调用者发送请求后，会立刻返回，当前线程不会阻塞，但调用需要定期轮询（一个个看）查看处理结果</li></ul><p>例子：</p><p>阻塞/非阻塞：我在等你干活的时候我在干啥？</p><ul><li>阻塞：啥也不干，死等</li><li>非阻塞：可以干别的，但也要时不时问问你的进度</li></ul><h3 id="同步与异步">同步与异步</h3><p>而同步与异步，用于描述调用结果的返回机制（或者叫通信机制）。</p><ul><li>同步：调用者发起请求后，会一直等待返回结果，即由调用者主动等待这个调用结果。</li><li>异步：调用者发起请求后，会立刻返回，但不会立刻得到这个结果，而是由被调者在执行结束后主动通知（如Callback）调用者。</li></ul><p>例子：</p><p>同步/异步：你干完了，怎么让我知道呢？</p><ul><li>同步：我只要不问，你就不告诉我</li><li>异步：你干完了，直接喊我过来就行</li></ul><p>以上的IO可以组合成4种组合方式：同步阻塞，同步非阻塞，异步阻塞，异步非阻塞</p><h3 id="用户态和内核态">用户态和内核态</h3><p>两种不同的权限等级</p><h4 id="用户态">用户态</h4><p>只能执行系统规定的指令，当需要执行某些系统特权指令（系统调用）时，需要切换到内核态<img src="8.png" alt="系统调用位置" /></p><h4 id="内核态">内核态</h4><p>能够执行特权指令，如：IO，内存分配等对硬件操作的指令</p><h2 id="五种-io-模型">五种 IO 模型</h2><p><img src="9.png" alt="五种 IO 模型" /></p><h3 id="阻塞-io">阻塞 IO</h3><p>优点：程序简单，线程挂起不会占用 CPU 资源<br />缺点：在高并发场景下，需要大量线程，那内存，线程切换开销是不可接受的<br />总之：不常用</p><h3 id="非阻塞-io">非阻塞 IO</h3><p>优点：线程立刻返回，可以干别的活<br />缺点：不断轮询效率低，数据到达和轮询有时间差，所以响应延迟高<br />总之：不常用，最多使用非阻塞 IO 的特性</p><h3 id="io-多路复用">IO 多路复用</h3><p>Redis 作为一个单线程的数据库，使用的就是 IO 多路复用</p><blockquote><p><strong>Redis 快的原因？</strong></p><ol type="1"><li>基于内存操作<br /></li><li>数据结构简单<br /></li><li>IO 多路复用<br /></li><li>主线程为单线程，避免上下文切换</li></ol></blockquote><p>unix 的 select()，poll()，linux 的 epoll()系列都是同步非阻塞，windows 的 IOCP 是异步非阻塞</p><p><img src="3.png" alt="三种多路复用区别" /> 优点：一个线程复用多个Socket，避免了多线程创建，切换，销毁的开销</p><h4 id="select">select</h4><p><a href="https://man7.org/linux/man-pages/man2/select.2.html">API文档</a> <img src="4.png" alt="select" /></p><p>优点：解决了线程切换的问题<br />缺点：</p><ul><li>FD 最大默认为 1024</li><li>每次调用需要将 FD 从用户态拷贝到内核态</li><li>不知道哪个 FD 就绪，需要全部遍历</li><li>参数每次调用都要重置（因为 <code>readfds, writefds, exceptfds</code>要作为返回值）</li></ul><h4 id="poll">poll</h4><p><a href="https://man7.org/linux/man-pages/man2/poll.2.html">API文档</a> <img src="5.png" alt="poll" /></p><p>优点：解决了线程切换的问题<br />缺点：</p><ul><li><del>FD 最大默认为 1024</del></li><li>每次调用需要将 FD 从用户态拷贝到内核态</li><li>不知道哪个 FD 就绪，需要全部遍历</li><li><del>参数每次调用都要重置（因为<code>readfds, writefds, exceptfds</code> 要作为返回值）</del></li></ul><h4 id="epoll">epoll</h4><p><a href="https://man7.org/linux/man-pages/man7/epoll.7.html">API文档</a> <img src="6.png" alt="epoll-1" /> <img src="7.png"alt="epoll-2" /></p><p>LT：Level-Triggered，水平触发（默认），<code>epoll_wait</code>检测到事件后，如该事件没有处理完毕，后续 <code>epoll_wait</code>都会返回该事件，更安全<br />ET：Edge-Triggered，边缘触发，<code>epoll_wait</code>检测到事件后，只会在当次返回，不管该事件是否被处理完毕，更快</p><p>优点：解决了线程切换的问题<br />缺点：</p><ul><li><del>FD 最大默认为 1024</del></li><li><del>每次调用需要将 FD 从用户态拷贝到内核态</del></li><li><del>不知道哪个 FD 就绪，需要全部遍历</del></li><li><del>参数每次调用都要重置（因为<code>readfds, writefds, exceptfds</code> 要作为返回值）</del></li><li>跨平台不好，只支持 Linux</li><li>相比较 select，epoll 太重了，遇到监听连接数和事件较少的场景，select可能更优</li></ul><h3 id="异步-io">异步 IO</h3><p>一般为非堵塞，除非刻意堵塞<br />应用进程执行 aio_read系统调用会立即返回，应用进程可以继续执行，不会被阻塞，内核会在所有操作完成之后向应用进程发送信号。</p><h3 id="信号驱动-io">信号驱动 IO</h3><p>在信号驱动IO模型中，当用户线程发起一个IO请求操作，会给对应的socket注册一个信号函数，然后用户线程会继续执行，当内核数据就绪时会发送一个信号给用户线程，用户线程接收到信号之后，便在信号函数中调用IO读写操作来进行实际的IO请求操作。这个一般用于UDP中，对TCP套接口几乎是没用的，原因是该信号产生得过于频繁，并且该信号的出现并没有告诉我们发生了什么事情</p><p>异步 IO 与信号驱动 IO 的区别在于，异步 IO 的信号是通知应用进程 IO完成，而信号驱动 IO 的信号是通知应用进程可以开始 IO。</p><p><img src="10.png" alt="比较" /></p><h2 id="总结">总结</h2><p>本文介绍了 IO 操作的基本概念，常见 IO的五种模型，多路复用的三种方式</p><p><strong>最后提几个问题供读者检验成果：</strong></p><ol type="1"><li>阻塞和非阻塞有何区别，取决于什么？</li><li>同步和异步有何区别？</li><li>select，poll，epoll 有何区别？</li><li>Redis 为什么快？</li><li>同一个端口可以同时给 TCP 程序和 UDP 程序使用吗？</li><li>IO 五个模型分别是什么？</li><li>socket 是什么，socket 地址是什么？</li></ol><h2 id="参考">参考</h2><p><a href="https://blog.ixk.me/post/talking-about-io">浅谈IO |青空之蓝</a><br /><a href="https://b23.tv/a9eq4SR">小白也看得懂的 IO多路复用解析（超详细案例）-哔哩哔哩</a><br /><ahref="https://segmentfault.com/a/1190000039898780#:~:text=IO%20%E6%A8%A1%E5%9E%8B%E6%98%AF%E6%8C%87%EF%BC%9A%E7%94%A8,%E6%A8%A1%E5%9E%8B%E5%92%8C%E5%BC%82%E6%AD%A5IO%20%E6%A8%A1%E5%9E%8B%E3%80%82">浅聊Linux的五种IO模型</a><br /><ahref="https://xmmarlowe.github.io/2021/06/28/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/%E7%94%A8%E6%88%B7%E6%80%81%E5%92%8C%E5%86%85%E6%A0%B8%E6%80%81/">用户态和内核态</a><br /><ahref="https://segmentfault.com/a/1190000041488709#:~:text=%E6%89%80%E8%B0%93%E7%9A%84I%2FO%E5%A4%9A,%E6%97%A0%E9%9C%80%E9%98%BB%E5%A1%9E%E7%AD%89%E5%BE%85%E6%89%80%E6%9C%89%E8%BF%9E%E6%8E%A5%E3%80%82">深入理解redis——Redis快的原因和IO多路复用深度解析</a></p>]]></content>
      
      
      <categories>
          
          <category> Network </category>
          
      </categories>
      
      
        <tags>
            
            <tag> IO 多路复用 </tag>
            
            <tag> 同步 </tag>
            
            <tag> 异步 </tag>
            
            <tag> 阻塞 </tag>
            
            <tag> 非阻塞 </tag>
            
            <tag> Socket </tag>
            
        </tags>
      
    </entry>
    
    
    
    <entry>
      <title>C++ 实现高性能内存池</title>
      <link href="/posts/c-shi-xian-gao-xing-neng-nei-cun-chi/"/>
      <url>/posts/c-shi-xian-gao-xing-neng-nei-cun-chi/</url>
      
        <content type="html"><![CDATA[<p>本篇文章将介绍内存池的原理以及实现<br />项目链接：<a class="theme-btn" href="https://github.com/cs-moushuai/MemoryPool" title="GitHub"><i class="fab fa-github fa-fw fa-lg"></i>GitHub</a></p><h2 id="什么是内存池">什么是内存池？</h2><p>内存池和线程池类似，一次申请大量的内存，程序需要内存就从池子里拿，析构就将内存放回池子里</p><h2 id="内存池的优缺点">内存池的优缺点</h2><h3 id="优点">优点</h3><ol type="1"><li>速度快，因为内存池绝大部分情况下是用户态、无锁、O(1)或O(logN)的时间复杂度，所以速度会比malloc/free要快很多。</li><li>避免内存碎片，频繁且不规律的malloc/free，尤其是大量小对象的时候，可能会导致内存释放后，空闲的内存块被已分配的内存块分割成无法合并的多块小“碎块”。此时突然申请一块大内存，虽然总的空闲内存大小是足够的，但我们却无法利用它们来完成内存分配。</li><li>增加内存的利用率，由于malloc在分配内存时，需要增加一些必要的簿记信息，也就是记录内存块信息的头部结构，因此每次内存分配都会导致一定程度的浪费。而内存池不同，可以做到按需分配。使用得当的话内存浪费会很小。</li></ol><h3 id="缺点">缺点</h3><p>使用内存池的缺点是会导致一定程度上的编程复杂度增加。不同方法实现的内存池有各自在使用上需要注意的地方，随随便便的用可能会引起意料之外的问题。</p><h2 id="什么时候使用内存池">什么时候使用内存池？</h2><p>当程序需要频繁在堆上申请和释放内存时</p><h2 id="内存池原理">内存池原理</h2><p>内存池的思想是，在真正使用内存之前，预先申请分配一定数量、大小预设的内存块留作备用。当有新的内存需求时，就从内存池中分出一部分内存块，若内存块不够再继续申请新的内存，当内存释放后就回归到内存块留作后续的复用，使得内存使用效率得到提升，一般也不会产生不可控制的内存碎片。</p><h3 id="结构">结构</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">union</span> Slot_ <span class="token punctuation">&#123;</span>    value_type element<span class="token punctuation">;</span> <span class="token comment">// 内存池内部元素类型</span>    Slot_<span class="token operator">*</span> next<span class="token punctuation">;</span> <span class="token comment">// 指向下一个 block</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span class="token keyword">typedef</span> <span class="token keyword">char</span><span class="token operator">*</span>               data_pointer_<span class="token punctuation">;</span><span class="token keyword">typedef</span> Slot_               slot_type_<span class="token punctuation">;</span><span class="token keyword">typedef</span> Slot_<span class="token operator">*</span>              slot_pointer_<span class="token punctuation">;</span>slot_pointer_ currentBlock_<span class="token punctuation">;</span> <span class="token comment">// 指向当前块</span>slot_pointer_ currentSlot_<span class="token punctuation">;</span> <span class="token comment">// 指向当前块的 ele</span>slot_pointer_ lastSlot_<span class="token punctuation">;</span> <span class="token comment">// 指向当前块的最后一个 ele</span>slot_pointer_ freeSlots_<span class="token punctuation">;</span> <span class="token comment">// 释放的空间都存在空闲链表中</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>定义了一个联合体，可以表现为元素，也可以表现为下一个 block 的指针</p><p><img src="memory_pool.001.jpeg" alt="内存池结构" /></p><p>其中 Next 指向下一个 Block（每个块默认大小为 4096B），Pad 为 Block减去 Next 后剩余大小对 ele 类型大小取模的结果，最后有若干个 ele 元素</p><h3 id="申请空间">申请空间</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token punctuation">,</span> size_t BlockSize<span class="token operator">></span><span class="token keyword">void</span> <span class="token class-name">MemoryPool</span><span class="token operator">&lt;</span>T<span class="token punctuation">,</span> BlockSize<span class="token operator">></span><span class="token double-colon punctuation">::</span><span class="token function">allocateBlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token comment">// 分配新 Block</span>    data_pointer_ newBlock <span class="token operator">=</span> <span class="token generic-function"><span class="token function">reinterpret_cast</span><span class="token generic class-name"><span class="token operator">&lt;</span>data_pointer_<span class="token operator">></span></span></span>                            <span class="token punctuation">(</span><span class="token keyword">operator</span> <span class="token keyword">new</span><span class="token punctuation">(</span>BlockSize<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 头插法</span>    <span class="token generic-function"><span class="token function">reinterpret_cast</span><span class="token generic class-name"><span class="token operator">&lt;</span>slot_pointer_<span class="token operator">></span></span></span><span class="token punctuation">(</span>newBlock<span class="token punctuation">)</span><span class="token operator">-></span>next <span class="token operator">=</span> currentBlock_<span class="token punctuation">;</span>    currentBlock_ <span class="token operator">=</span> <span class="token generic-function"><span class="token function">reinterpret_cast</span><span class="token generic class-name"><span class="token operator">&lt;</span>slot_pointer_<span class="token operator">></span></span></span><span class="token punctuation">(</span>newBlock<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 保留一个大小指向下一个区块的地址，计算 body 位置</span>    <span class="token comment">// 用 currentBlock_ 要类型转换</span>    data_pointer_ body <span class="token operator">=</span> newBlock <span class="token operator">+</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>slot_pointer_<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 计算 body 中填充位置</span>    size_type padding <span class="token operator">=</span> <span class="token function">padPointer</span><span class="token punctuation">(</span>body<span class="token punctuation">,</span> <span class="token keyword">alignof</span><span class="token punctuation">(</span>slot_type_<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// currentSlot_ 指向（body+padding）头，lastSlot_ 指向最后一个位置地址 +1</span>    currentSlot_ <span class="token operator">=</span> <span class="token generic-function"><span class="token function">reinterpret_cast</span><span class="token generic class-name"><span class="token operator">&lt;</span>slot_pointer_<span class="token operator">></span></span></span><span class="token punctuation">(</span>body <span class="token operator">+</span> padding<span class="token punctuation">)</span><span class="token punctuation">;</span>    lastSlot_ <span class="token operator">=</span> <span class="token generic-function"><span class="token function">reinterpret_cast</span><span class="token generic class-name"><span class="token operator">&lt;</span>slot_pointer_<span class="token operator">></span></span></span>                <span class="token punctuation">(</span>newBlock <span class="token operator">+</span> BlockSize <span class="token operator">-</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>slot_type_<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token punctuation">,</span> size_t BlockSize<span class="token operator">></span><span class="token keyword">inline</span> <span class="token keyword">typename</span> <span class="token class-name">MemoryPool</span><span class="token operator">&lt;</span>T<span class="token punctuation">,</span> BlockSize<span class="token operator">></span><span class="token double-colon punctuation">::</span>pointer<span class="token class-name">MemoryPool</span><span class="token operator">&lt;</span>T<span class="token punctuation">,</span> BlockSize<span class="token operator">></span><span class="token double-colon punctuation">::</span><span class="token function">allocate</span><span class="token punctuation">(</span>size_type n<span class="token punctuation">,</span> const_pointer hint<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span>freeSlots_<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        pointer res <span class="token operator">=</span> <span class="token generic-function"><span class="token function">reinterpret_cast</span><span class="token generic class-name"><span class="token operator">&lt;</span>pointer<span class="token operator">></span></span></span><span class="token punctuation">(</span>freeSlots_<span class="token punctuation">)</span><span class="token punctuation">;</span>        freeSlots_ <span class="token operator">=</span> freeSlots_<span class="token operator">-></span>next<span class="token punctuation">;</span>        <span class="token keyword">return</span> res<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span>        <span class="token comment">// todo 为什么优先使用 freeSlot_ 中的块，那当前的块不就浪费了吗</span>        <span class="token comment">// answer：freeSlots 存放的是 slot 不是 block，不影响当前块</span>        <span class="token keyword">if</span> <span class="token punctuation">(</span>currentSlot_ <span class="token operator">>=</span> lastSlot_<span class="token punctuation">)</span>            <span class="token function">allocateBlock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token generic-function"><span class="token function">reinterpret_cast</span><span class="token generic class-name"><span class="token operator">&lt;</span>pointer<span class="token operator">></span></span></span><span class="token punctuation">(</span>currentSlot_<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>申请空间先在空闲链表中找，若没有再再当前块中找，若用完了就去申请一个新块，分配新块中要注意填充空间，且分配空间用的是placement new，即只申请不构造而 new 是先申请后构造</p><p>placement new</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">data_pointer_ newBlock <span class="token operator">=</span> <span class="token generic-function"><span class="token function">reinterpret_cast</span><span class="token generic class-name"><span class="token operator">&lt;</span>data_pointer_<span class="token operator">></span></span></span>                            <span class="token punctuation">(</span><span class="token keyword">operator</span> <span class="token keyword">new</span><span class="token punctuation">(</span>BlockSize<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>在申请的空间上 construct</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token punctuation">,</span> size_t BlockSize<span class="token operator">></span><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">U</span><span class="token punctuation">,</span> <span class="token keyword">typename</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> Args<span class="token operator">></span><span class="token keyword">inline</span> <span class="token keyword">void</span><span class="token class-name">MemoryPool</span><span class="token operator">&lt;</span>T<span class="token punctuation">,</span> BlockSize<span class="token operator">></span><span class="token double-colon punctuation">::</span><span class="token function">construct</span><span class="token punctuation">(</span>U<span class="token operator">*</span> p<span class="token punctuation">,</span> Args<span class="token operator">&amp;&amp;</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> args<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token comment">// todo 为什么类型是 U 不是 T</span>    <span class="token comment">// answer：U 是 value_type，本质也是 T</span>    <span class="token keyword">new</span> <span class="token punctuation">(</span>p<span class="token punctuation">)</span> <span class="token function">U</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span><span class="token generic-function"><span class="token function">forward</span><span class="token generic class-name"><span class="token operator">&lt;</span>Args<span class="token operator">></span></span></span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="释放空间">释放空间</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">// 释放单个元素</span><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token punctuation">,</span> size_t BlockSize<span class="token operator">></span><span class="token keyword">inline</span> <span class="token keyword">void</span><span class="token class-name">MemoryPool</span><span class="token operator">&lt;</span>T<span class="token punctuation">,</span> BlockSize<span class="token operator">></span><span class="token double-colon punctuation">::</span><span class="token function">deallocate</span><span class="token punctuation">(</span>pointer p<span class="token punctuation">,</span> size_type n<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span>p<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        <span class="token generic-function"><span class="token function">reinterpret_cast</span><span class="token generic class-name"><span class="token operator">&lt;</span>slot_pointer_<span class="token operator">></span></span></span><span class="token punctuation">(</span>p<span class="token punctuation">)</span><span class="token operator">-></span>next <span class="token operator">=</span> freeSlots_<span class="token punctuation">;</span>        freeSlots_ <span class="token operator">=</span> <span class="token generic-function"><span class="token function">reinterpret_cast</span><span class="token generic class-name"><span class="token operator">&lt;</span>slot_pointer_<span class="token operator">></span></span></span><span class="token punctuation">(</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token comment">// 析构单个元素</span><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token punctuation">,</span> size_t BlockSize<span class="token operator">></span><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">U</span><span class="token operator">></span><span class="token keyword">inline</span> <span class="token keyword">void</span><span class="token class-name">MemoryPool</span><span class="token operator">&lt;</span>T<span class="token punctuation">,</span> BlockSize<span class="token operator">></span><span class="token double-colon punctuation">::</span><span class="token function">destroy</span><span class="token punctuation">(</span>U<span class="token operator">*</span> p<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span>p<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        p<span class="token operator">-></span><span class="token operator">~</span><span class="token function">U</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token comment">// 按块释放</span><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token punctuation">,</span> size_t BlockSize<span class="token operator">></span>MemoryPool<span class="token operator">&lt;</span>T<span class="token punctuation">,</span> BlockSize<span class="token operator">></span><span class="token double-colon punctuation">::</span><span class="token operator">~</span><span class="token function">MemoryPool</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">noexcept</span> <span class="token punctuation">&#123;</span>    <span class="token comment">// 不要写 currentSlot_ 会将值当作地址</span>    slot_pointer_ curr <span class="token operator">=</span> currentBlock_<span class="token punctuation">;</span>    <span class="token keyword">while</span> <span class="token punctuation">(</span>curr<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        slot_pointer_ t <span class="token operator">=</span> curr<span class="token operator">-></span>next<span class="token punctuation">;</span>        <span class="token comment">// 转化为 void* 避免调用 dtor</span>        <span class="token keyword">operator</span> <span class="token keyword">delete</span><span class="token punctuation">(</span><span class="token generic-function"><span class="token function">reinterpret_cast</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token keyword">void</span><span class="token operator">*</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>curr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        curr <span class="token operator">=</span> t<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>释放元素：先调用 <code>destroy</code> 后调用<code>deallocate</code><br />释放内存池：<code>~MemoryPool()</code></p><h3 id="移动">移动</h3><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token punctuation">,</span> size_t BlockSize<span class="token operator">></span>MemoryPool<span class="token operator">&lt;</span>T<span class="token punctuation">,</span> BlockSize<span class="token operator">></span><span class="token operator">&amp;</span>MemoryPool<span class="token operator">&lt;</span>T<span class="token punctuation">,</span> BlockSize<span class="token operator">></span><span class="token double-colon punctuation">::</span><span class="token keyword">operator</span><span class="token operator">=</span><span class="token punctuation">(</span>MemoryPool <span class="token operator">&amp;&amp;</span>rhs<span class="token punctuation">)</span><span class="token keyword">noexcept</span> <span class="token punctuation">&#123;</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">!=</span> <span class="token operator">&amp;</span>rhs<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        std<span class="token double-colon punctuation">::</span><span class="token function">swap</span><span class="token punctuation">(</span>currentBlock_<span class="token punctuation">,</span> rhs<span class="token punctuation">.</span>currentBlock_<span class="token punctuation">)</span><span class="token punctuation">;</span>        currentSlot_ <span class="token operator">=</span> rhs<span class="token punctuation">.</span>currentSlot_<span class="token punctuation">;</span>        lastSlot_ <span class="token operator">=</span> rhs<span class="token punctuation">.</span>lastSlot_<span class="token punctuation">;</span>        freeSlots_ <span class="token operator">=</span> rhs<span class="token punctuation">.</span>freeSlots_<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">return</span> <span class="token operator">*</span><span class="token keyword">this</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>注：移动相关的函数都是 <code>noexcept</code>的，因为移动后原对象就可能损毁，不能进行第二次移动，另外非移动的且能保证一定不抛出异常的也可以用<code>noexcept</code> 来提高性能</p><h2 id="测试">测试</h2><p>链表栈的 <code>push</code> 和 <code>pop</code> 操作</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">// 入栈</span><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token punctuation">,</span> <span class="token keyword">typename</span> <span class="token class-name">Alloc</span><span class="token operator">></span><span class="token keyword">void</span> <span class="token class-name">StackAlloc</span><span class="token operator">&lt;</span>T<span class="token punctuation">,</span> Alloc<span class="token operator">></span><span class="token double-colon punctuation">::</span><span class="token function">push</span><span class="token punctuation">(</span>T element<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token comment">// 为一个节点分配内存</span>    Node<span class="token operator">*</span> newNode <span class="token operator">=</span> allocator_<span class="token punctuation">.</span><span class="token function">allocate</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 调用节点的构造函数</span>    allocator_<span class="token punctuation">.</span><span class="token function">construct</span><span class="token punctuation">(</span>newNode<span class="token punctuation">,</span> <span class="token function">Node</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">// 入栈操作</span>    newNode<span class="token operator">-></span>data <span class="token operator">=</span> element<span class="token punctuation">;</span>    newNode<span class="token operator">-></span>prev <span class="token operator">=</span> head_<span class="token punctuation">;</span>    head_ <span class="token operator">=</span> newNode<span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token comment">// 出栈</span><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token punctuation">,</span> <span class="token keyword">typename</span> <span class="token class-name">Alloc</span><span class="token operator">></span>T <span class="token class-name">StackAlloc</span><span class="token operator">&lt;</span>T<span class="token punctuation">,</span> Alloc<span class="token operator">></span><span class="token double-colon punctuation">::</span><span class="token function">pop</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>    <span class="token comment">// 出栈操作 返回出栈元素</span>    T result <span class="token operator">=</span> head_<span class="token operator">-></span>data<span class="token punctuation">;</span>    Node<span class="token operator">*</span> tmp <span class="token operator">=</span> head_<span class="token operator">-></span>prev<span class="token punctuation">;</span>    allocator_<span class="token punctuation">.</span><span class="token function">destroy</span><span class="token punctuation">(</span>head_<span class="token punctuation">)</span><span class="token punctuation">;</span>    allocator_<span class="token punctuation">.</span><span class="token function">deallocate</span><span class="token punctuation">(</span>head_<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    head_ <span class="token operator">=</span> tmp<span class="token punctuation">;</span>    <span class="token keyword">return</span> result<span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>测试代码：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">ELEMS</span> <span class="token expression"><span class="token number">10000000</span></span></span><span class="token comment">// 重复次数</span><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">REPS</span> <span class="token expression"><span class="token number">100</span></span></span><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>    clock_t start<span class="token punctuation">;</span>    <span class="token comment">// 使用 STL 默认分配器</span>    StackAlloc<span class="token operator">&lt;</span><span class="token keyword">int</span><span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span>allocator<span class="token operator">&lt;</span><span class="token keyword">int</span><span class="token operator">>></span> stackDefault<span class="token punctuation">;</span>    start <span class="token operator">=</span> <span class="token function">clock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> j <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> j <span class="token operator">&lt;</span> REPS<span class="token punctuation">;</span> j<span class="token operator">++</span><span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        <span class="token function">assert</span><span class="token punctuation">(</span>stackDefault<span class="token punctuation">.</span><span class="token function">empty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> ELEMS<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>            stackDefault<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> ELEMS<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>            stackDefault<span class="token punctuation">.</span><span class="token function">pop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    std<span class="token double-colon punctuation">::</span>cout <span class="token operator">&lt;&lt;</span> <span class="token string">"Default Allocator Time: "</span><span class="token punctuation">;</span>    std<span class="token double-colon punctuation">::</span>cout <span class="token operator">&lt;&lt;</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">double</span><span class="token punctuation">)</span><span class="token function">clock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> start<span class="token punctuation">)</span> <span class="token operator">/</span> CLOCKS_PER_SEC<span class="token punctuation">)</span> <span class="token operator">&lt;&lt;</span> <span class="token string">" s\n\n"</span><span class="token punctuation">;</span>    <span class="token comment">// 使用内存池</span>    StackAlloc<span class="token operator">&lt;</span><span class="token keyword">int</span><span class="token punctuation">,</span> MemoryPool<span class="token operator">&lt;</span><span class="token keyword">int</span><span class="token operator">>></span> stackPool<span class="token punctuation">;</span>    start <span class="token operator">=</span> <span class="token function">clock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> j <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> j <span class="token operator">&lt;</span> REPS<span class="token punctuation">;</span> j<span class="token operator">++</span><span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        <span class="token function">assert</span><span class="token punctuation">(</span>stackPool<span class="token punctuation">.</span><span class="token function">empty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> ELEMS<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>            stackPool<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> ELEMS<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>            stackPool<span class="token punctuation">.</span><span class="token function">pop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    std<span class="token double-colon punctuation">::</span>cout <span class="token operator">&lt;&lt;</span> <span class="token string">"MemoryPool Allocator Time: "</span><span class="token punctuation">;</span>    std<span class="token double-colon punctuation">::</span>cout <span class="token operator">&lt;&lt;</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">double</span><span class="token punctuation">)</span><span class="token function">clock</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> start<span class="token punctuation">)</span> <span class="token operator">/</span> CLOCKS_PER_SEC<span class="token punctuation">)</span> <span class="token operator">&lt;&lt;</span> <span class="token string">" s\n\n"</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>通过链表栈的频繁 <code>push</code> 和 <code>pop</code>操作来测试性能</p><pre class="line-numbers language-text" data-language="text"><code class="language-text">Default Allocator Time: 59.2128 sMemoryPool Allocator Time: 12.0348 s<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h2 id="总结">总结</h2><p>通过这个项目收获了内存分配的相关知识，对于 C++的相关特性，如模板，面向对象，可变参数，性能测试都有涉猎<br />最后这个项目也可以用单例模式来实现，这样整个程序就只有一个实例了，可以进一步调高性能，留给读者思考</p>]]></content>
      
      
      <categories>
          
          <category> 数据结构与算法 </category>
          
      </categories>
      
      
        <tags>
            
            <tag> C++ </tag>
            
            <tag> 内存池 </tag>
            
        </tags>
      
    </entry>
    
    
    
    <entry>
      <title>递归 vs 迭代</title>
      <link href="/posts/di-gui-vs-die-dai/"/>
      <url>/posts/di-gui-vs-die-dai/</url>
      
        <content type="html"><![CDATA[<p>首先提出两个问题</p><ul><li>所有递归都可以改写成循环吗？</li><li>改写后会有什么好处？</li></ul><p>接下来我们仔细分析下具体的区别</p><h2 id="递归转换为迭代的方法">递归转换为迭代的方法</h2><p>对于递归而言有两种转换方式</p><h3 id="一直接转换法">一、直接转换法</h3><p><strong>方法</strong>：使用变量保存中间结果</p><p>计算阶乘：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">long</span> <span class="token function">fact</span><span class="token punctuation">(</span><span class="token keyword">int</span> n<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>　　<span class="token keyword">if</span> <span class="token punctuation">(</span>n<span class="token operator">==</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token number">1</span><span class="token punctuation">;</span>　　<span class="token keyword">else</span> <span class="token keyword">return</span> n<span class="token operator">*</span><span class="token function">fact</span><span class="token punctuation">(</span>n<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">long</span> <span class="token function">fact</span><span class="token punctuation">(</span><span class="token keyword">int</span> n<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>　　<span class="token keyword">int</span> res <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>　　<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span> i<span class="token operator">&lt;=</span>n<span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span>　      res <span class="token operator">*=</span> i<span class="token punctuation">;</span>　　<span class="token keyword">return</span> res<span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>可以看到，时间复杂度依旧为 <spanclass="math inline">\(\mathcal{O}(n)\)</span>，但空间复杂度已经从 <spanclass="math inline">\(\mathcal{O}(n) \to \mathcal{O}(1)\)</span>了，因为原本需要调用 <code>fact(...)</code> <spanclass="math inline">\(n\)</span> 次，但现在只需要 <spanclass="math inline">\(1\)</span> 次了</p><h3 id="二间接转换法">二、间接转换法</h3><p><strong>方法</strong>：使用栈保存中间结果</p><p>树的先序遍历：</p><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token comment">/** * Definition for a binary tree node. * struct TreeNode &#123; *     int val; *     TreeNode *left; *     TreeNode *right; *     TreeNode() : val(0), left(nullptr), right(nullptr) &#123;&#125; *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) &#123;&#125; *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) &#123;&#125; * &#125;; */</span><span class="token keyword">class</span> <span class="token class-name">Solution</span> <span class="token punctuation">&#123;</span><span class="token keyword">public</span><span class="token operator">:</span>    <span class="token keyword">void</span> <span class="token function">travel</span><span class="token punctuation">(</span>TreeNode <span class="token operator">*</span>root<span class="token punctuation">)</span>    <span class="token punctuation">&#123;</span>        <span class="token keyword">if</span><span class="token punctuation">(</span>root <span class="token operator">==</span> <span class="token keyword">nullptr</span><span class="token punctuation">)</span>        <span class="token punctuation">&#123;</span>            <span class="token keyword">return</span><span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span>        res<span class="token punctuation">.</span><span class="token function">push_back</span><span class="token punctuation">(</span>root<span class="token operator">-></span>val<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">travel</span><span class="token punctuation">(</span>root<span class="token operator">-></span>left<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token function">travel</span><span class="token punctuation">(</span>root<span class="token operator">-></span>right<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    vector<span class="token operator">&lt;</span><span class="token keyword">int</span><span class="token operator">></span> <span class="token function">preorderTraversal</span><span class="token punctuation">(</span>TreeNode<span class="token operator">*</span> root<span class="token punctuation">)</span>     <span class="token punctuation">&#123;</span>        <span class="token function">travel</span><span class="token punctuation">(</span>root<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> res<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token keyword">private</span><span class="token operator">:</span>    vector<span class="token operator">&lt;</span><span class="token keyword">int</span><span class="token operator">></span> res<span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">class</span> <span class="token class-name">Solution</span> <span class="token punctuation">&#123;</span><span class="token keyword">public</span><span class="token operator">:</span>    vector<span class="token operator">&lt;</span><span class="token keyword">int</span><span class="token operator">></span> <span class="token function">preorderTraversal</span><span class="token punctuation">(</span>TreeNode<span class="token operator">*</span> root<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        vector<span class="token operator">&lt;</span><span class="token keyword">int</span><span class="token operator">></span> ans<span class="token punctuation">;</span>        stack<span class="token operator">&lt;</span>TreeNode<span class="token operator">*</span><span class="token operator">></span> st<span class="token punctuation">;</span>        TreeNode <span class="token operator">*</span>p<span class="token operator">=</span>root<span class="token punctuation">;</span>        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>st<span class="token punctuation">.</span><span class="token function">empty</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">||</span> p<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>            <span class="token keyword">while</span> <span class="token punctuation">(</span>p<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>                ans<span class="token punctuation">.</span><span class="token function">emplace_back</span><span class="token punctuation">(</span>p<span class="token operator">-></span>val<span class="token punctuation">)</span><span class="token punctuation">;</span>                st<span class="token punctuation">.</span><span class="token function">emplace</span><span class="token punctuation">(</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>                p <span class="token operator">=</span> p<span class="token operator">-></span>left<span class="token punctuation">;</span>            <span class="token punctuation">&#125;</span>            p <span class="token operator">=</span> st<span class="token punctuation">.</span><span class="token function">top</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            st<span class="token punctuation">.</span><span class="token function">pop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>            p <span class="token operator">=</span> p<span class="token operator">-></span>right<span class="token punctuation">;</span>        <span class="token punctuation">&#125;</span>        <span class="token keyword">return</span> ans<span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>但此处的时间和空间复杂度却都没有变化，因为用栈来保存结果，本质就是在模拟函数调用的过程，并没有什么好处，但编写代码的难度却大大提高</p><p>所以，回答下开头的问题</p><h2 id="所有递归都可以改写成循环吗">所有递归都可以改写成循环吗？</h2><p>是的，所有递归都使用直接或间接的方式转换</p><h2 id="改写后会有什么好处">改写后会有什么好处？</h2><p>使用直接转换可以避免空间上的开销，使用间接转换本质在模拟函数调用没有意义</p><h2 id="总结">总结</h2><p>虽然递归有着很大的空间开销，但很多时候我们是很难用迭代写出我们想要的程序，而且空间上的开销也不是很重要，相比较迭代的自底向上，递归的自顶向下往往更符合人类的逻辑，很多时候做动态规划的题目没有思路的时候，都可以考虑用自顶向下的记忆化搜索来完成</p>]]></content>
      
      
      <categories>
          
          <category> 数据结构与算法 </category>
          
      </categories>
      
      
        <tags>
            
            <tag> C++ </tag>
            
            <tag> 递归 </tag>
            
            <tag> 迭代 </tag>
            
        </tags>
      
    </entry>
    
    
    
    <entry>
      <title>锂电池保养手册</title>
      <link href="/posts/li-dian-chi-bao-yang-shou-ce/"/>
      <url>/posts/li-dian-chi-bao-yang-shou-ce/</url>
      
        <content type="html"><![CDATA[<p>为什么有的人电池用了半年还有 <strong>100%</strong>，而有的人就剩下<strong>90%</strong> 了呢？</p><h2 id="原则">原则</h2><p>电池充电可以比喻成人吃饭，尽量少吃多餐</p><h2 id="现代锂电池充电规则">现代锂电池充电规则</h2><p><img src="1.jpeg" alt="放电流程" /></p><h2 id="注意点">注意点</h2><ol type="1"><li>避免在 <strong>20%</strong> 以下使用，尤其是 <strong>5%</strong>以下</li><li>电量保存在 <strong>25%-75%</strong></li><li>避免高温低温时使用，建议在 <strong>16-22度</strong> 间最好</li><li>高温时避免边冲边玩，快充也会提高温度，锂电池高温掉容量</li><li>长时间不用的电池要定时充电到 <strong>50%</strong>（每个6月）无电导致电池可能再也无法充电，满电可能导致容量下降</li><li>一个月进行一次充电循环，方法是将电池电量用到 <strong>15%</strong>左右，然后充满，就可以完成一次循环了</li></ol><blockquote><p>ps:一般设备，如macbook知道你经常充电就会将battery维持在80%以维持电压</p></blockquote><h2 id="电量对性能的影响">电量对性能的影响</h2><p>电量降低也会影响性能，而对于那些电池容量已经低于 <strong>80%</strong>的iPhone来说，影响体验的话，还是尽早换块新电池吧。</p><p>毕竟，在需要更极端的性能管理的情况下，体验上可能会受到影响，用苹果的话来说：</p><ul><li>应用启动时间变长</li><li>滚动时帧速率降低</li><li>背光灯变暗（可在“控制中心”手动调整）</li><li>扬声器音量降低幅度高达 -3dB</li><li>部分应用的帧速率逐渐降低</li><li>在最极端的情况下，相机闪光灯会被停用（会显示在相机用户界面上）</li><li>在后台刷新的应用在启动时可能需要重新载入</li></ul><h2 id="faq">FAQ</h2><h3 id="电池维持在-25-到-75-对电池好有这个说法吗">电池维持在 25 到 75对电池好，有这个说法吗？</h3><p>对的，因为高压低压对电池都不好</p><h3 id="电池会过冲吗">电池会过冲吗？</h3><p>不会，终端产品的锂电池都是双重保护的，适配器一个保护板，电池有一个保护板，超过保护板的最低电压它就停止放电了，充电同原理。基本不会存在过冲过放的问，但高压对电池不好<img src="2.png" alt="充电流程" /></p><h2 id="总结">总结</h2><p>电池是消耗品，换一个也就几百，必要的时候该怎么用就怎么用，如果真的想保护好电池，最简单的保障电量<strong>25%-75%</strong>，如果是 iphone的话，开启手机的优化充电模式，这样在你睡觉的时候大部分时间电量会保持在<strong>75%</strong>，且不要在手机温度过高的时候充电就够了。</p>]]></content>
      
      
      <categories>
          
          <category> 日常生活 </category>
          
      </categories>
      
      
        <tags>
            
            <tag> 数码 </tag>
            
            <tag> 锂电池 </tag>
            
        </tags>
      
    </entry>
    
    
  
  
</search>
