面试常见问题 1

<p>部分内容为GPT的回答。</p><h2 id=&#34;和红黑树的功能类似的还有哪些数据结构&#34;>和红黑树的功能类似的还有哪些数据结构</h2><p>红黑树是一种自平衡的二叉搜索树,它通过特定的属性和操作保持树的平衡,从而实现在最坏情况下仍能保持对数级别的查找、插入和删除时间复杂度。和红黑树提供相似功能的数据结构主要包括其他类型的自平衡二叉搜索树和一些特殊的数据结构,它们都能提供高效的查找、插入和删除操作。下面列出一些常见的例子:</p><h3 id=&#34;avl树&#34;>AVL树</h3><p>AVL树是最早被发明的自平衡二叉搜索树之一。它严格保持二叉树的平衡,即任何节点的两个子树的高度最大差为1。这种严格的平衡要求使得AVL树在查找操作中表现得非常好,但在频繁的插入和删除操作中可能会有较多的旋转调整,相比于红黑树可能略显低效。</p><h3 id=&#34;b树及其变种&#34;>B树及其变种</h3><p>B树是一种平衡的多路搜索树,主要用于数据库和文件系统。B树通过将节点中存储多个元素并允许节点有多个子节点来减少树的高度,从而优化磁盘读写操作。B+树和B*树是B树的变种,它们在B树的基础上做了进一步的优化,广泛应用于数据库索引。</p><h3 id=&#34;splay树&#34;>Splay树</h3><p>Splay树是一种自调整的二叉搜索树,它通过一种称为“伸展”的操作来保证最近进行的操作可以在较快的时间内完成。伸展树不保证树的严格平衡,但它能保证分摊的时间复杂度为O(log n)。</p><h3 id=&#34;treap&#34;>Treap</h3><p>Treap是一种结合了二叉搜索树和堆的数据结构,节点既按键排序又按优先级堆组织。它利用旋转操作来维持二叉搜索树的性质和堆的性质,实现了平衡树的效果。</p><h3 id=&#34;skip-list&#34;>Skip List</h3><p>跳跃表是一种概率性的数据结构,通过在多层链表上添加“快速通道”来实现快速查找。虽然不是树结构,跳跃表在功能上与红黑树相似,提供了高效的查找、插入和删除操作,其平均时间和空间复杂度都是O(log n)。跳跃表的实现相比于红黑树和AVL树更为简单,易于理解和实现。</p><h3 id=&#34;aa树&#34;>AA树</h3><p>AA树是一种变种的红黑树,它简化了红黑树的平衡规则和旋转操作。通过限制只有右链接可以是红色,AA树使得实现变得更加简单,同时保持了与红黑树相似的操作性能。</p><p>这些数据结构各有特点,适用于不同的场景。选择合适的数据结构需要根据实际的应用场景和性能要求来决定。</p><h2 id=&#34;五千万个整数求和尽量降低时间消耗怎么做&#34;>五千万个整数求和,尽量降低时间消耗。怎么做?</h2><p>对于五千万个整数求和的问题,主要目标是优化时间复杂度和减少执行时间。以下是一些优化策略,可以帮助提高求和操作的效率:</p><h3 id=&#34;1-使用有效的数据结构&#34;>1. 使用有效的数据结构</h3><ul><li>确保整数存储在连续的内存空间中,比如使用数组或列表,这样可以减少内存访问的开销。</li></ul><h3 id=&#34;2-循环优化&#34;>2. 循环优化</h3><ul><li>尽可能使用高效的循环结构,减少循环内不必要的计算和条件判断。</li></ul><h3 id=&#34;3-多线程或并行计算&#34;>3. 多线程或并行计算</h3><ul><li>利用现代计算机多核心的优势,可以将五千万个整数分成几个部分,每个核心处理一部分数据,然后将结果汇总。这种方式可以大大减少总的计算时间。</li><li>使用如OpenMP、Intel TBB(Thread Building Blocks)、Python的<code>concurrent.futures</code>模块或Java的<code>java.util.concurrent</code>包等并行框架和库来简化多线程编程的复杂性。</li></ul><h3 id=&#34;4-向量化运算&#34;>4. 向量化运算</h3><ul><li>在支持向量化的编程语言中(如使用NumPy库的Python),可以利用向量化运算来提高性能,因为这些运算是在底层进行优化的。</li></ul><h3 id=&#34;5-减少函数调用&#34;>5. 减少函数调用</h3><ul><li>在求和过程中尽量减少函数调用,比如避免在循环中调用函数,因为函数调用会增加额外的时间开销。</li></ul><h3 id=&#34;实践示例python-并行计算&#34;>实践示例(Python 并行计算)</h3><p>这里是一个使用Python的<code>concurrent.futures</code>模块进行并行计算求和的简单例子:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-python&#34; data-lang=&#34;python&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>import</span> concurrent.futures</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#f92672&#34;>import</span> numpy <span style=&#34;color:#66d9ef&#34;>as</span> np</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span><span style=&#34;color:#66d9ef&#34;>def</span> <span style=&#34;color:#a6e22e&#34;>parallel_sum</span>(arr, n_workers<span style=&#34;color:#f92672&#34;>=</span><span style=&#34;color:#ae81ff&#34;>4</span>):</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span> <span style=&#34;color:#75715e&#34;># 分割数组为多个子数组</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span> chunk_size <span style=&#34;color:#f92672&#34;>=</span> len(arr) <span style=&#34;color:#f92672&#34;>//</span> n_workers</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span> futures <span style=&#34;color:#f92672&#34;>=</span> []</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span> <span style=&#34;color:#66d9ef&#34;>with</span> concurrent<span style=&#34;color:#f92672&#34;>.</span>futures<span style=&#34;color:#f92672&#34;>.</span>ThreadPoolExecutor(max_workers<span style=&#34;color:#f92672&#34;>=</span>n_workers) <span style=&#34;color:#66d9ef&#34;>as</span> executor:</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> <span style=&#34;color:#66d9ef&#34;>for</span> i <span style=&#34;color:#f92672&#34;>in</span> range(n_workers):</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span> chunk <span style=&#34;color:#f92672&#34;>=</span> arr[i<span style=&#34;color:#f92672&#34;>*</span>chunk_size : (i<span style=&#34;color:#f92672&#34;>+</span><span style=&#34;color:#ae81ff&#34;>1</span>)<span style=&#34;color:#f92672&#34;>*</span>chunk_size]</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span> futures<span style=&#34;color:#f92672&#34;>.</span>append(executor<span style=&#34;color:#f92672&#34;>.</span>submit(np<span style=&#34;color:#f92672&#34;>.</span>sum, chunk))</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span> </span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> <span style=&#34;color:#75715e&#34;># 收集结果</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> result <span style=&#34;color:#f92672&#34;>=</span> sum(f<span style=&#34;color:#f92672&#34;>.</span>result() <span style=&#34;color:#66d9ef&#34;>for</span> f <span style=&#34;color:#f92672&#34;>in</span> futures)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> result</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span><span style=&#34;color:#75715e&#34;># 生成五千万个整数的数组</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span>arr <span style=&#34;color:#f92672&#34;>=</span> np<span style=&#34;color:#f92672&#34;>.</span>random<span style=&#34;color:#f92672&#34;>.</span>randint(<span style=&#34;color:#ae81ff&#34;>1</span>, <span style=&#34;color:#ae81ff&#34;>100</span>, <span style=&#34;color:#ae81ff&#34;>50000000</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span>result <span style=&#34;color:#f92672&#34;>=</span> parallel_sum(arr)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span>print(result)</span></span></code></pre></div><p>这个例子展示了如何使用Python的并发特性和NumPy库来提高求和操作的效率。通过将数据分割成多个块并并行计算每个块的和,然后再将这些和加起来,可以显著减少计算所需的时间。注意,实际效果会根据你的硬件配置、Python版本和其他因素而有所不同。</p><h2 id=&#34;static关键字在c中的作用&#34;>static关键字在C++中的作用</h2><p>在C++中,<code>static</code>关键字有几个不同的用途,根据其应用的上下文,它可以用于控制变量的存储期(生命周期)、可见性(链接性)、以及在类定义中控制成员的属性。以下是<code>static</code>在不同情况下的用法概述:</p><h3 id=&#34;1-控制局部变量的存储期&#34;>1. 控制局部变量的存储期</h3><p>在函数内部声明的局部变量通常在函数调用时创建,在函数返回时销毁。如果将局部变量声明为<code>static</code>,则该变量的生命周期会被延长到程序的整个运行期间,即使它的作用域仍然局限于声明它的那个函数内部。这意味着函数在多次调用之间可以保留变量的值。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>function</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span> <span style=&#34;color:#66d9ef&#34;>static</span> <span style=&#34;color:#66d9ef&#34;>int</span> counter <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#ae81ff&#34;>0</span>; <span style=&#34;color:#75715e&#34;>// counter的值在函数调用之间保持</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#75715e&#34;></span> counter<span style=&#34;color:#f92672&#34;>++</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> counter <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> std<span style=&#34;color:#f92672&#34;>::</span>endl;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span>}</span></span></code></pre></div><h3 id=&#34;2-控制全局变量的链接性&#34;>2. 控制全局变量的链接性</h3><p>在文件范围内声明的<code>static</code>变量或函数,其链接性为内部的(internal linkage),这意味着它们只在定义它们的文件内可见,对其他文件是隐藏的。这有助于避免命名冲突,并保护数据不被其他文件意外访问或修改。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>static</span> <span style=&#34;color:#66d9ef&#34;>int</span> globalVar <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#ae81ff&#34;>0</span>; <span style=&#34;color:#75715e&#34;>// 仅在此文件内可见</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#66d9ef&#34;>static</span> <span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>helperFunction</span>() { <span style=&#34;color:#75715e&#34;>// 仅在此文件内可用</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#75715e&#34;>// 函数实现</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><h3 id=&#34;3-类的静态成员&#34;>3. 类的静态成员</h3><p>在类定义中,<code>static</code>关键字用于声明静态成员变量和静态成员函数。静态成员属于整个类而不是类的任何特定对象,因此即使没有创建类的实例,也可以访问静态成员。所有实例共享同一静态成员数据。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>MyClass</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span> <span style=&#34;color:#66d9ef&#34;>static</span> <span style=&#34;color:#66d9ef&#34;>int</span> staticVar; <span style=&#34;color:#75715e&#34;>// 静态成员变量</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span> <span style=&#34;color:#66d9ef&#34;>static</span> <span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>staticFunction</span>() { <span style=&#34;color:#75715e&#34;>// 静态成员函数</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#75715e&#34;></span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> <span style=&#34;color:#e6db74&#34;>&#34;Accessing static function.&#34;</span> <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> std<span style=&#34;color:#f92672&#34;>::</span>endl;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span>};</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#66d9ef&#34;>int</span> MyClass<span style=&#34;color:#f92672&#34;>::</span>staticVar <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#ae81ff&#34;>0</span>; <span style=&#34;color:#75715e&#34;>// 静态成员变量的定义和初始化</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span><span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>example</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> MyClass<span style=&#34;color:#f92672&#34;>::</span>staticVar <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#ae81ff&#34;>5</span>; <span style=&#34;color:#75715e&#34;>// 访问静态成员变量</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span><span style=&#34;color:#75715e&#34;></span> MyClass<span style=&#34;color:#f92672&#34;>::</span>staticFunction(); <span style=&#34;color:#75715e&#34;>// 调用静态成员函数</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><h3 id=&#34;总结&#34;>总结</h3><ul><li>当<code>static</code>用于函数内的局部变量时,它延长了变量的生命周期。</li><li>当<code>static</code>用于文件范围内的全局变量或函数时,它限制了变量或函数的链接性,使其仅在定义它们的文件内可见。</li><li>在类的上下文中,<code>static</code>用于声明属于整个类而不是某个实例的成员,允许在没有类实例的情况下访问这些成员。</li></ul><p>这些特性使得<code>static</code>成为C++编程中一个非常有用和灵活的关键字。</p><h2 id=&#34;static-成员函数&#34;>Static 成员函数</h2><p>在C++中,类的静态成员函数是属于整个类而不是类的任何特定对象的函数。静态成员函数与静态成员变量一样,它们不依赖于类的实例。这意味着即使没有创建类的对象,也可以调用静态成员函数。静态成员函数只能访问其类的静态成员变量和其他静态成员函数;它们不能直接访问类的非静态成员变量或函数,因为这些成员需要一个具体的类实例。</p><h3 id=&#34;特性&#34;>特性</h3><ul><li><strong>类级别的函数</strong>:静态成员函数作用于类级别而非实例级别。</li><li><strong>访问限制</strong>:只能直接访问静态成员变量和其他静态成员函数,不能直接访问非静态成员。</li><li><strong>调用方式</strong>:通过类名和作用域解析操作符<code>::</code>调用,而不是通过类的实例。</li></ul><h3 id=&#34;使用场景&#34;>使用场景</h3><ul><li><strong>工具函数</strong>:当需要一个与类相关但不需要类实例就能执行的功能时,静态成员函数很有用。例如,计算两个参数的最大值或最小值等。</li><li><strong>单例模式</strong>:在实现单例模式时,用于获取类的唯一实例的函数通常声明为静态的。</li><li><strong>访问静态成员变量</strong>:用于访问和修改静态成员变量的值,尤其是当这些变量被声明为私有时,提供公共的静态成员函数来获取和设置它们的值是一种常见的做法。</li></ul><h3 id=&#34;示例代码&#34;>示例代码</h3><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#75715e&#34;>#include</span> <span style=&#34;color:#75715e&#34;>&lt;iostream&gt;</span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>Example</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span> <span style=&#34;color:#66d9ef&#34;>static</span> <span style=&#34;color:#66d9ef&#34;>int</span> staticVar;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span> <span style=&#34;color:#66d9ef&#34;>static</span> <span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>staticFunction</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> <span style=&#34;color:#e6db74&#34;>&#34;Accessing staticVar: &#34;</span> <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> staticVar <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> std<span style=&#34;color:#f92672&#34;>::</span>endl;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span> <span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>nonStaticFunction</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span> <span style=&#34;color:#75715e&#34;>// 静态成员函数可以被非静态成员函数调用</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span><span style=&#34;color:#75715e&#34;></span> staticFunction();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span>};</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span><span style=&#34;color:#75715e&#34;>// 静态成员变量的初始化</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>int</span> Example<span style=&#34;color:#f92672&#34;>::</span>staticVar <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#ae81ff&#34;>10</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span><span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>21</span><span> <span style=&#34;color:#75715e&#34;>// 通过类名调用静态成员函数</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>22</span><span><span style=&#34;color:#75715e&#34;></span> Example<span style=&#34;color:#f92672&#34;>::</span>staticFunction(); <span style=&#34;color:#75715e&#34;>// 输出: Accessing staticVar: 10</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>23</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>24</span><span> <span style=&#34;color:#75715e&#34;>// 修改静态成员变量</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>25</span><span><span style=&#34;color:#75715e&#34;></span> Example<span style=&#34;color:#f92672&#34;>::</span>staticVar <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#ae81ff&#34;>20</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>26</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>27</span><span> <span style=&#34;color:#75715e&#34;>// 再次调用静态成员函数</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>28</span><span><span style=&#34;color:#75715e&#34;></span> Example<span style=&#34;color:#f92672&#34;>::</span>staticFunction(); <span style=&#34;color:#75715e&#34;>// 输出: Accessing staticVar: 20</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>29</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>30</span><span> <span style=&#34;color:#75715e&#34;>// 创建类实例并调用非静态成员函数</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>31</span><span><span style=&#34;color:#75715e&#34;></span> Example example;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>32</span><span> example.nonStaticFunction(); <span style=&#34;color:#75715e&#34;>// 输出: Accessing staticVar: 20</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>33</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>34</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#ae81ff&#34;>0</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>35</span><span>}</span></span></code></pre></div><p>在这个例子中,<code>staticFunction</code>是一个静态成员函数,它访问了同一个类的静态成员变量<code>staticVar</code>。即使没有<code>Example</code>类的实例,也可以通过<code>Example::staticFunction()</code>来调用这个函数。静态成员函数是类的一部分,而不是任何特定实例的一部分,因此它们在设计类时提供了一种灵活的工具,尤其是在实现只与类相关而不依赖于类实例的行为时。</p><h2 id=&#34;不同文件中的static变量的初始化顺序有规定吗&#34;>不同文件中的static变量的初始化顺序有规定吗</h2><p>在C++中,不同文件(编译单元)中的静态变量(也称为静态存储期变量,包括全局变量、文件作用域的静态变量以及类的静态成员变量)的初始化顺序涉及到两个主要概念:在单个编译单元内的初始化顺序和跨多个编译单元的初始化顺序。</p><h3 id=&#34;单个编译单元内的初始化顺序&#34;>单个编译单元内的初始化顺序</h3><p>在一个给定的编译单元(通常是一个源文件)内,静态变量按照它们出现的顺序被初始化。这意味着第一个声明的静态变量会先被初始化,以此类推。这部分是明确且可预测的。</p><h3 id=&#34;跨多个编译单元的初始化顺序&#34;>跨多个编译单元的初始化顺序</h3><p>跨多个编译单元(不同的源文件)的静态变量初始化顺序是未定义的。这意味着,如果你有两个源文件,每个文件中都定义了一个静态变量,那么你不能保证这两个变量哪一个会先被初始化。这种情况通常被称为“静态初始化顺序问题”或“静态初始化顺序地狱”。</p><h3 id=&#34;静态初始化顺序问题的解决办法&#34;>静态初始化顺序问题的解决办法</h3><p>为了解决或避免跨编译单元的静态初始化顺序问题,C++提供了一种被称为“构造函数初始化”(Construct On First Use)的技巧。它通过将静态变量封装在函数内部来延迟变量的初始化,直到这个变量被首次使用时才进行。这样做的好处是确保了初始化的顺序性和确定性。</p><p>例如:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#75715e&#34;>// 获取静态变量的函数</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#75715e&#34;></span>SomeClass<span style=&#34;color:#f92672&#34;>&amp;</span> getStaticInstance() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span> <span style=&#34;color:#66d9ef&#34;>static</span> SomeClass instance; <span style=&#34;color:#75715e&#34;>// 首次调用此函数时初始化</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>return</span> instance;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span>}</span></span></code></pre></div><p>在这个例子中,<code>instance</code>变量在<code>getStaticInstance()</code>函数首次被调用时初始化。这保证了无论变量在程序中被首次访问的时间点或顺序如何,<code>instance</code>的初始化都是安全和确定的。</p><h3 id=&#34;总结-1&#34;>总结</h3><p>虽然C++标准没有规定不同编译单元中静态变量的初始化顺序,但通过使用特定的设计模式和技巧,如“构造函数初始化”模式,可以有效地避免由此产生的问题。这种方法不仅可以解决初始化顺序问题,还可以延迟对象的初始化直到真正需要时,从而提高程序的启动性能和资源使用效率。</p><h2 id=&#34;c单例模式代码&#34;>C++单例模式代码</h2><p>单例模式是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。在C++中实现单例模式通常涉及以下几个关键步骤:</p><ol><li>将构造函数、复制构造函数和赋值运算符标记为私有或删除,以防止外部创建类的多个实例。</li><li>在类内部提供一个静态私有指针变量,用于持有唯一的类实例。</li><li>提供一个公有的静态方法,用于获取这个唯一实例。</li></ol><p>下面是单例模式的一个基本实现示例:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#75715e&#34;>#include</span> <span style=&#34;color:#75715e&#34;>&lt;iostream&gt;</span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>Singleton</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span><span style=&#34;color:#66d9ef&#34;>private</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span> <span style=&#34;color:#75715e&#34;>// 私有静态成员变量,指向唯一实例</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>static</span> Singleton<span style=&#34;color:#f92672&#34;>*</span> instance;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span> <span style=&#34;color:#75715e&#34;>// 私有构造函数,防止外部直接创建实例</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span><span style=&#34;color:#75715e&#34;></span> Singleton() {}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span> <span style=&#34;color:#75715e&#34;>// 私有复制构造函数和赋值运算符,防止复制实例</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span><span style=&#34;color:#75715e&#34;></span> Singleton(<span style=&#34;color:#66d9ef&#34;>const</span> Singleton<span style=&#34;color:#f92672&#34;>&amp;</span>) <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#66d9ef&#34;>delete</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> Singleton<span style=&#34;color:#f92672&#34;>&amp;</span> <span style=&#34;color:#66d9ef&#34;>operator</span><span style=&#34;color:#f92672&#34;>=</span>(<span style=&#34;color:#66d9ef&#34;>const</span> Singleton<span style=&#34;color:#f92672&#34;>&amp;</span>) <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#66d9ef&#34;>delete</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span> <span style=&#34;color:#75715e&#34;>// 公有静态方法,用于获取唯一实例的引用</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>static</span> Singleton<span style=&#34;color:#f92672&#34;>&amp;</span> getInstance() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span> <span style=&#34;color:#66d9ef&#34;>if</span> (instance <span style=&#34;color:#f92672&#34;>==</span> <span style=&#34;color:#66d9ef&#34;>nullptr</span>) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span> instance <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#66d9ef&#34;>new</span> Singleton();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>21</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#f92672&#34;>*</span>instance;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>22</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>23</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>24</span><span> <span style=&#34;color:#75715e&#34;>// 示例方法</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>25</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>doSomething</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>26</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> <span style=&#34;color:#e6db74&#34;>&#34;Doing something.&#34;</span> <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> std<span style=&#34;color:#f92672&#34;>::</span>endl;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>27</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>28</span><span>};</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>29</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>30</span><span><span style=&#34;color:#75715e&#34;>// 初始化静态成员变量</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>31</span><span><span style=&#34;color:#75715e&#34;></span>Singleton<span style=&#34;color:#f92672&#34;>*</span> Singleton<span style=&#34;color:#f92672&#34;>::</span>instance <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#66d9ef&#34;>nullptr</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>32</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>33</span><span><span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>34</span><span> <span style=&#34;color:#75715e&#34;>// 获取单例的引用并调用方法</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>35</span><span><span style=&#34;color:#75715e&#34;></span> Singleton<span style=&#34;color:#f92672&#34;>::</span>getInstance().doSomething();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>36</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>37</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#ae81ff&#34;>0</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>38</span><span>}</span></span></code></pre></div><p>这个例子展示了单例模式的核心要素:私有的构造函数和静态方法<code>getInstance()</code>确保全局只能通过该方法访问唯一的实例。通过将复制构造函数和赋值运算符设置为私有或删除,防止了实例的复制。</p><p>需要注意的是,这个基本实现在多线程环境下可能不是线程安全的。在多线程程序中,可能需要额外的同步机制(如互斥锁)来确保在创建单例实例时只有一个线程可以执行初始化代码。</p><p>对于多线程安全的单例实现,C++11及以后的版本提供了一个更简洁的方法,依靠局部静态变量的特性来保证线程安全:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>Singleton</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span> <span style=&#34;color:#75715e&#34;>// 删除复制构造函数和赋值运算符</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span><span style=&#34;color:#75715e&#34;></span> Singleton(<span style=&#34;color:#66d9ef&#34;>const</span> Singleton<span style=&#34;color:#f92672&#34;>&amp;</span>) <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#66d9ef&#34;>delete</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span> Singleton<span style=&#34;color:#f92672&#34;>&amp;</span> <span style=&#34;color:#66d9ef&#34;>operator</span><span style=&#34;color:#f92672&#34;>=</span>(<span style=&#34;color:#66d9ef&#34;>const</span> Singleton<span style=&#34;color:#f92672&#34;>&amp;</span>) <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#66d9ef&#34;>delete</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span> <span style=&#34;color:#75715e&#34;>// 提供一个静态方法获取单例引用</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>static</span> Singleton<span style=&#34;color:#f92672&#34;>&amp;</span> getInstance() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> <span style=&#34;color:#66d9ef&#34;>static</span> Singleton instance; <span style=&#34;color:#75715e&#34;>// 局部静态变量</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>return</span> instance;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> <span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>doSomething</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> <span style=&#34;color:#e6db74&#34;>&#34;Doing something.&#34;</span> <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> std<span style=&#34;color:#f92672&#34;>::</span>endl;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span><span style=&#34;color:#66d9ef&#34;>private</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span> <span style=&#34;color:#75715e&#34;>// 私有构造函数</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span><span style=&#34;color:#75715e&#34;></span> Singleton() {}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span>};</span></span></code></pre></div><p>在这个版本中,<code>instance</code>是一个局部静态变量,C++标准保证了当<code>getInstance()</code>第一次被调用时,<code>instance</code>将被创建,并且只会创建一次,即使在多线程环境下也能保证这一点。这种方法简化了代码,同时保证了线程安全性。</p><h2 id=&#34;静态变量和线程安全的关系&#34;>静态变量和线程安全的关系?</h2><p>静态变量和线程安全之间的关系是一个重要的考虑点,特别是在多线程程序设计中。理解这一关系有助于开发出安全且高效的并发应用。</p><h3 id=&#34;静态变量&#34;>静态变量</h3><p>静态变量在程序的生命周期内只被初始化一次,并在后续的程序运行过程中保持其状态。在C++中,静态变量可以是全局静态变量、局部静态变量、或是类的静态成员变量。全局静态变量和类的静态成员变量在所有对象和函数调用中共享,而局部静态变量在其所在函数的所有调用中共享。</p><h3 id=&#34;线程安全&#34;>线程安全</h3><p>线程安全是指代码在多线程环境中被执行时,能够正确处理多个线程同时访问相同资源的情况,而不会导致数据损坏或不一致。如果一个函数或数据结构可以在多个线程中安全地被并发使用,不需要额外的同步机制,就称它为线程安全的。</p><h3 id=&#34;静态变量与线程安全&#34;>静态变量与线程安全</h3><p>由于静态变量在多个线程中共享,如果多个线程试图同时修改同一个静态变量,就可能导致数据冲突和不一致的问题。因此,静态变量本身不是线程安全的,除非采取了适当的同步措施。</p><ul><li><strong>全局静态变量和类的静态成员变量</strong>:当多个线程访问和修改这些变量时,如果没有适当的锁机制(例如互斥锁)来控制访问,就可能导致数据竞争和不一致性问题。</li><li><strong>局部静态变量</strong>:在C++11及以后的版本中,局部静态变量的初始化是线程安全的,即在第一次访问变量时进行的初始化操作是由编译器自动加锁的,确保只有一个线程可以初始化变量。然而,初始化之后对变量的访问和修改仍然需要额外的同步措施来保证线程安全。</li></ul><h3 id=&#34;保证线程安全的策略&#34;>保证线程安全的策略</h3><ul><li><strong>互斥锁</strong>:使用互斥锁(mutex)来同步对静态变量的访问。任何线程在访问变量之前必须首先获得锁,并在访问完成后释放锁。</li><li><strong>原子操作</strong>:对于简单的数据类型,可以使用原子操作来更新静态变量,原子操作保证了操作的不可分割性,从而避免了数据竞争。</li><li><strong>线程局部存储</strong>:如果静态变量不需要在多个线程间共享,可以考虑将其改为线程局部存储(Thread Local Storage, TLS),使得每个线程都有自己的变量副本。</li></ul><p>总之,虽然静态变量在多线程程序中提供了便利和效率,但正确管理对它们的访问至关重要,以确保程序的线程安全性和稳定性。在设计多线程程序时,应该仔细考虑如何同步对静态资源的访问,以避免潜在的竞争条件和其他线程安全问题。</p><h2 id=&#34;输入url到网页显示的过程&#34;>输入url到网页显示的过程</h2><p>当你在浏览器中输入一个URL并按下回车键后,发生了一系列复杂的过程,这些过程一起工作,将网页内容呈现到你的屏幕上。以下是这个过程的高级概述:</p><h3 id=&#34;1-解析url&#34;>1. 解析URL</h3><ul><li>**URL(统一资源定位符)**分析成不同的部分:协议(如HTTP或HTTPS)、服务器地址(域名)和资源路径。</li><li>浏览器会检查URL格式是否正确,如果URL不完整或有误,浏览器可能会尝试修正它或使用搜索引擎来寻找正确的地址。</li></ul><h3 id=&#34;2-域名解析dns查询&#34;>2. 域名解析(DNS查询)</h3><ul><li>浏览器会查找URL中域名对应的IP地址。这通常涉及到DNS(域名系统)查询。</li><li>首先,浏览器检查本地缓存中是否有此域名的记录;如果没有,浏览器会向配置的DNS服务器发送查询请求。</li></ul><h3 id=&#34;3-建立连接&#34;>3. 建立连接</h3><ul><li>一旦获取到服务器的IP地址,浏览器会向服务器发起连接。对于HTTP协议,这意味着建立一个TCP连接;对于HTTPS协议,还需要进行TLS握手以建立一个加密连接。</li></ul><h3 id=&#34;4-发送http请求&#34;>4. 发送HTTP请求</h3><ul><li>浏览器构建一个HTTP请求,并通过TCP连接发送给服务器。这个请求包括请求行(如<code>GET /index.html HTTP/1.1</code>)、请求头(包括浏览器类型、接受的内容类型等)和请求体(对于某些类型的请求)。</li></ul><h3 id=&#34;5-服务器处理请求并响应&#34;>5. 服务器处理请求并响应</h3><ul><li>服务器接收到请求后,会根据请求类型处理请求(如查找请求的资源)。</li><li>服务器将响应发送回浏览器,响应通常包括一个状态码(如200表示成功),响应头(包含内容类型、编码等信息)和响应体(即请求的资源内容)。</li></ul><h3 id=&#34;6-浏览器渲染页面&#34;>6. 浏览器渲染页面</h3><ul><li>浏览器接收到服务器的响应数据后,会根据响应的内容类型解析响应体。</li><li>如果是HTML文档,浏览器会解析HTML结构,构建DOM(文档对象模型)树。</li><li>浏览器解析CSS样式,并应用到DOM树上,构建渲染树。</li><li>执行JavaScript代码,可能会修改DOM树和渲染树。</li><li>浏览器布局渲染树中的元素,并将它们绘制到屏幕上,呈现最终的页面。</li></ul><h3 id=&#34;7-后续处理&#34;>7. 后续处理</h3><ul><li>如果页面中包含了需要进一步加载的资源(如图片、CSS文件、JavaScript文件等),浏览器会重复上述过程来获取这些资源,并更新页面内容。</li></ul><p>整个过程涉及网络、服务器、浏览器等多个层面的复杂交互。每一步都优化了速度和安全性,以确保用户能够快速、安全地获取和查看网页内容。</p><h2 id=&#34;什么时候会返回404&#34;>什么时候会返回404</h2><p>HTTP 404错误状态码表示服务器无法找到请求的资源。这种情况通常发生在以下几种场景中:</p><h3 id=&#34;1-url错误或拼写错误&#34;>1. URL错误或拼写错误</h3><p>如果URL中的路径或文件名存在拼写错误,服务器会因为找不到匹配的资源而返回404错误。例如,如果用户尝试访问的网页路径拼写错误(如<code>/hom</code>代替<code>/home</code>),服务器将无法找到该资源。</p><h3 id=&#34;2-资源被移动或删除&#34;>2. 资源被移动或删除</h3><p>如果之前存在的网页或资源被网站管理员移动到另一个位置,或者被完全删除,没有进行适当的重定向处理,那么对这个资源的请求将会返回404错误。这是导致404错误的常见原因。</p><h3 id=&#34;3-服务器配置问题&#34;>3. 服务器配置问题</h3><p>服务器配置错误也可能导致404错误。例如,如果Web服务器(如Apache或Nginx)的配置文件中指定的资源目录路径错误,或者配置了错误的重写规则,那么即使请求的资源存在,用户也可能收到404错误。</p><h3 id=&#34;4-域名解析错误&#34;>4. 域名解析错误</h3><p>尽管这种情况较少见,但如果域名未正确解析到正确的服务器,或者服务器上没有为该域名配置虚拟主机,那么请求可能会被发送到错误的服务器,从而可能返回404错误。</p><h3 id=&#34;5-链接过时&#34;>5. 链接过时</h3><p>对于一些内容管理系统(CMS)或动态生成的网站,页面的URL可能随着内容更新而改变。如果其他网站或用户的书签链接到了旧的URL,这将导致404错误。</p><h3 id=&#34;6-权限问题&#34;>6. 权限问题</h3><p>在某些情况下,服务器可能配置为对未授权访问某些资源返回404错误,而不是403错误(禁止访问),这是为了隐藏资源的存在,增加安全性。</p><h3 id=&#34;应对404错误的措施&#34;>应对404错误的措施</h3><ul><li><strong>检查URL</strong>:确认URL输入正确,没有拼写错误。</li><li><strong>使用搜索功能</strong>:如果网站提供搜索功能,可以尝试搜索想要找到的内容。</li><li><strong>检查网站的站点地图</strong>:站点地图列出了网站上所有可访问的页面链接。</li><li><strong>联系网站管理员</strong>:如果认为页面应该存在,可以尝试联系网站的管理员或支持团队获取帮助。</li><li><strong>设置自定义404页面</strong>:对于网站开发者来说,设置一个友好的404错误页面可以提供返回主页的链接或者搜索框,帮助用户找到他们感兴趣的内容。</li></ul><p>404错误虽然令人沮丧,但正确处理这些错误可以提升用户体验,并帮助访问者找到他们感兴趣的内容或返回网站的其它部分。</p><h2 id=&#34;tcp怎么实现可靠传输&#34;>TCP怎么实现可靠传输</h2><p>TCP(传输控制协议)通过一系列的机制来实现在不可靠的网络层之上的可靠数据传输。这些机制确保了数据正确、有序地传输,即使在网络条件不佳的情况下也能尽可能保证数据的完整性和顺序。以下是TCP实现可靠传输的主要机制:</p><h3 id=&#34;1-三次握手建立连接&#34;>1. 三次握手建立连接</h3><p>TCP使用一种称为“三次握手”的过程来建立连接,以确保双方都准备好进行数据传输。这个过程也同步双方的序列号,以便于后续的数据传输可以被正确排序,并跟踪哪些数据已被成功接收。</p><h3 id=&#34;2-数据包排序&#34;>2. 数据包排序</h3><p>每个TCP段都包含一个序列号,这允许接收方对接收到的数据包进行排序,以确保数据的正确顺序。</p><h3 id=&#34;3-确认应答与超时重传&#34;>3. 确认应答与超时重传</h3><p>接收方对收到的TCP段发送确认(ACK)回复,表明它已成功接收到该段。如果发送方在一个预定的超时期限内没有收到确认,它将重新发送该数据段。这个机制确保了数据的成功传输。</p><h3 id=&#34;4-流量控制&#34;>4. 流量控制</h3><p>TCP使用窗口大小来进行流量控制,这个窗口大小告诉发送方接收方还能接收多少数据。这避免了发送方发送太多数据,导致接收方处理不过来而丢失数据。</p><h3 id=&#34;5-拥塞控制&#34;>5. 拥塞控制</h3><p>TCP实现了几种拥塞控制算法(如慢启动、拥塞避免、快重传和快恢复)来减少网络中的拥塞。这些算法调整数据的传输速率,以响应网络中的拥塞情况,避免过多的数据包同时传输导致的网络拥塞和数据包丢失。</p><h3 id=&#34;6-保活机制&#34;>6. 保活机制</h3><p>TCP保活机制可以检测出空闲的连接,防止连接由于长时间的无活动而被意外关闭。通过定期发送保活探针,TCP可以判断对方是否仍然可达。</p><h3 id=&#34;7-四次挥手断开连接&#34;>7. 四次挥手断开连接</h3><p>TCP使用四次挥手的过程来安全地关闭连接。这个过程确保了双方都已经发送和接收了所有的数据。</p><p>通过上述机制,TCP能够提供一种可靠的数据传输服务,即使在网络状况不稳定的情况下也能尽可能保证数据的完整性和顺序。这些特性使得TCP成为许多需要可靠数据传输的应用(如Web浏览、电子邮件和文件传输)的理想选择。</p><h2 id=&#34;tcp连接过多怎么办&#34;>TCP连接过多怎么办</h2><p>当TCP连接过多时,可能会导致服务器资源紧张,性能下降,甚至服务不可用。处理这种情况需要综合考虑,采取多种策略来优化和管理TCP连接。以下是一些常见的处理方法:</p><h3 id=&#34;1-优化服务器配置&#34;>1. 优化服务器配置</h3><ul><li><strong>增加最大连接数</strong>:调整操作系统和服务器软件的配置,增加可以同时打开的最大连接数。</li><li><strong>调整TCP参数</strong>:比如调整TCP Keepalive时间,减少TIME_WAIT状态持续的时间,以便更快地回收和重用端口。</li></ul><h3 id=&#34;2-使用负载均衡&#34;>2. 使用负载均衡</h3><ul><li><strong>部署负载均衡器</strong>:在服务器前端部署负载均衡器,将流量分发到后端的多个服务器上。这样可以分散压力,提高整体的处理能力和可用性。</li></ul><h3 id=&#34;3-采用更高效的架构&#34;>3. 采用更高效的架构</h3><ul><li><strong>使用异步IO</strong>:传统的同步IO模型在处理大量并发连接时可能会遇到瓶颈。采用异步IO模型(如IO多路复用)可以更高效地处理大量并发连接。</li><li><strong>事件驱动架构</strong>:事件驱动的服务器架构(如Node.js)能够更好地支持高并发连接,提高资源利用率。</li></ul><h3 id=&#34;4-优化应用层&#34;>4. 优化应用层</h3><ul><li><strong>连接复用</strong>:在应用层面,尽量复用已建立的TCP连接(如HTTP Keep-Alive),减少频繁建立和断开连接的开销。</li><li><strong>限制连接时间</strong>:对于某些不需要长时间保持连接的应用,可以设定超时时间,超过时间限制后自动关闭连接。</li></ul><h3 id=&#34;5-资源监控和自动扩展&#34;>5. 资源监控和自动扩展</h3><ul><li><strong>监控资源使用</strong>:持续监控服务器的CPU、内存、网络等资源使用情况,及时发现瓶颈。</li><li><strong>自动扩展</strong>:在云环境中,可以设置自动扩展策略,根据负载情况自动增减服务器实例。</li></ul><h3 id=&#34;6-防御拒绝服务攻击&#34;>6. 防御拒绝服务攻击</h3><ul><li>如果TCP连接过多是由拒绝服务攻击(DoS/DDoS攻击)引起的,需要采取相应的防御措施,如部署专业的DDoS防御系统,限制IP连接速率等。</li></ul><p>处理TCP连接过多的问题需要从系统、网络和应用多个层面进行综合考虑和优化。在设计系统时,考虑到高并发和高可用性的需求,并采取适当的架构和技术来应对可能的高负载情况,是避免此类问题的关键。</p><h2 id=&#34;为什么有ip地址还有mac地址&#34;>为什么有IP地址还有MAC地址</h2><p>IP地址和MAC地址都是网络中设备通信的重要组成部分,但它们在网络通信中扮演着不同的角色,服务于不同的网络层。理解它们之间的区别和为什么两者都需要,可以帮助我们更好地理解网络是如何工作的。</p><h3 id=&#34;ip地址&#34;>IP地址</h3><p>IP地址(Internet Protocol Address)是网络层(特别是在TCP/IP模型中的第三层)的一个标识符,用于标识网络上的每个设备,以及帮助路由数据包从源头到目的地。IP地址主要用于不同网络或网络段之间的通信。因为网络设备可能频繁改变位置,进入不同的网络环境,IP地址可能会改变,特别是在使用DHCP服务时。</p><p>IP地址的主要功能包括:</p><ul><li><strong>标识</strong>:为每个连接到网络的设备提供唯一标识。</li><li><strong>路由</strong>:帮助网络中的路由器决定如何将数据包从源地址转发到目的地址。</li></ul><h3 id=&#34;mac地址&#34;>MAC地址</h3><p>MAC地址(Media Access Control Address),也称为物理地址,是数据链路层(在TCP/IP模型中的第二层)的一个标识符,嵌入到网络接口卡(NIC)硬件中。MAC地址是在设备制造过程中分配的,通常是不变的(尽管可以通过软件进行修改)。MAC地址主要用于同一个物理网络内的设备之间的通信。</p><p>MAC地址的主要功能包括:</p><ul><li><strong>物理地址标识</strong>:为网络上每个物理设备提供唯一标识。</li><li><strong>局域网通信</strong>:在局域网(LAN)内部,数据包的传输是基于MAC地址的。设备使用MAC地址来确定数据包是否是发往自己的。</li></ul><h3 id=&#34;为什么两者都需要&#34;>为什么两者都需要</h3><ul><li><strong>不同的网络层</strong>:IP地址和MAC地址分别服务于TCP/IP模型的不同层次。IP地址用于网络层,实现跨网络的数据传输。而MAC地址用于数据链路层,实现同一局域网内的设备通信。</li><li><strong>不同的功能和作用范围</strong>:IP地址提供逻辑地址,使得设备可以在全球范围内通信,而MAC地址提供物理地址,用于局域网内部的通信。</li><li><strong>路由和交换</strong>:在数据包传输过程中,路由器利用IP地址来路由数据包到正确的目的网络,而交换机则使用MAC地址来将数据包转发到正确的设备。</li></ul><p>综上所述,IP地址和MAC地址共同确保了数据能够在复杂的网络环境中,从源设备准确、高效地传输到目标设备。</p><h2 id=&#34;路由器有ip地址吗&#34;>路由器有IP地址吗</h2><p>是的,路由器具有IP地址。实际上,路由器通常会有至少两个IP地址:一个是用于局域网(LAN)侧的内部网络接口的IP地址,另一个是用于广域网(WAN)侧的外部网络接口的IP地址。这些IP地址使路由器能够在不同的网络环境中正确地路由数据包。</p><h3 id=&#34;局域网lan侧ip地址&#34;>局域网(LAN)侧IP地址</h3><ul><li><strong>私有IP地址</strong>:路由器的LAN侧IP地址通常是一个私有IP地址,这个地址是路由器内部网络的默认网关地址。设备在发送数据到局域网外部的互联网时,数据包会被发送到这个地址。</li><li><strong>网关地址</strong>:在家庭或小型办公室网络中,路由器的LAN侧IP地址通常被配置为192.168.x.1或10.0.x.1这样的地址(x是0到255之间的任意数字),它作为内部网络中所有设备的默认网关。</li></ul><h3 id=&#34;广域网wan侧ip地址&#34;>广域网(WAN)侧IP地址</h3><ul><li><strong>公有IP地址</strong>:路由器的WAN侧IP地址通常是由互联网服务提供商(ISP)分配的一个公有IP地址。这个地址是路由器在互联网上的标识,允许局域网内的设备通过路由器访问互联网。</li><li><strong>唯一性</strong>:为了能在互联网上被其他系统识别和访问,每个连接到互联网的设备必须有一个唯一的公有IP地址。由于公有IP地址是有限的,家庭和小型办公网络通常只有路由器拥有一个公有IP地址,而内部设备则使用NAT(网络地址转换)技术通过这个公有IP地址共享互联网连接。</li></ul><h3 id=&#34;管理ip地址&#34;>管理IP地址</h3><ul><li><strong>用于管理的IP地址</strong>:除了用于路由的IP地址外,路由器还可能配置有一个特别的管理IP地址,用于访问路由器的管理界面(如Web界面)。这通常是路由器LAN侧的IP地址。</li></ul><p>总之,路由器拥有IP地址是它完成数据包路由、网络通信和管理任务的基础。通过这些IP地址,路由器能够在局域网内提供连接到互联网的通道,同时允许网络管理员对其进行配置和管理。</p><h2 id=&#34;内存泄露怎么解决&#34;>内存泄露怎么解决</h2><p>内存泄露是指程序中已分配的内存未能成功释放,导致无法再被程序利用的情况。长时间运行或多次执行这样的程序可能会导致可用内存逐渐减少,最终影响程序性能或导致程序崩溃。解决内存泄露的问题通常需要几个步骤,包括检测、定位和修复泄露。</p><h3 id=&#34;检测内存泄露&#34;>检测内存泄露</h3><ol><li><p><strong>使用内存分析工具</strong>:大多数编程语言都提供了内存分析工具或库,如Valgrind、LeakSanitizer、Visual Studio的诊断工具、Xcode的Instruments等,可用于检测内存泄露。</p></li><li><p><strong>代码审查</strong>:定期进行代码审查,特别关注内存分配和释放的逻辑,可以帮助识别潜在的内存泄露问题。</p></li><li><p><strong>监控系统性能</strong>:通过监控应用程序的内存使用情况,可以发现异常的内存增长模式,这可能是内存泄露的迹象。</p></li></ol><h3 id=&#34;定位内存泄露&#34;>定位内存泄露</h3><ol><li><p><strong>分析内存分配</strong>:利用内存分析工具,可以获取内存分配和释放的详细报告,从而定位未被释放的内存块。</p></li><li><p><strong>日志和调试信息</strong>:在关键的内存分配和释放点添加日志信息,可以帮助追踪内存使用情况,进而定位泄露的位置。</p></li><li><p><strong>代码分析</strong>:静态代码分析工具可以帮助识别内存管理的常见问题,例如未匹配的内存分配和释放。</p></li></ol><h3 id=&#34;修复内存泄露&#34;>修复内存泄露</h3><ol><li><p><strong>确保匹配的内存释放</strong>:每次内存分配(例如<code>new</code>、<code>malloc</code>)都应该有对应的释放操作(例如<code>delete</code>、<code>free</code>)。</p></li><li><p><strong>使用智能指针(C++)</strong>:在C++中,使用智能指针(如<code>std::unique_ptr</code>、<code>std::shared_ptr</code>)可以自动管理内存,减少显式释放内存的需要。</p></li><li><p><strong>利用RAII原则(资源获取即初始化)</strong>:确保资源(包括内存)的分配和释放都在同一个对象的构造函数和析构函数中进行,利用作用域自动管理资源。</p></li><li><p><strong>避免内存泄露的编程模式</strong>:例如,在C++中避免循环引用(可通过<code>std::weak_ptr</code>解决),在Java或C#中注意事件处理器的注册和注销等。</p></li><li><p><strong>内存池</strong>:对于频繁分配和释放的小对象,使用内存池可以减少内存碎片和泄露的可能性。</p></li></ol><p>解决内存泄露的关键在于深入理解代码的内存使用情况,以及利用合适的工具和策略来监控、定位和修复问题。持续的性能监控、代码审查和测试是防止内存泄露的重要手段。</p><h2 id=&#34;为什么要有虚拟内存空间&#34;>为什么要有虚拟内存空间</h2><p>虚拟内存空间是现代计算机系统中的一个关键概念,它提供了一个抽象层,使得软件开发者和操作系统能够更有效地管理硬件资源。虚拟内存的引入主要基于以下几个原因和优势:</p><h3 id=&#34;1-内存扩展&#34;>1. 内存扩展</h3><ul><li><strong>扩大可用内存</strong>:虚拟内存允许系统使用硬盘空间作为额外的内存,从而扩大了可供程序使用的内存空间。这意味着即使物理RAM不足,程序也能运行,因为操作系统可以将部分数据暂时存储在硬盘的虚拟内存页中。</li></ul><h3 id=&#34;2-进程隔离&#34;>2. 进程隔离</h3><ul><li><strong>提供进程间隔离</strong>:每个进程都在其自己的虚拟地址空间中运行,这使得进程之间的内存访问相互隔离,防止一个进程的错误操作(如越界访问)影响到其他进程。</li></ul><h3 id=&#34;3-内存管理简化&#34;>3. 内存管理简化</h3><ul><li><strong>简化内存管理</strong>:虚拟内存使得操作系统可以为每个进程提供一致的地址空间,简化了内存的分配和管理。程序员无需担心物理内存的具体位置和限制,可以认为有一个几乎无限大的内存空间。</li></ul><h3 id=&#34;4-内存保护&#34;>4. 内存保护</h3><ul><li><strong>增强安全性和稳定性</strong>:操作系统可以通过虚拟内存来控制每个进程对内存的访问权限(如只读、读写或执行权限),增加了系统的安全性和稳定性。</li></ul><h3 id=&#34;5-数据共享&#34;>5. 数据共享</h3><ul><li><strong>方便数据共享与通信</strong>:虚拟内存机制也使得不同进程间共享内存成为可能,便于进程间的数据共享和通信。</li></ul><h3 id=&#34;6-物理内存优化&#34;>6. 物理内存优化</h3><ul><li><strong>优化物理内存使用</strong>:虚拟内存允许操作系统更灵活地管理物理内存,如通过页面置换算法(如最近最少使用LRU算法)来决定哪些数据应当保留在RAM中,哪些可以被移动到硬盘,从而最优化物理内存的使用。</li></ul><h3 id=&#34;7-支持多任务&#34;>7. 支持多任务</h3><ul><li><strong>支持多任务处理</strong>:虚拟内存为多任务操作系统提供了基础,使得多个应用程序能够同时运行,同时保证它们的运行环境互不干扰,提高了计算机系统的效率和响应速度。</li></ul><p>虚拟内存通过上述优势,不仅提高了计算机的性能和资源利用率,也极大地简化了程序设计和系统管理,是现代操作系统不可或缺的一部分。</p><h2 id=&#34;i是原子性的吗&#34;>i++是原子性的吗</h2><p><code>i++</code>操作(自增操作)在多数编程语言中并不是原子性的,尽管在单个线程的上下文中这个操作看起来只是简单地增加变量的值。<code>i++</code>实际上包含了三个独立的步骤:读取变量<code>i</code>的当前值、增加这个值、将新值写回到变量<code>i</code>。在多线程环境中,如果没有适当的同步机制,这三个步骤之间可能会被其他线程的操作打断,导致竞态条件和数据不一致的问题。</p><h3 id=&#34;为什么i不是原子性的&#34;>为什么<code>i++</code>不是原子性的</h3><ol><li><strong>读取</strong>:首先,程序需要读取变量<code>i</code>的当前值到CPU寄存器。</li><li><strong>修改</strong>:在CPU寄存器中,给这个值加1。</li><li><strong>写回</strong>:最后,将新值写回内存中的变量<code>i</code>。</li></ol><p>这个过程中的任何步骤都可能被其他线程打断,特别是在没有锁或其他同步机制保护的情况下。例如,两个线程几乎同时读取<code>i</code>的值,然后分别增加1并尝试写回,结果是<code>i</code>实际上只增加了1而不是2。</p><h3 id=&#34;如何确保操作的原子性&#34;>如何确保操作的原子性</h3><p>要确保类似<code>i++</code>这样的操作在多线程环境中的原子性,可以采用以下一种或多种方法:</p><ul><li><strong>使用互斥锁(Mutex)</strong>:通过在执行<code>i++</code>操作之前获取互斥锁,然后执行操作,最后释放锁,可以确保这个操作在完成之前不会被其他线程打断。</li><li><strong>原子操作函数</strong>:许多编程语言和库提供了原子操作API,比如C++11引入的<code>std::atomic</code>类型和相关操作,或Java的<code>java.util.concurrent.atomic</code>包中的类。这些API能够保证变量操作的原子性。</li><li><strong>使用硬件同步原语</strong>:某些平台提供了CPU指令级的支持来执行原子操作,如x86架构的CMPXCHG指令。编程语言或库的原子操作API底层可能会利用这些硬件特性。</li></ul><p>总之,<code>i++</code>操作本身在多线程环境下不是原子性的,需要通过同步机制或使用特定的原子操作API来保证其安全性。在设计多线程程序时,正确处理这类操作至关重要,以避免数据不一致和竞态条件。</p><h2 id=&#34;子类中变量初始化顺序和销毁顺序&#34;>子类中变量初始化顺序和销毁顺序</h2><p>在面向对象的编程中,特别是在使用如Java、C++等语言时,子类和父类中变量的初始化和销毁顺序遵循特定的规则,这些规则保证了对象的构造和析构过程的正确性和逻辑性。下面是这些语言中常见的初始化和销毁顺序的概述:</p><h3 id=&#34;java&#34;>Java</h3><h4 id=&#34;初始化顺序&#34;>初始化顺序</h4><ol><li><strong>父类静态变量和静态初始化块</strong>:按照它们在父类中出现的顺序。</li><li><strong>子类静态变量和静态初始化块</strong>:按照它们在子类中出现的顺序。</li><li><strong>父类非静态变量和非静态初始化块</strong>:按照它们在父类中出现的顺序。</li><li><strong>父类构造器</strong>。</li><li><strong>子类非静态变量和非静态初始化块</strong>:按照它们在子类中出现的顺序。</li><li><strong>子类构造器</strong>。</li></ol><h4 id=&#34;销毁顺序&#34;>销毁顺序</h4><p>Java中,对象的销毁是由垃圾回收器(GC)处理的,没有像C++中析构函数那样直接的销毁过程。但是,可以通过<code>finalize()</code>方法提供一定的清理逻辑。通常,<code>finalize()</code>方法的调用顺序与构造器的调用顺序相反,但依赖于GC的具体实现和行为,<code>finalize()</code>方法的调用时机和顺序可能是不确定的。</p><h3 id=&#34;c&#34;>C++</h3><h4 id=&#34;初始化顺序-1&#34;>初始化顺序</h4><ol><li><strong>父类构造器</strong>:首先调用基类的构造函数。</li><li><strong>成员变量初始化</strong>:按照它们声明的顺序初始化父类的成员变量。</li><li><strong>父类构造器体内的代码</strong>。</li><li><strong>成员变量初始化</strong>:按照它们声明的顺序初始化子类的成员变量。</li><li><strong>子类构造器体内的代码</strong>。</li></ol><h4 id=&#34;销毁顺序-1&#34;>销毁顺序</h4><p>销毁顺序与初始化顺序相反:</p><ol><li><strong>子类析构器体内的代码</strong>。</li><li><strong>子类成员变量的析构</strong>:按照初始化顺序的逆序进行销毁。</li><li><strong>父类析构器体内的代码</strong>。</li><li><strong>父类成员变量的析构</strong>:按照初始化顺序的逆序进行销毁。</li></ol><p>在C++中,析构函数是显式定义的,它们提供了在对象销毁时执行清理资源等操作的机会。与Java不同,C++的对象销毁是确定的,由程序员或对象作用域结束时自动触发。</p><p>这些初始化和销毁的规则确保了对象在其生命周期内的状态和行为的正确性,是面向对象编程中重要的概念。理解这些规则对于编写可靠和高效的代码至关重要。</p><h2 id=&#34;cpu如何实现除法&#34;>cpu如何实现除法</h2><p>CPU实现除法的方法可以根据其设计和指令集架构的不同而不同。在硬件层面,有几种常见的方法用于实现除法运算:</p><h3 id=&#34;1-长除法硬件实现&#34;>1. 长除法(硬件实现)</h3><p>这是最直观的方法,类似于我们在纸上执行的长除法过程,但是在二进制下进行。CPU通过一系列的移位、比较和减法操作来实现除法。这个过程涉及将被除数左移(相当于在二进制下的乘以2),然后与除数比较,如果被除数大于等于除数,就从被除数中减去除数,并在商的相应位置上放置一个1,否则放置一个0。这个过程重复进行,直到完成所有的位操作。</p><h3 id=&#34;2-逼近法硬件实现&#34;>2. 逼近法(硬件实现)</h3><ul><li><p><strong>牛顿-拉弗森迭代法(Newton-Raphson)</strong>:这种方法利用迭代逼近来找到除法的结果。它基于牛顿迭代法求解函数零点的数学原理,通过迭代计算逐步逼近真实的除法结果。这种方法特别适用于浮点除法。</p></li><li><p><strong>高德(Goldschmidt)迭代法</strong>:这是另一种迭代逼近方法,通过同时对被除数和除数进行乘法操作来逼近结果,最终将除法问题转化为乘法问题。</p></li></ul><h3 id=&#34;3-查表法软件硬件辅助实现&#34;>3. 查表法(软件/硬件辅助实现)</h3><p>对于一些特定的除数,可以通过查找预先计算好的除法结果表来快速得到结果。这种方法在执行小范围内的除法操作时效率很高,但对于大数的除法则不太实用,因为查表需要大量的存储空间。</p><h3 id=&#34;4-软件实现&#34;>4. 软件实现</h3><p>对于一些没有硬件除法支持的简单CPU或者特定应用场景,除法运算可能通过软件实现,即编译器将除法操作转换为一系列的基本操作,如移位、加法和减法。这种方法比直接的硬件支持要慢,但可以在不支持硬件除法的CPU上执行除法运算。</p><h3 id=&#34;性能考虑&#34;>性能考虑</h3><p>在CPU中,除法是相对较慢的操作,特别是浮点除法。因此,现代CPU设计采用高级算法和技术(如流水线处理、并行执行等)来优化除法操作的性能。在软件开发中,避免不必要的除法运算或将其替换为更快的操作(如移位)是常见的性能优化手段。</p><p>总的来说,CPU中除法的实现是复杂的,涉及多种算法和技术,旨在平衡计算的准确性和执行的效率。</p><h2 id=&#34;三个线程如何交替打印abc循环100次用c实现&#34;>三个线程如何交替打印ABC循环100次,用C++实现</h2><p>实现三个线程交替打印ABC循环100次的任务,可以通过同步机制来控制线程间的执行顺序。在C++中,可以使用<code>std::mutex</code>和<code>std::condition_variable</code>来实现这一目标。下面是一个具体的实现示例:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#75715e&#34;>#include</span> <span style=&#34;color:#75715e&#34;>&lt;iostream&gt;</span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#75715e&#34;>#include</span> <span style=&#34;color:#75715e&#34;>&lt;thread&gt;</span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#75715e&#34;>#include</span> <span style=&#34;color:#75715e&#34;>&lt;mutex&gt;</span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span><span style=&#34;color:#75715e&#34;>#include</span> <span style=&#34;color:#75715e&#34;>&lt;condition_variable&gt;</span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>ABCPrinter</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span> ABCPrinter(<span style=&#34;color:#66d9ef&#34;>int</span> count) <span style=&#34;color:#f92672&#34;>:</span> count_(count) {}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span> <span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>printA</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span> <span style=&#34;color:#66d9ef&#34;>for</span> (<span style=&#34;color:#66d9ef&#34;>int</span> i <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#ae81ff&#34;>0</span>; i <span style=&#34;color:#f92672&#34;>&lt;</span> count_; <span style=&#34;color:#f92672&#34;>++</span>i) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span> std<span style=&#34;color:#f92672&#34;>::</span>unique_lock<span style=&#34;color:#f92672&#34;>&lt;</span>std<span style=&#34;color:#f92672&#34;>::</span>mutex<span style=&#34;color:#f92672&#34;>&gt;</span> lock(m_);</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> cv_.wait(lock, [<span style=&#34;color:#66d9ef&#34;>this</span>]() { <span style=&#34;color:#66d9ef&#34;>return</span> currentChar_ <span style=&#34;color:#f92672&#34;>==</span> <span style=&#34;color:#e6db74&#34;>&#39;A&#39;</span>; });</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> <span style=&#34;color:#e6db74&#34;>&#39;A&#39;</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span> currentChar_ <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#e6db74&#34;>&#39;B&#39;</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span> cv_.notify_all();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span> <span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>printB</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>21</span><span> <span style=&#34;color:#66d9ef&#34;>for</span> (<span style=&#34;color:#66d9ef&#34;>int</span> i <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#ae81ff&#34;>0</span>; i <span style=&#34;color:#f92672&#34;>&lt;</span> count_; <span style=&#34;color:#f92672&#34;>++</span>i) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>22</span><span> std<span style=&#34;color:#f92672&#34;>::</span>unique_lock<span style=&#34;color:#f92672&#34;>&lt;</span>std<span style=&#34;color:#f92672&#34;>::</span>mutex<span style=&#34;color:#f92672&#34;>&gt;</span> lock(m_);</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>23</span><span> cv_.wait(lock, [<span style=&#34;color:#66d9ef&#34;>this</span>]() { <span style=&#34;color:#66d9ef&#34;>return</span> currentChar_ <span style=&#34;color:#f92672&#34;>==</span> <span style=&#34;color:#e6db74&#34;>&#39;B&#39;</span>; });</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>24</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> <span style=&#34;color:#e6db74&#34;>&#39;B&#39;</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>25</span><span> currentChar_ <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#e6db74&#34;>&#39;C&#39;</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>26</span><span> cv_.notify_all();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>27</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>28</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>29</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>30</span><span> <span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>printC</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>31</span><span> <span style=&#34;color:#66d9ef&#34;>for</span> (<span style=&#34;color:#66d9ef&#34;>int</span> i <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#ae81ff&#34;>0</span>; i <span style=&#34;color:#f92672&#34;>&lt;</span> count_; <span style=&#34;color:#f92672&#34;>++</span>i) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>32</span><span> std<span style=&#34;color:#f92672&#34;>::</span>unique_lock<span style=&#34;color:#f92672&#34;>&lt;</span>std<span style=&#34;color:#f92672&#34;>::</span>mutex<span style=&#34;color:#f92672&#34;>&gt;</span> lock(m_);</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>33</span><span> cv_.wait(lock, [<span style=&#34;color:#66d9ef&#34;>this</span>]() { <span style=&#34;color:#66d9ef&#34;>return</span> currentChar_ <span style=&#34;color:#f92672&#34;>==</span> <span style=&#34;color:#e6db74&#34;>&#39;C&#39;</span>; });</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>34</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> <span style=&#34;color:#e6db74&#34;>&#39;C&#39;</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>35</span><span> currentChar_ <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#e6db74&#34;>&#39;A&#39;</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>36</span><span> cv_.notify_all();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>37</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>38</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>39</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>40</span><span><span style=&#34;color:#66d9ef&#34;>private</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>41</span><span> std<span style=&#34;color:#f92672&#34;>::</span>mutex m_;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>42</span><span> std<span style=&#34;color:#f92672&#34;>::</span>condition_variable cv_;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>43</span><span> <span style=&#34;color:#66d9ef&#34;>char</span> currentChar_ <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#e6db74&#34;>&#39;A&#39;</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>44</span><span> <span style=&#34;color:#66d9ef&#34;>int</span> count_;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>45</span><span>};</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>46</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>47</span><span><span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>48</span><span> <span style=&#34;color:#66d9ef&#34;>const</span> <span style=&#34;color:#66d9ef&#34;>int</span> count <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#ae81ff&#34;>100</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>49</span><span> ABCPrinter printer(count);</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>50</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>51</span><span> std<span style=&#34;color:#f92672&#34;>::</span><span style=&#34;color:#66d9ef&#34;>thread</span> t1(<span style=&#34;color:#f92672&#34;>&amp;</span>ABCPrinter<span style=&#34;color:#f92672&#34;>::</span>printA, <span style=&#34;color:#f92672&#34;>&amp;</span>printer);</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>52</span><span> std<span style=&#34;color:#f92672&#34;>::</span><span style=&#34;color:#66d9ef&#34;>thread</span> t2(<span style=&#34;color:#f92672&#34;>&amp;</span>ABCPrinter<span style=&#34;color:#f92672&#34;>::</span>printB, <span style=&#34;color:#f92672&#34;>&amp;</span>printer);</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>53</span><span> std<span style=&#34;color:#f92672&#34;>::</span><span style=&#34;color:#66d9ef&#34;>thread</span> t3(<span style=&#34;color:#f92672&#34;>&amp;</span>ABCPrinter<span style=&#34;color:#f92672&#34;>::</span>printC, <span style=&#34;color:#f92672&#34;>&amp;</span>printer);</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>54</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>55</span><span> t1.join();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>56</span><span> t2.join();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>57</span><span> t3.join();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>58</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>59</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> std<span style=&#34;color:#f92672&#34;>::</span>endl;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>60</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>61</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#ae81ff&#34;>0</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>62</span><span>}</span></span></code></pre></div><p>这个程序中,<code>ABCPrinter</code>类控制着打印行为。类中有一个<code>std::mutex</code>用于同步,一个<code>std::condition_variable</code>用于线程间的条件等待和通知,以及一个<code>currentChar_</code>变量来标记当前应该打印哪个字符。</p><ul><li>每个打印函数(<code>printA</code>、<code>printB</code>、<code>printC</code>)都会尝试获得锁,并通过条件变量等待直到它们各自负责的字符(<code>currentChar_</code>)是当前应该打印的字符。</li><li>当条件满足时,打印相应的字符,并更新<code>currentChar_</code>为下一个字符,然后通过<code>cv_.notify_all()</code>唤醒所有等待的线程,这些线程再次检查条件是否满足自己打印的条件。</li><li>这个循环持续进行,直到打印了指定的次数。</li></ul><p>通过这种方式,三个线程可以协同工作,依次交替打印ABC,共计100次。</p><h2 id=&#34;unique_lock是什么&#34;>unique_lock是什么</h2><p><code>std::unique_lock</code>是C++标准库中的一个实用工具,提供了一种灵活的方式来管理带有互斥锁(<code>std::mutex</code>)的锁定和解锁操作。它是头文件<code>&lt;mutex&gt;</code>的一部分,是对基本互斥锁管理的一个封装,提供了比<code>std::lock_guard</code>更多的功能和灵活性。尽管<code>std::unique_lock</code>比<code>std::lock_guard</code>占用的资源稍多,但它在需要更复杂锁管理的场景下非常有用。</p><h3 id=&#34;主要特点和用途&#34;>主要特点和用途</h3><ol><li><p><strong>灵活的锁管理</strong>:<code>std::unique_lock</code>允许在运行时进行锁的获取和释放,而<code>std::lock_guard</code>仅在构造时加锁,在析构时解锁,不提供中途手动控制锁的功能。</p></li><li><p><strong>支持条件变量</strong>:<code>std::unique_lock</code>可以与<code>std::condition_variable</code>一起使用,用于等待条件或通知其他线程。<code>std::condition_variable</code>需要与<code>std::unique_lock&lt;std::mutex&gt;</code>一起使用来等待条件或发送通知。</p></li><li><p><strong>支持移动语义</strong>:<code>std::unique_lock</code>支持移动操作,这意味着可以将锁的所有权从一个<code>std::unique_lock</code>对象转移到另一个。这对于从函数返回已锁定的互斥锁或将其作为参数传递给函数等操作非常有用。</p></li></ol><h3 id=&#34;示例用法&#34;>示例用法</h3><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#75715e&#34;>#include</span> <span style=&#34;color:#75715e&#34;>&lt;mutex&gt;</span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#75715e&#34;>#include</span> <span style=&#34;color:#75715e&#34;>&lt;thread&gt;</span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#75715e&#34;>#include</span> <span style=&#34;color:#75715e&#34;>&lt;iostream&gt;</span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span>std<span style=&#34;color:#f92672&#34;>::</span>mutex mtx; <span style=&#34;color:#75715e&#34;>// 全局互斥锁</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>print_block</span>(<span style=&#34;color:#66d9ef&#34;>int</span> n, <span style=&#34;color:#66d9ef&#34;>char</span> c) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span> <span style=&#34;color:#75715e&#34;>// 在std::unique_lock对象构造时加锁</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span><span style=&#34;color:#75715e&#34;></span> std<span style=&#34;color:#f92672&#34;>::</span>unique_lock<span style=&#34;color:#f92672&#34;>&lt;</span>std<span style=&#34;color:#f92672&#34;>::</span>mutex<span style=&#34;color:#f92672&#34;>&gt;</span> lock(mtx);</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span> <span style=&#34;color:#66d9ef&#34;>for</span> (<span style=&#34;color:#66d9ef&#34;>int</span> i <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#ae81ff&#34;>0</span>; i <span style=&#34;color:#f92672&#34;>&lt;</span> n; <span style=&#34;color:#f92672&#34;>++</span>i) { std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> c; }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> <span style=&#34;color:#e6db74&#34;>&#39;\n&#39;</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span> <span style=&#34;color:#75715e&#34;>// lock在析构时自动解锁</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span><span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span> std<span style=&#34;color:#f92672&#34;>::</span><span style=&#34;color:#66d9ef&#34;>thread</span> t1(print_block, <span style=&#34;color:#ae81ff&#34;>50</span>, <span style=&#34;color:#e6db74&#34;>&#39;*&#39;</span>);</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span> std<span style=&#34;color:#f92672&#34;>::</span><span style=&#34;color:#66d9ef&#34;>thread</span> t2(print_block, <span style=&#34;color:#ae81ff&#34;>50</span>, <span style=&#34;color:#e6db74&#34;>&#39;$&#39;</span>);</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span> t1.join();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span> t2.join();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>21</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>22</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#ae81ff&#34;>0</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>23</span><span>}</span></span></code></pre></div><p>在这个例子中,两个线程使用同一个互斥锁来同步对<code>std::cout</code>的访问。<code>std::unique_lock&lt;std::mutex&gt;</code>在<code>print_block</code>函数中创建并自动管理锁的生命周期:自动加锁和在离开作用域时解锁。</p><p>总之,<code>std::unique_lock</code>提供了一种灵活的方式来控制互斥锁的锁定和解锁,特别适合需要条件变量、可移动锁、或者需要在运行时进行更复杂的锁管理操作的场景。</p><h2 id=&#34;cv的条件变为真的时候谁来唤醒呢&#34;>CV的条件变为真的时候,谁来唤醒呢?</h2><p>条件变为真时,线程被唤醒通常是由其他线程执行的操作导致的。这些操作包括调用<code>std::condition_variable</code>的<code>notify_one</code>或<code>notify_all</code>成员函数。这两个函数的作用是通知正在等待的一个或所有线程(分别对应<code>notify_one</code>和<code>notify_all</code>),条件可能已经满足,等待的线程应该再次检查条件。</p><p>这里是如何使用它们的简化示例:</p><ul><li><p><strong><code>notify_one</code></strong>: 当条件变为真时,某个线程调用<code>cv.notify_one()</code>会唤醒一个正在等待(通过<code>cv.wait()</code>或<code>cv.wait_for()</code>等方法)的线程。如果有多个线程在等待,只有一个线程(由调度程序随机选择)会被唤醒。</p></li><li><p><strong><code>notify_all</code></strong>: 相比之下,当条件变为真时,调用<code>cv.notify_all()</code>会唤醒所有正在等待的线程。这对于需要多个线程响应同一个事件的情况特别有用。</p></li></ul><h3 id=&#34;示例场景&#34;>示例场景</h3><p>假设有两个线程A和B:</p><ul><li>线程A执行某个任务,完成后需要通知线程B任务已完成,线程B可以开始执行依赖于A的任务。</li><li>线程A在完成任务后调用<code>cv.notify_one()</code>或<code>cv.notify_all()</code>(假设只有线程B在等待,两者效果相同)。</li><li>线程B在开始自己的任务前调用<code>cv.wait(lock, predicate)</code>等待线程A的通知。一旦线程A调用了<code>notify_one()</code>或<code>notify_all()</code>,线程B被唤醒。</li><li>被唤醒后,线程B会自动重新获得之前<code>wait</code>操作中释放的锁,并重新检查条件(谓词函数)。如果条件满足,线程B继续执行后续操作;如果不满足(虽然被唤醒,但条件可能由于某些原因未真正满足),线程B会再次进入等待状态。</li></ul><p>这种机制确保了线程间的协作和同步,允许线程在适当的时间点安全地进行操作,避免了资源竞争和不一致的风险。</p><h2 id=&#34;如何实现断点续传&#34;>如何实现断点续传</h2><p>实现断点续传的关键在于能够记录下载或上传的当前进度,并在传输被中断后能够从该进度处恢复而不是重新开始。断点续传通常用于处理大文件传输,尤其是在网络条件不稳定的情况下。以下是实现断点续传功能的基本步骤和策略:</p><h3 id=&#34;1-记录传输进度&#34;>1. 记录传输进度</h3><ul><li><strong>客户端和服务器都需要记录文件的传输进度</strong>。这可以通过保存已经传输的数据量(字节)来实现,或者记录已经成功传输的文件块的标识。</li></ul><h3 id=&#34;2-支持范围请求&#34;>2. 支持范围请求</h3><ul><li><strong>服务器需要支持范围请求(HTTP协议的<code>Range</code>头部)</strong>。这允许客户端请求文件的特定部分而不是整个文件。客户端可以通过发送一个包含<code>Range</code>头部的HTTP请求来实现,指定需要下载的文件部分的起始和结束字节位置。</li></ul><h3 id=&#34;3-客户端请求特定数据块&#34;>3. 客户端请求特定数据块</h3><ul><li><strong>客户端在恢复下载时请求未完成的数据块</strong>。基于已记录的进度,客户端计算出需要从哪个字节位置开始恢复下载,并发送带有<code>Range</code>头部的请求。</li></ul><h3 id=&#34;4-服务器响应范围请求&#34;>4. 服务器响应范围请求</h3><ul><li><strong>服务器识别<code>Range</code>请求并发送相应的文件片段</strong>。服务器需要正确处理带有<code>Range</code>头部的请求,只发送请求的文件部分。</li></ul><h3 id=&#34;5-校验和完整性验证&#34;>5. 校验和完整性验证</h3><ul><li><strong>使用校验和或哈希来确保数据完整性</strong>。在传输完成后,客户端可以对接收到的文件片段或整个文件计算校验和或哈希值,与服务器端提供的值进行比较,以验证数据的完整性和正确性。</li></ul><h3 id=&#34;示例http断点续传&#34;>示例:HTTP断点续传</h3><p>假设客户端已经下载了文件的前1024字节,现在希望从第1025字节开始继续下载:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-http&#34; data-lang=&#34;http&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>GET</span> /path/to/file <span style=&#34;color:#66d9ef&#34;>HTTP</span><span style=&#34;color:#f92672&#34;>/</span><span style=&#34;color:#ae81ff&#34;>1.1</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span>Host<span style=&#34;color:#f92672&#34;>:</span> <span style=&#34;color:#ae81ff&#34;>example.com</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span>Range<span style=&#34;color:#f92672&#34;>:</span> <span style=&#34;color:#ae81ff&#34;>bytes=1025-</span></span></span></code></pre></div><p>服务器响应:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-http&#34; data-lang=&#34;http&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>HTTP</span><span style=&#34;color:#f92672&#34;>/</span><span style=&#34;color:#ae81ff&#34;>1.1</span> <span style=&#34;color:#ae81ff&#34;>206</span> <span style=&#34;color:#a6e22e&#34;>Partial Content</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span>Content-Range<span style=&#34;color:#f92672&#34;>:</span> <span style=&#34;color:#ae81ff&#34;>bytes 1025-2048/2049</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span>Content-Length<span style=&#34;color:#f92672&#34;>:</span> <span style=&#34;color:#ae81ff&#34;>1024</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span>[数据块]</span></span></code></pre></div><h3 id=&#34;实现注意事项&#34;>实现注意事项</h3><ul><li><strong>持久化进度信息</strong>:为了在客户端或服务器重启后能够恢复传输,进度信息需要被持久化存储,例如保存在文件系统或数据库中。</li><li><strong>并发控制</strong>:如果支持多个客户端同时对同一文件进行断点续传,需要实现适当的并发控制机制,以防止数据冲突。</li><li><strong>安全性</strong>:在设计断点续传功能时,应考虑到数据传输的安全性,使用加密连接(如HTTPS)来保护传输过程中的数据。</li></ul><p>通过上述步骤和策略,可以有效实现文件的断点续传功能,提高大文件传输的效率和可靠性,尤其是在网络环境不稳定的情况下。</p><h2 id=&#34;explicit关键字在c中的作用&#34;>explicit关键字在C++中的作用</h2><p>在C++中,<code>explicit</code>关键字用于修饰类的构造函数,以防止隐式类型转换或拷贝初始化。使用<code>explicit</code>关键字可以避免因不经意的类型转换而导致的错误,使代码的意图更加清晰。</p><h3 id=&#34;隐式类型转换&#34;>隐式类型转换</h3><p>默认情况下,如果一个构造函数只接受一个参数,或者所有参数除了一个都有默认值,那么它可以被用于隐式类型转换。例如:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>MyClass</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span> MyClass(<span style=&#34;color:#66d9ef&#34;>int</span> x) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span> <span style=&#34;color:#75715e&#34;>// 构造函数的实现</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#75715e&#34;></span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span>};</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span><span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>func</span>(MyClass obj) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> <span style=&#34;color:#75715e&#34;>// func的实现</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span><span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> func(<span style=&#34;color:#ae81ff&#34;>10</span>); <span style=&#34;color:#75715e&#34;>// 隐式调用 MyClass(int) 构造函数</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#ae81ff&#34;>0</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span>}</span></span></code></pre></div><p>在上面的例子中,当<code>func(10)</code>被调用时,整数<code>10</code>会隐式地被用来构造一个<code>MyClass</code>的对象,因为<code>MyClass</code>有一个接受<code>int</code>类型参数的构造函数。</p><h3 id=&#34;使用explicit防止隐式转换&#34;>使用<code>explicit</code>防止隐式转换</h3><p>要防止这种隐式类型转换,可以在构造函数前加上<code>explicit</code>关键字:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>MyClass</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span> <span style=&#34;color:#66d9ef&#34;>explicit</span> MyClass(<span style=&#34;color:#66d9ef&#34;>int</span> x) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span> <span style=&#34;color:#75715e&#34;>// 构造函数的实现</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#75715e&#34;></span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span>};</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span><span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>func</span>(MyClass obj) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> <span style=&#34;color:#75715e&#34;>// func的实现</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span><span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> func(<span style=&#34;color:#ae81ff&#34;>10</span>); <span style=&#34;color:#75715e&#34;>// 这会引发编译错误</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span><span style=&#34;color:#75715e&#34;></span> func(MyClass(<span style=&#34;color:#ae81ff&#34;>10</span>)); <span style=&#34;color:#75715e&#34;>// 正确的调用方式</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#ae81ff&#34;>0</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span>}</span></span></code></pre></div><p>通过将构造函数声明为<code>explicit</code>,你告诉编译器不允许使用该构造函数进行隐式类型转换。这样,除非显式地调用构造函数(如<code>MyClass(10)</code>),否则编译器会报错。</p><h3 id=&#34;explicit的作用域&#34;><code>explicit</code>的作用域</h3><ul><li>C++11之前,<code>explicit</code>关键字只能用于修饰只接受一个参数的构造函数。</li><li>从C++11开始,<code>explicit</code>可以用于修饰接受多个参数的构造函数,以及转换运算符。</li></ul><p>例如,防止类的对象被隐式转换为其他类型:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>MyClass</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span> <span style=&#34;color:#75715e&#34;>// 构造函数</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>explicit</span> MyClass(<span style=&#34;color:#66d9ef&#34;>int</span> x) {}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span> <span style=&#34;color:#75715e&#34;>// 转换运算符</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>explicit</span> <span style=&#34;color:#66d9ef&#34;>operator</span> <span style=&#34;color:#a6e22e&#34;>bool</span>() <span style=&#34;color:#66d9ef&#34;>const</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> true;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span>};</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span><span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> MyClass obj(<span style=&#34;color:#ae81ff&#34;>10</span>);</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> <span style=&#34;color:#66d9ef&#34;>bool</span> flag <span style=&#34;color:#f92672&#34;>=</span> obj; <span style=&#34;color:#75715e&#34;>// 这会引发编译错误</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>bool</span> flag2 <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#66d9ef&#34;>static_cast</span><span style=&#34;color:#f92672&#34;>&lt;</span><span style=&#34;color:#66d9ef&#34;>bool</span><span style=&#34;color:#f92672&#34;>&gt;</span>(obj); <span style=&#34;color:#75715e&#34;>// 正确的调用方式</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#ae81ff&#34;>0</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span>}</span></span></code></pre></div><p>使用<code>explicit</code>关键字是一种良好的编程实践,它可以增加代码的安全性和可读性,防止意外的类型转换导致的错误。</p><h2 id=&#34;链表和数组的区别&#34;>链表和数组的区别</h2><h2 id=&#34;队列和栈的区别&#34;>队列和栈的区别</h2><h2 id=&#34;二叉树先序遍历中序遍历后序遍历&#34;>二叉树先序遍历、中序遍历、后序遍历</h2><h2 id=&#34;引用和指针的区别&#34;>引用和指针的区别</h2><h2 id=&#34;usingtypedef和define有什么区别&#34;>using,typedef和define有什么区别</h2><p><code>using</code>、<code>typedef</code>和<code>define</code>在C++中都是与类型和符号定义相关的语法元素,但它们在用法、功能和适用范围上有着明显的不同。</p><h3 id=&#34;typedef&#34;><code>typedef</code></h3><p><code>typedef</code>是C和C++中用来为类型定义一个新名字的关键字。它常用于简化复杂类型的名称,提高代码的可读性。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>typedef</span> <span style=&#34;color:#66d9ef&#34;>unsigned</span> <span style=&#34;color:#66d9ef&#34;>long</span> ulong;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>typedef</span> <span style=&#34;color:#a6e22e&#34;>void</span> (<span style=&#34;color:#f92672&#34;>*</span>FuncPtr)(<span style=&#34;color:#66d9ef&#34;>int</span>, <span style=&#34;color:#66d9ef&#34;>double</span>);</span></span></code></pre></div><p>上述例子中,<code>ulong</code>成为了<code>unsigned long</code>的别名,<code>FuncPtr</code>成为了指向特定函数的指针类型的别名。</p><h3 id=&#34;using&#34;><code>using</code></h3><p><code>using</code>是C++11引入的类型别名声明的新语法,功能上与<code>typedef</code>相似,但语法更加直观,尤其是在定义模板别名时。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>using</span> ulong <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#66d9ef&#34;>unsigned</span> <span style=&#34;color:#66d9ef&#34;>long</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>using</span> FuncPtr <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#66d9ef&#34;>void</span> (<span style=&#34;color:#f92672&#34;>*</span>)(<span style=&#34;color:#66d9ef&#34;>int</span>, <span style=&#34;color:#66d9ef&#34;>double</span>);</span></span></code></pre></div><p>对于模板别名,<code>using</code>的优势更为明显:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>template</span><span style=&#34;color:#f92672&#34;>&lt;</span><span style=&#34;color:#66d9ef&#34;>typename</span> T<span style=&#34;color:#f92672&#34;>&gt;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>using</span> Ptr <span style=&#34;color:#f92672&#34;>=</span> T<span style=&#34;color:#f92672&#34;>*</span>;</span></span></code></pre></div><p>上述代码定义了一个模板别名<code>Ptr</code>,可以用于任意类型的指针。</p><h3 id=&#34;define&#34;><code>#define</code></h3><p><code>#define</code>是C和C++中的预处理指令,用于定义宏。它在编译前对代码进行文本替换,可以用来定义常量值、函数宏,或是进行条件编译等。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#75715e&#34;>#define PI 3.14159</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#75715e&#34;>#define MAX(a, b) ((a) &gt; (b) ? (a) : (b))</span></span></span></code></pre></div><h3 id=&#34;主要区别&#34;>主要区别</h3><ul><li><strong>作用范围</strong>:<code>typedef</code>和<code>using</code>仅用于类型别名,增强代码可读性和简化复杂类型声明。<code>#define</code>是预处理指令,功能更广泛,但主要用于宏定义和条件编译。</li><li><strong>类型安全</strong>:<code>typedef</code>和<code>using</code>是类型安全的,它们定义的别名完全等同于原类型。<code>#define</code>进行的是文本替换,不涉及类型检查,有可能导致难以追踪的错误。</li><li><strong>适用范围</strong>:<code>typedef</code>和<code>using</code>仅适用于类型定义,而<code>#define</code>几乎可以用于定义任何文本替换。</li><li><strong>编译阶段</strong>:<code>typedef</code>和<code>using</code>在编译阶段处理,<code>#define</code>在预处理阶段处理。</li></ul><p>总结来说,尽管<code>typedef</code>、<code>using</code>和<code>#define</code>在某些场景下可能看起来有相似的作用(如定义别名),但它们的用途、功能和适用范围有很大差别。在现代C++编程中,推荐使用<code>using</code>来定义类型别名,因为它比<code>typedef</code>更灵活,且语义更清晰。而<code>#define</code>因其灵活性和潜在的风险,在定义常量和类型别名时应谨慎使用,更多的是用于条件编译等场景。</p><h2 id=&#34;const修饰成员函数会怎么样可以有两个f其中一个带const另一个不带吗&#34;>const修饰成员函数会怎么样?可以有两个f(),其中一个带const,另一个不带吗?</h2><h2 id=&#34;static定义的变量默认值是多少普通变量呢&#34;>static定义的变量默认值是多少?普通变量呢?</h2><h2 id=&#34;extern有什么作用&#34;>extern有什么作用</h2><h2 id=&#34;简述指针常量与常量指针区别&#34;>简述指针常量与常量指针区别</h2><h2 id=&#34;构造函数能否为虚函数&#34;>构造函数能否为虚函数</h2><h2 id=&#34;为什么基类的析构函数需要是序函数&#34;>为什么基类的析构函数需要是序函数?</h2><h2 id=&#34;重载隐藏重写覆盖三者的区别&#34;>重载、隐藏、重写(覆盖)三者的区别?</h2><p>重载:是指同一可访问区内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型(统一为void,否则报错)。</p><p>隐藏:是指派生类的函数屏蔽了与其同名的基类函数,注意只要同名函数,不管参数列表是否相同,基类函数都会被隐藏。</p><p>重写(覆盖):是指派生类中存在重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。只有函数体不同(花括号内),派生类调用时会调用派生类的重写函数,不会调用被重写函数。重写的基类中被重写的函数必须有virtual修饰。</p><p>重载的参数不同,函数体不同;隐藏的参数可以不同,函数体不同;重写或者覆盖仅仅函数体不同。</p><h2 id=&#34;重载为什么改变参数就可以实现调用不同的函数&#34;>重载为什么改变参数就可以实现调用不同的函数?</h2><p>重载(Overloading)是一种允许多个在同一作用域内的函数或操作符拥有相同名称,但是参数类型、个数或顺序不同的编程技术。编译器根据这些不同的参数列表来区分它们,这个过程称为重载解析(Overload Resolution)。当一个重载的函数被调用时,编译器通过匹配调用中使用的参数类型和数量,选择最合适的重载版本执行。下面解释为什么通过改变参数可以实现调用不同的函数:</p><h3 id=&#34;参数类型数量和顺序的不同&#34;>参数类型、数量和顺序的不同</h3><ol><li><p><strong>类型</strong>:如果两个函数的参数数量相同,但参数类型不同,编译器可以根据传递给函数的实参类型来决定调用哪个函数。</p></li><li><p><strong>数量</strong>:如果两个函数的参数类型相同或者兼容,但参数数量不同,编译器会根据传递的参数数量来决定调用哪个函数。</p></li><li><p><strong>顺序</strong>:如果两个函数的参数数量和类型都相同,但参数的顺序不同,编译器同样可以根据传递的参数的顺序来决定调用哪个函数。</p></li></ol><h3 id=&#34;编译器的重载解析过程&#34;>编译器的重载解析过程</h3><p>当调用一个重载的函数时,编译器执行一个重载解析过程,按照以下步骤:</p><ol><li><p><strong>候选函数集合</strong>:收集所有与调用匹配的重载函数,包括那些参数可以通过隐式转换匹配的函数。</p></li><li><p><strong>可行函数集合</strong>:从候选函数中筛选出实参可以被隐式转换以匹配形参类型的函数。</p></li><li><p><strong>最佳匹配</strong>:从可行函数中选择“最佳匹配”的函数。编译器会根据类型匹配的精确度(如是否需要类型转换,转换的复杂程度等)来决定最佳匹配。</p></li><li><p><strong>调用</strong>:根据最佳匹配调用相应的函数。</p></li></ol><p>如果编译器无法找到一个明确的最佳匹配,或者找到多个同样好的匹配,就会产生一个重载解析的歧义,编译时会报错。</p><h3 id=&#34;重载的实用性&#34;>重载的实用性</h3><p>重载使得函数名可以根据上下文有不同的行为,提高了代码的可读性和易用性。例如,标准库中的<code>std::cout</code>就重载了多种类型的<code>&lt;&lt;</code>操作符,使得我们可以方便地输出不同类型的数据。</p><h3 id=&#34;示例&#34;>示例</h3><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>print</span>(<span style=&#34;color:#66d9ef&#34;>int</span> i) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> <span style=&#34;color:#e6db74&#34;>&#34;Integer: &#34;</span> <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> i <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> std<span style=&#34;color:#f92672&#34;>::</span>endl;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>print</span>(<span style=&#34;color:#66d9ef&#34;>double</span> f) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> <span style=&#34;color:#e6db74&#34;>&#34;Double: &#34;</span> <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> f <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> std<span style=&#34;color:#f92672&#34;>::</span>endl;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span><span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>print</span>(<span style=&#34;color:#66d9ef&#34;>const</span> std<span style=&#34;color:#f92672&#34;>::</span>string<span style=&#34;color:#f92672&#34;>&amp;</span> s) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> <span style=&#34;color:#e6db74&#34;>&#34;String: &#34;</span> <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> s <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> std<span style=&#34;color:#f92672&#34;>::</span>endl;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span><span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> print(<span style=&#34;color:#ae81ff&#34;>10</span>); <span style=&#34;color:#75715e&#34;>// 调用 print(int)</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span><span style=&#34;color:#75715e&#34;></span> print(<span style=&#34;color:#ae81ff&#34;>3.14</span>); <span style=&#34;color:#75715e&#34;>// 调用 print(double)</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span><span style=&#34;color:#75715e&#34;></span> print(<span style=&#34;color:#e6db74&#34;>&#34;Hello&#34;</span>); <span style=&#34;color:#75715e&#34;>// 调用 print(const std::string&amp;)</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><p>在这个例子中,<code>print</code>函数被重载了三次,分别接受<code>int</code>、<code>double</code>和<code>std::string</code>类型的参数。编译器根据传递给<code>print</code>函数的参数类型来决定调用哪一个重载版本。</p><p>通过这种方式,重载为编程提供了更高的灵活性和表达力。</p><h2 id=&#34;重载对链接有什么影响重载的底层实现&#34;>重载对链接有什么影响?重载的底层实现?</h2><p>函数重载在编译阶段对函数名进行了修饰(或称为名字改编、名字矫正、mangling),以保证每个重载函数在程序的链接阶段有一个唯一的标识。这个过程对链接有重要影响,因为它确保了链接器可以正确地区分和链接各个重载函数,即使它们有相同的基础名称。</p><h3 id=&#34;名字修饰name-mangling&#34;>名字修饰(Name Mangling)</h3><ul><li><strong>定义</strong>:名字修饰是一种编译器技术,用于在内部符号表中生成唯一的函数和变量标识符。对于重载函数,编译器将函数的名称、参数类型列表(有时还包括命名空间或类名称)编码到生成的唯一标识符中。</li><li><strong>目的</strong>:主要目的是解决名称冲突问题,特别是在函数重载和模板实例化的情况下,这些情况下可能会有多个实体共享相同的名称。</li></ul><h3 id=&#34;链接阶段的影响&#34;>链接阶段的影响</h3><p>在链接阶段,链接器需要解析程序中的所有外部符号引用,将它们与相应的符号定义匹配起来。由于名字修饰,每个重载函数都有了独特的内部名称,链接器可以正确地识别和链接到正确的函数实现,即使多个函数具有相同的基本名称。</p><h3 id=&#34;不同编译器的差异&#34;>不同编译器的差异</h3><p>不同的编译器可能采用不同的名字修饰规则。这意味着用不同编译器编译的代码在链接时可能会因为名字修饰的不兼容而遇到问题,尤其是在尝试链接不同编译器生成的二进制库时。为了解决这个问题,可以采用以下策略:</p><ul><li><strong>使用相同的编译器</strong>:对于需要链接在一起的所有模块,尽量使用相同的编译器和编译选项。</li><li><strong>C语言接口</strong>:对于C++库,如果需要与使用不同编译器的代码链接,可以提供一个“纯C”的接口,因为C语言没有函数重载,也不进行名字修饰,具有更好的二进制兼容性。</li><li><strong>外部接口(Extern &ldquo;C&rdquo;)</strong>:在C++中,可以使用<code>extern &quot;C&quot;</code>来告诉C++编译器对于特定的函数或变量不要进行名字修饰,从而使得这些符号能够被不同编译器编译的代码所链接。</li></ul><p>通过这些方法,可以减少或避免由于名字修饰规则差异导致的链接问题,确保重载函数的正确链接和使用。</p><h2 id=&#34;构造函数可以被重载么析构函数呢&#34;>构造函数可以被重载么?析构函数呢?</h2><h2 id=&#34;new和malloc有什么区别&#34;>new和malloc有什么区别?</h2><h2 id=&#34;new-operator和operator-new的区别&#34;>new operator和operator new的区别?</h2><p>在C++中,<code>new</code>操作符和<code>operator new</code>函数经常令人混淆,但它们有着明显的不同和各自的作用。</p><h3 id=&#34;new操作符&#34;><code>new</code>操作符</h3><p><code>new</code>操作符用于动态分配内存并调用构造函数初始化对象。它是一个高级操作,执行了两个主要步骤:</p><ol><li><strong>内存分配</strong>:首先,<code>new</code>操作符调用<code>operator new</code>函数分配足够的内存以容纳特定类型的对象。这是一个底层操作,仅负责分配内存,并不负责构造对象。</li><li><strong>构造对象</strong>:然后,在分配的内存上调用对象的构造函数来初始化对象。</li></ol><p>这个过程可以通过下面的例子来说明:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span>MyClass<span style=&#34;color:#f92672&#34;>*</span> obj <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#66d9ef&#34;>new</span> MyClass();</span></span></code></pre></div><p>上面的代码首先使用<code>operator new</code>分配足够的内存来存储一个<code>MyClass</code>类型的对象,然后在这块内存上调用<code>MyClass</code>的默认构造函数初始化对象。</p><h3 id=&#34;operator-new函数&#34;><code>operator new</code>函数</h3><p><code>operator new</code>是一个全局函数或者类成员函数,仅负责分配内存,不涉及对象的构造。它是<code>new</code>操作符背后的内存分配机制。当你使用<code>new</code>操作符时,实际上是隐式调用了<code>operator new</code>函数来分配内存。</p><p>如果需要,可以重载<code>operator new</code>来提供自定义的内存分配策略。例如:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>void</span><span style=&#34;color:#f92672&#34;>*</span> <span style=&#34;color:#66d9ef&#34;>operator</span> <span style=&#34;color:#a6e22e&#34;>new</span>(size_t size) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span> <span style=&#34;color:#75715e&#34;>// 自定义内存分配逻辑</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>void</span><span style=&#34;color:#f92672&#34;>*</span> p <span style=&#34;color:#f92672&#34;>=</span> malloc(size);</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span> <span style=&#34;color:#75715e&#34;>// 处理内存分配失败的情况</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>if</span> (<span style=&#34;color:#f92672&#34;>!</span>p) <span style=&#34;color:#66d9ef&#34;>throw</span> std<span style=&#34;color:#f92672&#34;>::</span>bad_alloc();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>6</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> p;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>7</span><span>}</span></span></code></pre></div><p>需要注意的是,重载<code>operator new</code>需要非常谨慎,因为它会改变程序的基本内存分配行为。</p><h3 id=&#34;总结-2&#34;>总结</h3><ul><li><code>new</code>操作符是一个高级操作,用于分配内存并初始化对象。</li><li><code>operator new</code>函数是一个底层操作,仅用于分配内存,不负责对象的构造。</li><li>在执行<code>new</code>操作符时,实际上会调用<code>operator new</code>函数来分配内存,然后在分配的内存上调用构造函数来构造对象。</li><li>可以重载<code>operator new</code>和<code>operator delete</code>来提供自定义的内存分配和释放策略,但需要谨慎操作,以避免意外的行为。</li></ul><h2 id=&#34;深入解析newoperator-newnewplacement-new&#34;>深入解析new、operator new、::new、placement new</h2><p><a href=&#34;https://blog.csdn.net/songchuwang1868/article/details/81353577&#34;>https://blog.csdn.net/songchuwang1868/article/details/81353577</a></p><p>在C++中,<code>new</code>操作的不同形式提供了内存分配和对象构造的灵活手段。深入理解它们之间的区别对于编写高效、可靠的C++代码非常重要。下面是对<code>new</code>、<code>operator new</code>、<code>::new</code>和<code>placement new</code>的深入解析:</p><h3 id=&#34;1-new操作符&#34;>1. <code>new</code>操作符</h3><p><code>new</code>操作符用于动态分配内存,并调用构造函数初始化对象。它是一个高级操作,封装了内存分配和对象构造两个步骤:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span>MyClass<span style=&#34;color:#f92672&#34;>*</span> obj <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#66d9ef&#34;>new</span> MyClass(args);</span></span></code></pre></div><p>上述代码首先调用<code>operator new</code>函数分配足够的内存来存储<code>MyClass</code>类型的对象,然后在分配的内存上调用<code>MyClass</code>的构造函数,使用<code>args</code>作为参数。</p><h3 id=&#34;2-operator-new函数&#34;>2. <code>operator new</code>函数</h3><p><code>operator new</code>是一个全局函数或类成员函数,负责分配内存。当使用<code>new</code>操作符时,背后就是调用<code>operator new</code>来进行内存分配。与<code>new</code>操作符不同,<code>operator new</code>仅分配内存,不负责构造对象:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>void</span><span style=&#34;color:#f92672&#34;>*</span> ptr <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#66d9ef&#34;>operator</span> <span style=&#34;color:#66d9ef&#34;>new</span>(<span style=&#34;color:#66d9ef&#34;>sizeof</span>(MyClass));</span></span></code></pre></div><p>可以重载<code>operator new</code>来自定义内存分配策略。</p><h3 id=&#34;3-new&#34;>3. <code>::new</code></h3><p><code>::new</code>指的是全局作用域下的<code>new</code>操作符,用来明确调用全局的<code>operator new</code>函数,而不是某个类的重载版本。这在有重载<code>operator new</code>的情况下很有用,确保调用的是全局版本:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span>MyClass<span style=&#34;color:#f92672&#34;>*</span> obj <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#f92672&#34;>::</span><span style=&#34;color:#66d9ef&#34;>new</span> MyClass(args);</span></span></code></pre></div><h3 id=&#34;4-placement-new&#34;>4. Placement new</h3><p><code>placement new</code>允许在已分配的内存上构造对象。这种方式不分配内存,只调用对象的构造函数。<code>placement new</code>非常有用,特别是在需要在特定位置构造对象的场景中:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>void</span><span style=&#34;color:#f92672&#34;>*</span> ptr <span style=&#34;color:#f92672&#34;>=</span> malloc(<span style=&#34;color:#66d9ef&#34;>sizeof</span>(MyClass)); <span style=&#34;color:#75715e&#34;>// 先分配内存</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#75715e&#34;></span>MyClass<span style=&#34;color:#f92672&#34;>*</span> obj <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#66d9ef&#34;>new</span>(ptr) MyClass(args); <span style=&#34;color:#75715e&#34;>// 在ptr指向的内存上构造对象</span></span></span></code></pre></div><p>需要注意的是,使用<code>placement new</code>时,应当手动调用对象的析构函数,并负责释放内存:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span>obj<span style=&#34;color:#f92672&#34;>-&gt;~</span>MyClass(); <span style=&#34;color:#75715e&#34;>// 调用析构函数</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#75715e&#34;></span>free(ptr); <span style=&#34;color:#75715e&#34;>// 释放内存</span></span></span></code></pre></div><h3 id=&#34;总结-3&#34;>总结</h3><ul><li><strong><code>new</code>操作符</strong>:高级操作,分配内存并构造对象。</li><li><strong><code>operator new</code>函数</strong>:底层操作,仅分配内存,可被重载。</li><li><strong><code>::new</code></strong>:使用全局<code>operator new</code>,避免调用类的重载版本。</li><li><strong><code>placement new</code></strong>:在指定内存位置构造对象,不分配内存,需要手动管理内存和析构。</li></ul><p>理解这些不同的<code>new</code>形式及其用途,可以帮助开发者更有效地管理内存和对象的生命周期,编写出更加精细控制和高效的C++代码。</p><h2 id=&#34;虚函数表的结构是怎样的&#34;>虚函数表的结构是怎样的?</h2><p>虚函数表是一个函数指针数组,数组里存放的都是函数指针,指向虚函数所在的位置。 对象调用虚函数时,会根据虚指针找到虚表的位置,再根据虚函数声明的顺序找到虚函数在数组的哪个位置,找到虚函数的地址,从而调用虚函数。</p><h2 id=&#34;ab两个类类中有虚函数c继承ab有几张虚函数表&#34;>A,B两个类,类中有虚函数。C继承AB,有几张虚函数表?</h2><p>答:2张</p><p>再问:为什么2张?</p><p>多继承就会有多个虚函数表。因为每个父类的虚函数是不同的,指针也是不同的。</p><p>如果共用一张虚函数表,就分不清到底子类的实例化是针对哪一个基函数的。</p><h2 id=&#34;为什么不应该在构造函数中调用虚函数&#34;>为什么不应该在构造函数中调用虚函数</h2><p>在C++中,父类(基类)的构造函数中调用虚函数是合法的,但这可能不会按照初学者期望的方式工作。在基类构造期间调用虚函数时,并不会调用派生类(子类)中的重写版本,即使是在构造派生类对象的过程中。相反,会调用基类中该虚函数的版本,或者是更上层基类中该虚函数最近的重写版本。这是因为在基类构造期间,对象类型被视为基类类型,而不是派生类类型,从而防止了对尚未完全构造的对象执行操作。</p><h3 id=&#34;为什么不应该在构造函数中调用虚函数-1&#34;>为什么不应该在构造函数中调用虚函数</h3><p>调用尚未完全构造的对象的成员函数可能会导致未定义行为或错误。如果虚函数依赖于派生类中的某些成员变量,而这些成员变量在基类构造函数被调用时尚未初始化,那么虚函数可能无法正常工作或产生错误结果。</p><h3 id=&#34;示例-1&#34;>示例</h3><p>考虑以下示例代码:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#75715e&#34;>#include</span> <span style=&#34;color:#75715e&#34;>&lt;iostream&gt;</span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>Base</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span> Base() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span> callVirtual();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> <span style=&#34;color:#66d9ef&#34;>virtual</span> <span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>callVirtual</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> <span style=&#34;color:#e6db74&#34;>&#34;Base version of callVirtual</span><span style=&#34;color:#ae81ff&#34;>\n</span><span style=&#34;color:#e6db74&#34;>&#34;</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span>};</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>Derived</span> <span style=&#34;color:#f92672&#34;>:</span> <span style=&#34;color:#66d9ef&#34;>public</span> Base {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span> Derived() <span style=&#34;color:#f92672&#34;>:</span> Base() {}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span> <span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>callVirtual</span>() <span style=&#34;color:#66d9ef&#34;>override</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span> std<span style=&#34;color:#f92672&#34;>::</span>cout <span style=&#34;color:#f92672&#34;>&lt;&lt;</span> <span style=&#34;color:#e6db74&#34;>&#34;Derived version of callVirtual</span><span style=&#34;color:#ae81ff&#34;>\n</span><span style=&#34;color:#e6db74&#34;>&#34;</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>21</span><span>};</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>22</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>23</span><span><span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>24</span><span> Derived d;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>25</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#ae81ff&#34;>0</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>26</span><span>}</span></span></code></pre></div><p>输出将是:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span>Base version of callVirtual</span></span></code></pre></div><p>尽管<code>Derived</code>对象被构造,但在<code>Base</code>构造函数中调用<code>callVirtual()</code>时,只会调用<code>Base</code>类中的<code>callVirtual()</code>版本,而不是<code>Derived</code>类中重写的版本。</p><h3 id=&#34;最佳实践&#34;>最佳实践</h3><p>为了避免潜在的错误和混淆,最佳实践是在构造函数和析构函数中避免调用虚函数。如果需要在对象构造期间执行某些操作,并且这些操作需要在派生类中进行特定的实现,考虑使用其他设计模式,如工厂模式,其中对象在完全构造后立即进行初始化,或者通过非虚成员函数调用虚函数,该非虚成员函数在对象构造完成后明确调用。</p><p>在构造函数中调用虚函数通常不是一个好的做法,原因主要涉及到对象的构造过程和多态行为的安全性。这里有几个关键点解释了为什么在构造函数中调用虚函数可能会导致问题:</p><h3 id=&#34;1-对象构造的阶段性&#34;>1. 对象构造的阶段性</h3><p>当创建一个派生类的对象时,对象的构造是按顺序进行的,从基类开始,然后是派生类。在基类的构造函数执行期间,派生类的部分还没有被构造。这意味着,如果在基类构造函数中调用了一个虚函数,该虚函数如果被派生类重写,那么调用的将是基类版本的实现,即使这个调用发生在派生类的构造函数的上下文中。这是因为此时对象的动态类型仍然是基类,而非派生类,C++的多态性在此时还未完全建立。</p><h3 id=&#34;2-安全性和一致性&#34;>2. 安全性和一致性</h3><p>如果虚函数在基类构造期间被调用,并且该虚函数被派生类重写,由于派生类的构造器尚未执行,任何由派生类添加的成员变量都还未被初始化。如果重写的虚函数依赖于这些成员变量,那么它可能会访问未初始化的变量,导致未定义行为或程序错误。</p><h3 id=&#34;3-设计上的限制&#34;>3. 设计上的限制</h3><p>在构造函数中调用虚函数强加了设计上的限制,即要求派生类在其虚函数实现中只能使用那些在基类构造期间就已经初始化完毕的资源。这限制了派生类设计的灵活性,使得派生类的开发者需要对基类的内部实现细节有深入的了解。</p><h3 id=&#34;替代方案&#34;>替代方案</h3><p>为了避免这些问题,通常建议不在构造函数(以及析构函数)中调用虚函数。作为替代,可以考虑以下设计策略:</p><ul><li><strong>延迟初始化</strong>:通过在构造函数之后显式调用初始化函数来进行操作,这可以确保对象完全构造后再进行多态行为的相关操作。</li><li><strong>非虚成员函数调用</strong>:在构造函数中调用一个非虚成员函数,该非虚成员函数然后再调用一个虚函数。这样做同样需要小心,以确保不违反上述原则。</li><li><strong>设计模式</strong>:考虑使用工厂模式或者建造者模式来创建对象,这样可以在对象完全构造好之后再执行需要多态行为的操作。</li></ul><p>通过遵循这些指导原则,可以避免在对象构造期间因调用虚函数而可能引入的问题,使得代码更加安全和健壮。</p><h3 id=&#34;上面都是gpt下面是人话&#34;>上面都是GPT,下面是人话</h3><p>对于在构造函数中调用一个虚函数的情况,被调用的只是这个函数的本地版本。也就是说,虚机制在构造函数中不工作。</p><p>这种行为有两个理由:</p><p>第一个理由是概念上的。</p><p>在概念上,构造函数的工作是生成一个对象。在任何构造函数中,可能只是部分形成对象——我们只能知道基类已被初始化,但并不能知道哪个类是从这个基类继承来的。然而,虚函数在继承层次上是“向前”和“向外”进行调用。它可以调用在派生类中的函数。如果我们在构造函数中也这样做,那么我们所调用的函数可能操作还没有被初始化的成员,这将导致灾难发生。</p><p>第二个理由是机械上的。</p><p>当一个构造函数被调用时,它做的首要的事情之一就是初始化它的VPTR。然而,它只能知道它属于“当前”类——即构造函数所在的类。于是它完全不知道这个对象是否是基于其它类。当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码——既不是为基类,也不是为它的派生类(因为类不知道谁继承它)。所以它使用的VPTR必须是对于这个类的VTABLE。而且,只要它是最后的构造函数调用,那么在这个对象的生命期内,VPTR将保持被初始化为指向这个VTABLE。但如果接着还有一个更晚派生类的构造函数被调用,那么这个构造函数又将设置VPTR指向它的VTABLE,以此类推,直到最后的构造函数结束。VRTP的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用是按照从基类到最晚派生类的顺序的另一个理由。</p><p>但是,当这一系列构造函数调用正发生时,每个构造函数都已经设置VPTR指向它自己的VTABLE。如果函数调用使用虚机制,它将只产生通过它自己的VTABLE的调用,而不是最后派生的VTABLE(所有构造函数被调用后才会有最后派生的VTABLE)。另外,许多编译器认识到,如果在构造函数中进行虚函数调用,应该使用早绑定,因为它们知道晚绑定将只对本地函数产生调用。无论哪种情况,在构造函数中调用虚函数都不能得到预期的结果。</p><h2 id=&#34;静态函数可以是虚函数么为什么&#34;>静态函数可以是虚函数么?为什么?</h2><p>static成员不属于任何类对象或类实例,所以即使给此函数加上virutal也是没有任何意义的。</p><p>静态与非静态成员函数之间有一个主要的区别。那就是静态成员函数没有this指针。所以无法访问vptr. 进而不能访问虚函数表。</p><h2 id=&#34;析构函数可以是纯虚函数么&#34;>析构函数可以是纯虚函数么?</h2><p>是的,析构函数可以被声明为纯虚函数(pure virtual destructor)在C++中。这通常用于定义抽象基类(Abstract Base Class,ABC),即这样的类不打算被实例化,而是作为派生类的基础。声明纯虚析构函数的目的是确保基类有一个虚析构函数,允许通过基类指针正确地删除派生类的对象。</p><h3 id=&#34;纯虚析构函数&#34;>纯虚析构函数</h3><p>当你将析构函数声明为纯虚函数时,你表明了类是抽象的,不能直接实例化,并且你期望从它派生出新的类。但与其他纯虚函数不同,纯虚析构函数必须提供一个定义,因为派生类的析构过程中会调用它。</p><h3 id=&#34;示例-2&#34;>示例</h3><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>AbstractBase</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span> <span style=&#34;color:#66d9ef&#34;>virtual</span> <span style=&#34;color:#f92672&#34;>~</span>AbstractBase() <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#ae81ff&#34;>0</span>; <span style=&#34;color:#75715e&#34;>// 纯虚析构函数</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#75715e&#34;></span>};</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>6</span><span>AbstractBase<span style=&#34;color:#f92672&#34;>::~</span>AbstractBase() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>7</span><span> <span style=&#34;color:#75715e&#34;>// 必须提供实现,即使是空实现</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>8</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><p>在这个示例中,<code>AbstractBase</code>有一个纯虚析构函数,使得<code>AbstractBase</code>成为一个抽象基类。尽管析构函数是纯虚的,我们仍然提供了它的定义,这是必须的。</p><h3 id=&#34;注意事项&#34;>注意事项</h3><ul><li>即使类中有纯虚析构函数,这个类也需要提供析构函数的定义。这是因为当派生类被销毁时,析构函数的调用会沿着继承链向上进行,最终会调用到基类的析构函数。</li><li>如果一个类有纯虚析构函数,它可以没有其他的纯虚函数。但是,这样的类仍然是抽象类,不能直接实例化。</li><li>纯虚析构函数的存在不影响派生类的析构函数的实现。派生类应该提供自己的析构函数来确保正确的资源清理。派生类的析构函数会自动调用基类的析构函数。</li></ul><p>声明纯虚析构函数是一种表明类是为了被继承而设计,并且不应该直接实例化的方式。同时,它确保了派生类对象通过基类指针被正确销毁的能力,这对于避免内存泄漏等问题至关重要。</p><h2 id=&#34;定义一个a-pa-new-a5-delete-pa-类a的构造函数和析构函数分别执行了几次&#34;>定义一个A* pa= new A[5]; delete pa; 类A的构造函数和析构函数分别执行了几次?</h2><p>构造函数执行了5次,每new一个对象都会调用一个构造函数,析构函数只调用一次,如果调用delete[] pa 析构函数才会调用5次。</p><h2 id=&#34;reserve和resize的区别是什么&#34;>reserve和resize的区别是什么?</h2><h2 id=&#34;智能指针有几种分别介绍一下他们的底层实现&#34;>智能指针有几种?分别介绍一下他们的底层实现?</h2><h2 id=&#34;为什么需要智能指针&#34;>为什么需要智能指针</h2><h2 id=&#34;你刚才说到循环引用那你口述一个循环引用的实例在你说的这个实例中那你怎么用weak_ptr来解决呢&#34;>你刚才说到循环引用,那你口述一个循环引用的实例。在你说的这个实例中,那你怎么用weak_ptr来解决呢?</h2><h2 id=&#34;说一下lambda表达式的底层实现&#34;>说一下lambda表达式的底层实现</h2><p>C++中的lambda表达式在底层实现上可以被视为一个匿名类(或称为闭包类型)的实例。当编译器遇到lambda表达式时,它会生成一个与lambda行为相匹配的唯一的类类型,这个类会重载函数调用操作符<code>operator()</code>,使得该类的实例(对象)可以像函数那样被调用。这个自动生成的类通常被称为闭包类型,而生成的对象称为闭包对象。</p><h3 id=&#34;lambda表达式的组成&#34;>Lambda表达式的组成</h3><p>一个lambda表达式通常包含三个部分:捕获列表(capture list)、参数列表(parameter list)以及函数体(function body)。例如:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>auto</span> lambda <span style=&#34;color:#f92672&#34;>=</span> [capture](parameters) <span style=&#34;color:#f92672&#34;>-&gt;</span> return_type { body };</span></span></code></pre></div><h3 id=&#34;底层实现细节&#34;>底层实现细节</h3><ol><li><p><strong>闭包类型的生成</strong>:对于每个lambda表达式,编译器生成一个唯一的未命名(匿名)类。这个类实现了<code>operator()</code>,使得其对象可以通过函数调用语法使用。</p></li><li><p><strong>捕获列表的处理</strong>:</p><ul><li><strong>值捕获(By Value)</strong>:被捕获的变量在闭包对象创建时通过拷贝初始化。每个被捕获的变量都会成为闭包类型的一个数据成员。</li><li><strong>引用捕获(By Reference)</strong>:通过引用捕获的变量不会被拷贝,相反,闭包对象存储的是变量的引用。</li><li><strong>隐式捕获</strong>:编译器根据lambda表达式体中使用的外部变量自动生成捕获列表。</li></ul></li><li><p><strong>构造函数</strong>:闭包类型会有一个构造函数,用于初始化闭包对象中的数据成员。这个构造函数根据捕获列表来定义。</p></li><li><p><strong><code>operator()</code>的实现</strong>:闭包类型重载的<code>operator()</code>会包含lambda表达式的函数体作为其实现。这个重载允许闭包对象像函数那样被调用。</p></li></ol><h3 id=&#34;例子&#34;>例子</h3><p>考虑下面的lambda表达式:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>int</span> x <span style=&#34;color:#f92672&#34;>=</span> <span style=&#34;color:#ae81ff&#34;>4</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>auto</span> lambda <span style=&#34;color:#f92672&#34;>=</span> [x](<span style=&#34;color:#66d9ef&#34;>int</span> y) { <span style=&#34;color:#66d9ef&#34;>return</span> x <span style=&#34;color:#f92672&#34;>+</span> y; };</span></span></code></pre></div><p>编译器会为这个lambda生成类似于以下伪代码的闭包类型:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>UniqueClosure</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span> <span style=&#34;color:#66d9ef&#34;>int</span> captured_x;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span> UniqueClosure(<span style=&#34;color:#66d9ef&#34;>int</span> x) <span style=&#34;color:#f92672&#34;>:</span> captured_x(x) {}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span> <span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#a6e22e&#34;>operator</span>()(<span style=&#34;color:#66d9ef&#34;>int</span> y) <span style=&#34;color:#66d9ef&#34;>const</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>6</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> captured_x <span style=&#34;color:#f92672&#34;>+</span> y;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>7</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>8</span><span>};</span></span></code></pre></div><p>然后,<code>lambda</code>变量实际上是<code>UniqueClosure</code>类型的一个实例:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span>UniqueClosure <span style=&#34;color:#a6e22e&#34;>lambda</span>(x);</span></span></code></pre></div><h3 id=&#34;总结-4&#34;>总结</h3><p>C++中的lambda表达式底层通过生成一个特殊的闭包类型来实现,这个类型重载了函数调用操作符并存储了所有必要的捕获变量,使得lambda表达式既可以捕获周围的上下文,也可以像普通函数那样被调用。这个过程完全由编译器自动完成,对程序员透明。</p><h2 id=&#34;深拷贝和浅拷贝的区别&#34;>深拷贝和浅拷贝的区别</h2><h2 id=&#34;讲一下四种类型转换符&#34;>讲一下四种类型转换符?</h2><h2 id=&#34;进程间通信&#34;>进程间通信</h2><h2 id=&#34;线程间通信&#34;>线程间通信</h2><h2 id=&#34;协程是什么&#34;>协程是什么?</h2><h2 id=&#34;用户态和内核态区别&#34;>用户态和内核态区别</h2><h2 id=&#34;死锁产生的原因及四个必要条件&#34;>死锁产生的原因及四个必要条件</h2><h2 id=&#34;系统调用是什么有哪些系统调用&#34;>系统调用是什么,有哪些系统调用</h2><h2 id=&#34;孤儿进程僵尸进程&#34;>孤儿进程、僵尸进程</h2><h2 id=&#34;单核机器上写多线程程序是否需要考虑加锁为什么&#34;>单核机器上写多线程程序,是否需要考虑加锁,为什么?</h2><h2 id=&#34;静态链接和动态链接&#34;>静态链接和动态链接</h2><h2 id=&#34;硬链接和软链接&#34;>硬链接和软链接</h2><h2 id=&#34;为什么是成倍扩容而不是等差扩容&#34;>为什么是成倍扩容,而不是等差扩容</h2><h2 id=&#34;-范围解析运算符有哪些用法&#34;>:: 范围解析运算符有哪些用法</h2><h2 id=&#34;inline关键字&#34;>inline关键字</h2><h2 id=&#34;union关键字&#34;>union关键字</h2><h2 id=&#34;using关键字用法&#34;>using关键字用法</h2><h2 id=&#34;friend关键字&#34;>friend关键字</h2><h2 id=&#34;volatile关键字&#34;>volatile关键字</h2><h2 id=&#34;为避免对同一对象进行赋值操作this指针用法&#34;>为避免对同一对象进行赋值操作,this指针,用法</h2><p>在C++中,<code>this</code>指针是一个特殊的指针,它指向当前对象。使用<code>this</code>指针的一个常见场景是在成员函数中,特别是在赋值操作符<code>operator=</code>的重载函数中,来避免自赋值的情况。自赋值发生在一个对象被赋值给自身的时候,如果不进行检测,这可能导致意想不到的错误,尤其是在涉及动态内存分配的时候。</p><p>下面是如何使用<code>this</code>指针来避免自赋值的一个例子:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>MyClass</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span> MyClass<span style=&#34;color:#f92672&#34;>&amp;</span> <span style=&#34;color:#66d9ef&#34;>operator</span><span style=&#34;color:#f92672&#34;>=</span>(<span style=&#34;color:#66d9ef&#34;>const</span> MyClass<span style=&#34;color:#f92672&#34;>&amp;</span> other) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span> <span style=&#34;color:#75715e&#34;>// 检查自赋值</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>if</span> (<span style=&#34;color:#66d9ef&#34;>this</span> <span style=&#34;color:#f92672&#34;>==</span> <span style=&#34;color:#f92672&#34;>&amp;</span>other) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span> <span style=&#34;color:#75715e&#34;>// 是自赋值,直接返回*this</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#66d9ef&#34;>this</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span> <span style=&#34;color:#75715e&#34;>// 不是自赋值,执行赋值操作</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#75715e&#34;>// 例如,释放当前对象持有的资源</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#75715e&#34;>//delete[] this-&gt;data;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> <span style=&#34;color:#75715e&#34;>// 然后复制other的数据到当前对象</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#75715e&#34;>//this-&gt;data = new int[...];</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#75715e&#34;>//std::copy(...);</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#66d9ef&#34;>this</span>; <span style=&#34;color:#75715e&#34;>// 返回当前对象的引用</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span><span style=&#34;color:#75715e&#34;></span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span>};</span></span></code></pre></div><h3 id=&#34;自赋值检查的工作原理&#34;>自赋值检查的工作原理</h3><ul><li><strong>比较<code>this</code>和<code>&amp;other</code></strong>:通过比较当前对象的<code>this</code>指针和赋值右侧对象的地址<code>&amp;other</code>,可以检查赋值操作是否试图将对象赋值给自身。</li><li><strong>提前返回</strong>:如果检测到自赋值,函数直接返回<code>*this</code>,避免执行无意义或可能危险的资源释放和重新分配操作。</li><li><strong>正常赋值流程</strong>:如果不是自赋值,函数则继续执行正常的赋值逻辑,如释放旧资源,分配新资源,复制数据等。</li></ul><h3 id=&#34;为什么要避免自赋值&#34;>为什么要避免自赋值</h3><p>在某些情况下,如果不检查自赋值,赋值操作可能会破坏对象的状态。例如,如果赋值操作首先释放了对象持有的资源,然后试图从自身复制这些资源,那么在释放资源之后,对象将尝试从已经被释放的资源复制数据,这将导致未定义行为。</p><p>通过在赋值操作中添加自赋值检查,可以保证即使在自赋值的情况下,对象的状态也能保持一致和安全。这是一个良好的编程实践,特别是在处理涉及动态内存管理的复杂类时。</p><h2 id=&#34;什么是copy-on-write&#34;>什么是copy on write</h2><h2 id=&#34;模板特化与偏特化&#34;>模板特化与偏特化</h2><p>在C++模板编程中,模板特化和偏特化是两种重要的技术,它们允许程序员为模板提供特定类型的实现。虽然它们的目的相似,即为某些特定情况提供定制的模板代码,但它们在使用和概念上有着明显的区别。</p><h3 id=&#34;模板特化template-specialization&#34;>模板特化(Template Specialization)</h3><p>模板特化是指为模板的一个具体类型或类型组合提供一个特定的实现。当模板参数满足特化条件时,编译器会使用特化的实现而不是通用模板。模板特化可以应用于函数模板和类模板。</p><h4 id=&#34;全特化full-specialization&#34;>全特化(Full Specialization)</h4><p>当为模板的所有参数提供特定的类型时,称之为全特化。</p><p><strong>类模板全特化示例:</strong></p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>template</span><span style=&#34;color:#f92672&#34;>&lt;</span><span style=&#34;color:#66d9ef&#34;>typename</span> T<span style=&#34;color:#f92672&#34;>&gt;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>MyTemplate</span> { <span style=&#34;color:#75715e&#34;>/* 通用实现 */</span> };</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#75715e&#34;>// 全特化为int类型</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>template</span><span style=&#34;color:#f92672&#34;>&lt;&gt;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>6</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>MyTemplate</span><span style=&#34;color:#f92672&#34;>&lt;</span><span style=&#34;color:#66d9ef&#34;>int</span><span style=&#34;color:#f92672&#34;>&gt;</span> { <span style=&#34;color:#75715e&#34;>/* 特化实现 */</span> };</span></span></code></pre></div><p><strong>函数模板全特化示例:</strong></p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>template</span><span style=&#34;color:#f92672&#34;>&lt;</span><span style=&#34;color:#66d9ef&#34;>typename</span> T<span style=&#34;color:#f92672&#34;>&gt;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>void</span> myFunction(T value) { <span style=&#34;color:#75715e&#34;>/* 通用实现 */</span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#75715e&#34;>// 全特化为int类型</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>template</span><span style=&#34;color:#f92672&#34;>&lt;&gt;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>6</span><span><span style=&#34;color:#66d9ef&#34;>void</span> myFunction<span style=&#34;color:#f92672&#34;>&lt;</span><span style=&#34;color:#66d9ef&#34;>int</span><span style=&#34;color:#f92672&#34;>&gt;</span>(<span style=&#34;color:#66d9ef&#34;>int</span> value) { <span style=&#34;color:#75715e&#34;>/* 特化实现 */</span> }</span></span></code></pre></div><h3 id=&#34;模板偏特化partial-specialization&#34;>模板偏特化(Partial Specialization)</h3><p>模板偏特化是类模板的一种特殊形式,它允许为模板的一部分参数提供特定的类型,而不是全部参数。注意,函数模板不支持偏特化,偏特化仅适用于类模板。</p><p><strong>类模板偏特化示例:</strong></p><p>假设我们有一个模板用于处理指针,我们可以为指针类型提供一个偏特化版本:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>template</span><span style=&#34;color:#f92672&#34;>&lt;</span><span style=&#34;color:#66d9ef&#34;>typename</span> T<span style=&#34;color:#f92672&#34;>&gt;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>MyTemplate</span> { <span style=&#34;color:#75715e&#34;>/* 通用实现 */</span> };</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#75715e&#34;>// 偏特化为指针类型</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>template</span><span style=&#34;color:#f92672&#34;>&lt;</span><span style=&#34;color:#66d9ef&#34;>typename</span> T<span style=&#34;color:#f92672&#34;>&gt;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>6</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>MyTemplate</span><span style=&#34;color:#f92672&#34;>&lt;</span>T<span style=&#34;color:#f92672&#34;>*&gt;</span> { <span style=&#34;color:#75715e&#34;>/* 指针类型的特化实现 */</span> };</span></span></code></pre></div><p>在这个例子中,当<code>MyTemplate</code>的模板参数是任何类型的指针时,会使用偏特化版本。</p><h3 id=&#34;区别总结&#34;>区别总结</h3><ul><li><strong>全特化</strong>:为模板提供一个针对特定类型或类型组合的完全定制的实现。适用于函数模板和类模板。</li><li><strong>偏特化</strong>:只针对类模板,允许为模板的一部分参数提供特定的类型。它是对模板的进一步泛化,用于处理更具体的情况,但不像全特化那样针对全部参数。</li></ul><p>模板特化和偏特化是C++模板编程中强大的特性,允许开发者根据不同的类型参数定制模板的行为,提高了代码的灵活性和可重用性。</p><h2 id=&#34;为什么函数模板不支持偏特化&#34;>为什么函数模板不支持偏特化</h2><h2 id=&#34;写一个宏版本的min&#34;>写一个宏版本的MIN</h2><h2 id=&#34;auto作为返回值和模板一起怎么用&#34;>auto作为返回值和模板一起怎么用</h2><h2 id=&#34;tcp和udp区别&#34;>TCP和UDP区别</h2><h2 id=&#34;c-类对象的初始化顺序有多重继承情况下的顺序&#34;>C++ 类对象的初始化顺序,有多重继承情况下的顺序</h2><h2 id=&#34;如果三次握手时候每次握手信息对方没收到会怎么样简答&#34;>如果三次握手时候每次握手信息对方没收到会怎么样,简答</h2><h2 id=&#34;avl-和红黑树的差别&#34;>AVL 和红黑树的差别</h2><h2 id=&#34;数据库事务的特点&#34;>数据库事务的特点</h2><h2 id=&#34;虚拟地址如何转为物理地址&#34;>虚拟地址如何转为物理地址</h2><h2 id=&#34;说一下滑动窗口如果接收方滑动窗口满了发送方会怎么做&#34;>说一下滑动窗口,如果接收方滑动窗口满了,发送方会怎么做</h2><h2 id=&#34;页面置换算法&#34;>页面置换算法</h2><h2 id=&#34;既然有了mallocfreec中为什么还需要newdelete呢&#34;>既然有了malloc/free,C++中为什么还需要new/delete呢?</h2><h2 id=&#34;delete怎么实现&#34;>delete[]怎么实现</h2><h2 id=&#34;计算类的sizeof&#34;>计算类的sizeof</h2><h2 id=&#34;解决哈希冲突的方式&#34;>解决哈希冲突的方式?</h2><h2 id=&#34;结构体内存对齐方式和为什么要进行内存对齐&#34;>结构体内存对齐方式和为什么要进行内存对齐?</h2><h2 id=&#34;调试程序的方法&#34;>调试程序的方法</h2><h2 id=&#34;遇到coredump要怎么调试&#34;>遇到coredump要怎么调试</h2><h2 id=&#34;成员初始化列表的概念为什么用成员初始化列表会快一些性能优势&#34;>成员初始化列表的概念,为什么用成员初始化列表会快一些(性能优势)?</h2><h2 id=&#34;有三种情况是必须使用成员初始化列表进行初始化&#34;>有三种情况是必须使用成员初始化列表进行初始化</h2><p>常量成员的初始化,因为常量成员只能初始化不能赋值引用类型没有默认构造函数的对象必须使用成员初始化列表的方式进行初始化</p><p>见:<a href=&#34;https://www.nowcoder.com/discuss/353156833760190464#:~:text=%E8%AF%A6%E8%A7%81-,C%2B%2B%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8,-%EF%BC%8845%EF%BC%89%20%E7%94%A8%E8%BF%87&#34;>C++ 初始化列表</a></p><h2 id=&#34;c的调用惯例简单一点c函数调用的压栈过程&#34;>C++的调用惯例(简单一点C++函数调用的压栈过程)</h2><h2 id=&#34;一个函数或者可执行文件的生成过程或者编译过程是怎样的&#34;>一个函数或者可执行文件的生成过程或者编译过程是怎样的</h2><h2 id=&#34;定义和声明的区别&#34;>定义和声明的区别</h2><h2 id=&#34;被free回收的内存是立即返还给操作系统吗为什么&#34;>被free回收的内存是立即返还给操作系统吗?为什么</h2><h2 id=&#34;引用作为函数参数以及返回值的好处&#34;>引用作为函数参数以及返回值的好处</h2><h2 id=&#34;建立tcp服务器的各个系统调用&#34;>建立TCP服务器的各个系统调用</h2><h2 id=&#34;tcp和udp相关的协议与端口号&#34;>TCP和UDP相关的协议与端口号</h2><h2 id=&#34;http的请求方法有哪些get和post的区别&#34;>http的请求方法有哪些?get和post的区别。</h2><h2 id=&#34;tcp三次握手时的第一次的seq序号是怎样产生的&#34;>TCP三次握手时的第一次的seq序号是怎样产生的</h2><h2 id=&#34;一个机器能够使用的端口号上限是多少为什么可以改变吗那如果想要用的端口超过这个限制怎么办&#34;>一个机器能够使用的端口号上限是多少,为什么?可以改变吗?那如果想要用的端口超过这个限制怎么办?</h2><h2 id=&#34;对称密码和非对称密码体系&#34;>对称密码和非对称密码体系</h2><h2 id=&#34;数字证书是什么&#34;>数字证书是什么?</h2><h2 id=&#34;服务器出现大量close_wait的连接的原因以及解决方法&#34;>服务器出现大量close_wait的连接的原因以及解决方法</h2><h2 id=&#34;消息摘要算法列举一下介绍md5算法为什么md5是不可逆的有什么办法可以加强消息摘要算法的安全性让它不那么容易被破解呢&#34;>消息摘要算法列举一下,介绍MD5算法,为什么MD5是不可逆的,有什么办法可以加强消息摘要算法的安全性让它不那么容易被破解呢?</h2><h2 id=&#34;介绍一下ping的过程分别用到了哪些协议&#34;>介绍一下ping的过程,分别用到了哪些协议</h2><h2 id=&#34;tcpip的粘包与避免介绍一下&#34;>TCP/IP的粘包与避免介绍一下</h2><h2 id=&#34;一个ip配置多个域名靠什么识别&#34;>一个ip配置多个域名,靠什么识别?</h2><h2 id=&#34;dns的工作过程和原理&#34;>DNS的工作过程和原理</h2><h2 id=&#34;arp协议&#34;>ARP协议</h2><h2 id=&#34;关系型和非关系型数据库的区别&#34;>关系型和非关系型数据库的区别</h2><h2 id=&#34;说一下-mysql-执行一条查询语句的内部执行过程&#34;>说一下 MySQL 执行一条查询语句的内部执行过程?</h2><h2 id=&#34;数据库的索引类型有哪些&#34;>数据库的索引类型有哪些</h2><h2 id=&#34;说一下事务是怎么实现的&#34;>说一下事务是怎么实现的</h2><h2 id=&#34;mysql怎么建立索引怎么建立主键索引怎么删除索引&#34;>MySQL怎么建立索引,怎么建立主键索引,怎么删除索引?</h2><h2 id=&#34;索引的优缺点什么时候使用索引什么时候不能使用索引&#34;>索引的优缺点,什么时候使用索引,什么时候不能使用索引</h2><h2 id=&#34;索引的底层实现&#34;>索引的底层实现</h2><h2 id=&#34;b树和b树的区别&#34;>B树和B+树的区别</h2><h2 id=&#34;mysql的优化&#34;>Mysql的优化</h2><h3 id=&#34;高频访问&#34;>高频访问</h3><h3 id=&#34;并发优化&#34;>并发优化</h3><h2 id=&#34;索引最左前缀最左匹配&#34;>索引最左前缀/最左匹配</h2><h2 id=&#34;数据库中事务的acid&#34;>数据库中事务的ACID</h2><h2 id=&#34;什么是脏读不可重复读和幻读&#34;>什么是脏读,不可重复读和幻读?</h2><h2 id=&#34;数据库的隔离级别mysql和oracle的隔离级别分别是什么&#34;>数据库的隔离级别,mysql和Oracle的隔离级别分别是什么</h2><h2 id=&#34;mysql的表空间方式各自特点&#34;>Mysql的表空间方式,各自特点</h2><h2 id=&#34;数据库的范式&#34;>数据库的范式</h2><h2 id=&#34;乐观锁与悲观锁解释一下&#34;>乐观锁与悲观锁解释一下</h2><h2 id=&#34;乐观锁与悲观锁是怎么实现的&#34;>乐观锁与悲观锁是怎么实现的</h2><h2 id=&#34;linux的io模型介绍以及同步异步阻塞非阻塞的区别&#34;>Linux的I/O模型介绍以及同步异步阻塞非阻塞的区别</h2><h2 id=&#34;文件系统的理解ext4xfsbtrfs&#34;>文件系统的理解(EXT4,XFS,BTRFS)</h2><h2 id=&#34;什么是io多路复用&#34;>什么是IO多路复用</h2><h2 id=&#34;io复用的三种方法selectpollepoll深入理解包括三者区别内部原理实现&#34;>IO复用的三种方法(select,poll,epoll)深入理解,包括三者区别,内部原理实现?</h2><h2 id=&#34;epoll的et模式和lt模式et的非阻塞&#34;>Epoll的ET模式和LT模式(ET的非阻塞)</h2><h2 id=&#34;文件权限怎么看rwx&#34;>文件权限怎么看(rwx)</h2><h2 id=&#34;文件的三种时间mtime-atimectime分别在什么时候会改变&#34;>文件的三种时间(mtime, atime,ctime),分别在什么时候会改变</h2><h2 id=&#34;linux监控网络带宽的命令查看特定进程的占用网络资源情况命令&#34;>Linux监控网络带宽的命令,查看特定进程的占用网络资源情况命令</h2><h2 id=&#34;怎么修改一个文件的权限&#34;>怎么修改一个文件的权限</h2><h2 id=&#34;coredump是什么-怎么才能coredump&#34;>coredump是什么 怎么才能coredump</h2><h2 id=&#34;linux理论上最多可以创建多少个进程一个进程可以创建多少线程和什么有关&#34;>Linux理论上最多可以创建多少个进程?一个进程可以创建多少线程,和什么有关</h2><h2 id=&#34;冯诺依曼结构有哪几个模块分别对应现代计算机的哪几个部分&#34;>冯诺依曼结构有哪几个模块?分别对应现代计算机的哪几个部分?</h2><h2 id=&#34;进程之间的通信方法有哪几种&#34;>进程之间的通信方法有哪几种</h2><h2 id=&#34;进程调度方法详细介绍&#34;>进程调度方法详细介绍</h2><h2 id=&#34;什么是饥饿&#34;>什么是饥饿</h2><h2 id=&#34;可重入函数是什么&#34;>可重入函数是什么</h2><h2 id=&#34;内核空间和用户空间是怎样区分的&#34;>内核空间和用户空间是怎样区分的</h2><h2 id=&#34;同一个进程内的线程会共享什么资源&#34;>同一个进程内的线程会共享什么资源?</h2><h2 id=&#34;brk和mmap是什么&#34;>brk和mmap是什么</h2><h2 id=&#34;什么是字节序怎么判断是大端还是小端有什么用&#34;>什么是字节序?怎么判断是大端还是小端?有什么用?</h2><h2 id=&#34;写单例模式线程安全版本&#34;>写单例模式,线程安全版本</h2><h2 id=&#34;写三个线程交替打印abc&#34;>写三个线程交替打印ABC</h2><h2 id=&#34;二维码登录的实现过程&#34;>二维码登录的实现过程</h2><h2 id=&#34;不使用临时变量实现swap函数&#34;>不使用临时变量实现swap函数</h2><h2 id=&#34;找到数组里第k大的数字&#34;>找到数组里第k大的数字</h2><h2 id=&#34;top-k问题&#34;>Top K问题</h2><h2 id=&#34;b树-b树&#34;>B树 B+树</h2><h2 id=&#34;怎么写sql取表的前1000行数据&#34;>怎么写sql取表的前1000行数据</h2><h2 id=&#34;布隆过滤器&#34;>布隆过滤器</h2><h2 id=&#34;实现一个队列并且使它支持多线程&#34;>实现一个队列,并且使它支持多线程</h2><h2 id=&#34;100层楼只有2个鸡蛋想要判断出那一层刚好让鸡蛋碎掉给出策略&#34;>100层楼,只有2个鸡蛋,想要判断出那一层刚好让鸡蛋碎掉,给出策略</h2><h2 id=&#34;毒药问题1000瓶水其中有一瓶可以无限稀释的毒药要快速找出哪一瓶有毒需要几只小白鼠&#34;>毒药问题,1000瓶水,其中有一瓶可以无限稀释的毒药,要快速找出哪一瓶有毒,需要几只小白鼠</h2><h2 id=&#34;先手必胜策略问题100本书每次能够拿1-5本怎么拿能保证最后一次是你拿&#34;>先手必胜策略问题:100本书,每次能够拿1-5本,怎么拿能保证最后一次是你拿</h2><h2 id=&#34;放n只蚂蚁在一条树枝上蚂蚁与蚂蚁之间碰到就各自往反方向走问总距离或者时间&#34;>放n只蚂蚁在一条树枝上,蚂蚁与蚂蚁之间碰到就各自往反方向走,问总距离或者时间。</h2><h2 id=&#34;瓶子换饮料问题1000瓶饮料3个空瓶子能够换1瓶饮料问最多能喝几瓶&#34;>瓶子换饮料问题:1000瓶饮料,3个空瓶子能够换1瓶饮料,问最多能喝几瓶</h2><h2 id=&#34;在24小时里面时针分针秒针可以重合几次&#34;>在24小时里面时针分针秒针可以重合几次</h2><h2 id=&#34;生成随机数问题给定生成1到5的随机数rand5如何得到生成1到7的随机数函数rand7&#34;>生成随机数问题:给定生成1到5的随机数Rand5(),如何得到生成1到7的随机数函数Rand7()?</h2><h2 id=&#34;蓄水池采样算法&#34;>蓄水池采样算法</h2><h2 id=&#34;赛马有25匹马每场比赛只能赛5匹至少要赛多少场才能找到最快的3匹马&#34;>赛马:有25匹马,每场比赛只能赛5匹,至少要赛多少场才能找到最快的3匹马?</h2><h2 id=&#34;烧-香绳子其他-确定时间问题有两根不均匀的香燃烧完都需要一个小时问怎么确定15分钟的时长&#34;>烧 香/绳子/其他 确定时间问题:有两根不均匀的香,燃烧完都需要一个小时,问怎么确定15分钟的时长?</h2><h2 id=&#34;掰巧克力问题-nm块巧克力每次掰一块的一行或一列掰成11的巧克力需要多少次1000个人参加辩论赛1v1输了就退出需要安排多少场比赛快手提前批&#34;>掰巧克力问题 NM块巧克力,每次掰一块的一行或一列,掰成11的巧克力需要多少次?(1000个人参加辩论赛,1V1,输了就退出,需要安排多少场比赛)(快手提前批)</h2><h2 id=&#34;介绍一下hadoop&#34;>介绍一下Hadoop</h2><h2 id=&#34;说一下mapreduce的运行机制&#34;>说一下MapReduce的运行机制</h2><h2 id=&#34;消息队列是什么&#34;>消息队列是什么</h2><h2 id=&#34;为什么kafka吞吐量高介绍一下零拷贝&#34;>为什么kafka吞吐量高?/介绍一下零拷贝</h2><h2 id=&#34;spark是什么&#34;>spark是什么</h2><h2 id=&#34;kafka如何选举leader&#34;>kafka如何选举leader</h2><h2 id=&#34;斐波那契数列第n项olog-n求出&#34;>斐波那契数列第n项,O(log n)求出</h2><h2 id=&#34;react的特点以及数据流向diff算法主要做了啥&#34;>react的特点以及数据流向,diff算法主要做了啥?</h2><h2 id=&#34;内存为什么要分段分段就只是为了方便程序员吗&#34;>内存为什么要分段?分段就只是为了方便程序员吗?</h2><h2 id=&#34;https可以防止dns劫持吗&#34;>https可以防止DNS劫持吗?</h2><h2 id=&#34;设计一款聊天软件你打算采用什么方案&#34;>设计一款聊天软件,你打算采用什么方案?</h2><h2 id=&#34;cpu3级缓存&#34;>cpu3级缓存</h2><h2 id=&#34;快表tlb是什么&#34;>快表(TLB)是什么</h2><h2 id=&#34;http无状态优劣cookie&#34;>http无状态优劣—cookie</h2><h2 id=&#34;什么是局部性原理&#34;>什么是局部性原理</h2><h2 id=&#34;rabbitmq怎么用的&#34;>rabbitMQ怎么用的</h2><h2 id=&#34;redis怎么用的&#34;>redis怎么用的</h2><h2 id=&#34;数据库中哪些地方用了链表&#34;>数据库中哪些地方用了链表</h2><h2 id=&#34;http了解吗怎么样不用框架用原生语言解析请求的json数据&#34;>HTTP了解吗,怎么样不用框架用原生语言解析请求的Json数据</h2><h2 id=&#34;怎么样用数据结构和算法写一个定时器&#34;>怎么样用数据结构和算法写一个定时器</h2><h2 id=&#34;tcp是怎么样超时重传的&#34;>tcp是怎么样超时重传的</h2><h2 id=&#34;mqtt&#34;>MQTT</h2><h2 id=&#34;webserver-项目中如果所有线程都在工作收到请求怎么做&#34;>webserver 项目中如果所有线程都在工作,收到请求怎么做?</h2><h2 id=&#34;python-routine&#34;>python routine</h2><h2 id=&#34;自己写一个协程控制器包括添加协程暂停等功能&#34;>自己写一个协程控制器,包括添加协程,暂停等功能</h2><h2 id=&#34;写个生产者消费者模型&#34;>写个生产者消费者模型</h2><h2 id=&#34;线程安全的数据结构&#34;>线程安全的数据结构</h2><h2 id=&#34;线程池的创建方法&#34;>线程池的创建方法</h2><h2 id=&#34;c反射是什么&#34;>C++反射是什么</h2><h2 id=&#34;https加密协商出来的密钥的类型是什么&#34;>https加密协商出来的密钥的类型是什么</h2><h2 id=&#34;海量数据问题&#34;>海量数据问题</h2><p><a href=&#34;https://wangpengcheng.github.io/2019/12/17/hailiangshuju_problems/&#34;>https://wangpengcheng.github.io/2019/12/17/hailiangshuju_problems/</a></p><h2 id=&#34;c问题大全&#34;>C++问题大全</h2><p><a href=&#34;https://zhuanlan.zhihu.com/p/603532168&#34;>https://zhuanlan.zhihu.com/p/603532168</a></p>

2024/4/8
阅读更多

二分问题的统一解决方案

<p>思路来自 <a href=&#34;https://www.youtube.com/watch?v=QvcM99na30k&#34;>https://www.youtube.com/watch?v=QvcM99na30k</a>。本文为学习记录+结合自己的思考修改后得出的框架。</p><h2 id=&#34;引入&#34;>引入</h2><p>在算法题中有时候会遇到这种场景,对于一个问题,它的答案是一个整数且一定取在一个范围内,例如答案只可能是$[1, 10^9]$中的一个数字。且存在一个条件,使得对于任何小于答案的数字,这个条件都不成立;而对于任何大于等于答案的数字,这个条件都成立。那么这个时候就可以使用二分的方法来找到答案。</p><h2 id=&#34;框架与求解&#34;>框架与求解</h2><p>下面用严格的语言来表达这类问题(我称为二分问题):</p><p>存在一个函数 <code>check</code> , 它接受一个整数 <code>i</code> 作为输入, 且 <code>check(i)</code> 只可能等于 <code>True</code> or <code>False</code> .</p><p>并且,存在一个数 $k$ ,使得对于任何小于 $k$ 的整数 <code>i</code> ,都有 <code>check(i)==False</code> ,而对于任何大于等于$k$的整数 <code>i</code> ,有 <code>check(i)==True</code> . 我们现在有函数 <code>check</code> ,需要找到这个整数$k$. 且我们知道 $k$ 在 $[a,b]$ 区间内。</p><p>我们接下来提出一个框架来解决这个问题。</p><p>首先,我们从获取信息的角度思考这个问题。在我们去实际跑一次 <code>check</code> 函数之前,我们是没有获取任何信息的。这个时候我们可以认为所有的 $[a,b]$ 之间的整数都是未知状态。</p><p>而我们在运行了 <code>check(i)</code> 之后,由于问题的性质,我们获取到的信息不只是 <code>check(i)</code> 本身为真还是假。而是可以知道一个范围内的所有整数都满足或者不满足条件。</p><p>例如我运行了 <code>check(i)</code> ,得到 <code>check(i)==False</code>. 那么我就知道对于所有的 $[a,i]$ 闭区间内的整数,它们check的结果一定也都是 <code>False</code> .</p><p>而如果我运行了 <code>check(i)</code> ,得到 <code>check(i)==True</code>. 那么我就知道对于所有的 $[i,b]$ 闭区间内的整数,它们check的结果一定也都是 <code>True</code> .</p><p>定义一个闭区间 $[l,r]$ 为求解区间,使得它满足以下3条性质:</p><ol><li>$[l,r]$ 中全是我们<strong>未知</strong>它check的结果是True还是False的整数</li><li>对于任何大于 $r$ 的整数,check的结果一定是<code>True</code></li><li>对于任何小于 $l$ 的整数,check的结果一定是<code>False</code></li></ol><p>接下来我们把求解区间初始化为 $[a,b]$. 因为一开始,$[a,b]$ 中全是我们<strong>未知</strong>的。</p><p>相当于我们定义了两个变量<code>l,r</code>, 且初始化它们为<code>l&lt;-a, r&lt;-b</code>.</p><p>然后定义变量mid, <code>mid &lt;- l + (r-l)/2 </code>. mid也就是求解区间的中点。</p><p>然后计算<code>check(mid)</code>.</p><p>如果<code>check(mid)==True</code>,说明 $[mid, r]$ 都是 <code>True</code>. 因此 $[mid, r]$ 从<strong>未知</strong>变为<strong>已知</strong>。需要让新的r变为 mid - 1. 也就是执行 <code>r &lt;- (mid - 1)</code> .这个过程中维护了3条性质始终成立。</p><p>如果<code>check(mid)==False</code>,说明 $[l, mid]$ 都是 <code>False</code>. 因此 $[l, mid]$ 从<strong>未知</strong>变为<strong>已知</strong>。需要让新的l变为 mid + 1. 也就是执行 <code>l &lt;- (mid + 1)</code> .这个过程中维护了3条性质始终成立。</p><p>然后重复执行 <code>mid &lt;- l + (r-l)/2 </code> 和上述操作。 直到 $r &lt; l$ 为止。此时求解区间为空。也就是<strong>未知</strong>的区间为空。说明 $[a,b]$ 内的每一个整数的check情况已经全部已知。我们的目标就是把未知区间变为空。且保持3条性质均始终成立。</p><p>在未知区间为空之后,$r$ 一定等于 $l-1$. 此时,r就是最后一个False所在位置,而l就是第一个True所在位置。然后根据题目要求哪一个就返回哪一个就行。</p><h2 id=&#34;举例子说明可以先看这个部分&#34;>举例子说明(可以先看这个部分)</h2><p>例如对于一个check函数,假设我们把它应用在1,2,3,4,5,6,7,8,9上面,得到的结果分别是</p><p>$$[F,F,F,F,T,T,T,T,T]$$</p><p>那么取 <code>l &lt;- 0, r &lt;- 8</code>. 计算得到 <code>mid &lt;- 4</code>.</p><p>此时 <code>check(mid) == check(4) == T</code>. 说明 <code>[4,8]</code> 区间内的每一个整数check之后都是T. 因此它们从<strong>未知</strong>变成<strong>已知</strong>。 我们执行 <code>mid &lt;- (r - 1)</code>. 得到新的未知区间为 <code>[l,r] == [0,3]</code>.</p><p>计算 <code>mid &lt;- (l+r)/2</code>, 得到新的 <code>mid == 1</code>. 此时 <code>check(mid) == F</code>. 说明 <code>[0,1]</code> 区间均为F. 因此执行 <code>mid &lt;- (l + 1)</code>. 新的未知区间为 <code>[l,r] == [2,3]</code>.</p><p>计算 <code>mid &lt;- (l+r)/2</code>, 得到新的 <code>mid == 2</code>. 此时 <code>check(mid) == F</code>. 说明 <code>[0,2]</code> 区间均为F. 因此执行 <code>mid &lt;- (l + 1)</code>. 新的未知区间为 <code>[l,r] == [3,3]</code>.</p><p>计算 <code>mid &lt;- (l+r)/2</code>, 得到新的 <code>mid == 3</code>. 此时 <code>check(mid) == F</code>. 说明 <code>[0,3]</code> 区间均为F. 因此执行 <code>mid &lt;- (l + 1)</code>. 新的未知区间为 <code>[l,r] == [4,3] == Empty</code>.</p><p>计算结束。 此时 <code>l == 4, r == 3</code>. l就是第一个T所在的位置。 r就是最后一个F所在的位置。</p>

2024/4/3
阅读更多

Go 并发与并行的区别

2024/4/3
阅读更多

Cpp 笔记 新系列1

2024/4/3
阅读更多

Go 学习笔记 1

<h1 id=&#34;chatgpt摘抄&#34;>ChatGPT摘抄</h1><h2 id=&#34;go和c的区别&#34;>Go和C++的区别</h2><p>Go语言(通常称为Golang)和C++在多个方面有显著的区别,包括它们的设计哲学、类型系统、内存管理、并发模型等方面。下面是一些主要的区别:</p><ol><li><p><strong>设计哲学</strong>:</p><ul><li><strong>Go</strong> 设计简洁,以提高开发效率和易用性为目标。它旨在使得编写高性能、可靠、易于维护的代码更加直接和简单。</li><li><strong>C++</strong> 设计为一种通用编程语言,强调性能和灵活性。它支持多种编程范式(如过程式、面向对象、泛型编程),但这也使得它变得相对复杂。</li></ul></li><li><p><strong>类型系统</strong>:</p><ul><li><strong>Go</strong> 有一个静态类型系统,但它的类型推导使得代码更简洁。它支持接口,但不支持类和继承,而是通过组合和接口实现多态和代码复用。</li><li><strong>C++</strong> 也是静态类型的,但它支持更复杂的类型系统,包括类、继承、模板等。这提供了强大的灵活性,但也增加了复杂性。</li></ul></li><li><p><strong>内存管理</strong>:</p><ul><li><strong>Go</strong> 提供自动垃圾收集,减少了内存泄漏的风险,简化了内存管理。</li><li><strong>C++</strong> 需要程序员手动管理内存,虽然提供了智能指针等现代特性来帮助管理,但管理不当仍可能导致内存泄漏和其他问题。</li></ul></li><li><p><strong>并发模型</strong>:</p><ul><li><strong>Go</strong> 的并发模型是其核心特性之一,基于“goroutines”(轻量级线程)和通道。这使得编写并发程序变得更简单和安全。</li><li><strong>C++</strong> 支持多线程,但并发并不是语言核心的一部分。C++11及以后的版本引入了一些并发编程的标准库支持,但使用起来比Go更复杂。</li></ul></li><li><p><strong>标准库和生态系统</strong>:</p><ul><li><strong>Go</strong> 有一个丰富的标准库,尤其是对于网络编程和并发编程。Go的生态系统随着语言的普及而快速增长,特别是在云服务和微服务开发中。</li><li><strong>C++</strong> 拥有庞大的第三方库和工具集,适用于各种编程任务,但标准库相对较小。C++在高性能计算、游戏开发、系统编程等领域有着悠久和广泛的应用。</li></ul></li><li><p><strong>编译和执行</strong>:</p><ul><li><strong>Go</strong> 编译器倾向于生成单个静态链接的二进制文件,简化了部署和分发。</li><li><strong>C++</strong> 通常需要更复杂的构建系统,尤其是对于依赖于多个库和平台的项目。</li></ul></li></ol><p>总之,Go和C++在设计上有根本的不同。Go旨在简化并发编程,提高开发效率,适合快速开发大型分布式系统。而C++提供了更多的控制和灵活性,适合那些需要极致性能优化和复杂系统设计的场景。选择哪种语言取决于项目需求、团队熟悉度和特定任务的需求。</p><h2 id=&#34;讲一下go语言中的协程&#34;>讲一下go语言中的协程</h2><p>Go语言中的协程(goroutine)是Go语言并发设计的核心。goroutine 类似于线程,但在执行和资源消耗方面更为高效。它们由Go运行时(runtime)管理,而不是直接由操作系统管理。这样做的好处是goroutine的创建和销毁的成本比操作系统线程低得多,允许在同一程序中并发运行成千上万的goroutine。</p><h3 id=&#34;创建goroutine&#34;>创建goroutine</h3><p>在Go语言中,创建一个goroutine非常简单,只需要在函数调用前加上<code>go</code>关键字。例如:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>go</span> <span style=&#34;color:#a6e22e&#34;>funcName</span>(<span style=&#34;color:#a6e22e&#34;>params</span>)</span></span></code></pre></div><p>这会启动一个新的goroutine并在这个新的goroutine中调用<code>funcName</code>函数,<code>params</code>是传递给<code>funcName</code>函数的参数。</p><h3 id=&#34;特点&#34;>特点</h3><ol><li><strong>轻量级</strong>:每个goroutine的堆栈大小在初始化时很小,根据需要可以动态地增长和缩减。这种设计让成千上万的goroutine并发运行成为可能。</li><li><strong>不需要显式创建和销毁</strong>:goroutine由Go的运行时环境管理,程序员只需要创建它们,无需关心销毁和回收。</li><li><strong>使用通道(channels)进行通信</strong>:goroutine之间的同步通常通过channels进行,channels是Go语言中的一种类型安全的消息传递机制。使用channels,可以很容易地在goroutine之间传递数据和信号。</li></ol><h3 id=&#34;示例&#34;>示例</h3><p>下面的示例展示了如何使用goroutine和channels进行并发编程:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>package</span> <span style=&#34;color:#a6e22e&#34;>main</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#f92672&#34;>import</span> (</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span> <span style=&#34;color:#e6db74&#34;>&#34;time&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span><span style=&#34;color:#75715e&#34;>// 函数打印传入的消息</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>printMessage</span>(<span style=&#34;color:#a6e22e&#34;>message</span> <span style=&#34;color:#66d9ef&#34;>string</span>) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span> <span style=&#34;color:#66d9ef&#34;>for</span> <span style=&#34;color:#a6e22e&#34;>i</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#ae81ff&#34;>0</span>; <span style=&#34;color:#a6e22e&#34;>i</span> &lt; <span style=&#34;color:#ae81ff&#34;>5</span>; <span style=&#34;color:#a6e22e&#34;>i</span><span style=&#34;color:#f92672&#34;>++</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>message</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span> <span style=&#34;color:#a6e22e&#34;>time</span>.<span style=&#34;color:#a6e22e&#34;>Sleep</span>(<span style=&#34;color:#a6e22e&#34;>time</span>.<span style=&#34;color:#a6e22e&#34;>Millisecond</span> <span style=&#34;color:#f92672&#34;>*</span> <span style=&#34;color:#ae81ff&#34;>100</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span> <span style=&#34;color:#75715e&#34;>// 创建一个新的goroutine运行printMessage函数</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>go</span> <span style=&#34;color:#a6e22e&#34;>printMessage</span>(<span style=&#34;color:#e6db74&#34;>&#34;hello&#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span> <span style=&#34;color:#75715e&#34;>// 主goroutine中也运行printMessage函数</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>21</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>printMessage</span>(<span style=&#34;color:#e6db74&#34;>&#34;world&#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>22</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>23</span><span> <span style=&#34;color:#75715e&#34;>// 等待按键结束程序,避免主goroutine提前结束</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>24</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Scanln</span>()</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>25</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#e6db74&#34;>&#34;done&#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>26</span><span>}</span></span></code></pre></div><p>在这个例子中,<code>printMessage</code>函数将在两个不同的goroutine中并发执行:一个是通过<code>go printMessage(&quot;hello&quot;)</code>创建的goroutine,另一个是主goroutine。这两个goroutine将并发地运行,打印出交替的&quot;hello&quot;和&quot;world&quot;消息。</p><p>总之,goroutine是实现并发编程的强大工具,它使得编写高性能并发程序在Go语言中变得既简单又高效。</p><h2 id=&#34;协程和线程的区别是什么&#34;>协程和线程的区别是什么</h2><p>协程(Coroutines)和线程(Threads)都是程序执行流的基本单位,它们之间有一些关键的区别和优缺点。理解这些区别有助于选择最适合特定应用需求的并发模型。</p><h3 id=&#34;1-管理方式&#34;>1. 管理方式</h3><ul><li><strong>线程</strong>:通常由操作系统管理。操作系统负责线程的调度和管理,线程切换涉及到上下文切换,包括保存当前线程的状态并恢复另一个线程的状态,这是一个相对昂贵的操作。</li><li><strong>协程</strong>:由程序和运行时(runtime)管理,不直接依赖操作系统的线程管理。协程上下文切换的成本通常比线程低得多,因为这种切换是在用户态完成的,避免了内核态与用户态之间的切换开销。</li></ul><h3 id=&#34;2-资源消耗&#34;>2. 资源消耗</h3><ul><li><strong>线程</strong>:每个线程都有自己的堆栈和局部变量,占用的内存量相对较大。线程的数量受到系统资源的限制。</li><li><strong>协程</strong>:通常非常轻量,拥有很小的堆栈和低资源消耗。因此,在同一程序中可以同时运行成千上万个协程。</li></ul><h3 id=&#34;3-并发模型&#34;>3. 并发模型</h3><ul><li><strong>线程</strong>:多线程编程模型可以实现真正的并行执行(在多核处理器上),但线程间的同步和通信(如使用锁、信号量等)可能导致复杂的竞态条件和死锁问题。</li><li><strong>协程</strong>:通常在单线程中运行,实现并发而非并行。协程之间的通信和同步通过使用消息传递(如Go语言的channels)或事件循环(如Python的asyncio)等机制,这些机制旨在避免传统的锁和条件变量带来的问题。</li></ul><h3 id=&#34;4-控制流&#34;>4. 控制流</h3><ul><li><strong>线程</strong>:操作系统控制线程的调度,程序员通常不能精确控制执行流的切换点。</li><li><strong>协程</strong>:提供了更精细的控制能力,允许程序员确定何时以及如何切换任务,通过这种方式,可以更容易地编写非阻塞代码和管理并发。</li></ul><h3 id=&#34;5-使用场景&#34;>5. 使用场景</h3><ul><li><strong>线程</strong>:适用于需要实现真正并行任务处理的场景,尤其是在计算密集型应用中。</li><li><strong>协程</strong>:适合高并发、IO密集型应用,如网络服务器、异步处理任务等,能有效提高资源的利用率和程序的响应性。</li></ul><p>总结来说,线程是操作系统层面的并发执行单元,资源开销较大,适合于并行处理;而协程是更轻量级的用户态线程,它们提供了更高的并发性和更灵活的控制方式,适合于构建高并发的应用程序。正确地选择使用线程还是协程,取决于具体的应用场景和性能需求。</p><h2 id=&#34;假设我在go语言中开启了10个协程它们会运行在几个线程上几个cpu上&#34;>假设我在go语言中开启了10个协程,它们会运行在几个线程上,几个CPU上</h2><p>在Go语言中,协程(goroutine)的运行不是直接映射到操作系统线程上的,而是由Go运行时(runtime)进行管理。Go运行时使用了M:N调度模型(多个goroutines被多路复用到较少的操作系统线程上),这意味着数千个goroutines可能只用到很少数量的操作系统线程。具体到你的问题,让我们看看这个模型的两个主要组件以及如何影响goroutines的运行:</p><h3 id=&#34;1-gomaxprocs&#34;>1. GOMAXPROCS</h3><p><code>GOMAXPROCS</code>是一个环境变量,用于控制Go程序运行时可以使用的CPU核心数。默认情况下,它的值设为了你的机器上的逻辑CPU核心数。你可以通过<code>runtime.GOMAXPROCS()</code>函数查询或设置这个值。<code>GOMAXPROCS</code>决定了有多少个操作系统线程可以同时执行Go代码。</p><h3 id=&#34;2-调度器&#34;>2. 调度器</h3><p>Go的调度器是基于M:N模型实现的,其中:</p><ul><li><strong>M</strong> 代表操作系统线程(machine)。</li><li><strong>N</strong> 代表goroutines。</li></ul><p>调度器的任务是将N个goroutines分配给M个操作系统线程上执行。这意味着,即使你有数千个goroutines,Go运行时也可能只使用<code>GOMAXPROCS</code>指定数量的操作系统线程来执行这些goroutines。</p><h3 id=&#34;运行在多少个线程和cpu上&#34;>运行在多少个线程和CPU上?</h3><p>假设你开启了10个goroutines:</p><ul><li><strong>线程</strong>:这些goroutines的实际运行可能全部在一个单独的操作系统线程上,也可能分布在多个操作系统线程上,具体取决于<code>GOMAXPROCS</code>的设置和Go调度器的决定。如果<code>GOMAXPROCS</code>的值大于1(默认情况下通常如此),那么这些goroutines有可能被调度到不同的操作系统线程上执行。</li><li><strong>CPU</strong>:这些goroutines实际上在<code>GOMAXPROCS</code>设定的CPU核心上并发运行。如果<code>GOMAXPROCS</code>大于1,goroutines可以在多个CPU核心上并行执行。</li></ul><p>因此,如果<code>GOMAXPROCS</code>设置为大于1的值,你的10个goroutines可能会在多个线程和多个CPU上运行,具体分配由Go运行时的调度器动态管理。但要注意,goroutine到线程的具体映射以及调度行为是由Go运行时内部决定的,可能会随着Go版本和具体的运行情况而有所不同。</p><h2 id=&#34;go的通道是无序的这样怎么能做好并发呢&#34;>go的通道是无序的,这样怎么能做好并发呢</h2><p>在Go语言中,说通道(channels)是“无序的”可能会有点误导。实际上,通道本身维持着发送和接收操作的顺序:如果你按顺序发送了值A然后是值B到同一个通道,接收方也将首先接收到A,然后是B,这意味着在单个通道上,发送和接收是有序的。然而,当涉及到从多个并发发送者接收时,哪个发送者首先将消息送达到通道是不确定的,这可能导致看似“无序”的行为。</p><p>当你在处理并发时,确保程序的正确性通常依赖于同步和通信机制,而Go通过通道和goroutines提供了强大的工具来帮助你做到这一点。这里有一些策略来确保你在并发程序中使用通道时能够正确地同步和顺序操作:</p><ol><li><p><strong>使用无缓冲通道进行同步</strong>:无缓冲通道保证了发送操作会阻塞,直到接收操作发生,这可以用作goroutines之间的同步点。</p></li><li><p><strong>使用有缓冲通道控制并发</strong>:有缓冲通道允许在阻塞之前发送多个值。它们可以用来限制并发级别,比如通过控制同时运行的goroutines的数量。</p></li><li><p><strong>关闭通道来广播消息</strong>:关闭一个通道会通知所有的接收者没有更多的值会被发送到这个通道。这是一种广播机制,可以用来通知多个goroutine某个条件已经成立,比如终止。</p></li><li><p><strong>选择语句</strong>:<code>select</code> 语句允许你同时等待多个通道操作,并根据哪个通道准备就绪来执行相应的代码块。这可以用来处理来自多个来源的输入,同时保持逻辑上的清晰和顺序。</p></li><li><p><strong>顺序控制</strong>:当需要精确控制多个goroutine的执行顺序时,可以设计一种协议或使用一系列通道来顺序地进行操作,确保并发操作的正确顺序。</p></li></ol><p>通过这些机制,Go允许你构建复杂的并发程序,同时保持代码的简洁性和清晰度。正确地使用通道和goroutines,你可以有效地解决并发中的同步和顺序问题,从而实现高效且易于理解的并发逻辑。</p><h2 id=&#34;管道例子探究&#34;>管道例子探究</h2><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#a6e22e&#34;>s</span> <span style=&#34;color:#f92672&#34;>:=</span> []<span style=&#34;color:#66d9ef&#34;>int</span>{<span style=&#34;color:#ae81ff&#34;>7</span>, <span style=&#34;color:#ae81ff&#34;>2</span>, <span style=&#34;color:#ae81ff&#34;>8</span>, <span style=&#34;color:#f92672&#34;>-</span><span style=&#34;color:#ae81ff&#34;>9</span>, <span style=&#34;color:#ae81ff&#34;>4</span>, <span style=&#34;color:#ae81ff&#34;>0</span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span><span style=&#34;color:#a6e22e&#34;>c</span> <span style=&#34;color:#f92672&#34;>:=</span> make(<span style=&#34;color:#66d9ef&#34;>chan</span> <span style=&#34;color:#66d9ef&#34;>int</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>s</span>[:len(<span style=&#34;color:#a6e22e&#34;>s</span>)<span style=&#34;color:#f92672&#34;>/</span><span style=&#34;color:#ae81ff&#34;>2</span>])</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>s</span>[len(<span style=&#34;color:#a6e22e&#34;>s</span>)<span style=&#34;color:#f92672&#34;>/</span><span style=&#34;color:#ae81ff&#34;>2</span>:])</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#a6e22e&#34;>sum</span>(<span style=&#34;color:#a6e22e&#34;>s</span>[:len(<span style=&#34;color:#a6e22e&#34;>s</span>)<span style=&#34;color:#f92672&#34;>/</span><span style=&#34;color:#ae81ff&#34;>2</span>], <span style=&#34;color:#a6e22e&#34;>c</span>) <span style=&#34;color:#75715e&#34;>// 17</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#a6e22e&#34;>sum</span>(<span style=&#34;color:#a6e22e&#34;>s</span>[len(<span style=&#34;color:#a6e22e&#34;>s</span>)<span style=&#34;color:#f92672&#34;>/</span><span style=&#34;color:#ae81ff&#34;>2</span>:], <span style=&#34;color:#a6e22e&#34;>c</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span><span style=&#34;color:#a6e22e&#34;>x</span>, <span style=&#34;color:#a6e22e&#34;>y</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#f92672&#34;>&lt;-</span><span style=&#34;color:#a6e22e&#34;>c</span>, <span style=&#34;color:#f92672&#34;>&lt;-</span><span style=&#34;color:#a6e22e&#34;>c</span> <span style=&#34;color:#75715e&#34;>// 从 c 中接收</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>x</span>, <span style=&#34;color:#a6e22e&#34;>y</span>, <span style=&#34;color:#a6e22e&#34;>x</span><span style=&#34;color:#f92672&#34;>+</span><span style=&#34;color:#a6e22e&#34;>y</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span>}</span></span></code></pre></div><p>这样为什么不对?管道必须和goroutine搭配使用吗?</p><hr><p>你的新代码片段中,<code>sum</code> 函数不是在goroutines中执行的,而是直接顺序执行。问题在于,在你尝试向通道 <code>c</code> 发送数据时(使用 <code>c &lt;- sum</code> 语法),因为 <code>c</code> 是一个无缓冲的通道,发送操作会阻塞直到另一方准备好接收数据。在这种情况下,因为你的发送操作 (<code>c &lt;- sum</code>) 发生在主goroutine中,而且没有其他并行的goroutine去接收这些值,这将导致死锁:主goroutine在等待自己向通道发送数据的操作完成,但这永远不会发生,因为没有其他并发操作去从通道中接收数据。</p><p>通道确实设计为在goroutines之间进行通信的一种方式,它们非常适合用于并发编程。在单个goroutine内使用无缓冲通道进行发送和接收操作通常会导致死锁,因为无缓冲通道要求发送和接收操作几乎同时发生。</p><p>简而言之,你的最后一个示例不正确,原因如下:</p><ol><li><p><strong>死锁</strong>:由于 <code>sum</code> 函数调用并非在goroutines中执行,它们将顺序执行。第一个 <code>sum</code> 调用试图向通道 <code>c</code> 发送一个值,但会阻塞,因为没有其他goroutine在接收端等待这个值。程序将在第一个 <code>sum</code> 函数调用时阻塞,导致死锁。</p></li><li><p><strong>通道与goroutine搭配使用</strong>:虽然技术上不是必须将通道与goroutines搭配使用,但在实践中,这是避免死锁和实现有效并发程序的常见模式。使用无缓冲通道时,发送操作会阻塞,直到接收操作开始,反之亦然。这种机制非常适合在不同goroutines之间同步数据访问和传递消息。</p></li></ol><p>为了使你的程序按预期工作,你应该在goroutines中调用 <code>sum</code> 函数,并通过通道交换数据,正如之前正确的版本那样。这样,两个 <code>sum</code> 操作可以并发执行,主goroutine将等待并从通道中接收它们的结果,从而避免死锁并正确计算总和:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>go</span> <span style=&#34;color:#a6e22e&#34;>sum</span>(<span style=&#34;color:#a6e22e&#34;>s</span>[:len(<span style=&#34;color:#a6e22e&#34;>s</span>)<span style=&#34;color:#f92672&#34;>/</span><span style=&#34;color:#ae81ff&#34;>2</span>], <span style=&#34;color:#a6e22e&#34;>c</span>) <span style=&#34;color:#75715e&#34;>// 在goroutine中执行</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>go</span> <span style=&#34;color:#a6e22e&#34;>sum</span>(<span style=&#34;color:#a6e22e&#34;>s</span>[len(<span style=&#34;color:#a6e22e&#34;>s</span>)<span style=&#34;color:#f92672&#34;>/</span><span style=&#34;color:#ae81ff&#34;>2</span>:], <span style=&#34;color:#a6e22e&#34;>c</span>) <span style=&#34;color:#75715e&#34;>// 在另一个goroutine中执行</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#a6e22e&#34;>x</span>, <span style=&#34;color:#a6e22e&#34;>y</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#f92672&#34;>&lt;-</span><span style=&#34;color:#a6e22e&#34;>c</span>, <span style=&#34;color:#f92672&#34;>&lt;-</span><span style=&#34;color:#a6e22e&#34;>c</span> <span style=&#34;color:#75715e&#34;>// 从通道c中接收</span></span></span></code></pre></div><p>通过这种方式,每个 <code>sum</code> 调用都在自己的goroutine中执行,并且能够独立地完成其向通道的发送操作。这避免了死锁,使主goroutine能够从通道接收两个结果并正确计算总和。</p><hr><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>package</span> <span style=&#34;color:#a6e22e&#34;>main</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#f92672&#34;>import</span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#a6e22e&#34;>ch</span> <span style=&#34;color:#f92672&#34;>:=</span> make(<span style=&#34;color:#66d9ef&#34;>chan</span> <span style=&#34;color:#66d9ef&#34;>int</span>, <span style=&#34;color:#ae81ff&#34;>2</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#a6e22e&#34;>ch</span> <span style=&#34;color:#f92672&#34;>&lt;-</span> <span style=&#34;color:#ae81ff&#34;>1</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span><span style=&#34;color:#a6e22e&#34;>ch</span> <span style=&#34;color:#f92672&#34;>&lt;-</span> <span style=&#34;color:#ae81ff&#34;>2</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span><span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#f92672&#34;>&lt;-</span><span style=&#34;color:#a6e22e&#34;>ch</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#f92672&#34;>&lt;-</span><span style=&#34;color:#a6e22e&#34;>ch</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span>}</span></span></code></pre></div><p>为什么这个又对了?这不是只有一个goroutine吗?</p><p>你的最新示例之所以能够正确运行,是因为它使用了一个<strong>有缓冲的通道</strong>。与无缓冲通道不同,有缓冲通道允许在阻塞发送者或接收者之前存储一个固定数量的值。具体到你的例子中,通过 <code>make(chan int, 2)</code> 创建了一个可以存储两个整数的缓冲通道。这意味着你可以在没有任何接收者准备好接收之前,向通道发送(或“缓冲”)两个值。</p><p>这段代码中的关键点是:</p><ol><li><p><code>ch := make(chan int, 2)</code> 创建了一个有缓冲的通道,缓冲大小为2。这允许通道存储最多两个整数,直到它们被接收。</p></li><li><p><code>ch &lt;- 1</code> 和 <code>ch &lt;- 2</code> 这两行向通道发送两个整数。由于通道是有缓冲的,并且缓冲未满,这两个发送操作不会阻塞。这两个值被存储在通道的缓冲区中。</p></li><li><p><code>fmt.Println(&lt;-ch)</code> 和 <code>fmt.Println(&lt;-ch)</code> 这两行从通道中接收并打印这两个值。由于这两个值已经在通道的缓冲区中等待,接收操作立即发生,没有阻塞。</p></li></ol><p>在这个例子中,即使所有操作都在单个goroutine(主goroutine)中进行,程序也能正常运行,没有发生死锁。这是因为有缓冲通道提供了足够的空间来存储发送到通道中的值,直到它们被接收,从而避免了在发送操作时必须立即有一个接收者准备好接收值的需求。</p><p>有缓冲通道是并发Go程序中一个非常有用的工具,允许goroutines在某种程度上独立操作,而不必在每次通信时都完全同步。然而,设计有缓冲通道的程序时需要谨慎,以避免逻辑错误或死锁,特别是当缓冲区满时或在更复杂的通信模式中。</p><h2 id=&#34;go支持垃圾回收吗&#34;>Go支持垃圾回收吗?</h2><p>是的,Go语言支持垃圾回收(Garbage Collection,简称GC)。Go的垃圾回收器是一个实现了自动内存管理的程序部分,它负责检测并自动回收不再被程序使用的内存空间,从而防止内存泄漏。这意味着在Go中,开发者不需要(也不能)手动管理内存的分配和回收,大大简化了编程工作,减少了内存泄漏和其他与手动内存管理相关的错误。</p><p>Go的垃圾回收器设计为“非阻塞式”的,意味着它尽量减少对程序执行的干扰。从Go 1.5版本开始,引入了并发的、三色标记清除算法,该算法允许垃圾收集过程在大部分时间与程序的其他部分并发执行,减少了垃圾收集对程序性能的影响。尽管如此,垃圾收集过程仍然会引入一定的延迟,这在极少数延迟敏感的应用中可能成为一个考虑因素。</p><p>Go的运行时系统还包括其他几个与性能和资源管理相关的特性,如goroutine的调度和网络轮询器,它们一起确保了Go程序的高性能和高效的资源使用。开发者可以使用内置的分析工具(如pprof)来监视和优化程序的内存使用,以及诊断性能问题。</p><h2 id=&#34;如果go-代码会被预先编译成原生的机器码那么它在运行的时候怎么和runtime结合呢&#34;>如果Go 代码会被预先编译成原生的机器码,那么它在运行的时候怎么和runtime结合呢</h2><p>Go语言确实编译成原生机器码,这意味着Go程序在执行时不需要依赖于虚拟机,它们直接由操作系统调度。尽管如此,Go的执行模型和运行时(runtime)系统包括了许多复杂的功能,如垃圾回收(GC)、goroutine调度、通道通信、反射以及更多。这些功能的实现是预先集成在每个Go程序中的,部分原因是Go的编译器和链接器在构建最终的可执行文件时会自动包含运行时。</p><p>这里是Go运行时和编译后的机器码结合的一些关键方式:</p><ol><li><p><strong>运行时包含</strong>:当Go程序被编译时,运行时代码被静态链接到最终的二进制中。这包括了垃圾回收器、goroutine调度器等核心功能的实现。因此,即使Go代码被编译成了原生机器码,运行时的功能也被内嵌于程序中,随程序一起运行。</p></li><li><p><strong>初始化运行时</strong>:在Go程序启动时,它的入口点不是 <code>main.main</code> 函数。实际上,程序开始执行于运行时的初始化代码,该代码负责设置运行时环境,包括初始化内存管理器、启动垃圾回收器、创建主goroutine等。只有在这些准备工作完成之后,<code>main.main</code> 函数才会被调用。</p></li><li><p><strong>goroutine调度</strong>:Go语言的并发模型基于goroutines。运行时包含一个调度器,它在用户态模拟多线程环境,允许成千上万的goroutines伪并行地运行在较少的操作系统线程上。编译后的代码中包含与运行时交互的指令,这些指令使得goroutines能被合适地调度和管理。</p></li><li><p><strong>内置函数调用运行时</strong>:一些Go的内置函数,如 <code>make</code>、<code>new</code>,以及用于map、切片和通道操作的函数,实际上是对运行时系统的直接调用。这些函数的实现是在运行时代码中,而非Go的标准库中。</p></li><li><p><strong>接口和反射</strong>:Go的接口和反射机制也依赖于运行时的支持。运行时维护了类型信息,允许程序在运行时查询和操作类型,这是动态语言特性的一个重要基础。</p></li></ol><p>总的来说,虽然Go代码被编译成原生机器码,但通过将运行时的功能静态链接到每个程序、在程序启动时初始化运行时环境、以及在需要时通过编译后的代码调用运行时支持的机制,Go程序能够实现高级的语言特性,同时保持高性能和跨平台的兼容性。</p><h2 id=&#34;go语言中变量的生命周期&#34;>Go语言中变量的生命周期</h2><p>Go语言中变量的生命周期是由变量能够被程序访问的时间段定义的。理解变量的生命周期对于编写高效和无内存泄漏的程序至关重要。在Go中,变量的生命周期取决于它是如何声明的,主要分为两种情况:在堆(heap)上和在栈(stack)上。</p><h3 id=&#34;栈上的变量&#34;>栈上的变量</h3><p>栈上的变量通常具有短暂的生命周期。当声明一个局部变量时,它通常位于栈上,其生命周期与包含它的函数调用的生命周期相匹配。当函数被调用时,局部变量被创建;当函数返回时,这些局部变量被销毁。这意味着栈上的变量的生命周期是由它们所在的函数的执行时间来决定的。</p><h3 id=&#34;堆上的变量&#34;>堆上的变量</h3><p>堆上的变量有一个更长久的生命周期。当你使用<code>new</code>函数或者复合字面量(如切片、映射、通道等)创建一个变量,而这个变量的引用逃逸到函数外部时,这个变量就会在堆上分配。堆上的变量的生命周期不由单个函数调用控制,而是由是否仍然可达(即程序的其他部分是否还有指向该变量的引用)来决定。只要变量可达,它就会继续存在。当变量变得不可达时,它最终会被垃圾回收器回收。</p><h3 id=&#34;变量生命周期的决定因素&#34;>变量生命周期的决定因素</h3><ul><li><strong>编译器优化</strong>:Go编译器有时会做出决策,将本应在堆上分配的变量改为在栈上分配,如果它能确定变量的生命周期在函数范围内结束。这种优化减少了垃圾回收的负担。</li><li><strong>逃逸分析</strong>:Go编译器执行逃逸分析来决定变量应该分配在堆还是栈上。如果函数内部创建的变量在函数外部仍然被引用(即“逃逸”到函数外部),则这些变量需要在堆上分配,以保证它们在函数返回后仍然存在。</li></ul><p>理解变量的生命周期对于优化Go程序的性能和内存使用非常重要。例如,过度使用堆分配可能会导致频繁的垃圾回收,从而影响程序性能。通过适当的变量作用域控制和利用栈分配,可以提高程序效率。</p><h2 id=&#34;go-常量&#34;>Go 常量</h2><p>在 Go 语言中,常量是一个简单值的标识符,在程序运行时,其值不会被修改。常量可以是字符、字符串、布尔或数字类型的值。</p><p>定义常量的基本语法如下:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>const</span> <span style=&#34;color:#a6e22e&#34;>name</span> [<span style=&#34;color:#66d9ef&#34;>type</span>] = <span style=&#34;color:#a6e22e&#34;>value</span></span></span></code></pre></div><ul><li><code>const</code> 关键字用于声明常量。</li><li><code>name</code> 是常量的名称。</li><li><code>type</code> 是可选的,指定常量的类型。</li><li><code>value</code> 是常量的值。</li></ul><h3 id=&#34;示例-1&#34;>示例</h3><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>const</span> <span style=&#34;color:#a6e22e&#34;>Pi</span> = <span style=&#34;color:#ae81ff&#34;>3.14</span></span></span></code></pre></div><p>在这个例子中,<code>Pi</code> 是一个常量,其值为 <code>3.14</code>。</p><h3 id=&#34;常量的类型&#34;>常量的类型</h3><p>在 Go 中,常量可以是任何基本类型,如整型、浮点型、布尔型、字符串等。如果在声明常量时没有指定类型,Go 语言会根据常量的值推导出其类型。</p><h3 id=&#34;iota&#34;>iota</h3><p>Go 语言的 <code>iota</code> 关键字用于生成一组以递增整数形式表示的常量,通常用于声明一个枚举。<code>iota</code> 在 <code>const</code> 关键字出现时被重置为 0,然后在每声明一个常量时自动递增。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>const</span> (</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span> <span style=&#34;color:#a6e22e&#34;>a</span> = <span style=&#34;color:#66d9ef&#34;>iota</span> <span style=&#34;color:#75715e&#34;>// a == 0</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>b</span> <span style=&#34;color:#75715e&#34;>// b == 1</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>c</span> <span style=&#34;color:#75715e&#34;>// c == 2</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span><span style=&#34;color:#75715e&#34;></span>)</span></span></code></pre></div><p>在这个例子中,<code>a</code>、<code>b</code>、<code>c</code> 分别被赋予了递增的整数值 0、1、2。</p><h3 id=&#34;未类型化常量&#34;>未类型化常量</h3><p>在 Go 中,可以声明未类型化的常量,这意味着常量没有明确的类型。未类型化常量提供了更高的精度,并可以根据上下文转换为需要的类型。</p><h3 id=&#34;使用常量&#34;>使用常量</h3><p>常量可以用在任何变量可以使用的地方。使用常量可以提高代码的可读性和维护性。</p><p>总之,Go 语言中的常量是一个非常有用的特性,它允许你在程序中使用固定的值,而不担心这些值会被改变。通过使用 <code>const</code> 关键字和 <code>iota</code> 生成器,你可以方便地声明和管理常量。</p><h2 id=&#34;go-创建新结构体&#34;>Go 创建新结构体</h2><p>在 Go 语言中,结构体(struct)是一种将多个不同类型的变量组织到一个名字下的复合数据类型,常用于定义和创建数据实体。创建新结构体涉及两个步骤:定义结构体类型和实例化该类型的变量。</p><h3 id=&#34;步骤-1定义结构体类型&#34;>步骤 1:定义结构体类型</h3><p>定义结构体类型使用 <code>type</code> 和 <code>struct</code> 关键字。结构体类型通常在函数外部定义,以便可以在不同函数间共享。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>type</span> <span style=&#34;color:#a6e22e&#34;>Person</span> <span style=&#34;color:#66d9ef&#34;>struct</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span> <span style=&#34;color:#a6e22e&#34;>Name</span> <span style=&#34;color:#66d9ef&#34;>string</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span> <span style=&#34;color:#a6e22e&#34;>Age</span> <span style=&#34;color:#66d9ef&#34;>int</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span>}</span></span></code></pre></div><p>在这个例子中,我们定义了一个名为 <code>Person</code> 的结构体,它有两个字段:<code>Name</code> 和 <code>Age</code>。</p><h3 id=&#34;步骤-2实例化结构体&#34;>步骤 2:实例化结构体</h3><p>一旦定义了结构体类型,你就可以创建该类型的实例了。有几种不同的方式来实例化结构体。</p><h4 id=&#34;方法-1使用-new-关键字&#34;>方法 1:使用 <code>new</code> 关键字</h4><p><code>new</code> 关键字可以用来创建一个指向结构体类型的新实例的指针。此方法会分配内存并将结构体的字段初始化为零值。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>p1</span> <span style=&#34;color:#f92672&#34;>:=</span> new(<span style=&#34;color:#a6e22e&#34;>Person</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#a6e22e&#34;>p1</span>.<span style=&#34;color:#a6e22e&#34;>Name</span> = <span style=&#34;color:#e6db74&#34;>&#34;Alice&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#a6e22e&#34;>p1</span>.<span style=&#34;color:#a6e22e&#34;>Age</span> = <span style=&#34;color:#ae81ff&#34;>30</span></span></span></code></pre></div><h4 id=&#34;方法-2使用结构体字面量&#34;>方法 2:使用结构体字面量</h4><p>结构体字面量允许在一个表达式中创建结构体实例,并同时初始化其字段。</p><ul><li><strong>命名字段方式</strong>:</li></ul><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>p2</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>Person</span>{<span style=&#34;color:#a6e22e&#34;>Name</span>: <span style=&#34;color:#e6db74&#34;>&#34;Bob&#34;</span>, <span style=&#34;color:#a6e22e&#34;>Age</span>: <span style=&#34;color:#ae81ff&#34;>25</span>}</span></span></code></pre></div><ul><li><strong>按字段顺序初始化</strong>(不推荐,因为添加或修改字段时易出错):</li></ul><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>p3</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>Person</span>{<span style=&#34;color:#e6db74&#34;>&#34;Charlie&#34;</span>, <span style=&#34;color:#ae81ff&#34;>28</span>}</span></span></code></pre></div><h4 id=&#34;方法-3使用指针和结构体字面量&#34;>方法 3:使用指针和结构体字面量</h4><p>你也可以直接创建一个指向结构体实例的指针,并初始化其字段。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>p4</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#f92672&#34;>&amp;</span><span style=&#34;color:#a6e22e&#34;>Person</span>{<span style=&#34;color:#a6e22e&#34;>Name</span>: <span style=&#34;color:#e6db74&#34;>&#34;Diana&#34;</span>, <span style=&#34;color:#a6e22e&#34;>Age</span>: <span style=&#34;color:#ae81ff&#34;>32</span>}</span></span></code></pre></div><h3 id=&#34;示例-2&#34;>示例</h3><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>package</span> <span style=&#34;color:#a6e22e&#34;>main</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#f92672&#34;>import</span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#66d9ef&#34;>type</span> <span style=&#34;color:#a6e22e&#34;>Person</span> <span style=&#34;color:#66d9ef&#34;>struct</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span> <span style=&#34;color:#a6e22e&#34;>Name</span> <span style=&#34;color:#66d9ef&#34;>string</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span> <span style=&#34;color:#a6e22e&#34;>Age</span> <span style=&#34;color:#66d9ef&#34;>int</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span> <span style=&#34;color:#75715e&#34;>// 使用 new 关键字</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>p1</span> <span style=&#34;color:#f92672&#34;>:=</span> new(<span style=&#34;color:#a6e22e&#34;>Person</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> <span style=&#34;color:#a6e22e&#34;>p1</span>.<span style=&#34;color:#a6e22e&#34;>Name</span> = <span style=&#34;color:#e6db74&#34;>&#34;Alice&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> <span style=&#34;color:#a6e22e&#34;>p1</span>.<span style=&#34;color:#a6e22e&#34;>Age</span> = <span style=&#34;color:#ae81ff&#34;>30</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span> <span style=&#34;color:#75715e&#34;>// 使用结构体字面量</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>p2</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>Person</span>{<span style=&#34;color:#a6e22e&#34;>Name</span>: <span style=&#34;color:#e6db74&#34;>&#34;Bob&#34;</span>, <span style=&#34;color:#a6e22e&#34;>Age</span>: <span style=&#34;color:#ae81ff&#34;>25</span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span> <span style=&#34;color:#75715e&#34;>// 使用指针和结构体字面量</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>p3</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#f92672&#34;>&amp;</span><span style=&#34;color:#a6e22e&#34;>Person</span>{<span style=&#34;color:#a6e22e&#34;>Name</span>: <span style=&#34;color:#e6db74&#34;>&#34;Charlie&#34;</span>, <span style=&#34;color:#a6e22e&#34;>Age</span>: <span style=&#34;color:#ae81ff&#34;>28</span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>21</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>22</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>p1</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>23</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>p2</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>24</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>p3</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>25</span><span>}</span></span></code></pre></div><p>这个例子展示了不同的方式来创建和初始化结构体 <code>Person</code> 的实例。选择哪种方式取决于具体情况,比如是否需要在初始化时立即设置字段值,或者是否需要操作结构体指针。</p><h2 id=&#34;go切片append&#34;>Go切片append</h2><p>在 Go 语言中,切片(slice)是一个动态数组,它提供了一种灵活、强大的方式来管理数据集合。尽管 Go 语言的切片类型没有直接提供 <code>push back</code> 的方法(类似于其他语言中的数组或列表的 append 功能),但可以使用内建的 <code>append</code> 函数来实现向切片末尾添加元素的效果。</p><h3 id=&#34;使用-append-函数添加元素&#34;>使用 <code>append</code> 函数添加元素</h3><p><code>append</code> 函数可以将一个或多个新的元素添加到切片的末尾,并返回一个包含了这些新元素的新切片。如果原始切片有足够的容量来存储新增的元素,<code>append</code> 会直接在原切片的末尾添加这些元素。否则,它会自动分配一个新的底层数组,将原切片的元素和新元素一起复制到这个新数组中,然后返回这个新数组的切片表示。</p><h3 id=&#34;示例-3&#34;>示例</h3><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>package</span> <span style=&#34;color:#a6e22e&#34;>main</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#f92672&#34;>import</span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span> <span style=&#34;color:#75715e&#34;>// 创建一个整数切片</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>slice</span> <span style=&#34;color:#f92672&#34;>:=</span> []<span style=&#34;color:#66d9ef&#34;>int</span>{<span style=&#34;color:#ae81ff&#34;>1</span>, <span style=&#34;color:#ae81ff&#34;>2</span>, <span style=&#34;color:#ae81ff&#34;>3</span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> <span style=&#34;color:#75715e&#34;>// 向切片末尾添加一个元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>slice</span> = append(<span style=&#34;color:#a6e22e&#34;>slice</span>, <span style=&#34;color:#ae81ff&#34;>4</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span> <span style=&#34;color:#75715e&#34;>// 向切片末尾添加多个元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>slice</span> = append(<span style=&#34;color:#a6e22e&#34;>slice</span>, <span style=&#34;color:#ae81ff&#34;>5</span>, <span style=&#34;color:#ae81ff&#34;>6</span>, <span style=&#34;color:#ae81ff&#34;>7</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>slice</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span>}</span></span></code></pre></div><p>输出将是:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span>[1 2 3 4 5 6 7]</span></span></code></pre></div><h3 id=&#34;注意事项&#34;>注意事项</h3><ul><li><strong>容量自动扩展</strong>:<code>append</code> 操作可能会导致切片的容量增加。如果原切片的底层数组没有足够的空间来存放新添加的元素,Go 语言运行时会自动分配一个新的、更大的数组,并将原切片的元素以及新元素一起复制到这个新数组中。</li><li><strong>返回值</strong>:不要忘记 <code>append</code> 函数返回的是更新后的切片。你需要用返回的切片来更新你的切片变量,否则添加的新元素不会被保存。</li></ul><p>通过上面的示例和说明,你可以看到在 Go 语言中,虽然没有直接的 <code>push back</code> 函数或方法,但是通过使用 <code>append</code> 函数,我们可以轻松实现向切片末尾添加新元素的功能。</p><h2 id=&#34;go-切片怎样自动扩展&#34;>Go 切片怎样自动扩展</h2><p>在 Go 语言中,切片的自动扩展是通过内建的 <code>append</code> 函数来实现的。当你向一个切片添加元素,而该切片的底层数组没有足够的空间容纳更多元素时,<code>append</code> 会自动处理切片的扩容。这个过程涉及到分配一个新的底层数组,并将原有元素以及新添加的元素复制到这个新数组中。切片的容量(capacity)是指底层数组能够容纳的元素总数。</p><h3 id=&#34;扩容机制&#34;>扩容机制</h3><p>当使用 <code>append</code> 向切片添加元素,导致切片的长度超过其容量时,Go 语言运行时会自动进行如下步骤:</p><ol><li><strong>分配一个新的底层数组</strong>:新数组的容量通常是原数组容量的两倍,但这个增长因子可能根据添加的元素数量和切片的当前容量而变化,尤其是在切片的容量非常大或者添加了多个元素的情况下。</li><li><strong>复制原有元素到新数组</strong>:原切片的元素会被复制到新的底层数组中。</li><li><strong>添加新元素</strong>:新的元素会被添加到复制的元素之后。</li><li><strong>返回新切片</strong>:函数返回新的切片,这个切片引用了新的底层数组,并更新了其长度和容量。</li></ol><h3 id=&#34;示例代码&#34;>示例代码</h3><p>下面的示例演示了如何通过 <code>append</code> 自动扩展切片的容量。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>package</span> <span style=&#34;color:#a6e22e&#34;>main</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#f92672&#34;>import</span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span> <span style=&#34;color:#a6e22e&#34;>slice</span> <span style=&#34;color:#f92672&#34;>:=</span> make([]<span style=&#34;color:#66d9ef&#34;>int</span>, <span style=&#34;color:#ae81ff&#34;>0</span>, <span style=&#34;color:#ae81ff&#34;>2</span>) <span style=&#34;color:#75715e&#34;>// 创建一个初始容量为2的切片</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#75715e&#34;></span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span> <span style=&#34;color:#75715e&#34;>// 打印初始切片的长度和容量</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Printf</span>(<span style=&#34;color:#e6db74&#34;>&#34;Initial: Length=%d, Capacity=%d\n&#34;</span>, len(<span style=&#34;color:#a6e22e&#34;>slice</span>), cap(<span style=&#34;color:#a6e22e&#34;>slice</span>))</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span> <span style=&#34;color:#75715e&#34;>// 向切片添加元素,超过其初始容量</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>for</span> <span style=&#34;color:#a6e22e&#34;>i</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#ae81ff&#34;>1</span>; <span style=&#34;color:#a6e22e&#34;>i</span> <span style=&#34;color:#f92672&#34;>&lt;=</span> <span style=&#34;color:#ae81ff&#34;>5</span>; <span style=&#34;color:#a6e22e&#34;>i</span><span style=&#34;color:#f92672&#34;>++</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> <span style=&#34;color:#a6e22e&#34;>slice</span> = append(<span style=&#34;color:#a6e22e&#34;>slice</span>, <span style=&#34;color:#a6e22e&#34;>i</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> <span style=&#34;color:#75715e&#34;>// 打印每次添加元素后切片的长度和容量</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Printf</span>(<span style=&#34;color:#e6db74&#34;>&#34;After appending %d: Length=%d, Capacity=%d\n&#34;</span>, <span style=&#34;color:#a6e22e&#34;>i</span>, len(<span style=&#34;color:#a6e22e&#34;>slice</span>), cap(<span style=&#34;color:#a6e22e&#34;>slice</span>))</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span>}</span></span></code></pre></div><p>在这个示例中,初始切片的容量是2,但是随着元素的添加,切片的容量会自动增长,以容纳更多的元素。你会注意到,每当切片的容量不足以添加更多元素时,其容量会增加,通常是翻倍,尽管具体的增长策略可能会有所不同。</p><h3 id=&#34;注意事项-1&#34;>注意事项</h3><ul><li><strong>性能考虑</strong>:虽然 <code>append</code> 可以自动扩展切片的容量,但频繁地重新分配内存和复制数据可能会影响性能。如果你预先知道大概需要多少容量,使用 <code>make</code> 预分配一个足够大的切片可能会更高效。</li><li><strong>扩容策略</strong>:Go 语言的切片扩容策略可能会因版本和具体实现而略有差异。通常,对于较小的切片,扩容会翻倍其容量;对于较大的切片,增长因子可能会小于2,以减少因为扩容而浪费的内存。</li></ul><h2 id=&#34;go-实现stack-和-queue&#34;>Go 实现stack 和 queue</h2><p>在 Go 语言中,没有内置的栈(Stack)和队列(Queue)数据结构,但是可以使用切片(Slice)非常容易地实现它们。切片提供了灵活的操作,如 <code>append</code> 函数和切片操作,使得实现栈和队列成为可能。</p><h3 id=&#34;栈stack&#34;>栈(Stack)</h3><p>栈是一种后进先出(LIFO, Last In First Out)的数据结构,主要操作有:</p><ul><li><strong>Push</strong>:将一个元素放入栈顶。</li><li><strong>Pop</strong>:从栈顶移除一个元素。</li></ul><h4 id=&#34;实现栈&#34;>实现栈</h4><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>package</span> <span style=&#34;color:#a6e22e&#34;>main</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#f92672&#34;>import</span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#66d9ef&#34;>type</span> <span style=&#34;color:#a6e22e&#34;>Stack</span> []<span style=&#34;color:#66d9ef&#34;>int</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#75715e&#34;>// Push 添加一个元素到栈顶</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>func</span> (<span style=&#34;color:#a6e22e&#34;>s</span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>Stack</span>) <span style=&#34;color:#a6e22e&#34;>Push</span>(<span style=&#34;color:#a6e22e&#34;>v</span> <span style=&#34;color:#66d9ef&#34;>int</span>) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>s</span> = append(<span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>s</span>, <span style=&#34;color:#a6e22e&#34;>v</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span><span style=&#34;color:#75715e&#34;>// Pop 从栈顶移除一个元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>func</span> (<span style=&#34;color:#a6e22e&#34;>s</span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>Stack</span>) <span style=&#34;color:#a6e22e&#34;>Pop</span>() <span style=&#34;color:#66d9ef&#34;>int</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> <span style=&#34;color:#66d9ef&#34;>if</span> len(<span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>s</span>) <span style=&#34;color:#f92672&#34;>==</span> <span style=&#34;color:#ae81ff&#34;>0</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#e6db74&#34;>&#34;Stack is empty!&#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#f92672&#34;>-</span><span style=&#34;color:#ae81ff&#34;>1</span> <span style=&#34;color:#75715e&#34;>// 返回 -1 表示栈为空</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span><span style=&#34;color:#75715e&#34;></span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span> <span style=&#34;color:#a6e22e&#34;>index</span> <span style=&#34;color:#f92672&#34;>:=</span> len(<span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>s</span>) <span style=&#34;color:#f92672&#34;>-</span> <span style=&#34;color:#ae81ff&#34;>1</span> <span style=&#34;color:#75715e&#34;>// 获取栈顶元素索引</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>element</span> <span style=&#34;color:#f92672&#34;>:=</span> (<span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>s</span>)[<span style=&#34;color:#a6e22e&#34;>index</span>] <span style=&#34;color:#75715e&#34;>// 获取栈顶元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>s</span> = (<span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>s</span>)[:<span style=&#34;color:#a6e22e&#34;>index</span>] <span style=&#34;color:#75715e&#34;>// 移除栈顶元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>21</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#a6e22e&#34;>element</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>22</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>23</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>24</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>25</span><span> <span style=&#34;color:#66d9ef&#34;>var</span> <span style=&#34;color:#a6e22e&#34;>stack</span> <span style=&#34;color:#a6e22e&#34;>Stack</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>26</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>27</span><span> <span style=&#34;color:#a6e22e&#34;>stack</span>.<span style=&#34;color:#a6e22e&#34;>Push</span>(<span style=&#34;color:#ae81ff&#34;>1</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>28</span><span> <span style=&#34;color:#a6e22e&#34;>stack</span>.<span style=&#34;color:#a6e22e&#34;>Push</span>(<span style=&#34;color:#ae81ff&#34;>2</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>29</span><span> <span style=&#34;color:#a6e22e&#34;>stack</span>.<span style=&#34;color:#a6e22e&#34;>Push</span>(<span style=&#34;color:#ae81ff&#34;>3</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>30</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>31</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>stack</span>.<span style=&#34;color:#a6e22e&#34;>Pop</span>()) <span style=&#34;color:#75715e&#34;>// 输出: 3</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>32</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>stack</span>.<span style=&#34;color:#a6e22e&#34;>Pop</span>()) <span style=&#34;color:#75715e&#34;>// 输出: 2</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>33</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>stack</span>.<span style=&#34;color:#a6e22e&#34;>Pop</span>()) <span style=&#34;color:#75715e&#34;>// 输出: 1</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>34</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><h3 id=&#34;队列queue&#34;>队列(Queue)</h3><p>队列是一种先进先出(FIFO, First In First Out)的数据结构,主要操作有:</p><ul><li><strong>Enqueue</strong>:在队列末尾添加一个元素。</li><li><strong>Dequeue</strong>:从队列开头移除一个元素。</li></ul><h4 id=&#34;实现队列&#34;>实现队列</h4><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>package</span> <span style=&#34;color:#a6e22e&#34;>main</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#f92672&#34;>import</span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#66d9ef&#34;>type</span> <span style=&#34;color:#a6e22e&#34;>Queue</span> []<span style=&#34;color:#66d9ef&#34;>int</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#75715e&#34;>// Enqueue 在队列末尾添加一个元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>func</span> (<span style=&#34;color:#a6e22e&#34;>q</span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>Queue</span>) <span style=&#34;color:#a6e22e&#34;>Enqueue</span>(<span style=&#34;color:#a6e22e&#34;>v</span> <span style=&#34;color:#66d9ef&#34;>int</span>) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>q</span> = append(<span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>q</span>, <span style=&#34;color:#a6e22e&#34;>v</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span><span style=&#34;color:#75715e&#34;>// Dequeue 从队列开头移除一个元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>func</span> (<span style=&#34;color:#a6e22e&#34;>q</span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>Queue</span>) <span style=&#34;color:#a6e22e&#34;>Dequeue</span>() <span style=&#34;color:#66d9ef&#34;>int</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> <span style=&#34;color:#66d9ef&#34;>if</span> len(<span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>q</span>) <span style=&#34;color:#f92672&#34;>==</span> <span style=&#34;color:#ae81ff&#34;>0</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#e6db74&#34;>&#34;Queue is empty!&#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#f92672&#34;>-</span><span style=&#34;color:#ae81ff&#34;>1</span> <span style=&#34;color:#75715e&#34;>// 返回 -1 表示队列为空</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span><span style=&#34;color:#75715e&#34;></span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span> <span style=&#34;color:#a6e22e&#34;>element</span> <span style=&#34;color:#f92672&#34;>:=</span> (<span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>q</span>)[<span style=&#34;color:#ae81ff&#34;>0</span>] <span style=&#34;color:#75715e&#34;>// 获取队列开头元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>q</span> = (<span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>q</span>)[<span style=&#34;color:#ae81ff&#34;>1</span>:] <span style=&#34;color:#75715e&#34;>// 移除队列开头元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#a6e22e&#34;>element</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>21</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>22</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>23</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>24</span><span> <span style=&#34;color:#66d9ef&#34;>var</span> <span style=&#34;color:#a6e22e&#34;>queue</span> <span style=&#34;color:#a6e22e&#34;>Queue</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>25</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>26</span><span> <span style=&#34;color:#a6e22e&#34;>queue</span>.<span style=&#34;color:#a6e22e&#34;>Enqueue</span>(<span style=&#34;color:#ae81ff&#34;>1</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>27</span><span> <span style=&#34;color:#a6e22e&#34;>queue</span>.<span style=&#34;color:#a6e22e&#34;>Enqueue</span>(<span style=&#34;color:#ae81ff&#34;>2</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>28</span><span> <span style=&#34;color:#a6e22e&#34;>queue</span>.<span style=&#34;color:#a6e22e&#34;>Enqueue</span>(<span style=&#34;color:#ae81ff&#34;>3</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>29</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>30</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>queue</span>.<span style=&#34;color:#a6e22e&#34;>Dequeue</span>()) <span style=&#34;color:#75715e&#34;>// 输出: 1</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>31</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>queue</span>.<span style=&#34;color:#a6e22e&#34;>Dequeue</span>()) <span style=&#34;color:#75715e&#34;>// 输出: 2</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>32</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>queue</span>.<span style=&#34;color:#a6e22e&#34;>Dequeue</span>()) <span style=&#34;color:#75715e&#34;>// 输出: 3</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>33</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><p>在这些示例中,我们使用了切片和其操作来实现栈和队列的基本功能。这两种数据结构在实际应用中非常有用,特别是在需要特定数据处理顺序的场合。通过简单的方法,Go 语言使得实现这些数据结构变得既简单又直接。</p><h2 id=&#34;make函数在go中的作用&#34;>make函数在Go中的作用</h2><p>在 Go 语言中,<code>make</code> 函数是一个特殊的内置函数,它被用来初始化并分配内存给内置的数据类型,如切片(slice)、映射(map)和通道(channel)。不同于 <code>new</code> 函数,<code>make</code> 返回的是类型的初始化值,而不是指向该值的指针。<code>make</code> 的使用对于这些复合类型的正确初始化和使用是必要的,因为它们需要在内存中进行特定的布局和初始化。</p><h3 id=&#34;作用&#34;>作用</h3><ol><li><p><strong>切片(Slice)</strong>:</p><ul><li><code>make</code> 用于创建一个具有指定长度和容量的切片。如果未指定容量,那么容量将与长度相同。</li><li>使用 <code>make</code> 创建切片可以直接使用,无需担心初始值,因为所有元素都会被初始化为其类型的零值。</li><li>示例:<code>s := make([]int, 5, 10)</code> 创建了一个长度为 5,容量为 10 的 <code>int</code> 类型切片 <code>s</code>。</li></ul></li><li><p><strong>映射(Map)</strong>:</p><ul><li><code>make</code> 用于创建一个映射,并分配足够的内存,以便它可以开始存储键值对。创建的映射将为空,没有任何键值对。</li><li>使用 <code>make</code> 可以确保返回的映射是可用的,避免了将 <code>nil</code> 映射用于赋值或操作的错误。</li><li>示例:<code>m := make(map[string]int)</code> 创建了一个键类型为 <code>string</code>,值类型为 <code>int</code> 的空映射 <code>m</code>。</li></ul></li><li><p><strong>通道(Channel)</strong>:</p><ul><li><code>make</code> 用于创建一个通道,并指定其可以存储的值的类型以及(可选的)缓冲大小。</li><li>创建通道后,可以在协程(goroutines)之间用于通信和同步。</li><li>示例:<code>ch := make(chan int, 5)</code> 创建了一个可以存储 <code>int</code> 类型值,且有 5 个缓冲空间的通道 <code>ch</code>。</li></ul></li></ol><h3 id=&#34;语法&#34;>语法</h3><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span>make(<span style=&#34;color:#a6e22e&#34;>T</span>, <span style=&#34;color:#a6e22e&#34;>size</span>, <span style=&#34;color:#f92672&#34;>...</span><span style=&#34;color:#a6e22e&#34;>optional</span> <span style=&#34;color:#a6e22e&#34;>capacity</span>)</span></span></code></pre></div><ul><li><code>T</code>:要创建的类型(切片、映射或通道)。</li><li><code>size</code>:对于切片,它是长度(length);对于通道,它是缓冲大小(buffer capacity)。对于映射,此参数通常省略,因为映射的大小是动态增长的。</li><li><code>optional capacity</code>:可选,仅用于切片,指定切片的容量(capacity)。如果省略,容量将与长度相同。</li></ul><h3 id=&#34;注意&#34;>注意</h3><ul><li>使用 <code>make</code> 初始化的数据结构(切片、映射、通道)可以直接使用,而无需进一步的分配或初始化。</li><li>对于其他类型,如自定义结构体或基本数据类型,应使用 <code>new</code> 函数或字面量构造进行初始化。</li><li><code>make</code> 仅适用于切片、映射和通道,尝试用于其他类型将导致编译错误。</li></ul><p>通过正确使用 <code>make</code> 函数,Go 程序员可以确保他们的数据结构被正确初始化,准备好安全地用于后续操作。</p><h2 id=&#34;go中怎么样实现equaler接口&#34;>go中怎么样实现Equaler接口</h2><p>在Go语言中,实现一个<code>Equaler</code>接口主要涉及到定义一个接口,该接口包含一个方法,用于比较两个对象是否相等。这样的接口可以被任何想要支持等值比较操作的类型实现。下面是如何定义和实现这样一个<code>Equaler</code>接口的示例:</p><h3 id=&#34;步骤-1-定义equaler接口&#34;>步骤 1: 定义<code>Equaler</code>接口</h3><p>首先,你需要定义<code>Equaler</code>接口。通常,这个接口会包含一个<code>Equal</code>方法,这个方法接受另一个相同类型的对象作为参数,并返回一个布尔值,表示这两个对象是否相等。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#f92672&#34;>package</span> <span style=&#34;color:#a6e22e&#34;>main</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#75715e&#34;>// Equaler 接口定义了一个方法 Equal,任何实现了这个接口的类型</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#75715e&#34;>// 都必须提供 Equal 方法的具体实现。</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>type</span> <span style=&#34;color:#a6e22e&#34;>Equaler</span> <span style=&#34;color:#66d9ef&#34;>interface</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>6</span><span> <span style=&#34;color:#a6e22e&#34;>Equal</span>(<span style=&#34;color:#a6e22e&#34;>other</span> <span style=&#34;color:#a6e22e&#34;>Equaler</span>) <span style=&#34;color:#66d9ef&#34;>bool</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>7</span><span>}</span></span></code></pre></div><h3 id=&#34;步骤-2-实现equaler接口&#34;>步骤 2: 实现<code>Equaler</code>接口</h3><p>然后,你可以为你自己的类型实现<code>Equaler</code>接口。假设你有一个<code>Point</code>类型,你想要比较两个<code>Point</code>实例是否相等。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>package</span> <span style=&#34;color:#a6e22e&#34;>main</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#f92672&#34;>import</span> <span style=&#34;color:#e6db74&#34;>&#34;math&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#75715e&#34;>// Point 表示一个二维点。</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>type</span> <span style=&#34;color:#a6e22e&#34;>Point</span> <span style=&#34;color:#66d9ef&#34;>struct</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span> <span style=&#34;color:#a6e22e&#34;>X</span>, <span style=&#34;color:#a6e22e&#34;>Y</span> <span style=&#34;color:#66d9ef&#34;>float64</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#75715e&#34;>// Equal 实现了 Equaler 接口,用于比较两个 Point 是否相等。</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span><span style=&#34;color:#75715e&#34;>// 这里的other参数需要被类型断言为*Point,以便进行比较。</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>func</span> (<span style=&#34;color:#a6e22e&#34;>p</span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>Point</span>) <span style=&#34;color:#a6e22e&#34;>Equal</span>(<span style=&#34;color:#a6e22e&#34;>other</span> <span style=&#34;color:#a6e22e&#34;>Equaler</span>) <span style=&#34;color:#66d9ef&#34;>bool</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> <span style=&#34;color:#a6e22e&#34;>otherPoint</span>, <span style=&#34;color:#a6e22e&#34;>ok</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>other</span>.(<span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>Point</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> <span style=&#34;color:#66d9ef&#34;>if</span> !<span style=&#34;color:#a6e22e&#34;>ok</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#66d9ef&#34;>false</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#a6e22e&#34;>math</span>.<span style=&#34;color:#a6e22e&#34;>Abs</span>(<span style=&#34;color:#a6e22e&#34;>p</span>.<span style=&#34;color:#a6e22e&#34;>X</span><span style=&#34;color:#f92672&#34;>-</span><span style=&#34;color:#a6e22e&#34;>otherPoint</span>.<span style=&#34;color:#a6e22e&#34;>X</span>) &lt; <span style=&#34;color:#ae81ff&#34;>0.001</span> <span style=&#34;color:#f92672&#34;>&amp;&amp;</span> <span style=&#34;color:#a6e22e&#34;>math</span>.<span style=&#34;color:#a6e22e&#34;>Abs</span>(<span style=&#34;color:#a6e22e&#34;>p</span>.<span style=&#34;color:#a6e22e&#34;>Y</span><span style=&#34;color:#f92672&#34;>-</span><span style=&#34;color:#a6e22e&#34;>otherPoint</span>.<span style=&#34;color:#a6e22e&#34;>Y</span>) &lt; <span style=&#34;color:#ae81ff&#34;>0.001</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span>}</span></span></code></pre></div><h3 id=&#34;注意事项-2&#34;>注意事项</h3><ul><li>在<code>Equal</code>方法中,参数<code>other</code>被定义为<code>Equaler</code>类型。这允许方法接受任何实现了<code>Equaler</code>接口的类型。然而,在实际的比较中,你通常需要将<code>other</code>类型断言为你需要比较的具体类型(在这个例子中是<code>*Point</code>),以便能够访问该类型的字段。</li><li>使用类型断言(<code>otherPoint, ok := other.(*Point)</code>)时,需要处理断言失败的情况。如果断言失败,表示<code>other</code>不是一个<code>*Point</code>实例,此时应当返回<code>false</code>。</li><li>比较浮点数时使用了一个小的容忍度值(<code>0.001</code>)来判断两个点是否相等。这是因为直接比较浮点数可能会因为精度问题而导致不准确的比较结果。</li></ul><p>通过这种方式,你可以为任何类型实现<code>Equaler</code>接口,只要提供了一个适当的<code>Equal</code>方法即可。这种接口抽象允许你编写更通用、更灵活的代码,因为你可以编写依赖于<code>Equaler</code>接口的函数或方法,而不是依赖于特定的类型。</p><h2 id=&#34;go中能否让自定义的类型也可以使用来比较&#34;>go中能否让自定义的类型也可以使用==来比较</h2><p>在Go语言中,使用<code>==</code>运算符来比较两个值是否相等的能力是内建的,且适用于所有基本类型(如<code>int</code>、<code>float64</code>、<code>string</code>等)和一些复合类型(如数组)。对于自定义的类型,能否使用<code>==</code>取决于该类型的底层类型:</p><ol><li><p><strong>基本类型和结构体</strong>:如果你的自定义类型是基于基本类型的(例如通过<code>type</code>关键字定义的新类型),那么可以直接使用<code>==</code>进行比较,因为这种比较是对其底层类型进行的。对于结构体,如果所有字段都可以比较,则可以使用<code>==</code>来比较两个结构体实例是否相等。</p></li><li><p><strong>切片、映射和函数类型</strong>:对于这些类型,即使是作为自定义类型的底层类型,也不能使用<code>==</code>进行比较(切片、映射、函数只能和<code>nil</code>进行比较)。尝试这样做会导致编译错误。</p></li></ol><h3 id=&#34;示例-4&#34;>示例</h3><ul><li><strong>基于基本类型的自定义类型</strong>:</li></ul><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>package</span> <span style=&#34;color:#a6e22e&#34;>main</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#f92672&#34;>import</span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#66d9ef&#34;>type</span> <span style=&#34;color:#a6e22e&#34;>MyInt</span> <span style=&#34;color:#66d9ef&#34;>int</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span> <span style=&#34;color:#66d9ef&#34;>var</span> <span style=&#34;color:#a6e22e&#34;>a</span> <span style=&#34;color:#a6e22e&#34;>MyInt</span> = <span style=&#34;color:#ae81ff&#34;>5</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> <span style=&#34;color:#66d9ef&#34;>var</span> <span style=&#34;color:#a6e22e&#34;>b</span> <span style=&#34;color:#a6e22e&#34;>MyInt</span> = <span style=&#34;color:#ae81ff&#34;>5</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>a</span> <span style=&#34;color:#f92672&#34;>==</span> <span style=&#34;color:#a6e22e&#34;>b</span>) <span style=&#34;color:#75715e&#34;>// 输出: true</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><p>这里,<code>MyInt</code>是基于<code>int</code>类型的自定义类型,所以可以使用<code>==</code>来比较。</p><ul><li><strong>结构体类型</strong>:</li></ul><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>package</span> <span style=&#34;color:#a6e22e&#34;>main</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#f92672&#34;>import</span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#66d9ef&#34;>type</span> <span style=&#34;color:#a6e22e&#34;>Point</span> <span style=&#34;color:#66d9ef&#34;>struct</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span> <span style=&#34;color:#a6e22e&#34;>X</span>, <span style=&#34;color:#a6e22e&#34;>Y</span> <span style=&#34;color:#66d9ef&#34;>int</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span> <span style=&#34;color:#a6e22e&#34;>p1</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>Point</span>{<span style=&#34;color:#a6e22e&#34;>X</span>: <span style=&#34;color:#ae81ff&#34;>1</span>, <span style=&#34;color:#a6e22e&#34;>Y</span>: <span style=&#34;color:#ae81ff&#34;>2</span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span> <span style=&#34;color:#a6e22e&#34;>p2</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>Point</span>{<span style=&#34;color:#a6e22e&#34;>X</span>: <span style=&#34;color:#ae81ff&#34;>1</span>, <span style=&#34;color:#a6e22e&#34;>Y</span>: <span style=&#34;color:#ae81ff&#34;>2</span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>p1</span> <span style=&#34;color:#f92672&#34;>==</span> <span style=&#34;color:#a6e22e&#34;>p2</span>) <span style=&#34;color:#75715e&#34;>// 输出: true</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><p>在这个例子中,<code>Point</code>是一个结构体类型,由于它的所有字段都是可以比较的类型,所以两个<code>Point</code>类型的实例可以使用<code>==</code>进行比较。</p><p>总结来说,是否可以使用<code>==</code>来比较自定义类型的值取决于该类型的底层类型以及是否满足Go语言中<code>==</code>运算符适用的类型约束。对于复杂类型或需要更灵活的比较逻辑的情况,通常会实现一个比较函数或方法,如上一条回答中所述的<code>Equaler</code>接口实现。</p><h2 id=&#34;append是不是永远不会改变传入的切片的值&#34;>append是不是永远不会改变传入的切片的值</h2><p><code>append</code>函数在Go语言中的行为有一些特点需要理解,尤其是关于它如何影响传入的切片的值。<code>append</code>操作本身不会直接修改传入的切片的元素。但是,它可能会改变切片的长度和容量,以及返回一个可能指向新数组的切片。这里有几个关键点需要注意:</p><ol><li><p><strong>不修改原切片元素</strong>:<code>append</code>不会改变原有切片中的元素值。如果<code>append</code>操作不导致扩容,添加的元素会出现在原有元素之后。</p></li><li><p><strong>可能返回新的切片</strong>:如果<code>append</code>操作导致了切片扩容(即,需要更多的容量来容纳新元素),Go将分配一个新的数组,并将原切片的元素以及新添加的元素复制到这个新数组中。这时,<code>append</code>返回的切片将指向这个新的数组,而原来的切片仍指向原来的数组。</p></li><li><p><strong>不改变原切片引用</strong>:重要的是,无论<code>append</code>操作是否导致扩容,它都不会更改传入切片本身的引用。换句话说,<code>append</code>函数对原切片的修改是通过返回一个新的切片实现的,而不是直接修改传入的切片变量。</p></li></ol><h3 id=&#34;例子&#34;>例子</h3><p>下面的例子展示了<code>append</code>如何工作,以及它是如何影响切片的:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>package</span> <span style=&#34;color:#a6e22e&#34;>main</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#f92672&#34;>import</span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span> <span style=&#34;color:#a6e22e&#34;>originalSlice</span> <span style=&#34;color:#f92672&#34;>:=</span> []<span style=&#34;color:#66d9ef&#34;>int</span>{<span style=&#34;color:#ae81ff&#34;>1</span>, <span style=&#34;color:#ae81ff&#34;>2</span>, <span style=&#34;color:#ae81ff&#34;>3</span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#e6db74&#34;>&#34;Original slice:&#34;</span>, <span style=&#34;color:#a6e22e&#34;>originalSlice</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> <span style=&#34;color:#75715e&#34;>// Append without capacity overflow</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>modifiedSlice</span> <span style=&#34;color:#f92672&#34;>:=</span> append(<span style=&#34;color:#a6e22e&#34;>originalSlice</span>, <span style=&#34;color:#ae81ff&#34;>4</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#e6db74&#34;>&#34;Modified slice:&#34;</span>, <span style=&#34;color:#a6e22e&#34;>modifiedSlice</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#e6db74&#34;>&#34;Original slice after append:&#34;</span>, <span style=&#34;color:#a6e22e&#34;>originalSlice</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> <span style=&#34;color:#75715e&#34;>// Append that causes capacity overflow</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>for</span> <span style=&#34;color:#a6e22e&#34;>i</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#ae81ff&#34;>5</span>; <span style=&#34;color:#a6e22e&#34;>i</span> <span style=&#34;color:#f92672&#34;>&lt;=</span> <span style=&#34;color:#ae81ff&#34;>10</span>; <span style=&#34;color:#a6e22e&#34;>i</span><span style=&#34;color:#f92672&#34;>++</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span> <span style=&#34;color:#a6e22e&#34;>modifiedSlice</span> = append(<span style=&#34;color:#a6e22e&#34;>modifiedSlice</span>, <span style=&#34;color:#a6e22e&#34;>i</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#e6db74&#34;>&#34;Modified slice after more appends:&#34;</span>, <span style=&#34;color:#a6e22e&#34;>modifiedSlice</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#e6db74&#34;>&#34;Original slice remains unchanged:&#34;</span>, <span style=&#34;color:#a6e22e&#34;>originalSlice</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span>}</span></span></code></pre></div><p>在这个例子中,<code>originalSlice</code>在初次<code>append</code>后仍指向原来的数组,<code>modifiedSlice</code>可能也指向同一个数组(如果没有超出容量)或者指向一个新的数组(如果执行了扩容)。通过连续的<code>append</code>操作,最终<code>modifiedSlice</code>将指向一个不同的、更大的数组,而<code>originalSlice</code>保持不变,仍然指向最初的数组。</p><p>综上所述,<code>append</code>操作不会改变传入切片的值或其指向的数组中的元素,但它会根据需要扩容的情况返回一个新的切片,这个新切片可能指向一个新的底层数组。</p><h2 id=&#34;go-什么时候用make&#34;>go 什么时候用make</h2><p>在Go语言中,<code>nil</code>是一个预定义的标识符,用于表示指针、通道、函数、接口、映射或切片类型的零值。简而言之,<code>nil</code>用于表示一个未初始化的引用或一个空的引用。以下是<code>nil</code>在不同上下文中的含义和作用:</p><ol><li><strong>指针</strong>:对于指针类型,<code>nil</code>表示没有指向任何对象的指针。</li><li><strong>切片</strong>:对于切片类型,<code>nil</code>表示没有分配空间的切片。一个<code>nil</code>切片的长度和容量都是0,但它不等同于空切片(例如<code>[]int{}</code>)。</li><li><strong>映射</strong>:对于映射类型,<code>nil</code>表示没有分配空间的映射。尝试向一个<code>nil</code>映射添加键值对会引发运行时panic。</li><li><strong>通道</strong>:对于通道类型,<code>nil</code>表示没有分配空间的通道。向<code>nil</code>通道发送或从中接收会永远阻塞。</li><li><strong>函数</strong>:对于函数类型,<code>nil</code>可以表示一个没有被赋予任何实际函数的函数变量。</li><li><strong>接口</strong>:对于接口类型,<code>nil</code>表示没有绑定实现的接口。一个<code>nil</code>接口既没有存储任何值,也没有具体类型。</li></ol><h3 id=&#34;为什么需要nil&#34;>为什么需要<code>nil</code>?</h3><p><code>nil</code>的存在使得Go语言的错误处理和资源管理变得更加灵活和简洁。以下是一些使用<code>nil</code>的理由:</p><ul><li><strong>区分初始化状态</strong>:通过检查一个变量是否为<code>nil</code>,可以轻松判断出该变量是否已经初始化。这在处理可选的或者延迟初始化的资源时非常有用。</li><li><strong>简化错误处理</strong>:在Go中,返回错误的惯用方式是返回一个值和一个<code>error</code>类型的值。如果没有错误发生,这个<code>error</code>类型的值通常会被设置为<code>nil</code>,这样调用者就可以通过检查<code>error</code>是否为<code>nil</code>来判断操作是否成功。</li><li><strong>节省资源</strong>:<code>nil</code>切片和<code>nil</code>映射在没有分配内存之前不占用额外的内存,这允许开发者在不需要立即使用这些类型的情况下,延迟内存分配,从而更有效地使用资源。</li></ul><p>总之,<code>nil</code>在Go语言中扮演着重要的角色,它不仅帮助标识变量的初始化状态,还参与错误处理和资源管理,使代码更加简洁和高效。</p><h2 id=&#34;为什么map需要make&#34;>为什么map需要make</h2><p>在Go语言中,映射(map)是一种内建的数据类型,用于存储键值对的集合。映射的键可以是任何可比较的类型,例如整数、字符串等。映射的特性使其成为存储和检索数据的强大工具。然而,与其他一些语言不同,Go的映射需要通过<code>make</code>函数进行初始化才能使用,原因包括:</p><ol><li><p><strong>内存分配</strong>:映射在Go中是引用类型,这意味着当你声明一个映射变量时,变量本身并不直接包含数据,而是指向数据所在的内存地址。使用<code>make</code>函数初始化映射可以为其分配内存空间,从而使映射准备好存储键值对。如果不通过<code>make</code>进行初始化,映射变量将是<code>nil</code>,试图向其添加数据将导致运行时panic。</p></li><li><p><strong>避免<code>nil</code>映射的错误</strong>:直接使用未经初始化的<code>nil</code>映射进行赋值操作会导致运行时错误,因为<code>nil</code>映射没有分配内存空间,无法存储任何数据。通过<code>make</code>函数创建映射,你可以确保映射已经准备好接收数据,避免这种类型的错误。</p></li><li><p><strong>性能优化</strong>:使用<code>make</code>函数初始化映射时,可以选择性地指定初始容量,这有助于提高映射操作的效率。指定一个接近或等于预期存储元素数量的初始容量可以减少映射在存储数据时需要进行的内存重新分配的次数,从而提高性能。</p><p>示例:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>m</span> <span style=&#34;color:#f92672&#34;>:=</span> make(<span style=&#34;color:#66d9ef&#34;>map</span>[<span style=&#34;color:#66d9ef&#34;>string</span>]<span style=&#34;color:#66d9ef&#34;>int</span>, <span style=&#34;color:#ae81ff&#34;>100</span>) <span style=&#34;color:#75715e&#34;>// 创建一个初始容量为100的映射</span></span></span></code></pre></div></li></ol><p>简而言之,通过<code>make</code>初始化映射是为了为其分配内存,确保映射在使用前是非<code>nil</code>的,从而可以安全地添加或访问键值对。此外,<code>make</code>还提供了一种方式来优化映射的内存使用和性能。</p><h2 id=&#34;如何复制一个map&#34;>如何复制一个map</h2><p>在Go语言中,没有直接复制一个映射(map)的内建方法,因为映射是引用类型。如果你想复制一个映射,需要手动创建一个新映射并将原映射中的所有键值对复制到新映射中。这可以通过遍历原映射并将每个键值对添加到新映射来实现。以下是一个如何复制映射的示例:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>CopyMap</span>(<span style=&#34;color:#a6e22e&#34;>originalMap</span> <span style=&#34;color:#66d9ef&#34;>map</span>[<span style=&#34;color:#66d9ef&#34;>string</span>]<span style=&#34;color:#66d9ef&#34;>int</span>) <span style=&#34;color:#66d9ef&#34;>map</span>[<span style=&#34;color:#66d9ef&#34;>string</span>]<span style=&#34;color:#66d9ef&#34;>int</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span> <span style=&#34;color:#75715e&#34;>// 创建一个新的映射,用于存储复制的键值对</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>newMap</span> <span style=&#34;color:#f92672&#34;>:=</span> make(<span style=&#34;color:#66d9ef&#34;>map</span>[<span style=&#34;color:#66d9ef&#34;>string</span>]<span style=&#34;color:#66d9ef&#34;>int</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span> <span style=&#34;color:#75715e&#34;>// 遍历原映射,复制每个键值对到新映射</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>for</span> <span style=&#34;color:#a6e22e&#34;>key</span>, <span style=&#34;color:#a6e22e&#34;>value</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#66d9ef&#34;>range</span> <span style=&#34;color:#a6e22e&#34;>originalMap</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span> <span style=&#34;color:#a6e22e&#34;>newMap</span>[<span style=&#34;color:#a6e22e&#34;>key</span>] = <span style=&#34;color:#a6e22e&#34;>value</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span> <span style=&#34;color:#75715e&#34;>// 返回新的映射副本</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#a6e22e&#34;>newMap</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span>}</span></span></code></pre></div><p>在这个例子中,<code>CopyMap</code>函数接受一个<code>map[string]int</code>类型的映射作为参数,创建一个新的同类型映射,并遍历原映射,将所有键值对复制到新映射中。最后,函数返回这个新映射,实现了映射的复制。</p><p>需要注意的是,这种复制方法是浅复制(shallow copy),意味着如果映射的值是指针或包含指针的复杂类型(如切片、映射等),那么复制的是指针本身,而不是它指向的数据。如果需要深复制(deep copy)这样的值,你需要实现更复杂的逻辑来复制指针指向的数据,而不仅仅是指针本身。</p><h2 id=&#34;go实现stack&#34;>go实现stack</h2><p>在Go语言中,可以使用切片(slice)来实现一个栈(stack)。栈是一种后进先出(LIFO)的数据结构,支持两个基本操作:push(向栈中添加一个元素)和pop(从栈中移除最顶端的元素)。下面是如何使用切片实现栈的一个简单例子:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>package</span> <span style=&#34;color:#a6e22e&#34;>main</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span><span style=&#34;color:#f92672&#34;>import</span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#75715e&#34;>// Stack 表示栈的结构体</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>type</span> <span style=&#34;color:#a6e22e&#34;>Stack</span> <span style=&#34;color:#66d9ef&#34;>struct</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span> <span style=&#34;color:#a6e22e&#34;>elements</span> []<span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#75715e&#34;>// 使用切片存储栈元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#75715e&#34;>// NewStack 创建并返回一个空栈</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>NewStack</span>() <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>Stack</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#f92672&#34;>&amp;</span><span style=&#34;color:#a6e22e&#34;>Stack</span>{[]<span style=&#34;color:#66d9ef&#34;>int</span>{}}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span><span style=&#34;color:#75715e&#34;>// Push 向栈中添加一个元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>func</span> (<span style=&#34;color:#a6e22e&#34;>s</span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>Stack</span>) <span style=&#34;color:#a6e22e&#34;>Push</span>(<span style=&#34;color:#a6e22e&#34;>element</span> <span style=&#34;color:#66d9ef&#34;>int</span>) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span> <span style=&#34;color:#a6e22e&#34;>s</span>.<span style=&#34;color:#a6e22e&#34;>elements</span> = append(<span style=&#34;color:#a6e22e&#34;>s</span>.<span style=&#34;color:#a6e22e&#34;>elements</span>, <span style=&#34;color:#a6e22e&#34;>element</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>19</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>20</span><span><span style=&#34;color:#75715e&#34;>// Pop 从栈中移除最顶端的元素并返回该元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>21</span><span><span style=&#34;color:#75715e&#34;>// 如果栈为空,返回0和一个错误</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>22</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>func</span> (<span style=&#34;color:#a6e22e&#34;>s</span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>Stack</span>) <span style=&#34;color:#a6e22e&#34;>Pop</span>() (<span style=&#34;color:#66d9ef&#34;>int</span>, <span style=&#34;color:#66d9ef&#34;>error</span>) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>23</span><span> <span style=&#34;color:#66d9ef&#34;>if</span> len(<span style=&#34;color:#a6e22e&#34;>s</span>.<span style=&#34;color:#a6e22e&#34;>elements</span>) <span style=&#34;color:#f92672&#34;>==</span> <span style=&#34;color:#ae81ff&#34;>0</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>24</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#ae81ff&#34;>0</span>, <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Errorf</span>(<span style=&#34;color:#e6db74&#34;>&#34;stack is empty&#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>25</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>26</span><span> <span style=&#34;color:#75715e&#34;>// 获取栈顶元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>27</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>topElement</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>s</span>.<span style=&#34;color:#a6e22e&#34;>elements</span>[len(<span style=&#34;color:#a6e22e&#34;>s</span>.<span style=&#34;color:#a6e22e&#34;>elements</span>)<span style=&#34;color:#f92672&#34;>-</span><span style=&#34;color:#ae81ff&#34;>1</span>]</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>28</span><span> <span style=&#34;color:#75715e&#34;>// 移除栈顶元素</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>29</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>s</span>.<span style=&#34;color:#a6e22e&#34;>elements</span> = <span style=&#34;color:#a6e22e&#34;>s</span>.<span style=&#34;color:#a6e22e&#34;>elements</span>[:len(<span style=&#34;color:#a6e22e&#34;>s</span>.<span style=&#34;color:#a6e22e&#34;>elements</span>)<span style=&#34;color:#f92672&#34;>-</span><span style=&#34;color:#ae81ff&#34;>1</span>]</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>30</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#a6e22e&#34;>topElement</span>, <span style=&#34;color:#66d9ef&#34;>nil</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>31</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>32</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>33</span><span><span style=&#34;color:#75715e&#34;>// IsEmpty 检查栈是否为空</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>34</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>func</span> (<span style=&#34;color:#a6e22e&#34;>s</span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>Stack</span>) <span style=&#34;color:#a6e22e&#34;>IsEmpty</span>() <span style=&#34;color:#66d9ef&#34;>bool</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>35</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> len(<span style=&#34;color:#a6e22e&#34;>s</span>.<span style=&#34;color:#a6e22e&#34;>elements</span>) <span style=&#34;color:#f92672&#34;>==</span> <span style=&#34;color:#ae81ff&#34;>0</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>36</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>37</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>38</span><span><span style=&#34;color:#75715e&#34;>// Size 返回栈的大小</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>39</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>func</span> (<span style=&#34;color:#a6e22e&#34;>s</span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>Stack</span>) <span style=&#34;color:#a6e22e&#34;>Size</span>() <span style=&#34;color:#66d9ef&#34;>int</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>40</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> len(<span style=&#34;color:#a6e22e&#34;>s</span>.<span style=&#34;color:#a6e22e&#34;>elements</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>41</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>42</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>43</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>44</span><span> <span style=&#34;color:#a6e22e&#34;>stack</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>NewStack</span>()</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>45</span><span> <span style=&#34;color:#a6e22e&#34;>stack</span>.<span style=&#34;color:#a6e22e&#34;>Push</span>(<span style=&#34;color:#ae81ff&#34;>1</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>46</span><span> <span style=&#34;color:#a6e22e&#34;>stack</span>.<span style=&#34;color:#a6e22e&#34;>Push</span>(<span style=&#34;color:#ae81ff&#34;>2</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>47</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>stack</span>.<span style=&#34;color:#a6e22e&#34;>Pop</span>()) <span style=&#34;color:#75715e&#34;>// 输出:2 &lt;nil&gt;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>48</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>stack</span>.<span style=&#34;color:#a6e22e&#34;>IsEmpty</span>()) <span style=&#34;color:#75715e&#34;>// 输出:false</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>49</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>stack</span>.<span style=&#34;color:#a6e22e&#34;>Size</span>()) <span style=&#34;color:#75715e&#34;>// 输出:1</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>50</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><p>这个例子中,<code>Stack</code>类型使用一个<code>int</code>类型的切片来存储栈元素。<code>Push</code>方法通过<code>append</code>函数将一个新元素添加到切片的末尾,实现了向栈中添加元素。<code>Pop</code>方法检查栈是否为空,如果不为空,则移除并返回切片的最后一个元素,模拟了从栈中移除元素的操作。此外,<code>IsEmpty</code>和<code>Size</code>方法分别用于检查栈是否为空和获取栈的大小。</p><p>这个栈的实现是泛型的基础版本,只能存储<code>int</code>类型的元素。如果需要一个能够存储任何类型元素的栈,可以考虑使用<code>interface{}</code>类型代替<code>int</code>,或者使用Go 1.18及以上版本引入的泛型特性来实现。</p><h2 id=&#34;go语言new的作用&#34;>go语言new的作用</h2><p>在Go语言中,<code>new</code>是一个内置的函数,用于分配并初始化一个类型的空间,并返回指向这个新分配空间的指针。<code>new(T)</code>将分配类型<code>T</code>的零值内存空间,并返回一个指向该空间的指针,其类型为<code>*T</code>,其中<code>T</code>表示任意给定的类型。使用<code>new</code>分配的空间被初始化为类型的零值,例如,数值类型的零值是<code>0</code>,布尔类型的零值是<code>false</code>,指针类型的零值是<code>nil</code>等。</p><h3 id=&#34;使用new的示例&#34;>使用<code>new</code>的示例</h3><p>假设你有一个简单的结构体:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>type</span> <span style=&#34;color:#a6e22e&#34;>MyStruct</span> <span style=&#34;color:#66d9ef&#34;>struct</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span> <span style=&#34;color:#a6e22e&#34;>Field1</span> <span style=&#34;color:#66d9ef&#34;>int</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span> <span style=&#34;color:#a6e22e&#34;>Field2</span> <span style=&#34;color:#66d9ef&#34;>string</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span>}</span></span></code></pre></div><p>你可以使用<code>new</code>来创建这个结构体的一个实例:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>ms</span> <span style=&#34;color:#f92672&#34;>:=</span> new(<span style=&#34;color:#a6e22e&#34;>MyStruct</span>)</span></span></code></pre></div><p>这行代码将分配<code>MyStruct</code>的内存,并将<code>ms</code>初始化为指向这块内存的指针。由于使用了<code>new</code>,<code>MyStruct</code>中的字段<code>Field1</code>和<code>Field2</code>会被初始化为它们各自类型的零值,即<code>Field1</code>为<code>0</code>,<code>Field2</code>为<code>&quot;&quot;</code>(空字符串)。</p><h3 id=&#34;new与复合字面量的区别&#34;><code>new</code>与复合字面量的区别</h3><p>虽然<code>new</code>可以用来创建类型的实例并获取其指针,Go语言还提供了另一种创建并初始化类型实例的方法,即使用复合字面量(composite literals):</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>ms</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#f92672&#34;>&amp;</span><span style=&#34;color:#a6e22e&#34;>MyStruct</span>{}</span></span></code></pre></div><p>这种方法不仅创建了<code>MyStruct</code>的实例,并将其初始化为零值,还返回了指向这个实例的指针,类似于<code>new(MyStruct)</code>的作用。不过,复合字面量还允许你在创建实例的同时初始化字段值:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>ms</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#f92672&#34;>&amp;</span><span style=&#34;color:#a6e22e&#34;>MyStruct</span>{<span style=&#34;color:#a6e22e&#34;>Field1</span>: <span style=&#34;color:#ae81ff&#34;>10</span>, <span style=&#34;color:#a6e22e&#34;>Field2</span>: <span style=&#34;color:#e6db74&#34;>&#34;Hello&#34;</span>}</span></span></code></pre></div><h3 id=&#34;总结&#34;>总结</h3><p><code>new</code>函数在Go中用于分配并初始化一个类型的零值实例,并返回指向这个实例的指针。尽管<code>new</code>在某些情况下很有用,但在Go中,使用复合字面量初始化结构体及其指针更加常见,因为这种方法更灵活,可以直接在创建实例时初始化字段。</p><h2 id=&#34;go-range-本质&#34;>go range 本质</h2><p>在Go语言中,<code>range</code>是一个用于迭代各种集合元素的关键字,包括数组、切片、映射、字符串和通道(channel)。<code>range</code>的本质是提供了一种简洁的语法来遍历容器类型的元素,而具体的迭代逻辑是根据被迭代对象的类型而内置在Go语言中的。下面是<code>range</code>用法的一些关键点,帮助理解其本质:</p><h3 id=&#34;1-数组和切片&#34;>1. 数组和切片</h3><p>当使用<code>range</code>迭代数组或切片时,它会返回每个元素的索引和该索引处的值的副本。因为是副本,所以在迭代过程中修改这些值不会影响到原数组或切片中的元素。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>slice</span> <span style=&#34;color:#f92672&#34;>:=</span> []<span style=&#34;color:#66d9ef&#34;>int</span>{<span style=&#34;color:#ae81ff&#34;>1</span>, <span style=&#34;color:#ae81ff&#34;>2</span>, <span style=&#34;color:#ae81ff&#34;>3</span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>for</span> <span style=&#34;color:#a6e22e&#34;>i</span>, <span style=&#34;color:#a6e22e&#34;>v</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#66d9ef&#34;>range</span> <span style=&#34;color:#a6e22e&#34;>slice</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>i</span>, <span style=&#34;color:#a6e22e&#34;>v</span>) <span style=&#34;color:#75715e&#34;>// i 是索引,v 是该索引处的值的副本</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><h3 id=&#34;2-映射map&#34;>2. 映射(Map)</h3><p>迭代映射时,<code>range</code>返回每个键值对的键和值。由于映射的迭代顺序是不确定的,每次迭代同一个映射可能会得到不同的顺序。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>m</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#66d9ef&#34;>map</span>[<span style=&#34;color:#66d9ef&#34;>string</span>]<span style=&#34;color:#66d9ef&#34;>int</span>{<span style=&#34;color:#e6db74&#34;>&#34;a&#34;</span>: <span style=&#34;color:#ae81ff&#34;>1</span>, <span style=&#34;color:#e6db74&#34;>&#34;b&#34;</span>: <span style=&#34;color:#ae81ff&#34;>2</span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>for</span> <span style=&#34;color:#a6e22e&#34;>k</span>, <span style=&#34;color:#a6e22e&#34;>v</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#66d9ef&#34;>range</span> <span style=&#34;color:#a6e22e&#34;>m</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>k</span>, <span style=&#34;color:#a6e22e&#34;>v</span>) <span style=&#34;color:#75715e&#34;>// k 是键,v 是对应的值</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><h3 id=&#34;3-字符串&#34;>3. 字符串</h3><p>对于字符串,<code>range</code>迭代的是Unicode字符(rune),而不是字节。它返回当前字符的字节索引(不一定连续,因为Unicode字符可能占多个字节)和字符本身的rune值。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>s</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#e6db74&#34;>&#34;Go语言&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>for</span> <span style=&#34;color:#a6e22e&#34;>i</span>, <span style=&#34;color:#a6e22e&#34;>r</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#66d9ef&#34;>range</span> <span style=&#34;color:#a6e22e&#34;>s</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>i</span>, <span style=&#34;color:#a6e22e&#34;>r</span>) <span style=&#34;color:#75715e&#34;>// i 是rune的起始字节索引,r 是rune值</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><h3 id=&#34;4-通道channel&#34;>4. 通道(Channel)</h3><p>当从通道中迭代时,<code>range</code>会连续从通道接收值,直到通道被关闭。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>c</span> <span style=&#34;color:#f92672&#34;>:=</span> make(<span style=&#34;color:#66d9ef&#34;>chan</span> <span style=&#34;color:#66d9ef&#34;>int</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>go</span> <span style=&#34;color:#66d9ef&#34;>func</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span> <span style=&#34;color:#a6e22e&#34;>c</span> <span style=&#34;color:#f92672&#34;>&lt;-</span> <span style=&#34;color:#ae81ff&#34;>1</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span> <span style=&#34;color:#a6e22e&#34;>c</span> <span style=&#34;color:#f92672&#34;>&lt;-</span> <span style=&#34;color:#ae81ff&#34;>2</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span> close(<span style=&#34;color:#a6e22e&#34;>c</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>6</span><span>}()</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>7</span><span><span style=&#34;color:#66d9ef&#34;>for</span> <span style=&#34;color:#a6e22e&#34;>v</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#66d9ef&#34;>range</span> <span style=&#34;color:#a6e22e&#34;>c</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>8</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>v</span>) <span style=&#34;color:#75715e&#34;>// v 是从通道接收到的值</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>9</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><h3 id=&#34;本质理解&#34;>本质理解</h3><p><code>range</code>的本质是语法糖,它为不同的集合类型提供了一种统一的迭代接口。在底层,Go语言根据被迭代对象的类型执行相应的迭代逻辑。这种设计简化了遍历数据结构的代码,使得开发者可以使用相同的语法模式来迭代不同类型的集合,而无需担心每种类型背后具体的迭代实现细节。</p><h2 id=&#34;go-string如何push_back&#34;>go string如何push_back</h2><p>在Go语言中,字符串(<code>string</code>)是不可变的,这意味着一旦一个字符串被创建,它所包含的内容就不能被修改了。因此,Go中的字符串没有类似C++中<code>std::string</code>的<code>push_back</code>方法来直接在字符串的末尾添加一个字符。但是,你可以通过字符串连接的方式来“添加”字符或者另一个字符串到现有字符串的末尾,从而实现类似<code>push_back</code>的功能。</p><p>使用<code>+</code>操作符或<code>fmt.Sprintf</code>函数是实现这一点的常见方法:</p><h3 id=&#34;使用操作符&#34;>使用<code>+</code>操作符</h3><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>s</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#e6db74&#34;>&#34;hello&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#a6e22e&#34;>s</span> <span style=&#34;color:#f92672&#34;>+=</span> <span style=&#34;color:#e6db74&#34;>&#34; world&#34;</span> <span style=&#34;color:#75715e&#34;>// 将&#34; world&#34;添加到&#34;hello&#34;的末尾</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>s</span>) <span style=&#34;color:#75715e&#34;>// 输出: &#34;hello world&#34;</span></span></span></code></pre></div><h3 id=&#34;使用fmtsprintf&#34;>使用<code>fmt.Sprintf</code></h3><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>s</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#e6db74&#34;>&#34;hello&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#a6e22e&#34;>s</span> = <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Sprintf</span>(<span style=&#34;color:#e6db74&#34;>&#34;%s%s&#34;</span>, <span style=&#34;color:#a6e22e&#34;>s</span>, <span style=&#34;color:#e6db74&#34;>&#34; world&#34;</span>) <span style=&#34;color:#75715e&#34;>// 将&#34; world&#34;添加到&#34;hello&#34;的末尾</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>s</span>) <span style=&#34;color:#75715e&#34;>// 输出: &#34;hello world&#34;</span></span></span></code></pre></div><p>如果你只想添加一个字符,可以将该字符先转换为字符串:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>s</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#e6db74&#34;>&#34;hello&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#a6e22e&#34;>c</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#e6db74&#34;>&#39;!&#39;</span> <span style=&#34;color:#75715e&#34;>// 字符类型在Go中是rune</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#a6e22e&#34;>s</span> <span style=&#34;color:#f92672&#34;>+=</span> string(<span style=&#34;color:#a6e22e&#34;>c</span>) <span style=&#34;color:#75715e&#34;>// 将字符&#39;!&#39;添加到&#34;hello&#34;的末尾</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>s</span>) <span style=&#34;color:#75715e&#34;>// 输出: &#34;hello!&#34;</span></span></span></code></pre></div><p>请记住,每次字符串连接操作实际上都会创建一个新的字符串,因为Go中的字符串是不可变的。如果你需要执行大量的字符串连接操作,使用<code>strings.Builder</code>类型可能会更高效:</p><h3 id=&#34;使用stringsbuilder&#34;>使用<code>strings.Builder</code></h3><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>var</span> <span style=&#34;color:#a6e22e&#34;>builder</span> <span style=&#34;color:#a6e22e&#34;>strings</span>.<span style=&#34;color:#a6e22e&#34;>Builder</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#a6e22e&#34;>builder</span>.<span style=&#34;color:#a6e22e&#34;>WriteString</span>(<span style=&#34;color:#e6db74&#34;>&#34;hello&#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#a6e22e&#34;>builder</span>.<span style=&#34;color:#a6e22e&#34;>WriteString</span>(<span style=&#34;color:#e6db74&#34;>&#34; world&#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>builder</span>.<span style=&#34;color:#a6e22e&#34;>String</span>()) <span style=&#34;color:#75715e&#34;>// 输出: &#34;hello world&#34;</span></span></span></code></pre></div><p><code>strings.Builder</code>提供了一个可变的字符串缓冲区,可以更高效地构建字符串,特别是在涉及到大量字符串拼接操作时。它在内部管理一个字节切片(<code>[]byte</code>)来避免多次分配和复制字符串带来的性能开销。</p><h2 id=&#34;介绍go中的错误处理&#34;>介绍go中的错误处理</h2><p>在Go语言中,错误处理是通过使用<code>error</code>接口类型来实现的,这与许多其他语言使用异常(exceptions)的方式有所不同。<code>error</code>类型是Go语言标准库中的一个接口,定义如下:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>type</span> <span style=&#34;color:#66d9ef&#34;>error</span> <span style=&#34;color:#66d9ef&#34;>interface</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span> <span style=&#34;color:#a6e22e&#34;>Error</span>() <span style=&#34;color:#66d9ef&#34;>string</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span>}</span></span></code></pre></div><p>任何拥有<code>Error() string</code>方法的类型都可以实现<code>error</code>接口,从而成为一个可以用于错误处理的错误类型。Go语言鼓励显式的错误处理,这意味着函数在遇到错误情况时,通常会返回一个错误值(实现了<code>error</code>接口的值),调用者应检查此返回值来决定如何应对错误。</p><h3 id=&#34;错误处理的惯用法&#34;>错误处理的惯用法</h3><p>在Go中处理错误的典型方式是通过检查函数的返回值中的错误值:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>result</span>, <span style=&#34;color:#a6e22e&#34;>err</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>someFunction</span>()</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>if</span> <span style=&#34;color:#a6e22e&#34;>err</span> <span style=&#34;color:#f92672&#34;>!=</span> <span style=&#34;color:#66d9ef&#34;>nil</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span> <span style=&#34;color:#75715e&#34;>// 处理错误</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#e6db74&#34;>&#34;发生错误:&#34;</span>, <span style=&#34;color:#a6e22e&#34;>err</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#75715e&#34;>// 或者其他错误处理</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>6</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>7</span><span><span style=&#34;color:#75715e&#34;>// 继续正常逻辑</span></span></span></code></pre></div><h3 id=&#34;自定义错误&#34;>自定义错误</h3><p>你可以通过实现<code>error</code>接口创建自定义错误类型。这通常通过定义一个结构体,然后为它实现<code>Error() string</code>方法来完成:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#66d9ef&#34;>type</span> <span style=&#34;color:#a6e22e&#34;>MyError</span> <span style=&#34;color:#66d9ef&#34;>struct</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span> <span style=&#34;color:#a6e22e&#34;>Message</span> <span style=&#34;color:#66d9ef&#34;>string</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span> <span style=&#34;color:#a6e22e&#34;>Code</span> <span style=&#34;color:#66d9ef&#34;>int</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#66d9ef&#34;>func</span> (<span style=&#34;color:#a6e22e&#34;>e</span> <span style=&#34;color:#f92672&#34;>*</span><span style=&#34;color:#a6e22e&#34;>MyError</span>) <span style=&#34;color:#a6e22e&#34;>Error</span>() <span style=&#34;color:#66d9ef&#34;>string</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Sprintf</span>(<span style=&#34;color:#e6db74&#34;>&#34;%d - %s&#34;</span>, <span style=&#34;color:#a6e22e&#34;>e</span>.<span style=&#34;color:#a6e22e&#34;>Code</span>, <span style=&#34;color:#a6e22e&#34;>e</span>.<span style=&#34;color:#a6e22e&#34;>Message</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#75715e&#34;>// 使用自定义错误的函数示例</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span><span style=&#34;color:#75715e&#34;></span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>doSomething</span>() <span style=&#34;color:#66d9ef&#34;>error</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span> <span style=&#34;color:#75715e&#34;>// 假设发生了某种错误情况</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#f92672&#34;>&amp;</span><span style=&#34;color:#a6e22e&#34;>MyError</span>{<span style=&#34;color:#a6e22e&#34;>Message</span>: <span style=&#34;color:#e6db74&#34;>&#34;出了点问题&#34;</span>, <span style=&#34;color:#a6e22e&#34;>Code</span>: <span style=&#34;color:#ae81ff&#34;>404</span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span>}</span></span></code></pre></div><h3 id=&#34;错误检查与处理&#34;>错误检查与处理</h3><p>Go语言的错误处理模式鼓励开发者显式地检查和处理错误,这可以让错误的来源和处理更加清晰,有助于编写可靠和容易维护的代码。虽然这种方式可能使代码量略增,但它提高了程序的稳定性和可读性。</p><h3 id=&#34;errors包和fmterrorf&#34;><code>errors</code>包和<code>fmt.Errorf</code></h3><p>Go标准库提供了<code>errors</code>包,用于简化常见错误的创建。使用<code>errors.New</code>函数可以快速创建一个基本的错误实例:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#f92672&#34;>import</span> <span style=&#34;color:#e6db74&#34;>&#34;errors&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#a6e22e&#34;>err</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>errors</span>.<span style=&#34;color:#a6e22e&#34;>New</span>(<span style=&#34;color:#e6db74&#34;>&#34;发生了一个错误&#34;</span>)</span></span></code></pre></div><p>另外,<code>fmt</code>包的<code>Errorf</code>函数可以用来格式化错误信息并返回一个错误。这是创建带有格式化信息的错误的便捷方式:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>err</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Errorf</span>(<span style=&#34;color:#e6db74&#34;>&#34;发生了错误:%v&#34;</span>, <span style=&#34;color:#e6db74&#34;>&#34;具体信息&#34;</span>)</span></span></code></pre></div><h3 id=&#34;错误处理的进阶使用&#34;>错误处理的进阶使用</h3><p>在一些复杂的应用场景中,Go语言的错误处理还涉及错误的封装(wrapping)、检查(inspection)、以及自定义错误类型的设计,以满足更高级的错误处理需求。从Go 1.13开始,标准库<code>errors</code>包引入了对错误封装和展开的支持,使得错误处理更加灵活。</p><h2 id=&#34;go的错误处理和其他语言相比优点是什么缺点是什么&#34;>Go的错误处理,和其他语言相比,优点是什么?缺点是什么?</h2><p>Go语言的错误处理模型有其独特之处,相较于其他编程语言,这种模型既有优点也有缺点。</p><h3 id=&#34;优点&#34;>优点</h3><ol><li><strong>简明易懂</strong>:Go的错误处理非常直接。函数要么成功并返回结果,要么失败并返回错误。这种明确性使得错误处理逻辑易于理解和维护。</li><li><strong>显式错误处理</strong>:强制要求检查错误迫使开发者面对可能出现的错误情况,这有助于提高代码的健壮性。在Go中,忽略错误不是默许的做法,这与一些其他语言中可能通过异常处理隐式地忽略错误的情况不同。</li><li><strong>灵活性</strong>:由于错误被视为普通值,开发者可以使用Go的所有语言特性来处理错误,比如可以很容易地将错误信息记录到日志、添加额外的上下文信息,或者根据错误类型进行不同的处理。</li><li><strong>性能</strong>:相比异常处理机制,Go的错误处理方式通常有更好的性能。在Go中,错误作为返回值处理,避免了异常处理可能带来的性能开销。</li></ol><h3 id=&#34;缺点&#34;>缺点</h3><ol><li><strong>代码冗余</strong>:在每个可能发生错误的函数调用后都需要检查错误,这可能会导致大量重复的错误检查代码。虽然这提高了代码的明确性和可靠性,但也使得代码更加冗长。</li><li><strong>错误处理分散</strong>:错误需要在它们发生的地方立即被处理,这可能导致错误处理逻辑分散在代码库的各个部分,而不是集中处理。</li><li><strong>缺乏统一的错误处理结构</strong>:虽然Go的<code>interface{}</code>类型允许以统一的方式处理所有错误,但标准库并没有提供一个统一处理错误的结构或者模式,这可能导致不同的开发者或团队采用不同的错误处理策略,降低了代码的一致性。</li><li><strong>功能限制</strong>:与支持异常的语言相比,Go的错误处理模型在某些情况下可能不够灵活。例如,在需要清理资源的情况下,缺少类似于其他语言中<code>finally</code>块的机制,尽管可以通过<code>defer</code>语句在某种程度上实现相似的功能。</li></ol><p>总体而言,Go的错误处理模型强调简洁性和明确性,鼓励开发者显式地处理每个可能出现的错误。这种模型有其明显的优势,特别是在提高代码可读性和可维护性方面,但也要求开发者付出更多努力来管理错误处理代码的冗余和分散。</p><h2 id=&#34;go的错误处理和javac相比&#34;>Go的错误处理和java,C++相比?</h2><p>Go语言的错误处理与Java和C++的异常处理机制有显著不同。每种语言的错误处理机制都有其设计哲学、优势和局限。下面是一个比较概览:</p><h3 id=&#34;go&#34;>Go</h3><ul><li><strong>错误作为值</strong>:在Go中,错误被视为值,通过<code>error</code>接口表示。函数通常返回一个错误值,以指示是否发生了错误。</li><li><strong>显式检查</strong>:调用者必须显式检查错误值,以决定是否成功执行了函数。</li><li><strong>无异常机制</strong>:Go语言没有异常机制。所有的错误都通过正常的控制流和值来处理。</li></ul><p><strong>优势</strong>:</p><ul><li>易于理解和预测错误处理的流程。</li><li>鼓励开发者面对并显式处理错误情况。</li></ul><p><strong>局限</strong>:</p><ul><li>可能导致大量重复的错误检查代码。</li><li>错误处理逻辑可能会分散于代码的多个地方。</li></ul><h3 id=&#34;java&#34;>Java</h3><ul><li><strong>异常机制</strong>:Java使用异常来处理错误和异常情况。方法可以抛出异常,调用者需要捕获这些异常。</li><li><strong>检查型和非检查型异常</strong>:Java区分检查型(必须显式处理)和非检查型异常(可选处理)。</li><li><strong>try-catch-finally</strong>:通过<code>try-catch</code>语句块捕获和处理异常,<code>finally</code>块用于资源清理。</li></ul><p><strong>优势</strong>:</p><ul><li>异常提供了一种强制错误处理的机制,有助于捕获运行时错误。</li><li>通过异常类型,可以传递丰富的错误信息。</li></ul><p><strong>局限</strong>:</p><ul><li>异常可能导致控制流难以跟踪,尤其是在多层嵌套调用中。</li><li>过度使用异常可能会影响代码的可读性和性能。</li></ul><h3 id=&#34;c&#34;>C++</h3><ul><li><strong>异常机制</strong>:C++同样使用异常来处理错误情况。函数可以抛出异常,使用<code>try-catch</code>块捕获异常。</li><li><strong>资源管理</strong>:C++提倡RAII(资源获取即初始化)原则,利用对象生命周期自动管理资源,配合异常使用。</li><li><strong>noexcept</strong>:C++11引入了<code>noexcept</code>关键字,用于指示函数是否抛出异常。</li></ul><p><strong>优势</strong>:</p><ul><li>异常允许将错误处理代码从正常逻辑中分离出来,提高代码清晰度。</li><li>RAII原则和智能指针等机制与异常配合,有效管理资源。</li></ul><p><strong>局限</strong>:</p><ul><li>异常可能导致性能开销,特别是在深层嵌套调用中。</li><li>需要谨慎管理异常安全性和资源泄漏问题。</li></ul><h3 id=&#34;总结-1&#34;>总结</h3><ul><li><strong>Go</strong>的错误处理模式倾向于简单和显式,强迫开发者面对错误,但可能导致代码冗余。</li><li><strong>Java</strong>和**C++**使用异常机制处理错误,能够将错误处理代码与业务逻辑分离,提供更丰富的错误信息,但可能使得控制流程复杂化,且需要注意异常安全性和性能问题。</li></ul><p>选择哪种错误处理方式,很大程度上取决于项目的具体需求、团队的偏好和特定语言的生态。</p><h2 id=&#34;go中我不想定义自己的err类型应该用哪种默认的&#34;>go中我不想定义自己的err类型,应该用哪种默认的?</h2><p>如果你不想在Go中定义自己的错误类型,可以使用标准库<code>errors</code>包中的<code>errors.New</code>函数来创建一个简单的错误。这种方式适用于那些不需要从错误中提取额外信息的场景。<code>errors.New</code>接收一个字符串参数,返回一个符合<code>error</code>接口的错误对象。</p><h3 id=&#34;使用errorsnew&#34;>使用<code>errors.New</code></h3><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>import</span> (</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span> <span style=&#34;color:#e6db74&#34;>&#34;errors&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>someFunction</span>() <span style=&#34;color:#66d9ef&#34;>error</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span> <span style=&#34;color:#75715e&#34;>// 假设发生了某种错误情况</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#a6e22e&#34;>errors</span>.<span style=&#34;color:#a6e22e&#34;>New</span>(<span style=&#34;color:#e6db74&#34;>&#34;这里发生了一个错误&#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span> <span style=&#34;color:#a6e22e&#34;>err</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>someFunction</span>()</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> <span style=&#34;color:#66d9ef&#34;>if</span> <span style=&#34;color:#a6e22e&#34;>err</span> <span style=&#34;color:#f92672&#34;>!=</span> <span style=&#34;color:#66d9ef&#34;>nil</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>err</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span>}</span></span></code></pre></div><h3 id=&#34;格式化错误信息&#34;>格式化错误信息</h3><p>如果你需要在错误信息中包含变量或者更复杂的数据结构,可以使用<code>fmt.Errorf</code>来代替<code>errors.New</code>。<code>fmt.Errorf</code>允许你像使用<code>fmt.Sprintf</code>一样格式化错误字符串,并返回一个错误对象。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>import</span> (</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>someFunction</span>(<span style=&#34;color:#a6e22e&#34;>param</span> <span style=&#34;color:#66d9ef&#34;>int</span>) <span style=&#34;color:#66d9ef&#34;>error</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span> <span style=&#34;color:#75715e&#34;>// 使用 fmt.Errorf 格式化错误信息</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Errorf</span>(<span style=&#34;color:#e6db74&#34;>&#34;错误发生在 %d&#34;</span>, <span style=&#34;color:#a6e22e&#34;>param</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span> <span style=&#34;color:#a6e22e&#34;>err</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>someFunction</span>(<span style=&#34;color:#ae81ff&#34;>42</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span> <span style=&#34;color:#66d9ef&#34;>if</span> <span style=&#34;color:#a6e22e&#34;>err</span> <span style=&#34;color:#f92672&#34;>!=</span> <span style=&#34;color:#66d9ef&#34;>nil</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>err</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span>}</span></span></code></pre></div><p>从Go 1.13开始,<code>errors</code>包提供了<code>As</code>和<code>Is</code>函数,用于检查错误的类型和值。同时,<code>fmt.Errorf</code>支持使用<code>%w</code>动词来包装错误,使得你可以保留原始错误的同时添加上下文信息。即使你不定义自己的错误类型,这些功能也能帮助你更好地管理和检查错误。</p><h3 id=&#34;使用错误包装&#34;>使用错误包装</h3><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#f92672&#34;>import</span> (</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span> <span style=&#34;color:#e6db74&#34;>&#34;errors&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span> <span style=&#34;color:#e6db74&#34;>&#34;fmt&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#66d9ef&#34;>var</span> <span style=&#34;color:#a6e22e&#34;>ErrSpecific</span> = <span style=&#34;color:#a6e22e&#34;>errors</span>.<span style=&#34;color:#a6e22e&#34;>New</span>(<span style=&#34;color:#e6db74&#34;>&#34;特定错误&#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>someFunction</span>() <span style=&#34;color:#66d9ef&#34;>error</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> <span style=&#34;color:#75715e&#34;>// 返回一个包装了原始错误的新错误</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Errorf</span>(<span style=&#34;color:#e6db74&#34;>&#34;操作失败: %w&#34;</span>, <span style=&#34;color:#a6e22e&#34;>ErrSpecific</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>13</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>14</span><span> <span style=&#34;color:#a6e22e&#34;>err</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>someFunction</span>()</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>15</span><span> <span style=&#34;color:#66d9ef&#34;>if</span> <span style=&#34;color:#a6e22e&#34;>errors</span>.<span style=&#34;color:#a6e22e&#34;>Is</span>(<span style=&#34;color:#a6e22e&#34;>err</span>, <span style=&#34;color:#a6e22e&#34;>ErrSpecific</span>) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>16</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#e6db74&#34;>&#34;发生了特定的错误:&#34;</span>, <span style=&#34;color:#a6e22e&#34;>err</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>17</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>18</span><span>}</span></span></code></pre></div><p>使用<code>errors.New</code>和<code>fmt.Errorf</code>可以满足大多数不需要自定义错误类型的场景,并且通过错误包装和检查,你依然可以构建出结构化和可管理的错误处理逻辑。</p><h2 id=&#34;如何判断-map-中是否包含某个-key-&#34;>如何判断 map 中是否包含某个 key ?</h2><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>var</span> <span style=&#34;color:#a6e22e&#34;>sample</span> <span style=&#34;color:#66d9ef&#34;>map</span>[<span style=&#34;color:#66d9ef&#34;>int</span>]<span style=&#34;color:#66d9ef&#34;>int</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>if</span> <span style=&#34;color:#a6e22e&#34;>_</span>, <span style=&#34;color:#a6e22e&#34;>ok</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>sample</span>[<span style=&#34;color:#ae81ff&#34;>10</span>]; <span style=&#34;color:#a6e22e&#34;>ok</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span>} <span style=&#34;color:#66d9ef&#34;>else</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>6</span><span>}</span></span></code></pre></div><h2 id=&#34;stringsjoin--stringsbuilder--bytesbuffer----fmtsprintf&#34;>strings.Join ≈ strings.Builder &gt; bytes.Buffer &gt; &ldquo;+&rdquo; &gt; fmt.Sprintf</h2><p>在Go语言中,拼接字符串可以通过多种方式实现,每种方式在不同场景下的性能和适用性各有差异。下面是对这五种方法——<code>strings.Join</code>、<code>strings.Builder</code>、<code>bytes.Buffer</code>、<code>&quot;+&quot;</code>操作符、<code>fmt.Sprintf</code>——的分析,按照性能从高到低排列:</p><h3 id=&#34;1-stringsjoin&#34;>1. <code>strings.Join</code></h3><ul><li><strong>适用场景</strong>:当你有一个字符串切片,需要将其元素连接成一个单独的字符串时,<code>strings.Join</code>非常高效。</li><li><strong>性能</strong>:通常比<code>+</code>操作符和<code>fmt.Sprintf</code>更高效,因为它在内部计算结果字符串的确切长度,然后一次性分配足够的内存。</li><li><strong>示例</strong>:<div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>parts</span> <span style=&#34;color:#f92672&#34;>:=</span> []<span style=&#34;color:#66d9ef&#34;>string</span>{<span style=&#34;color:#e6db74&#34;>&#34;Hello&#34;</span>, <span style=&#34;color:#e6db74&#34;>&#34;world&#34;</span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#a6e22e&#34;>result</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>strings</span>.<span style=&#34;color:#a6e22e&#34;>Join</span>(<span style=&#34;color:#a6e22e&#34;>parts</span>, <span style=&#34;color:#e6db74&#34;>&#34; &#34;</span>)</span></span></code></pre></div></li></ul><h3 id=&#34;2-stringsbuilder&#34;>2. <code>strings.Builder</code></h3><ul><li><strong>适用场景</strong>:对于在循环或多个步骤中构建字符串尤其有用。自Go 1.10引入以来,它是推荐的字符串构建方法。</li><li><strong>性能</strong>:提供了高效的字符串构建能力,因为它避免了不必要的内存分配和复制。</li><li><strong>示例</strong>:<div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>var</span> <span style=&#34;color:#a6e22e&#34;>sb</span> <span style=&#34;color:#a6e22e&#34;>strings</span>.<span style=&#34;color:#a6e22e&#34;>Builder</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#a6e22e&#34;>sb</span>.<span style=&#34;color:#a6e22e&#34;>WriteString</span>(<span style=&#34;color:#e6db74&#34;>&#34;Hello&#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#a6e22e&#34;>sb</span>.<span style=&#34;color:#a6e22e&#34;>WriteString</span>(<span style=&#34;color:#e6db74&#34;>&#34; &#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#a6e22e&#34;>sb</span>.<span style=&#34;color:#a6e22e&#34;>WriteString</span>(<span style=&#34;color:#e6db74&#34;>&#34;world&#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>5</span><span><span style=&#34;color:#a6e22e&#34;>result</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>sb</span>.<span style=&#34;color:#a6e22e&#34;>String</span>()</span></span></code></pre></div></li></ul><h3 id=&#34;3-bytesbuffer&#34;>3. <code>bytes.Buffer</code></h3><ul><li><strong>适用场景</strong>:在需要频繁修改字符串或者处理二进制数据和字符串转换时适用。<code>bytes.Buffer</code>与<code>strings.Builder</code>类似,但更通用,也支持字节操作。</li><li><strong>性能</strong>:虽然性能略低于<code>strings.Builder</code>,但在需要处理字节和字符串的场景下是一个很好的选择。</li><li><strong>示例</strong>:<div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>var</span> <span style=&#34;color:#a6e22e&#34;>buf</span> <span style=&#34;color:#a6e22e&#34;>bytes</span>.<span style=&#34;color:#a6e22e&#34;>Buffer</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#a6e22e&#34;>buf</span>.<span style=&#34;color:#a6e22e&#34;>Write</span>([]byte(<span style=&#34;color:#e6db74&#34;>&#34;Hello&#34;</span>))</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#a6e22e&#34;>buf</span>.<span style=&#34;color:#a6e22e&#34;>WriteString</span>(<span style=&#34;color:#e6db74&#34;>&#34; world&#34;</span>)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span><span style=&#34;color:#a6e22e&#34;>result</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>buf</span>.<span style=&#34;color:#a6e22e&#34;>String</span>()</span></span></code></pre></div></li></ul><h3 id=&#34;4-操作符&#34;>4. <code>&quot;+&quot;</code>操作符</h3><ul><li><strong>适用场景</strong>:简单的字符串拼接操作,尤其是少量字符串时。</li><li><strong>性能</strong>:在拼接少数几个字符串时性能可接受,但在循环或大量字符串拼接场景中性能较差,因为每次拼接都会产生新的字符串,导致多次内存分配和复制。</li><li><strong>示例</strong>:<div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>result</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#e6db74&#34;>&#34;Hello&#34;</span> <span style=&#34;color:#f92672&#34;>+</span> <span style=&#34;color:#e6db74&#34;>&#34; &#34;</span> <span style=&#34;color:#f92672&#34;>+</span> <span style=&#34;color:#e6db74&#34;>&#34;world&#34;</span></span></span></code></pre></div></li></ul><h3 id=&#34;5-fmtsprintf&#34;>5. <code>fmt.Sprintf</code></h3><ul><li><strong>适用场景</strong>:需要格式化字符串时,如插入变量、调整格式等。</li><li><strong>性能</strong>:相比于其他方法,性能较低,因为它需要解析格式字符串并执行类型断言等操作,但它提供了非常强大的格式化功能。</li><li><strong>示例</strong>:<div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>name</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#e6db74&#34;>&#34;world&#34;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#a6e22e&#34;>result</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Sprintf</span>(<span style=&#34;color:#e6db74&#34;>&#34;Hello %s&#34;</span>, <span style=&#34;color:#a6e22e&#34;>name</span>)</span></span></code></pre></div></li></ul><h3 id=&#34;总结-2&#34;>总结</h3><p>选择哪种方法取决于具体的应用场景。对于简单或少量的字符串拼接,直接使用<code>&quot;+&quot;</code>可能是最直观的。当需要构建大量字符串时,<code>strings.Builder</code>是首选。<code>strings.Join</code>适合已经存储在切片中的字符串拼接。<code>fmt.Sprintf</code>提供了格式化的灵活性,适用于需要格式化输出的场景。<code>bytes.Buffer</code>则在处理既包含文本又包含二进制数据的情况下非常有用。</p><h2 id=&#34;stringsbuilder原理&#34;>strings.Builder原理</h2><p><code>strings.Builder</code> 是 Go 语言在 1.10 版本中引入的一个结构体,它提供了一种高效构建字符串的方法。与其他字符串拼接方法相比,<code>strings.Builder</code> 更为高效,主要因为它在内部优化了内存的分配和复制操作。下面是 <code>strings.Builder</code> 的一些关键原理和特点:</p><h3 id=&#34;1-最小化内存分配&#34;>1. 最小化内存分配</h3><p><code>strings.Builder</code> 在内部维护了一个字节切片(<code>[]byte</code>)。当向 <code>Builder</code> 添加字符串时,它实际上是将这些字符串的字节追加到内部的字节切片中。<code>Builder</code> 会根据需要自动增长这个字节切片的容量,但它尽量通过预分配足够的内存空间来避免频繁的内存重新分配。</p><h3 id=&#34;2-避免不必要的复制&#34;>2. 避免不必要的复制</h3><p>在传统的字符串拼接操作中,每一次拼接都可能产生一个新的字符串对象,这意味着需要复制字符串的内容到新的内存位置。相比之下,<code>strings.Builder</code> 通过直接在已有的字节切片上追加内容,避免了这种复制,从而提升了性能。</p><h3 id=&#34;3-高效的字符串生成&#34;>3. 高效的字符串生成</h3><p>当调用 <code>strings.Builder</code> 的 <code>String()</code> 方法来获取最终的字符串时,它会基于内部的字节切片创建一个新的字符串。由于 Go 语言字符串的不可变性,这个操作会涉及一次内存复制。然而,这仅仅发生在整个构建过程的最后,因此相比于多次字符串拼接,总体上仍然大大减少了内存复制的次数。</p><h3 id=&#34;4-grow-方法&#34;>4. <code>Grow</code> 方法</h3><p><code>strings.Builder</code> 提供了 <code>Grow</code> 方法,允许手动指定预期需要的额外容量。这可以进一步优化性能,因为它可以在大量数据被写入之前预分配足够的内存,从而避免自动扩容过程中的多次内存分配。</p><h3 id=&#34;5-安全性&#34;>5. 安全性</h3><p><code>strings.Builder</code> 的设计也考虑到了安全性。一旦通过 <code>String()</code> 方法获取了最终的字符串,再尝试向 <code>Builder</code> 写入新的数据将会导致 panic。这个设计确保了一旦字符串被生成,内部的字节切片不会被进一步修改,保持了字符串不可变的特性。</p><p>总的来说,<code>strings.Builder</code> 的设计充分利用了 Go 语言的特性,如切片的灵活性和字符串的不可变性,通过减少内存分配和复制的次数,提供了一种高效且安全的方式来构建字符串。</p><h2 id=&#34;go语言-是什么&#34;>Go语言 &hellip;是什么</h2><p>在Go语言中,<code>...</code>符号有两个主要用途:</p><h3 id=&#34;1-可变参数函数variadic-function&#34;>1. 可变参数函数(Variadic Function)</h3><p>当<code>...</code>用于函数的参数类型前时,表示该函数可以接收任意数量的该类型的参数。这使得函数可以灵活处理不同数量的参数,类似于其他语言中的可变参数列表。</p><p>例如,定义一个可接收多个<code>int</code>类型参数的函数,并计算它们的和:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>sum</span>(<span style=&#34;color:#a6e22e&#34;>nums</span> <span style=&#34;color:#f92672&#34;>...</span><span style=&#34;color:#66d9ef&#34;>int</span>) <span style=&#34;color:#66d9ef&#34;>int</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span> <span style=&#34;color:#a6e22e&#34;>total</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#ae81ff&#34;>0</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span> <span style=&#34;color:#66d9ef&#34;>for</span> <span style=&#34;color:#a6e22e&#34;>_</span>, <span style=&#34;color:#a6e22e&#34;>num</span> <span style=&#34;color:#f92672&#34;>:=</span> <span style=&#34;color:#66d9ef&#34;>range</span> <span style=&#34;color:#a6e22e&#34;>nums</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span> <span style=&#34;color:#a6e22e&#34;>total</span> <span style=&#34;color:#f92672&#34;>+=</span> <span style=&#34;color:#a6e22e&#34;>num</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#a6e22e&#34;>total</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span><span style=&#34;color:#66d9ef&#34;>func</span> <span style=&#34;color:#a6e22e&#34;>main</span>() {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>sum</span>(<span style=&#34;color:#ae81ff&#34;>1</span>, <span style=&#34;color:#ae81ff&#34;>2</span>, <span style=&#34;color:#ae81ff&#34;>3</span>)) <span style=&#34;color:#75715e&#34;>// 输出 6</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span><span style=&#34;color:#75715e&#34;></span> <span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>sum</span>(<span style=&#34;color:#ae81ff&#34;>1</span>, <span style=&#34;color:#ae81ff&#34;>2</span>, <span style=&#34;color:#ae81ff&#34;>3</span>, <span style=&#34;color:#ae81ff&#34;>4</span>, <span style=&#34;color:#ae81ff&#34;>5</span>)) <span style=&#34;color:#75715e&#34;>// 输出 15</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span><span style=&#34;color:#75715e&#34;></span>}</span></span></code></pre></div><p>在这个例子中,<code>sum</code>函数可以接收任意数量的<code>int</code>参数。</p><h3 id=&#34;2-切片参数展开slice-unfolding&#34;>2. 切片参数展开(Slice Unfolding)</h3><p>当<code>...</code>用于调用可变参数函数时,紧跟在切片变量后面,表示将切片的元素展开为参数列表传递给函数。</p><p>继续使用上面的<code>sum</code>函数作为例子,如果你有一个<code>int</code>切片,并希望将其元素作为参数传递给<code>sum</code>,可以这样做:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-go&#34; data-lang=&#34;go&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#a6e22e&#34;>nums</span> <span style=&#34;color:#f92672&#34;>:=</span> []<span style=&#34;color:#66d9ef&#34;>int</span>{<span style=&#34;color:#ae81ff&#34;>1</span>, <span style=&#34;color:#ae81ff&#34;>2</span>, <span style=&#34;color:#ae81ff&#34;>3</span>, <span style=&#34;color:#ae81ff&#34;>4</span>, <span style=&#34;color:#ae81ff&#34;>5</span>}</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#a6e22e&#34;>fmt</span>.<span style=&#34;color:#a6e22e&#34;>Println</span>(<span style=&#34;color:#a6e22e&#34;>sum</span>(<span style=&#34;color:#a6e22e&#34;>nums</span><span style=&#34;color:#f92672&#34;>...</span>)) <span style=&#34;color:#75715e&#34;>// 输出 15</span></span></span></code></pre></div><p>在这个例子中,<code>nums...</code>将<code>nums</code>切片中的每个元素展开,作为独立的参数传递给<code>sum</code>函数。</p><h3 id=&#34;总结-3&#34;>总结</h3><p><code>...</code>在Go语言中用作可变参数函数定义和切片参数展开的语法元素,提供了编写灵活函数接口和调用的能力。</p>

2024/3/31
阅读更多

软件使用小技巧

<h2 id=&#34;chrome&#34;>Chrome</h2><p>如何在MacOS下使chrome全屏,并且是不显示tab栏和地址栏的真全屏?</p><p>首先Fn+F使它假全屏,之后按 ⌘ + ⇧ + F , (Command + Shift + F) 使得它真全屏.</p><h2 id=&#34;qemu&#34;>Qemu</h2><p>按Option+Control+G 并不能release mouse。需要按下Option+Control+Command+G才可以。</p>

2024/3/10
阅读更多

如何准备程序员面试

<p>本文既讲解国内和北美面试中间的共同点,也对于两地不同的情况进行单独讲解。例如国内的八股文和北美的BQ。</p><p>本文主要面向的读者是还没参加过正式工作的学生群体。作者也没有跳槽或者高级别的工作经验。如有不合理、不详尽之处,请联系本人改正。十分感谢。</p><p>首先讲解一些互联网上常见的术语。本文可能用到其中的一些。如果你在本文阅读过程中看到看不懂的词语,请使用ctrl+F搜索。</p><h2 id=&#34;术语表&#34;>术语表</h2><p>JD: (Job Description) 工作岗位描述</p><p>bar: 招人的标准。类似高考的分数线。一般用来形容难度高低。</p><p>DP: (data point) 数据点。一般含义就是指自己or身边朋友的例子。例如我GPA2.0被牛津录取了,我就可以在网上说,给大家贡献一个DP,GPA2.0被牛津录取。或者我在面试谷歌的过程中遇到了Leetcode1234这道题,我就可以说贡献一个DP,谷歌面试会考Leetcode1234.</p><p>OA: (Online Assessment) 在线笔试</p><p>BQ: (behavioral question) 和代码无关的问题。例如你在工作中遇到难题会怎么办?</p><p>VO: (Virtual Onsite) 电话或者视频面试。</p><p>HC: (Head Count) 人头数。也就是岗位计划招几个人。例如你面试了一个组,表现很好,但是他们这个组今年的hc只有1个人,而他们在面试你之前,已经签了1个人。那么这个时候由于hc不够,你就无法被录用。</p><p>CV/Resume: 简历</p><p>IC: (Individual Contributor) 个人贡献者,与之对应的是Manager。IC指的就是写代码的,Manager指的是管理岗位。</p><p>toxic: 有毒。往往用于形容令人不适的团队氛围或公司文化。</p><p>NG: (New Grad) 指刚毕业的学生。</p><p>ROI: (return on investment) 投资回报率</p><p>Skip: 你老板的老板。你也可以分别称呼他们为skip manager和 direct manager.</p><p>RA: (research assistant) 研究助理,你本科在实验室打杂也可以称自己做了2年的RA。</p><p>corner case: 写代码的时候遇到的极端or边界情况。</p><h2 id=&#34;写在前面的话&#34;>写在前面的话</h2><h3 id=&#34;心态&#34;>心态</h3><!-- raw HTML omitted --><!-- raw HTML omitted --><p>我们作为面试者,在求职的过程中,最终目标当然是拿到工作。因此很多时候你没有必要准备到万无一失了再投递简历或者面试。或者由于自己的不自信而不敢去投递或面试。哪怕心里很慌,也要先拿下offer再说。一般对于学生求职者来说,在拿到offer到正式上班中间还有好几个月的时间,如果你真的觉得自己不能胜任这份工作,那么你还有好几个月可以继续学习钻研相关领域。你如果总是想让自己的技术水平先达到高级工程师水平再去求职,那永远也准备不完。</p><p>所以,第一点,永远不要胆怯。</p><h3 id=&#34;重要概念&#34;>重要概念</h3><p>Match大于一切. 即你和这个职位是否匹配是大于一切的,大于你的学历,能力。你再有优势,如果和职位不够匹配,那也很难拿到offer。</p><h2 id=&#34;简历&#34;>简历</h2><p>推荐使用网上的现成模版来写。例如<a href=&#34;https://github.com/skyzh/chicv&#34;>https://github.com/skyzh/chicv</a>.</p><p>简历上没必要写GPA,除非这是你的优势。</p><p>简历大概分为教育经历,工作经历,项目等部分。</p><p>工作经历可以包含实习或者RA的经历。描写一下自己主要做了什么工作。要写出自己的impact并突出自己的技术点,不要只写自己做了什么。如果你做出来的东西对于公司没有任何影响,那等于没做。</p><p>(impact: 你对这个世界的影响)</p><p>例如写我在字节跳动开发推荐系统。就不如写:我在字节跳动的XX部门,使用C++和python语言,使用XXX技术框架,独立/和团队一起,使用XXX算法,改进了推荐系统在XX方面的性能,最后经过统计,使得性能提高了30%。</p><p>一般建议写上一些数字,例如使得效率提高了30%等,可以增强可信度和说服力。</p><p>重复一下就是,主要两点。一是体现自己的能力,二是体现自己的impact。</p><p>项目部分,有github链接是最好的。自己平常写的个人项目建议上传到github上面。项目就主要是突出自己的技术栈了。个人自己写的开源项目一般很难产生impact。</p><p>简历以一页为宜。如果你想写的太多,你可以针对不同岗位写多份简历。例如你投后端的岗位,就没必要花大量篇幅写前端的东西。</p><h2 id=&#34;投简历&#34;>投简历</h2><p><strong>能早投就早投!</strong></p><p><strong>能早投就早投!</strong></p><p><strong>能早投就早投!</strong></p><p><strong>能早投就早投!</strong></p><p>大公司,大家都耳熟能详。直接进入官网投递即可。</p><p>小公司,国内可以使用boss直聘等软件。国外可以使用simplify等软件。</p><p>内推: 内推建议多找自己身边认识的人、学长学姐、网友等。</p><p>领英直接联系hr: 这也是一种方法。首先你需要先把自己领英的界面搞好,然后找hr进行私聊。</p><h2 id=&#34;oa笔试&#34;>OA(笔试)</h2><p>最好在收到的当天就做了。不然别人做的早,可能先把hc占了。</p><p>珍惜每一个OA。</p><h2 id=&#34;技术面试&#34;>技术面试</h2><h3 id=&#34;从面试官的角度看面试&#34;>从面试官的角度看面试</h3><p>在你面试的时候,往往你会注意到面试官在敲键盘。他是在记录你面试时候的表现。一份面试官的记录可能如下:</p><blockquote><p>8:00pm 开始</p></blockquote><blockquote><p>8:10 自我介绍太啰嗦</p></blockquote><blockquote><p>8:12 给出问题。面试者询问一些clarification</p></blockquote><blockquote><p>8:15 面试者给出暴力解法,有效。时间复杂度太高,让面试者先实现暴力算法,再问面试者能否提高。</p></blockquote><blockquote><p>8:25 实现暴力算法,完成。</p></blockquote><blockquote><p>8:30 通过了几组样例,让面试者自己书写样例。自行书写的样例不错。</p></blockquote><blockquote><p>8:35 面试者分析了暴力解法的时间复杂度。</p></blockquote><blockquote><p>8:40 面试者无法想到如何优化。给出提示XXX。</p></blockquote><blockquote><p>8:50 面试者实现了提示。</p></blockquote><p>这些记录是面试官日后向公司提交关于你的报告的时候所需要参考的。因为人的记忆力是有限的,所以需要记录。</p><p>此外,面试官一般还会对你在不同的维度进行打分。例如代码、解决问题、沟通能力等方面。</p><p>解决问题包括你对这个问题的理解、分析和表达。你是否真正理解了题目?你能否分析不同的解决方案之间的优劣和时空复杂度?你能否清晰的讲述自己的想法?</p><p>代码包括代码质量,是否有bug,变量命名和代码风格等。</p><p>沟通能力包括你能否把自己的思路讲清楚。能否逻辑清晰地回答面试官的问题?</p><p>总而言之可以分为下面几点:</p><ol><li>通过例子和反问来理解题目。 例如涉及图的题,就可以反问面试官,这是一个联通图吗?</li><li>描述自己的思路</li><li>把思路变成数据结构和算法</li><li>描述自己的代码逻辑</li><li>思考是否覆盖所有的corner case</li><li>解法不是最优解的时候,发现哪一部分重复计算了or发现了新的思路,从而进行优化。</li></ol><p>最重要的点是让面试官觉得<strong>他的思路和你是同步的</strong>。</p><h3 id=&#34;自我介绍&#34;>自我介绍</h3><p>不要太长。简要说明自己的身份和优点就可以了。</p><h3 id=&#34;resume-deep-dive&#34;>Resume Deep Dive</h3><p>面试官会对着你的简历询问相关的问题。例如你用C++写了一个项目,他可能会问你在这个过程中用到了C++的哪些特性。或者针对你的实习经历,他会问你能再描述一下你做了什么吗?你当时遇到的困难是什么?</p><p>这些只要自己对着简历,把自己想象成面试官,然后自己问自己一些问题进行准备就可以了。</p><h3 id=&#34;coding&#34;>Coding</h3><p>写代码部分。推荐的网站是Leetcode.</p><p><a href=&#34;https://www.techinterviewhandbook.org/grind75&#34;>https://www.techinterviewhandbook.org/grind75</a> 这个网站上面对Leetcode的题目做了分类精选。非常推荐。如果时间紧,就先刷这个上面的题目。</p><p>做题重要的是学习思想,而不是死记硬背。算法题是背不完的。</p><h3 id=&#34;system-design&#34;>System Design</h3><p>这个属于北美的特殊要求。国内的同学可以跳过这一段。</p><p>看书,DDIA。解决所有问题。</p><h3 id=&#34;八股文&#34;>八股文</h3><p>这个国内比较多,北美偶尔也有考的。</p><p>推荐的方法是首先分类。计算机网络,操作系统,编译原理,数据库。然后自己针对薄弱的地方学习。时间够的话直接在B站看南京大学或者MIT的课程,把整门课都复习一遍。这是正道。时间不够的话看这个网站,<a href=&#34;https://www.xiaolincoding.com/&#34;>https://www.xiaolincoding.com/</a>.</p><h3 id=&#34;bq&#34;>BQ</h3><p>北美向</p><p><a href=&#34;https://sugarac.gitbooks.io/high-frequency-interview-questions-and-answers/content/bq-by-xiao-tu-dao.html&#34;>https://sugarac.gitbooks.io/high-frequency-interview-questions-and-answers/content/bq-by-xiao-tu-dao.html</a></p><p><a href=&#34;https://www.youtube.com/c/DanCroitor/videos&#34;>https://www.youtube.com/c/DanCroitor/videos</a></p><h3 id=&#34;反问&#34;>反问</h3><p>反问环节一般出现在面试最后,面试官会说,我的问题问完了,你有什么要问我的吗?这是一个常规流程。</p><h3 id=&#34;反问适用的常见问题&#34;>反问适用的常见问题</h3><p>以下问题有的只能用来询问组长(team leader),不要用他们去询问做IC的面试官。例如您对我未来2年的期望是什么?这应该很好区分。</p><p>下面是总结的常见反问:</p><ol><li>你的/我入职后的 每天的工作流程是什么?</li></ol><p>可以了解对方和自己的工作流程。常规反问。</p><ol start=&#34;2&#34;><li>我该在未来几个月(入职前),继续学习哪些知识来准备这份工作?</li></ol><p>表示自己对工作的重视。但是不要把语气说成自己目前还不能胜任这份工作的样子。</p><ol start=&#34;3&#34;><li>团队的氛围怎么样?有没有什么活动?</li><li>团队内的人数如何?有几个人?分别角色是什么?</li></ol><p>摸清团队情况,有助于了解这份工作的前景。例如这是一个新创建的团队,就要考虑自己未来的职业规划是否打算加入一个新创建的团队。如果是一个人很多的大组,那可能竞争比较激烈。<strong>要记住你加入的是一个团队,而不是一个公司。</strong> 你的日常工作、心情、升职等机会都是和团队直接相关的。学生思维常常会只看公司,而忽略团队。</p><ol start=&#34;5&#34;><li>我们团队未来1-2年的规划是什么?</li><li>如果我加入团队,那么我的职责会是什么?你期待我立刻产出什么内容?你期待我在未来1-2年产出什么内容?</li></ol><p>了解面试官对你的期望,和你自己的职业规划是否相符合。例如你没打算做前端,最后却给你分配到做前端去了。也可以了解老板对你未来的发展是不是有一些想法,有的话说明对你还是比较重视的。</p><ol start=&#34;7&#34;><li>我的哪一段经历最让你感兴趣?</li><li>你怎样评价一个项目是否成功?</li></ol><p>可以看一下老板属于哪种类型。更看重速度还是质量?喜欢创新一些还是稳定?可以看一下自己的工作风格和老板是不是一致。</p><ol start=&#34;9&#34;><li><p>你如何评价一个工程师的表现是否优秀?你最在乎哪一点?</p></li><li><p>你觉得我哪些地方可以提高?</p></li></ol><p>可以从回答中了解自己的面试结果。但是问这个要慎重,有的面试官不喜欢别人问这个问题。(他也知道回答这个约等于提前告诉你面试结果)</p><ol start=&#34;11&#34;><li>你对于像我这样的新手程序员有什么人生经验可以传授的?</li><li>你怎么样处理和其他人的冲突?</li><li>你每天工作中最大的挑战是什么?</li></ol><h2 id=&#34;hr面&#34;>HR面</h2><p>HR面常见的出现时间是投完简历之后的第一轮面试,或者技术面全部通过之后的最后一轮面试。这点不同公司差别很大。阿里的HR权力较大,一般HR面在最后需要认真准备。有的公司技术面全通过之后的HR面基本就是走个过场了。有的公司第一轮就是HR面,这属于一种初步筛选。</p><h3 id=&#34;常见问题&#34;>常见问题</h3><ol><li>你为什么对本公司的这个职位感兴趣?</li><li>你有没有做过什么项目?</li><li>你在过去的项目中经历过最难的事情是什么?你是怎么克服的?有什么收获?</li><li>你最满意的项目是什么?</li><li>你做过的失败的项目里面印象深刻的是哪一个?</li><li>你对加班的看法?</li><li>你最大的缺点是什么?</li><li>你最大的优点是什么?</li><li>你对薪资的要求?</li><li>你在未来2年/5年的职业规划是什么?</li><li>在完成某项工作时,你认为领导要求的方式不是最好的,自己还有更好的方法,你应该怎么做?</li><li>你对跳槽的看法?</li></ol><h3 id=&#34;回答策略&#34;>回答策略</h3><p>STAR方法:</p><p>STAR原则是结构化面试当中非常重要的一个理论:</p><p>S—Situation:事情是在什么情况下发生;</p><p>T—Task:你是如何明确你的任务的;</p><p>A—Action:针对这样的情况分析,你采用了什么行动方式;</p><p>R—Result:结果怎样,在这样的情况下你学习到了什么。</p><p>面试官会按照STAR方法来问你,你也按照STAR方法来回答就好了。</p><h2 id=&#34;写在后面&#34;>写在后面</h2><blockquote><p>天生我材必有用,千金散尽还复来。</p></blockquote><p>相信自己。</p>

2024/3/8
阅读更多

UCSD CSE 230 Midterm Review Note

<h2 id=&#34;01-lambda&#34;>01-lambda</h2><p>Programs are expressions e (also called λ-terms) of one of three kinds:</p><ul><li>Variable<ul><li>x, y, z</li></ul></li><li>Abstraction (aka nameless function definition)<ul><li>(\x -&gt; e)</li><li>x is the formal parameter, e is the body</li><li>“for any x compute e”</li></ul></li><li>Application (aka function call)<ul><li>(e1 e2)</li><li>e1 is the function, e2 is the argument</li><li>in your favorite language: e1(e2)</li></ul></li></ul><hr><p>Execute = rewrite step-by-step</p><ul><li>Following simple rules</li><li>until no more rules apply</li></ul><hr><p>An variable x is free in e if there exists a free occurrence of x in e.</p><p>If e has no free variables it is said to be <strong>closed</strong>.</p><p>Closed expressions are also called <strong>combinators</strong></p><p>What is the shortest closed expression? \x-&gt;x</p><hr><p>Rewrite Rules of Lambda Calculus</p><p>β-step (aka function call)α-step (aka renaming formals)</p><hr><p>Semantics: RedexA redex is a term of the form</p><p>(\x -&gt; e1) e2</p><p>A function (\x -&gt; e1)</p><ul><li>x is the parameter</li><li>e1 is the returned expressionApplied to an argument e2</li><li>e2 is the argument</li></ul><hr><p>Semantics: β-Reduction</p><p>A redex b-steps to another term …</p><p>(\x -&gt; e1) e2 =b&gt; e1[x := e2]</p><p>where e1[x := e2] means</p><p><strong>“e1 with all free occurrences of x replaced with e2” and as long as no free variables of e2 get captured</strong></p><p>Computation by search-and-replace:</p><ul><li><p>If you see an abstraction applied to an argument, take the body of the abstraction and replace all free occurrences of the formal by that argument</p></li><li><p>We say that (\x -&gt; e1) e2 β-steps to e1[x := e2]</p></li></ul><hr><p>Semantics: α-Renaming</p><p>\x -&gt; e =a&gt; \y -&gt; e[x := y]where not (y in FV(e))</p><ul><li>We rename a formal parameter x to y</li><li>By replace all occurrences of x in the body with y</li><li>We say that \x -&gt; e α-steps to \y -&gt; e[x := y]</li></ul><hr><p>Recall redex is a λ-term of the form</p><p>(\x -&gt; e1) e2</p><p>A λ-term is in <strong>normal form</strong> if it contains no redexes.</p><hr><p>Semantics: EvaluationA λ-term e evaluates to e&rsquo; if</p><ol><li><p>There is a sequence of stepse =?&gt; e_1 =?&gt; &hellip; =?&gt; e_N =?&gt; e&#39;where each =?&gt; is either =a&gt; or =b&gt; and N &gt;= 0</p></li><li><p>e&rsquo; is in <strong>normal form</strong></p></li></ol><p><strong>So the result of a evaluation must be a normal form!!!</strong></p><hr><p><strong>ELSA</strong></p><p>Named λ-terms:</p><p>let ID = \x -&gt; x &ndash; abbreviation for \x -&gt; x</p><p>To substitute name with its definition, use a =d&gt; step:</p><p>ID apple=d&gt; (\x -&gt; x) apple &ndash; expand definition=b&gt; apple &ndash; beta-reduce</p><p>Evaluation:</p><ul><li>e1 =*&gt; e2: e1 reduces to e2 in 0 or more steps<ul><li>where each step is =a&gt;, =b&gt;, or =d&gt;</li></ul></li><li>e1 =~&gt; e2: e1 evaluates to e2 and e2 is in normal form</li></ul>

2023/11/1
阅读更多

Infra知识点

<p>本文为摘录网络内容. 由于时间原因未记录原文地址,谷歌即可找到原文。</p><h2 id=&#34;简述数据库三大范式&#34;>简述数据库三大范式</h2><p>第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。</p><p>数据库第二范式:关系模式必须满足第一范式,并且所有非主属性都完全依赖于主码。注意,符合第二范式的关系模型可能还存在数据冗余、更新异常等问题。关系模型(学号,姓名,专业编号,专业名称)中,学号-&gt;姓名,而专业编号-&gt;专业名称,不满足数据库第二范式</p><p>数据库第三范式:关系模型满足第二范式,所有非主属性对任何候选关键字都不存在传递依赖。即每个属性都跟主键有直接关系而不是间接关系。接着以学生表举例,对于关系模型(学号,姓名,年龄,性别,所在院校,院校地址,院校电话)院校地址,院校电话和学号不存在直接关系,因此不满足第三范式。</p><h2 id=&#34;简述mysql的架构&#34;>简述MySQL的架构</h2><p>MySQL可以分为应用层,逻辑层,数据库引擎层,物理层。</p><p>应用层:负责和客户端,响应客户端请求,建立连接,返回数据。</p><p>逻辑层:包括SQK接口,解析器,优化器,Cache与buffer。</p><p>数据库引擎层:有常见的MyISAM,InnoDB等等。</p><p>物理层:负责文件存储,日志等等。</p><h2 id=&#34;简述执行sql语言的过程&#34;>简述执行SQL语言的过程</h2><p>客户端首先通过连接器进行身份认证和权限相关如果是执行查询语句的时候,会先查询缓存,但MySQL 8.0 版本后该步骤移除。没有命中缓存的话,SQL 语句就会经过解析器,分析语句,包括语法检查等等。通过优化器,将用户的SQL语句按照 MySQL 认为最优的方案去执行。执行语句,并从存储引擎返回数据。</p><h2 id=&#34;简述mysql的共享锁排它锁&#34;>简述MySQL的共享锁排它锁</h2><p>共享锁也称为读锁,相互不阻塞,多个客户在同一时刻可以同时读取同一个资源而不相互干扰。排他锁也称为写锁,会阻塞其他的写锁和读锁,确保在给定时间内只有一个用户能执行写入并防止其他用户读取正在写入的同一资源。</p><h2 id=&#34;简述mysql中的按粒度的锁分类&#34;>简述MySQL中的按粒度的锁分类</h2><p>表级锁: 对当前操作的整张表加锁,实现简单,加锁快,但并发能力低。</p><p>行锁: 锁住某一行,如果表存在索引,那么记录锁是锁在索引上的,如果表没有索引,那么 InnoDB 会创建一个隐藏的聚簇索引加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。</p><p>Gap 锁:也称为间隙锁: 锁定一个范围但不包括记录本身。其目的是为了防止同一事物的两次当前读出现幻读的情况。</p><p>Next-key Lock: 行锁+gap锁。</p><h2 id=&#34;如何解决数据库死锁&#34;>如何解决数据库死锁</h2><p>预先检测到死锁的循环依赖,并立即返回一个错误。当查询的时间达到锁等待超时的设定后放弃锁请求。</p><h2 id=&#34;简述乐观锁和悲观锁&#34;>简述乐观锁和悲观锁</h2><p>乐观锁:对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁,只有到数据提交的时候才通过一种机制来验证数据是否存在冲突。</p><p>悲观锁:对于数据冲突保持一种悲观态度,在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作,一般数据库本身锁的机制都是基于悲观锁的机制实现的。</p><h2 id=&#34;简述innodb存储引擎&#34;>简述InnoDB存储引擎</h2><p>InnoDB 是 MySQL 的默认事务型引擎,支持事务,表是基于聚簇索引建立的。支持表级锁和行级锁,支持外键,适合数据增删改查都频繁的情况。</p><p>InnoDB 采用 MVCC 来支持高并发,并且实现了四个标准的隔离级别。其默认级别是 REPEATABLE READ,并通过间隙锁策略防止幻读,间隙锁使 InnoDB 不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定防止幻行的插入。</p><h2 id=&#34;索引是什么&#34;>索引是什么?</h2><p>索引是存储引擎中用于快速找到记录的一种数据结构。在关系型数据库中,索引具体是一种对数据库中一列或多列的值进行排序的存储结构。</p><h2 id=&#34;为什么引入索引&#34;>为什么引入索引?</h2><p>为了提高数据查询的效率。索引对数据库查询良好的性能非常关键,当表中数据量越来越大,索引对性能的影响越重要。</p><h2 id=&#34;mysql有哪些常见索引类型&#34;>Mysql有哪些常见索引类型?</h2><p>数据结构角度B-Tree索引 哈希索引 R-Tree索引 全文索引</p><h2 id=&#34;物理存储角度&#34;>物理存储角度</h2><p>主键索引(聚簇索引):叶子节点存的是整行的数据 非主键索引(二级索引):叶子节点存的主键的值</p><h2 id=&#34;简述聚集索引和稀疏索引&#34;>简述聚集索引和稀疏索引</h2><p>聚集索引按每张表的主键构建一棵B+树,数据库中的每个搜索键值都有一个索引记录,每个数据页通过双向链表连接。表数据访问更快,但表更新代价高。</p><h2 id=&#34;稀疏索引不会为每个搜索关键字创建索引记录搜索过程需要我们首先按索引记录进行操作并按顺序搜索直到找到所需的数据为止&#34;>稀疏索引不会为每个搜索关键字创建索引记录。搜索过程需要,我们首先按索引记录进行操作,并按顺序搜索,直到找到所需的数据为止。</h2><h2 id=&#34;简述辅助索引与回表查询&#34;>简述辅助索引与回表查询</h2><p>辅助索引是非聚集索引,叶子节点不包含记录的全部数据,包含了一个书签用来告诉InnoDB哪里可以找到与索引相对应的行数据。</p><p>通过辅助索引查询,先通过书签查到聚集索引,再根据聚集索引查对应的值,需要两次,也称为回表查询。</p><h2 id=&#34;简述联合索引和最左匹配原则&#34;>简述联合索引和最左匹配原则</h2><p>联合索引是指对表上的多个列的关键词进行索引。</p><p>对于联合索引的查询,如果精确匹配联合索引的左边连续一列或者多列,则mysql会一直向右匹配直到遇到范围查询(&gt;,&lt;,between,like)就停止匹配。Mysql会对第一个索引字段数据进行排序,在第一个字段基础上,再对第二个字段排序。</p><h2 id=&#34;简述覆盖索引&#34;>简述覆盖索引</h2><p>覆盖索引指一个索引包含或覆盖了所有需要查询的字段的值,不需要回表查询,即索引本身存了对应的值。</p><h2 id=&#34;为什么数据库不用红黑树用b树&#34;>为什么数据库不用红黑树用B+树</h2><p>红黑树的出度为 2,而 B Tree 的出度一般都非常大。红黑树的树高 h 很明显比 B Tree 大非常多,IO次数很多,导致会比较慢,因此检索的次数也就更多。</p><p>B+Tree 相比于 B-Tree 更适合外存索引,拥有更大的出度,IO次数较少,检索效率会更高。</p><h2 id=&#34;基于主键索引的查询和非主键索引的查询有什么区别&#34;>基于主键索引的查询和非主键索引的查询有什么区别?</h2><p>对于select * from 主键=XX,基于主键的普通查询仅查找主键这棵树,对于select * from 非主键=XX,基于非主键的查询有可能存在回表过程(回到主键索引树搜索的过程称为回表),因为非主键索引叶子节点仅存主键值,无整行全部信息。</p><h2 id=&#34;非主键索引的查询一定会回表吗&#34;>非主键索引的查询一定会回表吗?</h2><p>不一定,当查询语句的要求字段全部命中索引,不用回表查询。如select 主键 from 非主键=XX,此时非主键索引叶子节点即可拿到主键信息,不用回表。</p><h2 id=&#34;-------&#34;>&mdash;&mdash;-</h2><h2 id=&#34;火焰图&#34;>火焰图</h2><table><thead><tr><th>类型</th><th>横轴</th><th>纵轴</th><th>解决方法</th><th>采样方式</th></tr></thead><tbody><tr><td>ON-CPU</td><td>cpu占用的时间</td><td>调用栈</td><td>找出占用高的函数,分析代码热路径</td><td>固定频率采样cpu调用栈</td></tr><tr><td>OFF-CPU</td><td>阻塞时间</td><td>调用栈</td><td>IO,网络等阻塞;锁、死锁导致的</td><td>固定频率采样阻塞事件调用栈</td></tr><tr><td>内存</td><td>内存申请/释放函数调用次数</td><td>调用栈</td><td>内存泄漏问题;内存占用高的对象/申请内存多的函数;</td><td>有四种方式:跟踪malloc/free;跟踪brk;跟踪mmap;跟踪页错误</td></tr></tbody></table><p>如果是 CPU 则使用 On-CPU 火焰图,如果是 IO 或锁则使用 Off-CPU 火焰图.如果无法确定, 那么可以通过压测工具来确认:通过压测工具看看能否让 CPU 使用率趋于饱和, 如果能那么使用 On-CPU 火焰图如果不管怎么压, CPU 使用率始终上不来, 那么多半说明程序被 IO 或锁卡住了, 此时适合使用 Off-CPU 火焰图.如果还是确认不了, 那么不妨 On-CPU 火焰图和 Off-CPU 火焰图都搞搞, 正常情况下它们的差异会比较大, 如果两张火焰图长得差不多, 那么通常认为 CPU 被其它进程抢占了</p><p>火焰图分析技巧</p><p>纵轴代表调用栈的深度(栈桢数),用于表示函数间调用关系:下面的函数是上面函数的父函数。横轴代表调用频次,一个格子的宽度越大,越说明其可能是瓶颈原因。不同类型火焰图适合优化的场景不同,比如 on-cpu 火焰图适合分析 cpu 占用高的问题函数,off-cpu 火焰图适合解决阻塞和锁抢占问题。无意义的事情:横向先后顺序是为了聚合,跟函数间依赖或调用关系无关;火焰图各种颜色是为方便区分,本身不具有特殊含义多练习:进行性能优化有意识的使用火焰图的方式进行性能调优(如果时间充裕)</p><p>生成和创建火焰图需要如下几个步骤</p><table><thead><tr><th>流程</th><th>描述</th><th>脚本</th></tr></thead><tbody><tr><td>捕获堆栈</td><td>使用 perf/systemtap/dtrace 等工具抓取程序的运行堆栈</td><td>perf/systemtap/dtrace</td></tr><tr><td>折叠堆栈</td><td>trace 工具抓取的系统和程序运行每一时刻的堆栈信息, 需要对他们进行分析组合, 将重复的堆栈累计在一起, 从而体现出负载和关键路径</td><td>FlameGraph 中的 stackcollapse 程序</td></tr><tr><td>生成火焰图</td><td>分析 stackcollapse 输出的堆栈信息生成火焰图</td><td>flamegraph.pl</td></tr></tbody></table><p>perf 命令(performance 的缩写)讲起, 它是 Linux 系统原生提供的性能分析工具, 会返回 CPU 正在执行的函数名以及调用栈(stack)</p><p>不同的观测对象对应着不同的 profiler,仅就 CPU 而言,profiler 也数不胜数。</p><p>按照观测范围来分类,CPU 上的 profiler 大致可以分为两大类:进程级(per-process,某些地方也叫做应用级)和系统级(system wide),其中:</p><p>进程级只观测一个进程或线程上发生的事情系统级不局限在某一个进程上,观测对象为整个系统上运行的所有程序需要注意的是,某些工具既能观测整个系统也支持观测单个进程,比如 perf,因此这样的工具同时属于两个类别。</p><p>按照观测方法来分类,大致可以分为 event based 和 sampling based 两大类。其中:</p><p>event based:在一个指定的 event 集合上进行,比如进入或离开某个/某些特定的函数、分配内存、异常的抛出等事件。event based profiler 在一些文章中也被称为 tracing profiler 或 tracersampling based:以某一个指定的频率对运行的程序的某些信息进行采样,通常情况下采样的对象是程序的调用栈</p><h2 id=&#34;浅谈性能优化的十种手段&#34;>浅谈性能优化的十种手段</h2><p>性能优化通常是“时间”与“空间”的互换与取舍。本文分两个部分,在上篇,讲解六种通用的“时间”与“空间”互换取舍的手段:</p><p>索引术压缩术缓存术预取术削峰填谷术批量处理术</p><p>在下篇,介绍四种进阶性的内容,大多与提升并行能力有关:</p><p>八门遁甲 —— 榨干计算资源影分身术 —— 水平扩容奥义 —— 分片术秘术 —— 无锁术</p><h3 id=&#34;索引&#34;>索引</h3><p>索引的原理是拿额外的存储空间换取查询时间,增加了写入数据的开销,但使读取数据的时间复杂度一般从O(n)降低到O(logn)甚至O(1)。索引不仅在数据库中广泛使用,前后端的开发中也在不知不觉运用。</p><p>在数据集比较大时,不用索引就像从一本没有目录而且内容乱序的新华字典查一个字,得一页一页全翻一遍才能找到;用索引之后,就像用拼音先在目录中先找到要查到字在哪一页,直接翻过去就行了。书籍的目录是典型的树状结构,那么软件世界常见的索引有哪些数据结构,分别在什么场景使用呢?</p><p>哈希表(Hash Table):哈希表的原理可以类比银行办业务取号,给每个人一个号(计算出的Hash值),叫某个号直接对应了某个人,索引效率是最高的O(1),消耗的存储空间也相对更大。K-V存储组件以及各种编程语言提供的Map/Dict等数据结构,多数底层实现是用的哈希表。</p><p>二叉搜索树(Binary Search Tree):有序存储的二叉树结构,在编程语言中广泛使用的红黑树属于二叉搜索树,确切的说是“不完全平衡的”二叉搜索树。从C++、Java的TreeSet、TreeMap,到Linux的CPU调度,都能看到红黑树的影子。Java的HashMap在发现某个Hash槽的链表长度大于8时也会将链表升级为红黑树,而相比于红黑树“更加平衡”的AVL树反而实际用的更少。平衡多路搜索树(B-Tree):这里的B指的是Balance而不是Binary,二叉树在大量数据场景会导致查找深度很深,解决办法就是变成多叉树,MongoDB的索引用的就是B-Tree。</p><p>叶节点相连的平衡多路搜索树(B+ Tree):B+Tree是B-Tree的变体,只有叶子节点存数据,叶子与相邻叶子相连,MySQL的索引用的就是B+树,Linux的一些文件系统也使用的B+树索引inode。其实B+树还有一种在枝桠上再加链表的变体:B*树,暂时没想到实际应用。</p><p>日志结构合并树(LSM Tree):Log Structured Merge Tree,简单理解就是像日志一样顺序写下去,多层多块的结构,上层写满压缩合并到下层。LSM Tree其实本身是为了优化写性能牺牲读性能的数据结构,并不能算是索引,但在大数据存储和一些NoSQL数据库中用的很广泛,因此这里也列进去了。字典树(Trie Tree):又叫前缀树,从树根串到树叶就是数据本身,因此树根到枝桠就是前缀,枝桠下面的所有数据都是匹配该前缀的。这种结构能非常方便的做前缀查找或词频统计,典型的应用有:自动补全、URL路由。其变体基数树(Radix Tree)在Nginx的Geo模块处理子网掩码前缀用了;Redis的Stream、Cluster等功能的实现也用到了基数树(Redis中叫Rax)。</p><p>跳表(Skip List):是一种多层结构的有序链表,插入一个值时有一定概率“晋升”到上层形成间接的索引。跳表更适合大量并发写的场景,可以认为是随机平衡的二叉搜索树,不存在红黑树的再平衡问题,Redis强大的ZSet底层数据结构就是哈希加跳表。</p><p>倒排索引(Inverted index):这样翻译不太直观,可以叫“关键词索引”,比如书籍末尾列出的术语表就是一种倒排索引,标识出了每个术语出现在哪些页,这样我们要查某个术语在哪用的,从术语表一查,翻到所在的页数即可。倒排索引在全文搜索引擎中经常用到,比如ElasticSearch一个核心的机制就是倒排索引;Prometheus的时序数据库按标签查询时,也是在用倒排索引。</p><p>数据库主键之争:自增长 vs UUID。主键是很多数据库非常重要的索引,尤其是MySQL这样的RDBMS会经常面临这个难题:是用自增长的ID还是随机的UUID做主键?</p><p>自增长ID的性能最高,但不好做分库分表后的全局唯一ID,自增长的规律可能泄露业务信息;而UUID不具有可读性且太占存储空间。争执的结果就是找一个兼具二者的优点的折衷方案:用雪花算法生成分布式环境全局唯一的ID作为业务表主键,性能尚可、不那么占存储、又能保证全局单调递增,但雪花算法又引入了额外的复杂性,再次体现了取舍之道。</p><p>再回到数据库中的索引,建索引要注意哪些点呢?</p><p>定义好主键并尽量使用主键,多数数据库中,主键是效率最高的聚簇索引;在Where或Group By、Order By、Join On条件中用到的字段也要按需建索引或联合索引,MySQL中搭配explain命令可以查询DML是否利用了索引;类似枚举值这样重复度太高的字段不适合建索引(如果有位图索引可以建),频繁更新的列不太适合建索引;单列索引可以根据实际查询的字段升级为联合索引,通过部分冗余达到索引覆盖,以避免回表的开销;尽量减少索引冗余,比如建A、B、C三个字段的联合索引,Where条件查询A、A and B、A and B and C 都可以利用该联合索引,就无需再给A单独建索引了;根据具体的数据库特有的索引特性选择适合的方案,比如像MongoDB可以建自动删除数据的TTL索引、不索引空值的稀疏索引、地理位置信息的Geo索引等等</p><h3 id=&#34;缓存&#34;>缓存</h3><p>缓存优化性能的原理和索引一样,是拿额外的存储空间换取查询时间,理论依据是程序的局部性原理。缓存无处不在,设想一下我们在浏览器打开这篇文章,会有多少层缓存呢?</p><p>首先解析DNS时,浏览器一层DNS缓存、操作系统一层DNS缓存、DNS服务器链上层层缓存;发送一个GET请求这篇文章,服务端很可能早已将其缓存在KV存储组件中了;即使没有击中缓存,数据库服务器内存中也缓存了最近查询的数据;即使没有击中数据库服务器的缓存,数据库从索引文件中读取,操作系统已经把热点文件的内容放置在Page Cache中了;即使没有击中操作系统的文件缓存,直接读取文件,大部分固态硬盘或者磁盘本身也自带高速缓存;数据取到之后服务器用模板引擎渲染出HTML,模板引擎早已解析好缓存在服务端内存中了;历经数十毫秒之后,终于服务器返回了一个渲染后的HTML,浏览器端解析DOM树,发送请求来加载JS/CSS等静态资源;需要加载的静态资源可能因Cache-Control在浏览器本地磁盘和内存中已经缓存了;即使本地缓存到期,也可能因Etag没变服务器告诉浏览器304 Not Modified继续缓存;即使Etag变了,静态资源服务器也因其他用户访问过早已将文件缓存在内存中了;加载的JS文件会丢到JS引擎执行,其中可能涉及的种种缓存就不再展开了;整个过程中链条上涉及的所有的计算机和网络设备,执行的热点代码和数据很可能会载入CPU的多级高速缓存。</p><p>这里列举的仅仅是一部分常见的缓存,就有多种多样的形式:从廉价的磁盘到昂贵的CPU高速缓存,最终目的都是用额外的空间来换取宝贵的时间。</p><p>缓存是“银弹”吗?</p><p>不,Phil Karlton 曾说过:</p><p>计算机科学中只有两件困难的事情:缓存失效和命名规范。There are only two hard things in Computer Science: cache invalidation and naming things.缓存的使用除了带来额外的复杂度以外,还面临如何处理缓存失效的问题。</p><p>多线程并发编程需要用各种手段(比如Java中的synchronized volatile)防止并发更新数据,一部分原因就是防止线程本地缓存的不一致;缓存失效衍生的问题还有:缓存穿透、缓存击穿、缓存雪崩。解决黑客用不存在的Key来穿透攻击的问题,需要用空值缓存或布隆过滤器;解决单个缓存过期后,瞬间被大量恶意查询击穿的问题需要做查询互斥;解决某个时间点大量缓存同时过期的雪崩问题,需要添加随机TTL等等;热点数据如果是多级缓存,在发生修改时需要清除或修改各级缓存,这些操作往往不是原子操作,又会涉及各种不一致问题。</p><p>除了通常意义上的缓存外,对象重用的池化技术,也可以看作是一种缓存的变体。常见的诸如JVM,V8这类运行时的常量池、数据库连接池、HTTP连接池、线程池、Golang的sync.Pool对象池等等。在需要某个资源时从现有的池子里直接拿一个,稍作修改或直接用于另外的用途,池化重用也是性能优化常见手段。</p><h3 id=&#34;名词布隆过滤器&#34;>名词:布隆过滤器</h3><p>TODO</p><h3 id=&#34;压缩&#34;>压缩</h3><p>说完了两个“空间换时间”的,我们再看一个“时间换空间”的办法——压缩。压缩的原理是消耗计算的时间,换一种更紧凑的编码方式来表示数据。</p><p>为什么要拿时间换空间?时间不是最宝贵的资源吗?</p><p>举一个视频网站的例子,如果不对视频做任何压缩编码,因为带宽有限,巨大的数据量在网络传输的耗时会比编码压缩的耗时多得多。对数据的压缩虽然消耗了时间去换取更小的空间存储,但更小的存储空间会在另一个维度带来更大的时间收益。</p><p>这个例子本质上是:“操作系统内核与网络设备处理负担 vs 压缩解压的计算负担”的权衡和取舍。</p><p>我们在通常使用的是无损压缩,比如下面这些场景:</p><p>HTTP协议中Accept-Encoding添加Gzip/deflate,服务端对接受压缩的文本(JS/CSS/HTML)请求做压缩,大部分图片格式本身已经是压缩的无需压缩;HTTP2协议的头部HPACK压缩;JS/CSS文件的混淆和压缩(Uglify/Minify);一些RPC协议和消息队列传输的消息中,采用二进制编码和压缩(Gzip、Snappy、LZ4等等);缓存服务存体积过大的数据,通常也会事先压缩一下再存;大文件的存储,或者不常用的历史数据存储,采用更高压缩比的算法存储;JVM的对象指针压缩,JVM在32G以下的堆内存情况下默认开启“UseCompressedOops”,用4个byte就可以表示一个对象的指针,这也是JVM尽量不要把堆内存设置到32G以上的原因;MongoDB的二进制存储的BSON相对于纯文本的JSON也是一种压缩,或者说更紧凑的编码。但更紧凑的编码也意味着更差的可读性,这一点也是需要取舍的。纯文本的JSON比二进制编码要更占存储空间,却是REST API的主流,因为数据交换的场景下可读性是非常重要的。</p><p>信息论告诉我们,无损压缩的极限是信息熵。进一步减小体积只能以损失部分信息为代价,也就是有损压缩。</p><p>那么,有损压缩有哪些应用呢?</p><p>预览和缩略图,低速网络下视频降帧、降清晰度,都是对信息的有损压缩;音视频等多媒体数据的采样和编码大多是有损的,比如MP3是利用傅里叶变换,有损地存储音频文件;jpeg等图片编码也是有损的。虽然有像WAV/PCM这类无损的音频编码方式,但多媒体数据的采样本身就是有损的,相当于只截取了真实世界的极小一部分数据;散列化,比如K-V存储时Key过长,先对Key执行一次“傻”系列(SHA-1、SHA-256)哈希算法变成固定长度的短Key。另外,散列化在文件和数据验证(MD5、CRC、HMAC)场景用的也非常多,无需耗费大量算力对比完整的数据。除了有损/无损压缩,但还有一个办法,就是压缩的极端——从根本上减少数据或彻底删除。</p><p>能减少的就减少:</p><p>JS打包过程“摇树”,去掉没有使用的文件、函数、变量;使用更“简洁”的通信协议,比如开启HTTP/2和高版本的TLS,减少了Round Trip,节省了TCP连接,自带大量性能优化;减少不必要的信息传输,比如Cookie的数量,去掉不必要的HTTP请求头;更新采用增量更新,比如HTTP的PATCH,只传输变化的属性而不是整条数据;缩短单行日志的长度、缩短URL、在具有可读性情况下用短的属性名等等;使用位图和位操作,用风骚的位操作最小化存取的数据。典型的例子有:用Redis的位图来记录统计海量用户登录状态;布隆过滤器用位图排除不可能存在的数据;大量开关型的设置的存储等等。</p><p>能删除的就删除:</p><p>删掉不用的数据;删掉不用的索引;删掉不该打的日志;删掉不必要的通信,不去发不必要的HTTP、RPC请求或调用,轮询改发布订阅;终极方案:砍掉整个功能。</p><blockquote><p>No code is the best way to write secure and reliable applications. Write nothing; deploy nowhere. —— Kelsey Hightower</p></blockquote><h3 id=&#34;预取&#34;>预取</h3><p>预取通常搭配缓存一起用,其原理是在缓存基础上更进一步,再加上一次“时间换时间”,也就是:用事先预取的耗时,换取第一次加载的时间。当可以猜测出以后的某个时间很有可能会用到某种数据时,把数据预先取到需要用的地方,能大幅度提升用户体验或服务端响应速度。</p><p>预取作为提升性能的手段会在哪些场景用呢?</p><p>视频或直播类网站,在播放前先缓冲一小段时间,就是预取数据。有的在播放时不仅预取这一条数据,甚至还会预测下一个要看的其他内容,提前把数据取到本地;HTTP/2 Server Push,在浏览器请求某个资源时,服务器顺带把其他相关的资源一起推回去,HTML/JS/CSS几乎同时到达浏览器端,相当于浏览器被动预取了资源;一些客户端软件会用常驻进程的形式,提前预取数据或执行一些代码,这样可以极大提高第一次使用的打开速度;服务端同样也会用一些预热机制,一方面热点数据预取到内存提前形成多级缓存;另一方面也是对运行环境的预热,载入CPU高速缓存、热点函数JIT编译成机器码等等;热点资源提前预分配到各个服务实例,比如:秒杀、售票的库存性质的数据;分布式唯一ID等等。天上不会掉馅饼,预取也是有副作用的。正如烤箱预热需要消耗时间和额外的电费,在软件代码中做预取/预热的副作用通常是启动慢一些、占用一些闲时的计算资源、可能取到的不一定是后面需要的。</p><h3 id=&#34;削峰填谷术&#34;>削峰填谷术</h3><p>削峰填谷的原理也是“时间换时间”,谷时换峰时。削峰填谷与预取是反过来的:预取是事先花时间做,削峰填谷是事后花时间做。就像三峡大坝可以抗住短期巨量洪水,事后雨停再慢慢开闸防水。软件世界的“削峰填谷”是类似的,只是不用三峡大坝实现,而是用消息队列、异步化等方式。</p><p>常见的有这几类问题,我们分别来看每种对应的解决方案:</p><p>针对前端、客户端的启动优化或首屏优化:代码和数据等资源的延时加载、分批加载、后台异步加载、或按需懒加载等等。背压控制——限流、节流、去抖等手段。“一夫当关,万夫莫开”,从入口处削峰,防止一些恶意的重复请求以及请求过于频繁的爬虫,甚至是一些DDoS攻击。简单做法有网关层根据单个IP或用户用漏桶控制请求速率和上限;前端做按钮的节流去抖防止重复点击;网络层开启TCP SYN Cookie防止恶意的SYN洪水攻击等等。彻底杜绝爬虫、黑客手段的恶意洪水攻击是很难的,DDoS这类属于网络安全范畴了。针对正常的业务请求”洪峰“,用消息队列暂存再异步化处理:常见的后端消息队列Kafka、RocketMQ甚至Redis等等都可以做缓冲层,第一层业务处理直接校验后丢到消息队列中,在洪峰过去后慢慢消费消息队列中的消息,执行具体的业务。另外执行过程中的耗时和耗计算资源的操作,也可以继续丢到消息队列或数据库中,等到谷时处理。捋平毛刺:有时候“洪峰”不一定来自外界,如果系统内部大量定时任务在同一时间执行,或与业务高峰期重合,很容易在监控中看到“毛刺”——短时间负载极高。一般解决方案就是错峰执行定时任务,或者分配到其他非核心业务系统中,把“毛刺”摊平。比如很多数据分析型任务都放在业务低谷期去执行,大量定时任务在创建时尽量加一些随机性来分散执行时间。避免错误风暴带来的次生“洪峰”:有时候网络抖动或短暂宕机,业务会出现各种异常或错误。这时处理不好很容易带来次生灾害,比如:很多代码都会做错误重试,不加控制的大量重试,甚至会导致网络抖动恢复后的瞬间,积压的大量请求再次冲垮整个系统;还有一些代码没有做超时、降级等处理,可能导致大量的等待耗尽TCP连接,进而导致整个系统被冲垮。解决之道就是做限定次数、间隔指数级增长的Back-Off重试,设定超时、降级策略。</p><h3 id=&#34;批量处理术&#34;>批量处理术</h3><p>批量处理同样可以看成“时间换时间”,其原理是减少了重复的事情,是一种对执行流程的压缩。以个别批量操作更长的耗时为代价,在整体上换取了更多的时间。</p><p>批量处理的应用也非常广泛,我们还是从前端开始讲:</p><p>打包合并的JS文件、雪碧图等等,都是将一批资源集中到一起,一次性传输;前端使用requestAnimationFrame在UI渲染时批量处理积压的变化,而不是有变化立刻更新,在游戏开发中也有类似的应用;前后端中使用队列暂存临时产生的数据,积压到一定数量再批量处理;在不影响可扩展性情况下,一个接口传输多种需要的数据,减少大量ajax调用(GraphQL在这一点就做到了极致);系统间通信尽量发送整批数据,比如消息队列的发布订阅、存取缓存服务的数据、RPC调用、插入或更新数据库等等,能批量做尽可能批量做,因为这些系统间通信的I/O时间开销已经很昂贵了;数据积压到一定程度再落盘,操作系统本身的写文件就是这么做的,Linux的fwrite只是写入缓冲区暂存,积压到一定程度再fsync刷盘。在应用层,很多高性能的数据库和K-V存储的实现都体现了这一点:一些NoSQL的LSM Tree的第一层就是在内存中先积压到一定大小再往下层合并;Redis的RDB结合AOF的落盘机制;Linux系统调用也提供了批量读写多个缓冲区文件的系统调用 readv/writev 等等;延迟地批量回收资源,比如JVM的Survivor Space的S0和S1区互换、Redis的Key过期的清除策略。</p><p>批量处理如此好用,那么问题来了,每一批放多大最合适呢?</p><p>这个问题其实没有定论,有一些个人经验可以分享。</p><p>前端把所有文件打包成单个JS,大部分时候并不是最优解。Webpack提供了很多分块的机制,CSS和JS分开、JS按业务分更小的Chunk结合懒加载、一些体积大又不用在首屏用的第三方库设置external或单独分块,可能整体性能更高。不一定要一批搞定所有事情,分几个小批次反而用户体验的性能更好。Redis的MGET、MSET来批量存取数据时,每批大小不宜过大,因为Redis主线程只有一个,如果一批太大执行期间会让其他命令无法响应。经验上一批50-100个Key性能是不错的,但最好在真实环境下用真实大小的数据量化度量一下,做Benchmark测试才能确定一批大小的最优值。MySQL、Oracle这类RDBMS,最优的批量Insert的批大小也视数据行的特性而定。我之前在2U8G的Oracle上用一些普遍的业务数据做过测试,批量插入时每批5000-10000条数据性能是最高的,每批过大会导致DML的解析耗时过长,甚至单个SQL语句体积超限,单批太多反而得不偿失。消息队列的发布订阅,每批的消息长度尽量控制在1MB以内,有些云服务商提供的消息队列限制了最大长度,那这个长度可能就是性能拐点,比如AWS的SQS服务对单条消息的限制是256KB。总之,多大一批可以确保单批响应时间不太长的同时让整体性能最高,是需要在实际情况下做基准测试的,不能一概而论。而批量处理的副作用在于:处理逻辑会更加复杂,尤其是一些涉及事务、并发的问题;需要用数组或队列用来存放缓冲一批数据,消耗了额外的存储空间。</p><h3 id=&#34;空间&#34;>空间</h3><p>空间都去哪儿了?在计算机历史上,非易失存储技术的发展速度超过了摩尔定律。除了嵌入式设备、数据库系统等等,现在大部分场景已经不太需要优化持久化存储的空间占用了,这里主要讲的是另一个相对稀缺的存储形式 —— RAM,或者说主存/内存。</p><p>以JVM为例。</p><p>堆里面有很多我们创建的对象(Object)。</p><p>每个Object都有一个包含Mark和类型指针的Header,占12个字节每个成员变量,根据数据类型的不同占不同的字节数,如果是另一个对象,其对象指针占4个字节数组会根据声明的大小,占用N倍于其类型Size的字节数成员变量之间需要对齐到4字节,每个对象之间需要对齐到8字节如果在32G以上内存的机器上,禁用了对象指针压缩,对象指针会变成8字节,包括Header中的Klass指针,这也就不难理解为什么堆内存超过32G,JVM的性能直线下降了。</p><p>举个例子,一个有8个int类型成员的对象,需要占用48个字节(12+32+4),如果有十万个这样的Object,就需要占用4.58MB的内存了。这个数字似乎看起来不大,而实际上一个Java服务的堆内存里面,各种各样的对象占用的内存通常比这个数字多得多,大部分内存耗在char[]这类数组或集合型数据类型上。</p><p>堆内存外面,又是另一个世界了。</p><p>从操作系统进程的角度去看,也有不少耗内存的大户,不管什么Runtime都逃不开这些空间开销:每个线程需要分配MB级别的线程栈,运行的程序和数据会缓存下来,用到的输入输出设备需要缓冲区……</p><p>代码“写出来”的内存占用,仅仅是冰山之上的部分,真正的内存占用比“写出来”的要更多,到处都存在空间利用率的问题。</p><p>比如,即使我们在Java代码中只是写了 response.getWriter().print(&ldquo;OK&rdquo;),给浏览器返回2字节,网络协议栈的层层封装,协议头部不断增加的额外数据,让最终返回给浏览器的字节数远超原始的2字节,像IP协议的报头部就至少有20个字节,而数据链路层的一个以太网帧头部至少有18字节。</p><p>如果传输的数据过大,各层协议还有最大传输单元MTU的限制,IPv4一个报文最大只能有64K比特,超过此值需要分拆发送并在接收端组合,更多额外的报头导致空间利用率降低(IPv6则提供了Jumbogram机制,最大单包4G比特,“浪费”就减少了)。</p><p>这部分的“浪费”有多大呢?下面的链接有个表格,传输1460个字节的载荷,经过有线到无线网络的转换,至少再添120个字节,空间利用率&lt;92.4%。</p><p>这种现象非常普遍,使用抽象层级越高的技术平台,平台提供高级能力的同时,其底层实现的“信息密度”通常越低。像Java的Object Header就是使用JVM的代价,而更进一步使用动态类型语言,要为灵活性付出空间的代价则更大。哈希表的自动扩容,强大的反射能力等等,背后也付出了空间的代价。</p><p>再比如,二进制数据交换协议通常比纯文本协议更加节约空间。但多数厂家我们仍然用JSON、XML等纯文本协议,用信息的冗余来换取可读性。即便是二进制的数据交互格式,也会存在信息冗余,只能通过更好的协议和压缩算法,尽量去逼近压缩的极限 —— 信息熵。</p><h2 id=&#34;profile-guiided-optimization&#34;>Profile Guiided Optimization</h2><h3 id=&#34;错误分支预测优化&#34;>错误分支预测优化</h3><p>下面用简单的一个 if 判断语句来说明为什么减少错误分支预测可以实现正优化。看下面示例代码:</p><p>if condition {// 执行逻辑1} else {// 执行逻辑2}在编译时,由于编译器并不能假设 condition 为 true 或者 false 的概率,所以按照定义的顺序:如果 condition 为 true 执行逻辑 1,如果条件不满足跳跃至 else 执行逻辑 2。在 CPU 的实际执行中,由于指令顺序执行以及 pipeline 预执行等机制,因此,会优先执行当前指令紧接着的下一条指令。上面的指令如果 condition 为 true 那么整个流水线便一气呵成,没有跳转的开销。相反的,如果 condition 为 false,那么 pipeline 中先前预执行的逻辑 1 计算则会被作废,转而需要从 else 处的重新加载指令,并重新执行逻辑 2,这些消耗会显著降低指令的执行性能。</p><p>如果在实际运行中,condition 为 true 的概率比较大,那么该代码片段会比较高效,反之则低效。借助对程序运行期的 pprof profile 数据进行采集,则可以得到上面的分支判断中,实际走 if 分支和走 else 分支的次数。借助该统计数据,在 PGO 编译中,若走 else 分支的概率较大(相差越大效果越明显),编译器便可以对输出的机器指令进行调整,使其生成的指令从而对 执行逻辑 2 更加有利。其实很简单比如汇编指令 je (等于就跳转) 等价替换成 jne(不等于就跳转)。</p><h3 id=&#34;虚函数优化&#34;>虚函数优化</h3><p>C++的虚函数使用起来非常方便,代码的抽象层次也非常好,但是他还是有一定的开销相比普通函数,如果大量使用虚函数在性能要求非常高的场景对性能还是有一定的影响,主要体现在如下的方面:</p><p>空间开销:由于需要为每一个包含虚函数的类生成一个虚函数表,所以程序的二进制文件大小会相应的增大。其次,对于包含虚函数的类的实例来说,每个实例都包含一个虚函数表指针用于指向对应的虚函数表,所以每个实例的空间占用都增加一个指针大小(32 位系统 4 字节,64 位系统 8 字节)。这些空间开销可能会造成缓存的不友好,在一定程度上影响程序性能。</p><p>虚函数表查找:虚函数增加了一次内存寻址,通过虚函数指针找到虚函数表,有一点点开销但是还好。</p><p>间接调用(indirect call)开销:由于运行期的实际函数(或接口)代码地址是动态赋值的,机器指令无法做更多优化,只能直接执行 call 指令(间接调用)。对于直接调用而言,是不存在分支跳转的,因为跳转地址是编译器确定的,CPU 直接去跳转地址取后面的指令即可,不存在分支预测,这样可以保证 CPU 流水线不被打断。而对于间接寻址,由于跳转地址不确定,所以此处会有多个分支可能,这个时候需要分支预测器进行预测,如果分支预测失败,则会导致流水线冲刷,重新进行取指、译码等操作,对程序性能有很大的影响。</p><p>无法内联优化:由于 virtual 函数的实现本身是多态的,编译中无法得出实际运行期会执行的实现,因此也无法进行内联优化。同时在很多场景下,调用一个函数只是为了得到部分返回值或作用,但函数实现通常还执行了某些额外计算,这些计算本可以通过内联优化消除,由于无法内联,indirect call 会执行更多无效的计算。</p><p>阻碍进一步的编译优化:indirect call 相当于是指令中的一个屏障,由于其本身是一个运行期才能确定的调用,它在编译期会使各种控制流判断以及代码展开失效,从而限制进一步编译及链接的优化空间。</p><p>-&gt; 去虚拟化 (不是虚拟机的虚拟,而是虚函数的虚拟)</p><h3 id=&#34;使用final关键字去虚拟化&#34;>使用final关键字去虚拟化</h3><p>Basic devirtualization我们通过下面一个例子来简单说明编译器是如何去虚拟化的:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>A</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span> <span style=&#34;color:#66d9ef&#34;>virtual</span> <span style=&#34;color:#66d9ef&#34;>int</span> foo() { <span style=&#34;color:#66d9ef&#34;>return</span> ; }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span>};</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>B</span> <span style=&#34;color:#f92672&#34;>:</span> <span style=&#34;color:#66d9ef&#34;>public</span> A {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span> <span style=&#34;color:#66d9ef&#34;>int</span> foo() { <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#ae81ff&#34;>2</span>; }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span>};</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span><span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#a6e22e&#34;>test</span>(B<span style=&#34;color:#f92672&#34;>*</span> b) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>12</span><span> <span style=&#34;color:#66d9ef&#34;>return</span> b<span style=&#34;color:#f92672&#34;>-&gt;</span>foo() <span style=&#34;color:#f92672&#34;>+</span> ; }</span></span></code></pre></div><p>当调用 test(B *b)里面的 b-&gt;foo()函数时,编译器并不知道 b 是一个真正的 B 类型,还是 B 的子类型,所以编译生成的代码会包含间接调用。</p><p>而如果我们给class B的代码中增加final关键字。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>class</span> <span style=&#34;color:#a6e22e&#34;>B</span> <span style=&#34;color:#f92672&#34;>:</span> <span style=&#34;color:#66d9ef&#34;>public</span> A {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span><span style=&#34;color:#66d9ef&#34;>public</span><span style=&#34;color:#f92672&#34;>:</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span> <span style=&#34;color:#66d9ef&#34;>int</span> value() <span style=&#34;color:#66d9ef&#34;>final</span> { <span style=&#34;color:#66d9ef&#34;>return</span> <span style=&#34;color:#ae81ff&#34;>2</span>; }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span>};</span></span></code></pre></div><p>这样编译器知道 class B 不可能有子类,可以进行去虚拟化优化(-fdevirtualize ).</p><h3 id=&#34;猜测性去虚拟化&#34;>猜测性去虚拟化</h3><p>Speculative devirtualization根据实际运行情况,去推导去虚拟化。还是举一个简单的例子来说明下:A* ptr-&gt;foo(),ptr 是一个指针,他可以是 A 也可以是 B,甚至是他们的子类,编译器在编译无法确定其类型。假设在实际的生产环境中的,ptr 大概率是 A 对象,而不是 B 对象或者其子类对象,speculative devirtualization,gcc 的编译参数(-fdevirtualize-speculatively) 优化就会尝试进行如下的转换:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>if</span> (ptr<span style=&#34;color:#f92672&#34;>-&gt;</span>foo <span style=&#34;color:#f92672&#34;>==</span> A<span style=&#34;color:#f92672&#34;>::</span>foo)</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span> A<span style=&#34;color:#f92672&#34;>::</span>foo ();</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#66d9ef&#34;>else</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span> ptr<span style=&#34;color:#f92672&#34;>-&gt;</span>foo ();</span></span></code></pre></div><p>经过此转换后,将间接调用转换成直接调用,就可以进行直接调用优化,比如说 inline 等。</p><h2 id=&#34;性能优化相关的书籍&#34;>性能优化相关的书籍</h2><p><a href=&#34;http://book.easyperf.net/perf_book&#34;>http://book.easyperf.net/perf_book</a></p><ol><li><p>Optimizing software in C++: An optimization guide for Windows, Linux and Мас platforms</p></li><li><p>Optimizing subroutines in assembly language: An optimization guide for x86 platforms</p></li><li><p>The microarchitecture of Intel, AMD and VIA CPUs: An optimization guide for assemblyprogrammers and compiler makers</p></li><li><p>Instruction tables: Lists of instruction latencies, throughputs and micro-operationbreakdowns for Intel, AMD and VIA CPUs</p></li><li><p>Calling conventions for different C++ compilers and operating systemsSoftware optimization resources. C++ and assembly. Windows, Linux, BSD, Mac OS X。</p></li></ol><h2 id=&#34;简单的代码层面优化&#34;>简单的代码层面优化</h2><p>整数运算效率高于浮点</p><p>除法和取余 在标准处理器中,一个32位的除法需要使用20至140次循环操作。对于ARM处理器,有的版本需要20+4.3N次循环。因此,可以通过乘法表达式来替代除法:比如循环里面需要一直除以一个数,可以在循环外先求出这个数的倒数,然后带入到循环内通过乘法计算如果知道被除数的和除数的符号,判断$\frac{a}{b} &gt; c$,如果a,b为正,可以转换为 $a&gt;b∗c$.</p><p>通过2的幂次进行除法和取余数如果除法中的除数是2的幂次,编译器会使用移位操作来执行除法。有符号signed的除法需要移位到0和负数,因此需要更多的时间执行。</p><p>其实很多技巧编译器都可以自动完成。。。</p><h2 id=&#34;volatile&#34;>volatile</h2><p>volatile用于声明一个变量可被其他线程改变,阻止编译器依赖变量始终具有代码中先前分配的值的假设来进行优化 。</p><h2 id=&#34;thread-local&#34;>thread-local</h2><p>大多数编译器可以使用关键字 __thread 或 __declspec(thread) 来实现静态变量和全局变量的线程本地存储。这样的变量对于每个线程都有一个实例 。线程本地存储是低效的,因为它是通过存储在线程访问块中的指针进行访问的。因此建议尽量避免线程本地存储,代之以stack存储。</p><h2 id=&#34;进程上下文切换&#34;>进程上下文切换</h2><p>从用户态切换到内核态需要通过系统调用来完成,这里就会发生进程上下文切换(特权模式切换),当切换回用户态同样发生上下文切换。</p><p>一般每次上下文切换都需要几十纳秒到数微秒的 CPU 时间,如果切换较多还是很容易导致 CPU 时间的浪费在寄存器、内核栈以及虚拟内存等资源的保存和恢复上,这里同样会导致系统平均负载升高。</p><p>Linux 为每个 CPU 维护一个就绪队列,将 R 状态进程按照优先级和等待 CPU 时间排序,选择最需要的 CPU 进程执行。这里运行进程就涉及了进程上下文切换的时机:</p><p>进程时间片耗尽、。进程在系统资源不足(内存不足)。进程主动sleep。有优先级更高的进程执行。硬中断发生</p><h2 id=&#34;线程上下文切换&#34;>线程上下文切换</h2><p>线程和进程:</p><p>当进程只有一个线程时,可以认为进程就等于线程。当进程拥有多个线程时,这些线程会共享相同的虚拟内存和全局变量等资源。这些资源在上下文切换时是不需要修改的。线程也有自己的私有数据,比如栈和寄存器等,这些在上下文切换时也是需要保存的。所以线程上下文切换包括了 2 种情况:</p><p>不同进程的线程,这种情况等同于进程切换。通进程的线程切换,只需要切换线程私有数据、寄存器等不共享数据</p><h2 id=&#34;proc&#34;>/Proc</h2><p>user(通常缩写为 us),代表用户态 CPU 时间。注意,它不包括下面的 nice 时间,但包括了 guest 时间。nice(通常缩写为 ni),代表低优先级用户态 CPU 时间,也就是进程的 nice 值被调整为 1-19 之间时的 CPU 时间。这里注意,nice 可取值范围是 -20 到 19,数值越大,优先级反而越低。system(通常缩写为 sys),代表内核态 CPU 时间。idle(通常缩写为 id),代表空闲时间。注意,它不包括等待 I/O 的时间(iowait)。iowait(通常缩写为 wa),代表等待 I/O 的 CPU 时间。irq(通常缩写为 hi),代表处理硬中断的 CPU 时间。softirq(通常缩写为 si),代表处理软中断的 CPU 时间。steal(通常缩写为 st),代表当系统运行在虚拟机中的时候,被其他虚拟机占用的 CPU 时间。guest(通常缩写为 guest),代表通过虚拟化运行其他操作系统的时间,也就是运行虚拟机的 CPU 时间。guest_nice(通常缩写为 gnice),代表以低优先级运行虚拟机的时间。</p><h2 id=&#34;小问题&#34;>小问题:</h2><ol><li>使得CPU为正弦曲线</li><li>编写程序查看自己的L1缓存大小</li></ol><h2 id=&#34;如何优化程序性能&#34;>如何优化程序性能</h2><h3 id=&#34;单核&#34;>单核</h3><ul><li>消除冗余: 代码重构,临时变量,数据结构与算法,池化,inline,RVO,vDSO,COW,延迟计算,零拷贝</li></ul><h3 id=&#34;名词池化&#34;>名词:池化</h3><h3 id=&#34;名词vdso&#34;>名词:vDSO</h3><p>virtual dynamic shared object</p><p>快速系统调用之争在x86-32系统大行其道的时代,调用系统调用的方法就是int $0x80。这种方法的执行速度非常慢,原因是它需要经历一个完整的中断处理过程,这包括Linux内核以及与中断流程相关的处理器微码的执行开销。</p><p>后来为了提升系统调用的性能,Intel最先实现了专门的快速系统调用指令sysenter和系统调用返回指令sysexit;后来AMD针锋相对地实现了另一组专门的快速系统调用指令syscall和系统调用返回指令sysret。</p><p>快速系统调用的“快”字,体现在以下几个方面:</p><p>处理器在切换到内核态后不再自动往内核栈中自动保存任何上下文信息了,这样避免了访内开销。处理器也不再自动加载内核栈的值到rsp寄存器了,节省了指令开销。syscall和sysret指令只能用在平坦内存模型中,因此在执行快速系统调用时bypass了MMU的分段单元的检查,节省了微码的执行开销。处理器微码不再需要走中断处理和中断恢复流程,大幅度提高了执行性能。与Intel的快速系统调用指令相比,AMD的syscall/sysret要更快更灵活:</p><p>执行syscall和sysret指令时,不再需要处理器自动保存和恢复用户栈指针了,因此也不需要再事先设置MSR来指定要恢复和保存的用户栈指针了。通过系统调用进入内核态后,rflags寄存器中哪些位应该清0原本是固定的,但是如果是用syscall来执行系统调用的话,那这些位是可以通过编程来事先设置的。最后,Intel也提供了对sysenter/sysexit指令的支持。之后,为了获得最好的兼容性(Intel和AMD通用),x86-64 Linux内核将快速系统调用的支持方式统一到了syscall/sysret。</p><p>题外话:Intel要求在内核启动阶段必须事先设置好IA32_EFER.SCE位才能在64位处理器模式中使用syscall和sysret指令,否则会触发#GP异常。</p><p>吐槽:死傲娇!64位处理器模式都是人家AMD先出的,你还在乎个指令?</p><h2 id=&#34;linux性能优化思路&#34;>linux性能优化思路</h2><h3 id=&#34;icache-miss&#34;>icache miss</h3><ul><li>减少调用栈深度</li><li>短小且多次用到的函数inline,省去栈帧变化开销</li><li>精简热路径代码</li><li>分支预测 likely/unlikely</li><li>减少参数,通过寄存器传参,尽量避免栈传参</li></ul><h3 id=&#34;dcache-miss&#34;>dcache miss</h3><ul><li><p>思路:</p><ul><li>尽量让数据在进入cache到出cache这段时间里被充分使用</li><li>保障代码局部性</li><li>时间局部性:刚访问的数据很快被再次访问</li><li>空间局部性:相邻数据很快被再访问</li></ul></li><li><p>方法:</p><ul><li>调整结构体字段:将短时间内可能同时访问的字段放在临近位置</li><li>cacheline对齐:关键结构需要cacheline对齐。(注意避免false sharing)</li><li>结构体最常访问字典放在最前面,最好不超过一个cacheline</li><li>少用全局变量。</li></ul></li></ul><h3 id=&#34;tlb-miss&#34;>TLB miss</h3><ul><li>大页</li><li>热点函数和数据集中在一个页 (代码段大页)</li></ul><h3 id=&#34;top-down-性能分析模型&#34;>Top-Down 性能分析模型</h3><ul><li>是什么:将 CPU pipeline 和指令执行效率联系在一起,是一个通用的程序性能评估模型。</li><li>概念:<ul><li>pipeline slot: 处理一个uops所需的硬件资源。每个核,在每个时钟周期有几个可用的pipeline slot,称为pipeline width</li><li>IPC,英文全称“Instruction Per Clock”,中文翻译过来就是每个时钟的指令,即CPU每一时钟周期内所执行的指令多少.</li></ul></li><li>pipeline指令执行情况:<ul><li>Retiring:指令执行正常,经历了取指,译码,执行,回写.例如 对于4-width的机器,100% retiring 意味着IPC为4.</li><li>Bad Speculation:预测错误导致pipeline slot被浪费掉。</li><li>branch mispredict:在分支预测错误时解码的指令。</li><li>Machine clear: 清空整个pipeline(锁竞争)</li><li>Front End Bound:前端提供的指令不足,导致后端处于等待状态</li><li>fetch latency:指令延迟,可能因为itlb(cache) miss,或 pipeline 刷新</li><li>fetch bandwidth:指令带宽不足,程序的指令分支比重过大。</li><li>Back End Bound:后端缺乏必要的资源导致pipeline停顿:</li><li>core bound:计算单元缺乏并行度。</li><li>memory bound:momory 无法及时提供数据。</li></ul></li></ul><h2 id=&#34;c优化模块边界&#34;>C++优化:模块边界</h2><p>众所周知,C++ 的一个源文件就是一个模块,而编译器以一个模块为一个编译单元,生成一个二进制目标文件。最后再将所有的目标文件链接到一起形成一个可执行程序或动态库。</p><p>但这种机制也导致了编译器很难跨模块优化,优化的操作被局限在一个源文件内。编译器无法知道这个模块内的操作与其他模块内操作的联系,是否可以打乱重排、合并或内联。最后模块之间通过查找符号名进行链接,但哪怕是链接融合到一起,其实也只是单纯的代码拷贝,链接器无力在二进制层面做进一步整体优化。这是历史遗留问题,早年的硬件内存不支持同时加载整个项目的源码进行编译,也是现在 C/C++ 一直为人所诟病的一点。</p><p>C/C++ 的编译系统和其他高级语言存在很大的差异,在其他高级语言中,无论是 Java,亦或是新兴的 Golang/Rust,都是以包(package)的形式来管理代码,包内的所有源码都会被放在一起编译,编译一个文件时能获得另一个文件里的源码内容。理论上不管是编译速度,还是整体优化效果,都会优于 C/C++。不过,也有一些应对措施。</p><h3 id=&#34;header-only&#34;>header-only</h3><p>一般来说,我们会将模块需要导出的数据结构和函数声明放在头文件中,这样其它模块就能知道这个模块提供了怎样的功能,但既然函数声明能放在头文件中,函数定义为什么不能也放在头文件中呢?使用 header-only 大概有这样几个动机:</p><p>方便分发,所有的代码都在头文件里,一个 include 就能使用,不需要考虑库的链接,非常省心C++ 模板实例化必须要有函数定义性能考虑:一些常用操作可能到处都在高频使用,属于是性能热点,将实现放在头文件中开放出去,有利于编译器进一步优化</p><h3 id=&#34;unity-build&#34;>Unity Build</h3><p>将多个 c/cpp 文件组合在一起来编译,打破模块边界。</p><h3 id=&#34;链接时优化lto&#34;>链接时优化(LTO)</h3><p>Unity Build 虽好,但仍有诸多限制。比如不同的两个代码文件中,全局命名空间下,都存在相同名字的全局变量和函数。那么 unity-build 就会带来编译冲突问题,编译器通常会报全局变量重定义错误。这时就需要手动修改代码。还是想要更加无感的方法,链接时是可以优化的!</p><h2 id=&#34;c-别名优化&#34;>C++ 别名优化</h2><blockquote><p>-fstrict-aliasing allow the compiler to assume the strictest aliasing rules applicable to the language being compiled. For C (and C++), this activates optimizations based on the type of expressions. In particular, an object of one type is assumed never to reside at the same address as an object of a different type, unless the types are almost the same. The -fstrict-aliasing option is enabled at levels -O2, -O3, -Os.</p></blockquote><p>简而言之就是当两个指针或引用的类型不一样时,编译器会假定它们不会指向同一块内存,方便编译器进一步优化代码。来看一个例子:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#66d9ef&#34;>void</span> <span style=&#34;color:#a6e22e&#34;>foo</span>(<span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#f92672&#34;>&amp;</span>a, <span style=&#34;color:#66d9ef&#34;>int</span> <span style=&#34;color:#f92672&#34;>&amp;</span>b) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span> a <span style=&#34;color:#f92672&#34;>=</span> b <span style=&#34;color:#f92672&#34;>+</span> <span style=&#34;color:#ae81ff&#34;>1</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span> a <span style=&#34;color:#f92672&#34;>=</span> b <span style=&#34;color:#f92672&#34;>+</span> <span style=&#34;color:#ae81ff&#34;>1</span>;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span>}</span></span></code></pre></div><p>因为类型相同,编译器不敢做进一步优化,进行了两次相同的操作,每次都从内存中重新取 b 的值,因为 a 和 b 的背后完全可能是同一个变量。</p><p>那肯定有人会问,我很明确知道两个变量背后不是同一地址,但是它们的类型就是一样,编译器有没有办法优化呢?</p><p>这时就轮到 restrict 出场了,restrict 属于 C 标准,但是并没有进入 C++ 标准,C++ 可以使用 __restrict,各大主流编译器都有实现。</p><p>但是需要注意文档中这样一句话:</p><blockquote><p>For example, an unsigned int can alias an int, but not a void* or a double. A character type may alias any other type.</p></blockquote><p>也就是说,char 类型指针总是会被当做其它类型指针的别名,需要手动声明 restrict</p><h2 id=&#34;程序喵-c性能优化白皮书&#34;>程序喵 C++性能优化白皮书</h2><p>作为一个程序员,想要性能优化,最好要了解些硬件,特别是CPU架构的一些知识点:</p><p>流水线分支预测寄存器重命名数据预取指令重排和乱序执行同时多线程(超线程)数据并行 SIMD 单指令多数据</p><p>还要了解CPU的特点:</p><p>一个处理器上,多条指令可能同时执行一个处理器上,代码的执行结果会和程序员可观察到的顺序一致,但其他处理器观察到的执行结果可能不是一个顺序顺序、无跳转的代码性能最高相邻且对齐的数据访问性能最高</p><p>链接期优化:link-time optimization LTO</p><p>LTO可以:</p><p>跨编译单元的函数内联跨编译单元的程序整体优化死代码消除</p><p>关于C++语言层面的优化,可以在下面这些方向做优化:</p><p>优先栈内存,次之堆内存巧妙使用RAII管理资源移动语义虽然不好理解,但也可以巧妙使用移动语义减少对象的非必要拷贝模板和泛型技巧华而不实,给开发标准库的人使用还好,而且调试难度也较高,我们普通业务开发者只需要做到能看懂即可(个人见解)异常是可以考虑使用的,可以看看ISO C++网站和C++ 核心指南62,异常会导致程序的二进制体积有膨胀(5%-15%),异常不能代替所有的错误码,因为异常catch会使得程序性能下降。作者认为:使用异常对于大部分C++项目仍然适用,不使用异常的麻烦大于好处,除非真因为二进制文件和实时性方面的原因需要禁用异常。字符串默认类型时 const char[],传参时会退化成const char*,创建全局字符串最好使用const char[]标准库容器的方法至少提供了基本异常安全保证:要了解强异常安全保证和无异常保证。vector的移动构造函数标记为noexcept才会使用移动构造,移动构造函数需要标记为noexcept,如果没有标记,代码性能可能会有较大的负面影响。shared_ptr构造优先使用make_shared</p><p>了解function,function用作回调很方便,支持类型擦除,它还有个好处,可以用来存储带状态的函数对象,不像C语言那样需要个void*存储状态。但需要了解它的开销,貌似48个字节是个坎。</p><p>堆内存管理:可以了解下jemalloc mimalloc tcmalloc12.输入输出流可以考虑使用ios_base::sync_with_stdio(false)关闭同步,性能会提升,也最好使用\n取代endl,免得频繁刷新缓冲区。可考虑使用fmt</p><p>并发</p><p>需要了解内存序的概念一些优质的多生产者多消费者并发队列moodycamed::ConcurrentQueueatomic_queueFolly中MPMCQueue标准库也有些并行策略:execution::seq 序列执行,不可并行par:可并行化par_unseq:可并行化 向量化unseq:可向量化</p><h2 id=&#34;名词numa&#34;>名词:NUMA</h2><h2 id=&#34;cpu的各级缓存&#34;>CPU的各级缓存</h2><p>首先说说L1i和L1d的区别,i指的是instruction指令缓存,d是数据data缓存。作为冯·诺依曼体系的计算机,x86价格的指令和数据在内存中是统一管理的。但由于两者内容访问特性的不同(指令刷新率更低且不会被复写),L1的缓存是做了区分的。当前的Intel平台中L1缓存的时延为3个时钟周期,以2.0GHz的CPU计算约1.5纳秒。这种级别的时延可以极大的加速超线程以及CPU分支预测带来的性能优势。L2缓存的时延是L1的5倍左右,即8ns。每个CPU的物理核心都有自己独立的L2缓存空间。而L3的时延在50~70个时钟周期,30ns。不同于L2,L3缓存是多个核心共享的,L3在使用场景中最大的用途是减少数据回写内存的频率,加速多核心之间的数据同步。说到L3的“多核心之间”共享,传统的设计是每个CPU插槽或者一块硅片共享一个L3空间,由于内存空间地址是唯一的,这就可能牵扯到同一份内存地址存在两份相同的缓存内容。MESI状态控制就是为了同步数据在多个L3空间之间的流转而设置的。</p><p>内存和cpu cache的访问统一都是64byte对齐的。也就是说即便你只需要读取1bit的数据,CPU还是会把64byte的数据从内存逐步扔到L1。</p><p>CPU中事实上还存在着TLB(Translation Look aside Buffer,页表缓存)的组件类似于cache的功能。它主要负责缓存页表逻辑到物理地址的对应关系。跟L1类似,TLB也分为iTLB和dTLB分别对应了指令页表和数据页表的地址转换结果。</p><h3 id=&#34;inclusive-cache和non-inclusive-cache的区别&#34;>inclusive cache和Non-inclusive cache的区别</h3><p>名称已经很直白了,inclusive/non-inclusive就是数学上的“包含”和“不包含”关系。</p><p>目前的趋势是逐步从inclusive cache向non-inclusive转变。</p><p>如果有了解过LRU,你会发现如果把所有的cache理解为一个整体的话,其实每次的数据读取都会伴随着其他缓存数据的更新。传统上的多层cache考虑到实现难度,严格要求数据保持L3-L2-L1各有一份拷贝。数据update后,一旦被LRU踢出当前缓存则合并更新到下级缓存。L3的大小即为所有缓存数据的最大容量。另一方面,CPU在不断的演进之后,core的数目越来越多。从前面的内容中你可以知道每个CPU核心都会有独立的L1/L2。那理论上如果继续沿用inclusive L3,L3的容量就必须大大于所有核心上L1/L2的总和才有意义。显然这将会是设计瓶颈。于是就有了non-inclusive的L3——其实non-inclusive的L2很早就有了。</p><p>方便你理解的话:non-inclusive cache意味着下级cache事实上是上级cache的回收站。当上级缓存的数据被踢出的时候,踢出的数据回写下级cache。以skylake为例L1没有命中的情况:</p><p>L2 miss,跟L3逻辑上同级的SF(snoop filter)记录了各个L2中数据的状态。检查L3和snoop filter记录,获得L3或者其他L2是否有所需数据。</p><p>数据不存在于L3和sf时:数据从内存直接载入L2。L1由L2获取数据后将该cache line与当前L1中的旧数据交换空间。淘汰的L1数据踢出了L2中的旧数据,数据将从L2写入L3。L3获得L2淘汰数据并保存,踢出一份旧数据并检查是否需要回写内存。最后更新snoop filter中L2的数据变化。</p><p>数据存在于L3时,像是一个两两交换位置的过程(然而并不是真的两两交换)。数据从L3载入L2。L1由L2获取数据后将该cache line与当前L1中的旧数据交换空间。淘汰的L1数据踢出了L2中的旧数据,L2旧数据将从L2回写L3,更新SF。</p><p>数据只存在于sf,意味着当前数据正在被其他核心的L1/L2缓存,有概率内存中是脏数据(已经更新但没有回写内存)CPU将触发MESI流程确保全局一致性(参见:缓存一致性保障一文)</p><h2 id=&#34;front-end-bound-优化&#34;>Front end bound 优化</h2><p>Front-End的职责即取指令(可能会根据预测提前取指令)、解码、分发给后端pipeline, 它的性能受限于两个方面,latency和bandwidth。对于latency,一般就是取指令(比如L1 ICache、iTLB未命中或解释型编程语言python\java等)、decoding (一些特殊指令或者排队问题)导致延迟。当Front-End 受限了,pipeline利用率就会降低。对于BandWidth 将它划分成了MITE,DSB和LSD三个子类。</p><ol><li><p>代码尽可能减少代码的footprint:C/C++可以利用编译器的优化选项来帮助优化,比如GCC -O* 都会对footprint进行优化或者通过指定-fomit-frame-pointer也可以达到效果</p></li><li><p>充分利用CPU硬件特性:宏融合(macro-fusion)宏融合特性可以将2条指令合并成一条微指令,它能提升Front-End的吞吐。所以建议循环条件中的类型采用无符号的数据类型可以使用到宏融合特性提升Front-End 吞吐量。</p></li><li><p>调整代码布局(co-locating-hot-code):①充分利用编译器的PGO 特性:-fprofile-generate -fprofile-use</p></li></ol><p>②可以通过__attribute__ ((hot)) <strong>attribute</strong> ((code)) 来调整代码在内存中的布局,hot的代码</p><p>在解码阶段有利于CPU进行预取。</p><ol start=&#34;4&#34;><li>分支预测① 消除分支可以减少预测的可能性能:比如小的循环可以展开比如循环次数小于64次(可以使用GCC选项 -funroll-loops)</li></ol><p>② 尽量用if 代替:? ,不建议使用a=b&gt;0? x:y 因为这个是没法做分支预测的</p><p>③ 尽可能减少组合条件,使用单一条件比如:if(a||b) {}else{} 这种代码CPU没法做分支预测的</p><p>④对于多case的switch,尽可能将最可能执行的case 放在最前面</p><p>⑤ 我们可以根据其静态预测算法投其所好,调整代码布局,满足以下条件:</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span><span style=&#34;color:#66d9ef&#34;>bool</span> is_expect <span style=&#34;color:#f92672&#34;>=</span> true;</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span> <span style=&#34;color:#66d9ef&#34;>if</span>(is_expect) {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span> <span style=&#34;color:#75715e&#34;>// 被执行的概率高代码尽可能放在这里</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span><span style=&#34;color:#75715e&#34;></span> } <span style=&#34;color:#66d9ef&#34;>else</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span> <span style=&#34;color:#75715e&#34;>// 被执行的概率低代码尽可能放在这里</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span><span style=&#34;color:#75715e&#34;></span> }</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span><span style=&#34;color:#75715e&#34;>// 后置条件,使条件分支的具有向后目标的分支不太可能的目标</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span><span style=&#34;color:#75715e&#34;></span> </span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span> <span style=&#34;color:#66d9ef&#34;>do</span> {</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span> <span style=&#34;color:#75715e&#34;>// 这里的代码尽可能减少运行</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span><span style=&#34;color:#75715e&#34;></span> } <span style=&#34;color:#66d9ef&#34;>while</span>(conditions);</span></span></code></pre></div><h2 id=&#34;back-end-bound优化&#34;>back end bound优化</h2><p>这一类别的优化涉及到CPU Cache的使用优化,CPU cache[14]它的存在就是为了弥补超高速的 CPU与DRAM之间的速度差距。CPU 中存在多级cache(register\L1\L2\L3) ,另外为了加速virtual memory address 与 physical address 之间转换引入了TLB。</p><p>如果没有cache,每次都到DRAM中加载指令,那这个延迟是没法接受的。</p><p>优化建议:调整算法减少数据存储,减少前后指令数据的依赖提高指令运行的并发度根据cache line调整数据结构的大小避免L2、L3 cache伪共享</p><ol><li>合理使用缓存行对齐CPU的缓存是弥足珍贵的,应该尽量的提高其使用率,平常使用过程中可能存在一些误区导致CPU cache有效利用率比较低。</li></ol><p>缓存行对齐使用原则:</p><p>多个线程存在同时写一个对象、结构体的场景(即存在伪共享的场景)对象、结构体过大的时候将高频访问的对象属性尽可能的放在对象、结构体首部</p><ol start=&#34;2&#34;><li>伪共享</li></ol><p>前面主要是缓存行误用的场景,这里介绍下如何利用缓存行解决SMP 体系下的伪共享(false shared)。多个CPU同时对同一个缓存行的数据进行修改,导致CPU cache的数据不一致也就是缓存失效问题。为什么伪共享只发生在多线程的场景,而多进程的场景不会有问题?这是因为linux 虚拟内存的特性,各个进程的虚拟地址空间是相互隔离的,也就是说在数据不进行缓存行对齐的情况下,CPU执行进程1时加载的一个缓存行的数据,只会属于进程1,而不会存在一部分是进程1、另外一部分是进程2。</p><p>伪共享之所以对性能影响很大,是因为他会导致原本可以并行执行的操作,变成了并发执行。这是高性能服务不能接受的,所以我们需要对齐进行优化,方法就是CPU缓存行对齐(cache line align)解决伪共享,本来就是一个以空间换取时间的方案。</p><ol start=&#34;3&#34;><li>Bad Speculation分支预测当Back-End 删除了微指令,就出现Bad Speculation,这意味着Front-End 对这些指令所作的取指令、解码都是无用功,所以为什么说开发过程中应该尽可能的避免出现分支或者应该提升分支预测准确度能够提升服务的性能。虽然CPU 有BTB记录历史预测情况,但是这部分cache 是非常稀缺,它能缓存的数据非常有限。</li></ol><p>分支预测在Font-End中用于加速CPU获取指定的过程,而不是等到需要读取指令的时候才从主存中读取指令。Front-End可以利用分支预测提前将需要预测指令加载到L2 Cache中,这样CPU 取指令的时候延迟就极大减小了,所以这种提前加载指令时存在误判的情况的,所以我们应该避免这种情况的发生,c++常用的方法就是:</p><p>在使用if的地方尽可能使用gcc的内置分支预测特性。</p><p>避免间接跳转或者调用</p><p>在c++中比如switch、函数指针或者虚函数在生成汇编语言的时候都可能存在多个跳转目标,这个也是会影响分支预测的结果,虽然BTB可改善这些但是毕竟BTB的资源是很有限的。</p><p>[1] CPI(cycle per instruction) 平均每条指令的平均时钟周期个数</p><p>[2] IPC (instruction per cycle) 每个CPU周期的指令吞吐数</p><p>[3] uOps 现代处理器每个时钟周期至少可以译码 4 条指令。译码过程产生很多小片的操作,被称作微指令(micro-ops, uOps)</p><p>[4] pipeline slot pipeline slot 表示用于处理uOps 所需要的硬件资源,TMAM中假定每个 CPU core在每个时钟周期中都有多个可用的流水线插槽。流水线的数量称为流水线宽度。</p><p>[5] MIPS(MillionInstructions Per Second) 即每秒执行百万条指令数 MIPS= 1/(CPI×时钟周期)= 主频/CPI</p><p>[6]cycle 时钟周期:cycle=1/主频</p><p>[7] memory footprint 程序运行过程中所需要的内存大小.包括代码段、数据段、堆、调用栈还包括用于存储一些隐藏的数据比如符号表、调试的数据结构、打开的文件、映射到进程空间的共享库等。</p><p>[8] MITE Micro-instruction Translation Engine</p><p>[9]DSB Decode stream Buffer 即decoded uop cache</p><p>[10]LSD Loop Stream Detector</p><p><img src="https://imgproxy.citrusreader.com/dMAnQRG1fOq0D0-Brp7g_Ohl5CLF-NA3mOoQfKoho8U/raw:1/aHR0cHM6Ly9ibG9nLm95eWtvLmNvbS9wb3N0cy9pbmZyYS95b3VodWEucG5nJTIy" alt="&#34;youhua&#34;"></p><p><img src="https://imgproxy.citrusreader.com/9UDy8SlxlGm0mZ5jah233gujhJuIMIxCARDggkDwLDg/raw:1/aHR0cHM6Ly9ibG9nLm95eWtvLmNvbS9wb3N0cy9pbmZyYS8lMjJpbWFnZS5wbmclMjI" alt="&#34;tools0&#34;"></p><p><img src="https://imgproxy.citrusreader.com/2dBNJHS5WuC1Ui4mhxSVUyrW6IZ52BgGU92rfmyTsQk/raw:1/aHR0cHM6Ly9ibG9nLm95eWtvLmNvbS9wb3N0cy9pbmZyYS8lMjJpbWFnZS0xLnBuZyUyMg" alt="&#34;tools1&#34;"></p><p><img src="https://imgproxy.citrusreader.com/gr7OzJ2jlm3-s3Q4W9g8ZBTSgdgs7CmsYjLlG2voWWo/raw:1/aHR0cHM6Ly9ibG9nLm95eWtvLmNvbS9wb3N0cy9pbmZyYS8lMjJpbWFnZS0yLnBuZyUyMg" alt="&#34;img2&#34;"></p><p><img src="https://imgproxy.citrusreader.com/9CIEChQy3WrtcWsbrSTGdr869jgbtpepe0uk2xze3G4/raw:1/aHR0cHM6Ly9ibG9nLm95eWtvLmNvbS9wb3N0cy9pbmZyYS8lMjJpbWFnZS0zLnBuZyUyMg" alt="&#34;taolu&#34;"></p><p><img src="https://imgproxy.citrusreader.com/x_A9rK_-1bEhFU1VrgT86pCyprrINWCo4MCcUpD1a58/raw:1/aHR0cHM6Ly9ibG9nLm95eWtvLmNvbS9wb3N0cy9pbmZyYS8lMjJpbWFnZS00LnBuZyUyMg" alt="&#34;app0&#34;"></p><p><img src="https://imgproxy.citrusreader.com/mUIb-WUIcLc0_H38YT3Tdw_Rp7axzT-WaBDvOOwTedE/raw:1/aHR0cHM6Ly9ibG9nLm95eWtvLmNvbS9wb3N0cy9pbmZyYS8lMjJpbWFnZS01LnBuZyUyMg" alt="&#34;app1&#34;"></p>

2023/10/12
阅读更多

New Macbook Setup

<h2 id=&#34;install-homebrew&#34;>Install HomeBrew</h2><p>see <a href=&#34;https://brew.sh/&#34;>https://brew.sh/</a>.</p><h2 id=&#34;增加软件仓库&#34;>增加软件仓库</h2><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span>brew tap homebrew/cask-fonts </span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span>brew tap homebrew/cask</span></span></code></pre></div><p>在增加了homebrew/cask之后,安装cask中的软件不需要<code>brew install --cask</code>, 只需要直接<code>brew install</code>即可。</p><blockquote><p>但是官方不建议这么做,官方做法是使用<code>brew install --cask XXX</code></p></blockquote><h2 id=&#34;use-homebrew-to-install-apps&#34;>Use homebrew to install Apps</h2><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span><span style=&#34;color:#75715e&#34;>## Install Rust CLI Apps</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span>brew install bat fd wget bottom lsd ripgrep</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span><span style=&#34;color:#75715e&#34;>## Install cask Apps</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span>brew install --cask google-chrome visual-studio-code wechat qq iterm2 iina</span></span></code></pre></div><h2 id=&#34;install-snap&#34;>Install snap</h2><p>With Snap App, you can use <code>Command ⌘ + 1</code> to call the first app in your dock.</p><p>从App Store下载。这是免费的。</p><h2 id=&#34;install-flow&#34;>Install Flow</h2><p>Flow可以定时提醒自己休息。从App Store下载或者brew安装都可以。</p><h2 id=&#34;tex&#34;>Tex</h2><p>安装MacTex即可。</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span>brew install --cask mactex</span></span></code></pre></div><h2 id=&#34;下载字体&#34;>下载字体</h2><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span>brew install font-jetbrains-mono-nerd-font</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span>brew install font-jetbrains-mono</span></span></code></pre></div><h2 id=&#34;配置iterm2和zsh&#34;>配置iterm2和zsh</h2><p>在<code>~/.zshrc</code>中配置<code>alias ins=&quot;brew install&quot;</code></p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span>ins zsh-fast-syntax-highlighting</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span>ins zsh-autosuggestions</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>3</span><span>ins zsh-completions</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>4</span><span>ins zsh-history</span></span></code></pre></div><p>安装了之后需要在zshrc中配置才能生效如下</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 1</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 2</span><span>source /opt/homebrew/share/zsh-autosuggestions/zsh-autosuggestions.zsh</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 3</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 4</span><span><span style=&#34;color:#66d9ef&#34;>if</span> type brew &amp;&gt;/dev/null; <span style=&#34;color:#66d9ef&#34;>then</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 5</span><span> FPATH<span style=&#34;color:#f92672&#34;>=</span><span style=&#34;color:#66d9ef&#34;>$(</span>brew --prefix<span style=&#34;color:#66d9ef&#34;>)</span>/share/zsh-completions:$FPATH</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 6</span><span> zstyle <span style=&#34;color:#e6db74&#34;>&#39;:completion:*&#39;</span> matcher-list <span style=&#34;color:#e6db74&#34;>&#39;&#39;</span> <span style=&#34;color:#e6db74&#34;>&#39;m:{a-zA-Z}={A-Za-z}&#39;</span> <span style=&#34;color:#e6db74&#34;>&#39;r:|=*&#39;</span> <span style=&#34;color:#e6db74&#34;>&#39;l:|=* r:|=*&#39;</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 7</span><span> autoload -Uz compinit</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 8</span><span> compinit</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;> 9</span><span><span style=&#34;color:#66d9ef&#34;>fi</span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>10</span><span></span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>11</span><span>source /opt/homebrew/opt/zsh-fast-syntax-highlighting/share/zsh-fast-syntax-highlighting/fast-syntax-highlighting.plugin.zsh</span></span></code></pre></div><h2 id=&#34;安装zoxide&#34;>安装zoxide</h2><p>zoxide会记忆你最近访问的目录。例如你最近访问过<code>~/aaa/bb/banana</code>,那么不管你当前处在哪一个目录,你都可以用<code>z ba</code>来跳转到banana目录下吗。<code>z b</code>, <code>z ba</code>,<code>z ban</code>都是可以的。安装方法:<code>ins zoxide</code>之后在zshrc中配置<code>eval &quot;$(zoxide init zsh)&quot;</code></p><h2 id=&#34;安装p10k&#34;>安装p10k</h2><p>参考https://github.com/romkatv/powerlevel10k</p><h2 id=&#34;使终端中的option----可以实现跨单词跳转&#34;>使终端中的option ⌥ + ← 可以实现跨单词跳转</h2><p>在zshrc中配置</p><div class=&#34;highlight&#34;><pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;><code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>1</span><span>bindkey <span style=&#34;color:#e6db74&#34;>&#34;\e\e[D&#34;</span> backward-word</span></span><span style=&#34;display:flex;&#34;><span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;>2</span><span>bindkey <span style=&#34;color:#e6db74&#34;>&#34;\e\e[C&#34;</span> forward-word</span></span></code></pre></div><h2 id=&#34;使用espanso&#34;>使用espanso</h2><p>espanso是一个键盘快捷键映射软件。例如在任何地方输入:date会自动变为当前的日期。输入:name会自动显示自己的姓名等。</p>

2023/10/4
阅读更多

推荐订阅

Recent content on forecho's Blog

Provides a better reading experience (full articles) over the official ones.