<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Clay 的技术空间</title>
  
  <subtitle>用进废退 | 艺不压身</subtitle>
  <link href="https://www.techgrow.cn/atom.xml" rel="self"/>
  
  <link href="https://www.techgrow.cn/"/>
  <updated>2026-06-15T13:55:33.000Z</updated>
  <id>https://www.techgrow.cn/</id>
  
  <author>
    <name>Clay</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Debian 13 通过 Shell 脚本实现磁盘监控报警</title>
    <link href="https://www.techgrow.cn/posts/b58c3ba0.html"/>
    <id>https://www.techgrow.cn/posts/b58c3ba0.html</id>
    <published>2026-06-15T13:55:33.000Z</published>
    <updated>2026-06-15T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/503279b0.html">CentOS 7 通过 Shell 脚本实现磁盘监控报警</a></li><li><a href="/posts/b58c3ba0.html">Debian 13 通过 Shell 脚本实现磁盘监控报警</a></li></ul><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文将介绍 Debian 13 系统如何使用 Shell 脚本，实现对磁盘使用率的监控，并发送告警邮件（支持腾讯企业邮箱或者 QQ 邮箱）。</p><span id="more"></span><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><h3 id="防火墙配置"><a href="#防火墙配置" class="headerlink" title="防火墙配置"></a>防火墙配置</h3><ul><li>安装 UFW 服务 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install ufw -y</span><br></pre></td></tr></tbody></table></figure><ul><li>防火墙开放 22 端口（<strong>避免 SSH 远程连接断开</strong>）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo ufw allow 22/tcp</span><br></pre></td></tr></tbody></table></figure><ul><li>防火墙开放 465 端口（SMTPS，用于邮件发送）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo ufw allow 465/tcp</span><br></pre></td></tr></tbody></table></figure><ul><li>启用防火墙（<strong>务必先开放 SSH 的 22 端口再启用防火墙</strong>）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo ufw <span class="built_in">enable</span></span><br></pre></td></tr></tbody></table></figure><ul><li>查看防火墙运行状态和已开放端口 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo ufw status verbose</span><br></pre></td></tr></tbody></table></figure><h3 id="安装邮件服务"><a href="#安装邮件服务" class="headerlink" title="安装邮件服务"></a>安装邮件服务</h3><ul><li>安装邮件服务 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装 s-nail</span></span><br><span class="line">sudo apt install s-nail -y</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建从 mail 到 s-nail 的软链接（可选操作，后续也可以直接使用 s-nail 命令）</span></span><br><span class="line">sudo ln -s /usr/bin/s-nail /usr/bin/mail</span><br></pre></td></tr></tbody></table></figure><h3 id="导入-NSS-证书"><a href="#导入-NSS-证书" class="headerlink" title="导入 NSS 证书"></a>导入 NSS 证书</h3><ul><li>安装 NSS 工具 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install libnss3-tools -y</span><br></pre></td></tr></tbody></table></figure><ul><li>导入 NSS 证书（<strong>这里使用 root 用户</strong>）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建用于存储证书的目录</span></span><br><span class="line">sudo mkdir -p /root/.certs/</span><br><span class="line"></span><br><span class="line"><span class="comment"># 从 QQ 邮箱的 SMTP 服务器（端口 465）获取 SSL 证书链，并提取出证书部分（BEGIN/END 标记之间），保存到 qq-smtp.crt 文件</span></span><br><span class="line">sudo sh -c <span class="string">"echo -n | openssl s_client -connect smtp.qq.com:465 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' &gt; /root/.certs/qq-smtp.crt"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 将提取的证书中的 "GeoTrust SSL CA" 证书添加到 NSS 数据库（-d 指定目录），信任属性设置为 "C,,"（表示作为 CA 证书，但不用于客户端 / 服务器认证）</span></span><br><span class="line">sudo certutil -A -n <span class="string">"GeoTrust SSL CA"</span> -t <span class="string">"C,,"</span> -d /root/.certs -i /root/.certs/qq-smtp.crt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将提取的证书中的 "GeoTrust Global CA" 证书添加到 NSS 数据库，同样设置信任属性为 "C,,"</span></span><br><span class="line">sudo certutil -A -n <span class="string">"GeoTrust Global CA"</span> -t <span class="string">"C,,"</span> -d /root/.certs -i /root/.certs/qq-smtp.crt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 列出 NSS 数据库中所有已安装的证书，用于验证证书是否成功导入</span></span><br><span class="line">sudo certutil -L -d /root/.certs</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li>(1) 在导入证书时，要特别注意当前使用的是哪个用户进行操作。</li><li>(2) 当使用 root 用户导入证书，那么在测试邮件发送、测试磁盘监控脚本、添加定时任务（比如 Crontab）的时候也必须使用 root 用户。</li><li>(3) 同理，当使用普通用户导入证书，那么在测试邮件发送、测试磁盘监控脚本、添加定时任务（比如 Crontab）的时候也必须使用普通用户。</li></ul></div><ul><li>获取腾讯邮箱授权密码 </li></ul><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">1. 获取授权码：</span><br><span class="line">   - 腾讯邮箱（包括腾讯企业邮箱和 QQ 邮箱）需要在邮箱设置中生成 SMTP 服务授权码。</span><br><span class="line">   - 登录邮箱，前往 设置 -&gt; 帐号设置 -&gt; SMTP 服务，生成授权码并使用它代替密码。</span><br><span class="line"></span><br><span class="line">2. 安全性设置：</span><br><span class="line">   - 确保已启用 SMTP 服务和客户端登录支持。</span><br><span class="line"></span><br><span class="line">3. SMTP 服务器地址：</span><br><span class="line">   - QQ 邮箱： smtp.qq.com</span><br><span class="line">   - 腾讯企业邮箱：smtp.exmail.qq.com</span><br></pre></td></tr></tbody></table></figure><ul><li>编辑系统配置文件，在文件末尾追加以下内容（<strong>请自行更改以下配置信息，切忌直接复制，这里使用的是 QQ 邮箱</strong>）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 备份配置文件</span></span><br><span class="line">sudo cp /etc/s-nail.rc /etc/s-nail.rc.bak</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编辑配置文件</span></span><br><span class="line">sudo vi /etc/s-nail.rc</span><br><span class="line"></span><br><span class="line"><span class="built_in">set</span> from=clay@qq.com</span><br><span class="line"><span class="built_in">set</span> smtp=<span class="string">"smtps://smtp.qq.com:465"</span></span><br><span class="line"><span class="built_in">set</span> smtp-auth-user=clay@qq.com</span><br><span class="line"><span class="built_in">set</span> smtp-auth-password=xxxxxxxxxxxx</span><br><span class="line"><span class="built_in">set</span> smtp-auth=login</span><br><span class="line"><span class="built_in">set</span> ssl-verify=ignore</span><br><span class="line"><span class="built_in">set</span> nss-config-dir=/root/.certs</span><br></pre></td></tr></tbody></table></figure><table><thead><tr><th>配置项</th><th>说明</th></tr></thead><tbody><tr><td><code>set from</code></td><td>设置发件人的邮箱地址，邮件的 “From” 字段会显示此地址。</td></tr><tr><td><code>set smtp</code></td><td>指定使用腾讯邮箱的 SMTP 服务器地址和端口号（465 为 SMTPS 加密端口）。若使用的是腾讯企业邮箱，那么应该配置为 <code>set smtp="smtps://smtp.exmail.qq.com:465"</code>。</td></tr><tr><td><code>set smtp-auth-user</code></td><td>指定用于 SMTP 身份验证的用户名，通常是发件人的完整邮箱地址。</td></tr><tr><td><code>set smtp-auth-password</code></td><td>指定用于 SMTP 身份验证的密码或授权码（QQ 邮箱需使用专用授权码）。</td></tr><tr><td><code>set smtp-auth</code></td><td>设置 SMTP 身份验证的模式为 <code>login</code>，常见的认证方式之一。</td></tr><tr><td><code>set ssl-verify</code></td><td>忽略 SSL 证书验证（适用于证书问题或调试环境，但不建议在生产环境中忽略验证）。</td></tr><tr><td><code>set nss-config-dir</code></td><td>指定 NSS（Network Security Services）库的证书配置目录，用于管理 SSL/TLS 证书和加密连接。</td></tr></tbody></table><h3 id="测试邮件发送"><a href="#测试邮件发送" class="headerlink" title="测试邮件发送"></a>测试邮件发送</h3><ul><li>测试邮件发送（<strong>这里使用 root 用户</strong>）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo sh -c <span class="string">"echo 'TestMail~' | mail -s 'TestMail' xxx@qq.com 2&gt;/dev/null"</span></span><br></pre></td></tr></tbody></table></figure><ul><li>如果邮件发送失败，可以通过添加 <code>-v</code> 参数输出详细的日志信息，然后进一步排查原因 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo sh -c <span class="string">"echo 'TestMail~' | mail -v -s 'TestMail' xxx@qq.com"</span></span><br></pre></td></tr></tbody></table></figure><h2 id="脚本实现"><a href="#脚本实现" class="headerlink" title="脚本实现"></a>脚本实现</h2><ul><li>创建磁盘监控脚本文件，并添加以下内容 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo vi /opt/disk_monitor.sh</span><br></pre></td></tr></tbody></table></figure><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 告警阈值</span></span><br><span class="line">THRESHOLD=90</span><br><span class="line"></span><br><span class="line"><span class="comment"># 邮件接收者（使用空格分割多个邮件接收者）</span></span><br><span class="line">EMAIL=<span class="string">"xxx@qq.com"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 磁盘设备</span></span><br><span class="line">DISK_DEVICE=/dev/sda1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 日志文件</span></span><br><span class="line">LOG_FILE=/tmp/disk_monitor.log</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建日志文件</span></span><br><span class="line"><span class="keyword">if</span> [ ! -f <span class="string">"<span class="variable">$LOG_FILE</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">    touch <span class="string">"<span class="variable">$LOG_FILE</span>"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取服务器的公网 IP</span></span><br><span class="line">IP_ADDRESS=$(curl -s https://checkip.amazonaws.com)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 判断服务器的公网 IP 是否为空</span></span><br><span class="line"><span class="keyword">if</span> [ -n <span class="string">"<span class="variable">$IP_ADDRESS</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">   IP_ADDRESS=<span class="string">"0.0.0.0"</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取指定磁盘设备的使用率</span></span><br><span class="line">CURRENT_USAGE=$(df -h <span class="variable">$DISK_DEVICE</span> | awk <span class="string">'NR==2 {print $5}'</span> | sed <span class="string">'s/%//g'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 写入日志文件</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">"<span class="subst">$(date '+%Y-%m-%d %H:%M:%S')</span> 磁盘设备 <span class="variable">$DISK_DEVICE</span> 的使用率为 <span class="variable">$CURRENT_USAGE</span>%"</span> &gt;&gt; <span class="variable">$LOG_FILE</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 判断磁盘使用率是否超过阈值</span></span><br><span class="line"><span class="keyword">if</span> [ <span class="string">"<span class="variable">$CURRENT_USAGE</span>"</span> -ge <span class="string">"<span class="variable">$THRESHOLD</span>"</span> ]; <span class="keyword">then</span></span><br><span class="line">    <span class="comment"># 发送告警邮件</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"警告: 磁盘设备 <span class="variable">$DISK_DEVICE</span> 的空间使用率超过 <span class="variable">$CURRENT_USAGE</span>%, 请登录服务器 (<span class="variable">$IP_ADDRESS</span>) 及时清理文件."</span> | mail -s <span class="string">"磁盘空间告警"</span> <span class="variable">$EMAIL</span> 2&gt;/dev/null</span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></tbody></table></figure><ul><li>脚本文件授权 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo chmod 755 /opt/disk_monitor.sh</span><br></pre></td></tr></tbody></table></figure><ul><li>脚本测试执行（<strong>这里使用 root 用户</strong>）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo bash /opt/disk_monitor.sh</span><br></pre></td></tr></tbody></table></figure><div class="admonition note"><p class="admonition-title">提示</p><ul><li>如果只希望监控根目录（<code>/</code>）的磁盘使用率，可以使用命令 <code>df -h / | awk 'NR==2 {print $5}' | sed 's/%//g'</code>。</li><li>如果有多个邮件接收者，可以将上述代码更改为 <code>EMAIL="example1@qq.com example2@qq.com</code>，多个邮箱地址之间使用空格分隔，<code>mail</code> 命令支持将邮件发送给 <code>EMAIL</code> 变量中的所有接收者。</li></ul></div><h2 id="定时检测"><a href="#定时检测" class="headerlink" title="定时检测"></a>定时检测</h2><div class="admonition note"><p class="admonition-title">提示</p><p>在 Debian 13 中，官方推荐使用 systemd 自带的 timer 功能取代传统的 cron 服务。当然，如果您更习惯 cron 的配置方式，也可以选择手动安装 cron 服务，然后使用 <code>crontab -e</code> 命令来管理定时任务。</p></div><ul><li>创建磁盘监控的服务单元 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建系统配置文件，添加以下配置信息</span></span><br><span class="line">sudo vi /etc/systemd/system/disk-monitor.service</span><br><span class="line"></span><br><span class="line">[Unit]</span><br><span class="line">Description=Disk Monitoring Service</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=oneshot</span><br><span class="line">ExecStart=/opt/disk_monitor.sh</span><br></pre></td></tr></tbody></table></figure><ul><li>创建磁盘监控的定时器单元（比如每 30 分钟执行一次磁盘使用率检测）</li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建系统配置文件，添加以下配置信息</span></span><br><span class="line">sudo vi /etc/systemd/system/disk-monitor.timer</span><br><span class="line"></span><br><span class="line">[Unit]</span><br><span class="line">Description=Run disk monitor every 30 minutes</span><br><span class="line"></span><br><span class="line">[Timer]</span><br><span class="line">OnCalendar=*:0/30</span><br><span class="line">Persistent=<span class="literal">true</span></span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=timers.target</span><br></pre></td></tr></tbody></table></figure><ul><li>启动并启用磁盘监控的定时器 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 重新加载系统配置文件</span></span><br><span class="line">sudo systemctl daemon-reload</span><br><span class="line"></span><br><span class="line"><span class="comment"># 开机启动并立刻启用磁盘监控的定时器</span></span><br><span class="line">sudo systemctl <span class="built_in">enable</span> --now disk-monitor.timer</span><br></pre></td></tr></tbody></table></figure><ul><li>查看磁盘监控定时器的运行状态 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl list-timers --all</span><br></pre></td></tr></tbody></table></figure><h2 id="验证检测"><a href="#验证检测" class="headerlink" title="验证检测"></a>验证检测</h2><ul><li>查看磁盘监控脚本执行输出的日志信息 </li></ul><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看日志文件</span></span><br><span class="line">sudo tail -f -n 50 /tmp/disk_monitor.log</span><br></pre></td></tr></tbody></table></figure><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2026-06-15 21:15:30 磁盘设备 /dev/sda1 的使用率为 85%</span><br></pre></td></tr></tbody></table></figure><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://www.cnblogs.com/guowenrui/p/17220838.html">Linux 使用 mailx 发送腾讯邮件</a></li><li><a href="https://www.cnblogs.com/huangjiabobk/p/18302228">Linux Shell 脚本监控磁盘与邮件告警</a></li><li><a href="https://blog.csdn.net/WQZ_1998/article/details/108087371">Linux 使用 Shell 脚本监控磁盘与邮件告警</a></li></ul>]]></content>
    
    
    <summary type="html">本文主要介绍 Debian 13 如何使用 Shell 脚本实现磁盘空间监控报警。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="Debian" scheme="https://www.techgrow.cn/tags/Debian/"/>
    
    <category term="Shell 脚本编程" scheme="https://www.techgrow.cn/tags/Shell-%E8%84%9A%E6%9C%AC%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>Debian 13 安装 Docker 与 Docker-Compose</title>
    <link href="https://www.techgrow.cn/posts/351cd7b1.html"/>
    <id>https://www.techgrow.cn/posts/351cd7b1.html</id>
    <published>2026-06-11T13:18:02.000Z</published>
    <updated>2026-06-11T13:18:02.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/efd9e9f2.html">CentOS 7 安装 Docker 与 Docker-Compose</a></li><li><a href="/posts/a4847c57.html">Debian 11 安装 Docker 与 Docker-Compose</a></li><li><a href="/posts/a19ep68d.html">Debian 12 安装 Docker 与 Docker-Compose</a></li><li><a href="/posts/351cd7b1.html">Debian 13 安装 Docker 与 Docker-Compose</a></li></ul><h2 id="官方资源"><a href="#官方资源" class="headerlink" title="官方资源"></a>官方资源</h2><ul><li><a href="https://www.docker.com/">Docker 官方网站</a></li><li><a href="https://docs.docker.com/">Docker 官方文档</a></li><li><a href="https://hub.docker.com/">Docker Hub 官方网站</a></li></ul><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><h3 id="卸载已有软件"><a href="#卸载已有软件" class="headerlink" title="卸载已有软件"></a>卸载已有软件</h3><p>卸载已安装的 Docker，避免对后续的安装步骤产生影响。</p><span id="more"></span><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 卸载旧版本，包括有关的配置文件</span></span><br><span class="line">sudo apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras</span><br><span class="line"></span><br><span class="line"><span class="comment"># 保证卸载干净</span></span><br><span class="line">sudo apt-get remove docker docker-engine docker.io containerd runc</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除文件</span></span><br><span class="line">sudo rm -rf /etc/docker</span><br><span class="line">sudo rm -rf /var/lib/docker</span><br><span class="line">sudo rm -rf /usr/libexec/docker</span><br><span class="line">sudo rm -rf /var/lib/containerd</span><br><span class="line">sudo rm -rf /etc/apt/keyrings</span><br></pre></td></tr></tbody></table></figure><h3 id="安装依赖软件"><a href="#安装依赖软件" class="headerlink" title="安装依赖软件"></a>安装依赖软件</h3><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release</span><br></pre></td></tr></tbody></table></figure><h2 id="安装-Docker"><a href="#安装-Docker" class="headerlink" title="安装 Docker"></a>安装 Docker</h2><h3 id="添加存储库"><a href="#添加存储库" class="headerlink" title="添加存储库"></a>添加存储库</h3><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 添加 Docker 的官方 GPG 密钥</span></span><br><span class="line">sudo curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加 Docker 的 APT 存储库</span></span><br><span class="line">sudo <span class="built_in">echo</span> <span class="string">"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian <span class="subst">$(lsb_release -cs)</span> stable"</span> | tee /etc/apt/sources.list.d/docker.list &gt; /dev/null</span><br><span class="line"></span><br><span class="line"><span class="comment"># 更新 APT 索引</span></span><br><span class="line">sudo apt update</span><br></pre></td></tr></tbody></table></figure><h3 id="Docker-安装"><a href="#Docker-安装" class="headerlink" title="Docker 安装"></a>Docker 安装</h3><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装最新版本的 Docker</span></span><br><span class="line">sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin</span><br></pre></td></tr></tbody></table></figure><div class="admonition note"><p class="admonition-title">提示</p><p>Docker Buildx 是 Docker 官方推出的一款增强型构建工具，旨在简化和优化多架构容器映像的构建流程。它整合了 QEMU 模拟器和多平台构建能力，使得开发者可以在单一的构建过程中同时为多个平台（如 x86、ARM 等）构建容器映像，极大地提高了构建效率和开发体验。</p></div><h3 id="Docker-启动"><a href="#Docker-启动" class="headerlink" title="Docker 启动"></a>Docker 启动</h3><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 开机自启动 Docker</span></span><br><span class="line">sudo systemctl <span class="built_in">enable</span> docker</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动 Docker</span></span><br><span class="line">sudo systemctl start docker</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 Docker 的运行状态</span></span><br><span class="line">sudo systemctl status docker</span><br></pre></td></tr></tbody></table></figure><div class="admonition note"><p class="admonition-title">普通用户使用 Docker 命令</p><p>如果希望让非 <code>root</code> 用户也能使用 Docker 命令，可以将该用户添加到 <code>docker</code> 用户组，比如 <code>sudo usermod -aG docker your-user-name</code>。</p></div><h3 id="Docker-配置源"><a href="#Docker-配置源" class="headerlink" title="Docker 配置源"></a>Docker 配置源</h3><p>由于 Docker Hub 存在不可抗拒因素，导致在国内无法访问，因此需要配置 Docker 使用国内镜像源。</p><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建配置目录</span></span><br><span class="line">sudo mkdir -p /etc/docker</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建配置文件</span></span><br><span class="line">sudo tee /etc/docker/daemon.json &lt;&lt;-<span class="string">'EOF'</span></span><br><span class="line">{</span><br><span class="line">  <span class="string">"registry-mirrors"</span>: [</span><br><span class="line">     <span class="string">"https://ustc-edu-cn.mirror.aliyuncs.com"</span>,</span><br><span class="line">     <span class="string">"https://mirror.iscas.ac.cn"</span>,</span><br><span class="line">     <span class="string">"https://docker.nju.edu.cn"</span>,</span><br><span class="line">     <span class="string">"https://docker.m.daocloud.io"</span>,</span><br><span class="line">     <span class="string">"https://ccr.ccs.tencentyun.com"</span>,</span><br><span class="line">     <span class="string">"https://dockerhub.timeweb.cloud"</span></span><br><span class="line">    ]</span><br><span class="line">}</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使配置文件生效</span></span><br><span class="line">sudo systemctl daemon-reload</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重启 Docker</span></span><br><span class="line">sudo systemctl restart docker</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 Docker 的运行状态</span></span><br><span class="line">sudo systemctl status docker</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 Docker 的运行信息</span></span><br><span class="line">sudo docker info</span><br></pre></td></tr></tbody></table></figure><h3 id="Docker-验证安裝"><a href="#Docker-验证安裝" class="headerlink" title="Docker 验证安裝"></a>Docker 验证安裝</h3><p>验证是否正确安装了 Docker，以下命令将下载一个测试镜像并在容器中运行它，容器在运行时将输出一条参考消息并退出</p><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo docker run hello-world</span><br></pre></td></tr></tbody></table></figure><h2 id="安装-Docker-Compose"><a href="#安装-Docker-Compose" class="headerlink" title="安装 Docker-Compose"></a>安装 Docker-Compose</h2><div class="admonition note"><p class="admonition-title">提示</p><p>推荐使用第二种方式（基于 <code>curl</code> 命令）进行安装，因为可以指定具体安装哪个版本的 Docker-Compose，而且后期升级维护也更方便。</p></div><h3 id="第一种安装方式"><a href="#第一种安装方式" class="headerlink" title="第一种安装方式"></a>第一种安装方式</h3><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装 Docker-Compose 插件</span></span><br><span class="line">sudo apt install -y docker-compose-plugin</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证 Docker-Compose 插件</span></span><br><span class="line">sudo docker compose version</span><br></pre></td></tr></tbody></table></figure><h3 id="第二种安装方式"><a href="#第二种安装方式" class="headerlink" title="第二种安装方式"></a>第二种安装方式</h3><p>使用 <code>curl</code> 命令来下载 Docker-Compose 的二进制包，除此之外还可以在 <a href="https://github.com/docker/compose/releases">GitHub Releases</a> 上面手动下载对应的文件，然后上传到目标服务器。</p><figure class="highlight sh"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 下载文件</span></span><br><span class="line">sudo curl -L <span class="string">"https://github.com/docker/compose/releases/download/1.29.1/docker-compose-<span class="subst">$(uname -s)</span>-<span class="subst">$(uname -m)</span>"</span> -o /usr/<span class="built_in">local</span>/bin/docker-compose</span><br><span class="line"></span><br><span class="line"><span class="comment"># 文件授权</span></span><br><span class="line">sudo chmod +x /usr/<span class="built_in">local</span>/bin/docker-compose</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证下载</span></span><br><span class="line">sudo docker-compose -v</span><br></pre></td></tr></tbody></table></figure><h2 id="常见安装问题"><a href="#常见安装问题" class="headerlink" title="常见安装问题"></a>常见安装问题</h2><h3 id="info-命令输出警告信息"><a href="#info-命令输出警告信息" class="headerlink" title="info 命令输出警告信息"></a>info 命令输出警告信息</h3><ul><li>问题描述：当 Docker 安装完成之后，执行 <code>docker info</code> 命令，发现有以下警告信息存在：</li></ul><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">WARNING: No cpu cfs quota support</span><br><span class="line">WARNING: No cpu cfs period support</span><br><span class="line">WARNING: No cpu shares support</span><br><span class="line">WARNING: No cpuset support</span><br><span class="line">WARNING: No io.weight support</span><br><span class="line">WARNING: No io.weight (per device) support</span><br><span class="line">WARNING: No io.max (rbps) support</span><br><span class="line">WARNING: No io.max (wbps) support</span><br><span class="line">WARNING: No io.max (riops) support</span><br><span class="line">WARNING: No io.max (wiops) support</span><br></pre></td></tr></tbody></table></figure><ul><li>解决办法：重启 Debian 服务器，让系统重新加载 <code>cgroup</code> 规则</li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="/posts/57bca56f.html">Docker 介绍与安装</a></li><li><a href="/posts/b31f4d18.html">Docker-Compose 安装与使用</a></li><li><a href="https://blog.csdn.net/weixin_49187218/article/details/136935982">Debian 11 上安装 Docker</a></li><li><a href="https://blog.csdn.net/qq_30818545/article/details/124514016">Debian11 之 Docker 稳定版本安装</a></li></ul>]]></content>
    
    
    <summary type="html">本文主要介绍 Debian 13 如何安装 Docker 与 Docker-Compose。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="容器化" scheme="https://www.techgrow.cn/tags/%E5%AE%B9%E5%99%A8%E5%8C%96/"/>
    
    <category term="Debian" scheme="https://www.techgrow.cn/tags/Debian/"/>
    
  </entry>
  
  <entry>
    <title>Debian 13 生产环境安装 Nginx</title>
    <link href="https://www.techgrow.cn/posts/de53727a.html"/>
    <id>https://www.techgrow.cn/posts/de53727a.html</id>
    <published>2026-05-27T11:50:21.000Z</published>
    <updated>2026-05-27T11:50:21.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><h3 id="系统环境"><a href="#系统环境" class="headerlink" title="系统环境"></a>系统环境</h3><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Linux option 6.12.90+deb13-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.90-1 (2026-05-22) x86_64 GNU/Linux</span><br></pre></td></tr></tbody></table></figure><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><h3 id="安装-Supervisor"><a href="#安装-Supervisor" class="headerlink" title="安装 Supervisor"></a>安装 Supervisor</h3><p>Supervisor 主要用于管理 Nginx 的开机自启动（带守护进程）。</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装</span></span><br><span class="line"><span class="keyword"># yum</span> install<span class="params"> -y</span> supervisor</span><br><span class="line"></span><br><span class="line"><span class="comment"># 开机自启动</span></span><br><span class="line"><span class="keyword"># systemctl</span> <span class="built_in">enable</span> supervisord</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动服务</span></span><br><span class="line"><span class="keyword"># systemctl</span> start supervisord</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看服务状态</span></span><br><span class="line"><span class="keyword"># systemctl</span> status supervisord</span><br></pre></td></tr></tbody></table></figure><span id="more"></span><h3 id="创建-Nginx-用户和用户组"><a href="#创建-Nginx-用户和用户组" class="headerlink" title="创建 Nginx 用户和用户组"></a>创建 Nginx 用户和用户组</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 切换root用户</span></span><br><span class="line"><span class="keyword">$ sudo</span><span class="params"> -i</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建nginx用户组</span></span><br><span class="line"><span class="keyword"># groupadd</span> nginx</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建nginx用户（不允许远程登录）</span></span><br><span class="line"><span class="keyword"># useradd</span><span class="params"> -g</span> nginx nginx<span class="params"> -s</span> /bin/<span class="literal">false</span></span><br></pre></td></tr></tbody></table></figure><h3 id="更改最大打开文件描述符数"><a href="#更改最大打开文件描述符数" class="headerlink" title="更改最大打开文件描述符数"></a>更改最大打开文件描述符数</h3><ul><li><a href="/posts/88a10b.html">Centos 7 更改最大文件描述符数</a></li><li><a href="/posts/a29573a6.html">Debian 12 更改最大文件描述符数</a></li></ul><h2 id="Nginx-编译安装"><a href="#Nginx-编译安装" class="headerlink" title="Nginx 编译安装"></a>Nginx 编译安装</h2><h3 id="下载文件"><a href="#下载文件" class="headerlink" title="下载文件"></a>下载文件</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建下载目录</span></span><br><span class="line"><span class="keyword"># mkdir</span><span class="params"> -p</span> /home/nginx/software</span><br><span class="line"></span><br><span class="line"><span class="comment"># 下载</span></span><br><span class="line"><span class="keyword"># cd</span> /home/nginx/software</span><br><span class="line"><span class="keyword"># wget</span> http://nginx.org/download/nginx-1.26.3.tar.gz</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解压</span></span><br><span class="line"><span class="keyword"># tar</span><span class="params"> -xvf</span> nginx-1.26.3.tar.gz</span><br></pre></td></tr></tbody></table></figure><h3 id="编译安装"><a href="#编译安装" class="headerlink" title="编译安装"></a>编译安装</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 进入下载目录</span></span><br><span class="line"><span class="keyword"># cd</span> /home/nginx/software/nginx-1.26.3</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装依赖库</span></span><br><span class="line"><span class="keyword"># apt</span>-get install build-essential libpcre2-dev zlib1g zlib1g-dev openssl libssl-dev</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置</span></span><br><span class="line">./configure \<span class="params"></span></span><br><span class="line"><span class="params">  --user</span>=nginx \<span class="params"></span></span><br><span class="line"><span class="params">  --group</span>=nginx \<span class="params"></span></span><br><span class="line"><span class="params">  --prefix</span>=/usr/<span class="built_in">local</span>/nginx \<span class="params"></span></span><br><span class="line"><span class="params">  --with</span>-pcre \<span class="params"></span></span><br><span class="line"><span class="params">  --with</span>-http_v2_module \<span class="params"></span></span><br><span class="line"><span class="params">  --with</span>-http_ssl_module \<span class="params"></span></span><br><span class="line"><span class="params">  --with</span>-http_realip_module \<span class="params"></span></span><br><span class="line"><span class="params">  --with</span>-http_gzip_static_module \<span class="params"></span></span><br><span class="line"><span class="params">  --with</span>-http_stub_status_module</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编译安装</span></span><br><span class="line"><span class="keyword"># make</span> &amp;&amp; make install</span><br><span class="line"></span><br><span class="line"><span class="comment"># 备份默认的配置文件</span></span><br><span class="line"><span class="keyword"># cd</span> /usr/<span class="built_in">local</span>/nginx/conf</span><br><span class="line"><span class="keyword"># cp</span> nginx.conf nginx.conf.default</span><br><span class="line"></span><br><span class="line"><span class="comment"># 文件授权</span></span><br><span class="line"><span class="keyword"># chown</span><span class="params"> -R</span> nginx:nginx /usr/<span class="built_in">local</span>/nginx</span><br></pre></td></tr></tbody></table></figure><h3 id="配置-Nginx"><a href="#配置-Nginx" class="headerlink" title="配置 Nginx"></a>配置 Nginx</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 编辑nginx的配置文件</span></span><br><span class="line"><span class="keyword"># vim</span> /usr/<span class="built_in">local</span>/nginx/conf/nginx.conf</span><br><span class="line">worker_processes  4;</span><br><span class="line">error_log  logs/error.<span class="built_in">log</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 校验配置文件是否正确</span></span><br><span class="line"><span class="comment"># /usr/local/nginx/sbin/nginx -t</span></span><br></pre></td></tr></tbody></table></figure><h3 id="配置防火墙"><a href="#配置防火墙" class="headerlink" title="配置防火墙"></a>配置防火墙</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 启动防火墙</span></span><br><span class="line"><span class="keyword"># ufw</span> <span class="built_in">enable</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 开放端口</span></span><br><span class="line"><span class="keyword"># ufw</span> allow 80/tcp</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看已开放的端口</span></span><br><span class="line"><span class="keyword"># ufw</span> status verbose</span><br></pre></td></tr></tbody></table></figure><h2 id="Nginx-服务管理"><a href="#Nginx-服务管理" class="headerlink" title="Nginx 服务管理"></a>Nginx 服务管理</h2><h3 id="开机自启动-Nginx"><a href="#开机自启动-Nginx" class="headerlink" title="开机自启动 Nginx"></a>开机自启动 Nginx</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建nginx的supervistor配置文件</span></span><br><span class="line"><span class="keyword"># touch</span> /etc/supervisor/conf.d/nginx.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># 编辑nginx的supervistor配置文件</span></span><br><span class="line"><span class="keyword"># vim</span> /etc/supervisor/conf.d/nginx.conf</span><br><span class="line">[program:nginx]</span><br><span class="line">directory=/usr/<span class="built_in">local</span>/nginx</span><br><span class="line"><span class="built_in">command</span>=/usr/<span class="built_in">local</span>/nginx/sbin/nginx<span class="params"> -g</span> <span class="string">'daemon off;'</span><span class="params"> -c</span> /usr/<span class="built_in">local</span>/nginx/conf/nginx.conf</span><br><span class="line">user=root</span><br><span class="line">numprocs=1</span><br><span class="line">autostart=<span class="literal">true</span></span><br><span class="line">autorestart=<span class="literal">true</span></span><br><span class="line">startretries=10</span><br><span class="line">process_name=%(program_name)s</span><br><span class="line">stdout_logfile_backups=5</span><br><span class="line">stdout_logfile_maxbytes=10MB</span><br><span class="line">stdout_logfile=/var/<span class="built_in">log</span>/supervisor/nginx.<span class="built_in">log</span></span><br><span class="line">stderr_logfile_backups=5</span><br><span class="line">stderr_logfile_maxbytes=10MB</span><br><span class="line">stderr_logfile=/var/<span class="built_in">log</span>/supervisor/nginx-error.<span class="built_in">log</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 上面的配置，主进程会以root用户运行，worker进程会以nginx用户运行</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 重载nginx的supervistor配置文件，会自动启动nginx服务</span></span><br><span class="line"><span class="keyword"># supervisorctl</span> reload</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看nginx的运行状态</span></span><br><span class="line"><span class="keyword"># supervisorctl</span> status nginx</span><br><span class="line">nginx RUNNING   pid 9451, uptime 0:00:56     <span class="comment">#如果输出此日志信息，说明nginx启动成功，否则查看nginx的启动日志来排查问题</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试访问nginx</span></span><br><span class="line"><span class="keyword"># curl</span><span class="params"> -I</span><span class="params"> -X</span> GET 127.0.0.1:80</span><br></pre></td></tr></tbody></table></figure><h3 id="管理-Nginx-服务"><a href="#管理-Nginx-服务" class="headerlink" title="管理 Nginx 服务"></a>管理 Nginx 服务</h3><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 关闭</span></span><br><span class="line"><span class="keyword"># supervisorctl</span> stop nginx</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动</span></span><br><span class="line"><span class="keyword"># supervisorctl</span> start nginx</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重启</span></span><br><span class="line"><span class="keyword"># supervisorctl</span> restart nginx</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看状态</span></span><br><span class="line"><span class="keyword"># supervisorctl</span> status nginx</span><br><span class="line"></span><br><span class="line"><span class="comment"># 平滑更新nginx的配置文件</span></span><br><span class="line"><span class="comment"># /usr/local/nginx/sbin/nginx -s reload</span></span><br></pre></td></tr></tbody></table></figure><h2 id="Nginx-配置文件概述"><a href="#Nginx-配置文件概述" class="headerlink" title="Nginx 配置文件概述"></a>Nginx 配置文件概述</h2><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">安装目录：/usr/local/nginx</span><br><span class="line">配置文件：/usr/local/nginx/conf/nginx.conf</span><br><span class="line">错误日志：/usr/local/nginx/logs/error.log</span><br><span class="line">访问日志：/usr/local/nginx/logs/access.log</span><br><span class="line">nginx的supervistor配置文件：/etc/supervisor/conf.d/nginx.conf</span><br></pre></td></tr></tbody></table></figure><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://juejin.cn/post/7061998261904605214">详解 Debian 系统上如何编译安装 Nginx</a></li></ul>]]></content>
    
    
    <summary type="html">本文主要介绍 Debian 13 生产环境如何编译安装 Nginx。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="Web服务器" scheme="https://www.techgrow.cn/tags/Web%E6%9C%8D%E5%8A%A1%E5%99%A8/"/>
    
    <category term="Debian" scheme="https://www.techgrow.cn/tags/Debian/"/>
    
  </entry>
  
  <entry>
    <title>磁盘阵列（RAID）技术介绍</title>
    <link href="https://www.techgrow.cn/posts/44a8c7ec.html"/>
    <id>https://www.techgrow.cn/posts/44a8c7ec.html</id>
    <published>2026-04-05T14:08:21.000Z</published>
    <updated>2026-04-05T14:08:21.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="RAID-历史"><a href="#RAID-历史" class="headerlink" title="RAID 历史"></a>RAID 历史</h2><p>1988 年美国加州大学伯克利分校的 D.A.Patterson 教授等首次在论文 “A Case of Redundant Array of Inexpensive Disks” 中提出了 RAID 概念，即廉价冗余磁盘阵列（ Redundant Array of Inexpensive Disks）。由于当时大容量磁盘比较昂贵，RAID 的基本思想是将多个容量较小、相对廉价的磁盘进行有机组合，从而以较低的成本获得与昂贵大容量磁盘相当的容量、性能、可靠性。随着磁盘成本和价格的不断降低，” 廉价” 已经变得毫无意义。因此，RAID 咨询委员会（RAID Advisory Board，RAB）决定用 “独立” 替代 “廉价”，于是 RAID 变成了独立磁盘冗余阵列（Redundant Array of Independent Disks）。但这仅仅是名称的变化，实质内容并没有改变。</p><span id="more"></span><h2 id="RAID-等级"><a href="#RAID-等级" class="headerlink" title="RAID 等级"></a>RAID 等级</h2><ul><li><p>RAID 这种设计思想很快被业界接纳，RAID 技术作为高性能、高可靠的存储技术，得到了非常广泛的应用。RAID 主要利用镜像、数据条带和数据校验三种技术来获取高性能、可靠性、容错能力和扩展性，根据对这三种技术的使用策略和组合架构，可以把 RAID 分为不同的等级，以满足不同数据应用的需求。</p></li><li><p>D.A.Patterson 教授等的论文中定义了 RAID0 ~ RAID6 原始 RAID 等级。随后存储厂商又不断推出 RAID7、RAID10、RAID01、RAID50、RAID53、RAID100 等 RAID 等级，但这些并无统一的标准。<strong>目前业界与学术界公认的标准是 RAID0 ~ RAID6，而在实际应用领域中使用最多的 RAID 等级是 RAID0、RAID1、RAID3、RAID5、RAID6 和 RAID10</strong>。</p></li><li><p>RAID 每一个等级代表一种实现方法和技术，等级之间并无高低之分。在实际应用中，应当根据用户的数据特点，综合考虑可用性、性能和成本来选择合适的 RAID 等级，以及具体的实现方式。</p></li></ul><div class="admonition note"><p class="admonition-title">RAID 等级的读法</p><p>RAID0、RAID1、RAID10、RAID100 等通常按数字逐位读出，例如 RAID10 读作 "RAID 一零"，而不是 "RAID 十"。</p></div><h2 id="RAID-分类"><a href="#RAID-分类" class="headerlink" title="RAID 分类"></a>RAID 分类</h2><p>从实现角度来看，RAID 主要分为软 RAID、硬 RAID 以及混合 RAID 三种。</p><h3 id="软-RAID"><a href="#软-RAID" class="headerlink" title="软 RAID"></a>软 RAID</h3><p>所有功能都是由操作系统和 CPU 来完成，没有独立的 RAID 控制处理芯片和 I/O 处理芯片，性能是最低的，但成本也是最低。</p><h3 id="硬-RAID"><a href="#硬-RAID" class="headerlink" title="硬 RAID"></a>硬 RAID</h3><p>配备了专门的 RAID 控制处理芯片和 I/O 处理芯片以及阵列缓冲，不占用 CPU 资源。性能是最高的，但成本也是最高。</p><h3 id="混合-RAID"><a href="#混合-RAID" class="headerlink" title="混合 RAID"></a>混合 RAID</h3><p>具备 RAID 控制处理芯片，但没有专门的 I/O 处理芯片，需要 CPU 和驱动程序来处理 I/O。性能和成本介于软 RAID 和硬 RAID 之间。</p><h2 id="RAID-关键技术"><a href="#RAID-关键技术" class="headerlink" title="RAID 关键技术"></a>RAID 关键技术</h2><h3 id="镜像技术"><a href="#镜像技术" class="headerlink" title="镜像技术"></a>镜像技术</h3><ul><li><p>镜像技术是一种冗余技术，为磁盘提供数据备份功能，防止磁盘发生故障而造成数据丢失。对于 RAID 而言，镜像技术最典型地的用法就是，同时在磁盘阵列中产生两个完全相同的数据副本，并且分布在两个不同的磁盘上。</p></li><li><p>镜像技术提供了完全的数据冗余能力，当一个数据副本失效不可用时，外部系统仍可正常访问另一副本，不会对应用系统运行和性能产生影响。而且，镜像不需要额外的计算和校验，故障修复非常快，直接复制即可。</p></li><li><p><strong>镜像技术支持从多个副本并发读取以提升读性能；写入时会同时写入所有副本（支持并行写），但由于需要等待全部磁盘写入完成，写性能通常不会提升，反而可能略有下降，最终写入速度取决于写入最慢的那块磁盘。</strong></p></li><li><p>镜像技术提供了非常高的数据安全性，其代价也是非常昂贵的，需要至少双倍的存储空间。高成本限制了镜像的广泛应用，主要应用于至关重要的数据保护，这种场合下的数据丢失可能会造成非常巨大的损失。</p></li></ul><h3 id="数据条带化技术"><a href="#数据条带化技术" class="headerlink" title="数据条带化技术"></a>数据条带化技术</h3><ul><li>数据条带化技术是一种自动将 I/O 操作负载均衡到多个物理磁盘上的技术。更具体地说就是，将一块连续的数据分成很多小部分并把它们分别存储到不同磁盘上。这就能使多个进程可以并发访问数据的多个不同部分，从而获得最大程度上的 I/O 并行能力，极大地提升性能。</li></ul><h3 id="数据校验技术"><a href="#数据校验技术" class="headerlink" title="数据校验技术"></a>数据校验技术</h3><ul><li><p>数据校验技术是指 RAID 在写入数据的同时进行校验计算，并将得到的校验数据存储在 RAID 成员磁盘中。校验数据可以集中保存在某个磁盘或分散存储在多个不同磁盘中。当其中一部分数据出错时，就可以对剩余数据和校验数据进行反校验计算重建丢失的数据。</p></li><li><p>数据校验技术相对于镜像技术的优势在于节省大量存储开销，但由于每次数据读写都要进行大量的校验运算，对计算机的 CPU 运算速度要求很高，<strong>且必须使用硬件 RAID 控制器</strong>。在数据重建恢复方面，检验技术比镜像技术复杂得多且慢得多。</p></li></ul><h2 id="RAID-常见等级详解"><a href="#RAID-常见等级详解" class="headerlink" title="RAID 常见等级详解"></a>RAID 常见等级详解</h2><h3 id="JBOD"><a href="#JBOD" class="headerlink" title="JBOD"></a>JBOD</h3><p><img data-src="../../../asset/2026/04/disk-raid-1.png"></p><ul><li><strong>JBOD（Just a Bunch of Disks，磁盘簇），表示一个没有控制软件提供协调控制的磁盘集合，这是 RAID 区别与 JBOD 的主要因素。</strong>JBOD 将多个物理磁盘串联起来，提供一个巨大的逻辑磁盘。</li><li>JBOD 的数据存放机制是由第一块磁盘开始按顺序往后存储，当前磁盘的存储空间用完后，再依次往后面的磁盘存储数据。JBOD 存储性能完全等同于单块磁盘，而且也不提供数据安全保护。</li><li>JBOD 本质上只是简单提供一种扩展存储空间的机制，JBOD 可用存储容量等于所有成员磁盘的存储空间之和。</li><li>JBOD 通常是指磁盘柜，而不论其是否提供 RAID 功能。不过，JBOD 并非官方术语，官方称为 <code>Spanning</code>。</li></ul><h3 id="RAID0"><a href="#RAID0" class="headerlink" title="RAID0"></a>RAID0</h3><p><img data-src="../../../asset/2026/04/disk-raid-2.png"></p><ul><li><p><strong>RAID0 是一种简单的、无数据校验的数据条带化技术。</strong>实际上不是一种真正的 RAID，因为它并不提供任何形式的冗余策略。RAID0 将所在磁盘条带化后组成大容量的存储空间，将数据分散存储在所有磁盘中，以独立访问方式实现多块磁盘的并读访问。</p></li><li><p><strong>一个由 N 块磁盘组成的 RAID0，它的理论读写性能是单个磁盘性能的 N 倍</strong>，但由于总线带宽等多种因素的限制，实际的性能提升低于理论值。</p></li><li><p>由于 RAID0 可以并发执行 I/O 操作，总线带宽得到充分利用，并且不需要进行数据校验，因此 <strong>RAID0 的性能在所有 RAID 等级中是最高的</strong>。</p></li><li><p>RAID0 具有低成本、高读写性能、100% 的高存储空间利用率等优点，但是它不提供数据冗余保护，一旦磁盘损坏，将无法恢复数据。</p></li><li><p>RAID0 的适用场景：</p><ul><li>对数据的顺序读写速度要求不高，对数据的安全性和可靠性要求不高，但对系统性能要求很高的场景，比如：</li><li>视频剪辑 / 渲染缓存（临时素材处理，追求高读写速度）</li><li>游戏加载盘（加快加载速度，对数据丢失不敏感）</li><li>缓存服务器（如临时缓存、CDN 缓存数据）</li><li>大数据 / 科学计算临时数据盘（中间结果可丢失）</li></ul></li><li><p>RAID0 与 JBOD 相同点：</p><ul><li>存储容量：都是成员磁盘容量总和</li><li>磁盘利用率都是 100%，即都没有做任何的数据冗余备份</li></ul></li><li><p> RAID0 与 JBOD 不同点：</p><ul><li>JBOD：数据是顺序存放的，一个磁盘存满后才会开始存放到下一个磁盘</li><li> RAID0：通过数据条带技术将数据分布到多个磁盘，实现并行读写，在理想情况下其读写性能可接近单盘的 N 倍（N 为磁盘数量），但实际性能会受到硬件和访问模式等因素影响</li></ul></li></ul><h3 id="RAID1"><a href="#RAID1" class="headerlink" title="RAID1"></a>RAID1</h3><p><img data-src="../../../asset/2026/04/disk-raid-3.png"></p><ul><li><p><strong>RAID1 是一种镜像技术，它将数据完全一致地分别写到工作磁盘和镜像磁盘，它的磁盘空间利用率为 50%。</strong></p></li><li><p><strong>RAID1 支持从多个副本并发读取以提升读性能，写入时会同时写入所有副本（支持并行写），但由于需要等待全部磁盘写入完成，写性能通常不会提升，反而可能略有下降</strong>。</p></li><li><p>RAID1 提供了最佳的数据保护，一旦工作磁盘发生故障，系统将自动切换到镜像磁盘，不会影响使用。</p></li><li><p>RAID1 是为了增强数据安全性使两块磁盘数据呈现完全镜像，从而达到安全性好、技术简单、管理方便。RAID1 拥有完全容错的能力，但实现成本高。</p></li><li><p>RAID1 的适用场景：</p><ul><li>对顺序读写性能要求较高，或对数据安全性要求较高的场景，比如：</li><li>数据库服务器（如订单、用户数据，要求高可靠性）</li><li>金融交易系统（不允许丢失数据）</li><li>企业文件服务器 / NAS（重要文件存储）</li><li>操作系统盘（保证系统不因磁盘损坏而宕机）</li></ul></li></ul><h3 id="RAID10"><a href="#RAID10" class="headerlink" title="RAID10"></a>RAID10</h3><p><img data-src="../../../asset/2026/04/disk-raid-4.png"></p><ul><li><p>RAID10 是一个 RAID1 与 RAID0 的组合体，所以它继承了 RAID1 的高安全性和 RAID0 的高性能。</p></li><li><p>RAID10 是先进行数据镜像（RAID1），再在多个镜像组之间进行数据条带化（RAID0），即先保证数据冗余，再提升读写性能。</p></li></ul><div class="admonition warning"><p class="admonition-title">RAID10 与 RAID01 的核心区别</p><ul><li>RAID10：先冗余（镜像），再提速（条带化）</li><li>RAID01：先提速（条带化），再冗余（镜像），可靠性不如 RAID10</li></ul></div><h3 id="RAID01"><a href="#RAID01" class="headerlink" title="RAID01"></a>RAID01</h3><p><img data-src="../../../asset/2026/04/disk-raid-5.png"></p><ul><li><p>RAID01 是一个 RAID0 与 RAID1 的组合体，所以它继承了 RAID0 的高性能和 RAID1 的高安全性。</p></li><li><p>RAID01 是先进行数据条带化（RAID0），再对条带后的数据整体做镜像（RAID1），即先提升读写性能，再保证数据冗余。</p></li><li><p>RAID01 的可靠性低于 RAID10，一旦某个条带组中的一块磁盘损坏，整个条带组都会失效，从而使磁盘阵列退化为单一镜像组，容错能力较弱。</p></li></ul><div class="admonition warning"><p class="admonition-title">RAID10 与 RAID01 的选择建议</p><p>RAID10 的容错率（可靠性）要比 RAID01 高，所以生产环境中一般是不使用 RAID01，而是使用 RAID10。</p></div><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://cloud.tencent.com/developer/article/2046751">RAID 技术全解图解</a></li><li><a href="https://zhuanlan.zhihu.com/p/51170719">RAID 磁盘阵列是什么</a></li></ul>]]></content>
    
    
    <summary type="html">本文主要介绍磁盘阵列技术，包括 RAID0、RAID1、RAID10、RAID01 等。</summary>
    
    
    
    
    <category term="Linux运维" scheme="https://www.techgrow.cn/tags/Linux%E8%BF%90%E7%BB%B4/"/>
    
  </entry>
  
  <entry>
    <title>Linux 网络编程随笔</title>
    <link href="https://www.techgrow.cn/posts/a3872b61.html"/>
    <id>https://www.techgrow.cn/posts/a3872b61.html</id>
    <published>2026-03-23T13:55:33.000Z</published>
    <updated>2026-03-23T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="TCP-连接，一端断电和进程崩溃有什么区别"><a href="#TCP-连接，一端断电和进程崩溃有什么区别" class="headerlink" title="TCP 连接，一端断电和进程崩溃有什么区别"></a>TCP 连接，一端断电和进程崩溃有什么区别</h2><span id="more"></span><blockquote><p>问题剖析</p></blockquote><p>TCP 连接，没有打开 Keepalive 选项，没有数据交互，现在一端突然断电或者一端的进程崩溃了，这两种情况有什么区别呢？这是腾讯的一道面试题，其中有几个关键词：</p><ul><li>TCP 连接没有开启 Keepalive；</li><li>一直没有数据交互；</li><li>进程崩溃；</li><li>主机宕机；</li></ul><blockquote><p>什么是 TCP Keepalive</p></blockquote><p>TCP Keepalive 其实就是 TCP 的保活机制，它的工作原理如下：</p><p><img data-src="../../../asset/2026/03/tcp-keepalive-1.png"></p><p>如果两端的 TCP 连接一直没有数据交互，达到了触发 TCP 保活机制的条件，那么系统内核里的 TCP 协议栈就会发送探测报文。</p><ul><li>如果对端程序是正常工作的。当 TCP 保活的探测报文发送给对端，对端会正常响应，这样 <strong>TCP 保活时间会被重置</strong>，等待下一个 TCP 保活时间的到来。</li><li>如果对端主机宕机，或对端由于其他原因导致报文不可达。当 TCP 保活的探测报文发送给对端后，石沉大海，没有响应，连续几次，达到保活探测次数后，<strong>TCP 会报告该 TCP 连接已经消亡</strong>。</li></ul><p>所以，TCP 保活机制可以在双方没有数据交互的情况，通过探测报文，来确定对方的 TCP 连接是否存活。<br>注意，应用程序若想使用 TCP 保活机制需要通过 Socket 接口设置 <code>SO_KEEPALIVE</code> 选项才能够生效，如果没有设置，那么就无法使用 TCP 保活机制。</p><blockquote><p>主机宕机</p></blockquote><p>在没有开启 TCP Keepalive，且双方一直没有数据交互的情况下，如果客户端的「主机宕机」了，会发生什么？</p><p>客户端主机宕机了，<strong>服务端是无法感知到的</strong>，再加上服务端没有开启 TCP Keepalive，又没有数据交互的情况下，<strong>服务端的 TCP 连接将会一直处于 ESTABLISHED 连接状态，直到服务端重启进程</strong>。</p><p>所以，这里可以得知一个点，在没有使用 TCP 保活机制且双方不传输数据的情况下，一方的 TCP 连接处在 <code>ESTABLISHED</code> 状态，并不代表另一方的连接还一定正常。</p><blockquote><p>进程崩溃</p></blockquote><p>在没有开启 TCP Keepalive，且双方一直没有数据交互的情况下，如果服务端的「进程崩溃」了，会发生什么？</p><p>TCP 的连接信息是由系统内核维护的，所以当服务端的进程崩溃后，系统内核需要回收该进程的所有 TCP 连接资源，于是系统内核会发送第一次挥手 FIN 报文，后续的挥手过程也都是在系统内核完成，并不需要进程参与，所以即使服务端的进程退出了，还是能与客户端完成 TCP 四次挥手的过程。</p><p>这里可以尝试做实验，使用 <code>kill -9</code> 来模拟服务端进程崩溃的情况，发现在 kill 掉进程后，服务端会发送 FIN 报文，与客户端进行四次挥手。</p><p>所以，即使没有开启 TCP Keepalive，且双方也没有数据交互的情况下，如果任意一方的进程发生了崩溃，这个过程系统内核是可以感知的到的，于是系统内核就会发送 FIN 报文给对方，然后与对方进行 TCP 四次挥手。</p><blockquote><p>有数据传输的场景</p></blockquote><p>以上就是对这道面试题的回答，接下来我们看看在「有数据传输」的场景下的一些异常情况：</p><p>第一种，客户端主机宕机，又迅速重启，会发生什么？<br>第二种，客户端主机宕机，一直没有重启，会发生什么？</p><hr><p>假设客户端主机宕机，又迅速重启，会发生什么？</p><p>在客户端主机宕机后，服务端向客户端发送的报文会得不到任何的响应，在一定时长后，服务端就会触发超时重传机制，重传未得到响应的报文给客户端。</p><p>在服务端重传报文的过程中，客户端主机重启完成后，客户端的内核就会接收重传的报文，然后根据报文的信息传递给对应的进程：</p><ul><li>如果客户端主机上没有进程绑定该 TCP 报文的目标端口号，那么客户端内核就会<strong>回复 RST 报文，重置该 TCP 连接</strong>；</li><li>如果客户端主机上有进程绑定该 TCP 报文的目标端口号，由于客户端主机重启后，之前的 TCP 连接的数据结构已经丢失了，客户端内核里协议栈会发现找不到该 TCP 连接的 Socket 结构体，于是就会<strong>回复 RST 报文，重置该 TCP 连接</strong>。</li></ul><p>所以，<strong>只要有任意一方的主机重启完成后，收到之前 TCP 连接的报文，都会回复 RST 报文给对端，以断开连接</strong>。</p><hr><p>假设客户端主机宕机，一直没有重启，会发生什么？</p><p>这种情况，服务端超时重传报文的次数达到一定阈值后，系统内核就会判定出该 TCP 有问题，然后通过 Socket 接口告诉应用程序该 TCP 连接出问题了，于是服务端的 TCP 连接就会断开。</p><p>那 TCP 的数据报文具体重传几次呢？在 Linux 系统中，提供一个叫 <code>tcp_retries2</code> 配置项（<code>cat /proc/sys/net/ipv4/tcp_retries2</code>），默认值是 <code>15</code>；这个内核参数是控制在 TCP 连接建立的情况下，超时重传的最大次数。</p><p>不过 <code>tcp_retries2</code> 设置了 15 次，并不代表 TCP 超时重传了 15 次才会通知应用程序终止该 TCP 连接，内核会根据 <code>tcp_retries2</code> 设置的值，计算出一个 <code>timeout</code>（如果 <code>tcp_retries2 = 15</code>，那么计算得到的 <code>timeout = 924600ms</code>），如果重传间隔超过这个 <code>timeout</code>，则认为超过了阈值，就会停止重传，然后就会断开 TCP 连接。</p><p>在发生超时重传的过程中，每一轮的超时时间（RTO）都是倍数增长的，比如：如果第一轮 RTO 是 200 毫秒，那么第二轮 RTO 是 400 毫秒，第三轮 RTO 是 800 毫秒，以此类推。</p><p>而 RTO 是基于 RTT（一个包的往返时间）来计算的，如果 RTT 较大，那么计算出来的 RTO 就越大，那么经过几轮重传后，很快就达到了上面的 <code>timeout</code> 阀值了。</p><p>举个例子，如果 <code>tcp_retries2 = 15</code>，那么计算得到的 <code>timeout = 924600ms</code>，如果重传总间隔时长超过了 <code>924600ms</code> 就会停止重传，然后就会断开 TCP 连接：</p><ul><li>如果 RTT 比较小，那么 RTO 初始值就约等于下限 <code>200ms</code>，也就是第一轮的超时时间是 200 毫秒，由于 <code>timeout</code> 总时长是 <code>924600ms</code>，表现出来的现象刚好就是重传了 15 次，超过了 <code>timeout</code> 值，从而断开 TCP 连接</li><li>如果 RTT 比较大，假设 RTO 初始值计算得到的是 <code>1000ms</code>，也就是第一轮的超时时间是 1 秒，那么根本不需要重传 15 次，重传总间隔时长就会超过 <code>924600ms</code>。</li></ul><p>最小 RTO 和最大 RTO 已经是在 Linux 内核中定义好了：</p><figure class="highlight c"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TCP_RTO_MAX ((unsigned)(120*HZ))</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> TCP_RTO_MIN ((unsigned)(HZ/5))</span></span><br></pre></td></tr></tbody></table></figure><p>Linux <code>2.6+</code> 使用 1000 毫秒的 HZ，因此 <code>TCP_RTO_MIN</code> 约为 200 毫秒，<code>TCP_RTO_MAX</code> 约为 120 秒。</p><p>如果 <code>tcp_retries</code> 设置为 15，且 RTT 比较小，那么 RTO 初始值就约等于下限 <code>200ms</code>，这意味着它需要 <code>924.6</code> 秒才会将断开的 TCP 连接并通知给上层（即应用程序），每一轮的 RTO 增长关系如下表格：</p><table><thead><tr><th align="center">Retransmission</th><th align="center">RTO (ms)</th><th align="center">Time before a timeout (secs)</th><th align="center">Time before a timeout (mins)</th></tr></thead><tbody><tr><td align="center">1</td><td align="center">200</td><td align="center">0.2 secs</td><td align="center">0.0 mins</td></tr><tr><td align="center">2</td><td align="center">400</td><td align="center">0.6 secs</td><td align="center">0.0 mins</td></tr><tr><td align="center">3</td><td align="center">800</td><td align="center">1.4 secs</td><td align="center">0.0 mins</td></tr><tr><td align="center">4</td><td align="center">1600</td><td align="center">3.0 secs</td><td align="center">0.1 mins</td></tr><tr><td align="center">5</td><td align="center">3200</td><td align="center">6.2 secs</td><td align="center">0.1 mins</td></tr><tr><td align="center">6</td><td align="center">6400</td><td align="center">12.6 secs</td><td align="center">0.2 mins</td></tr><tr><td align="center">7</td><td align="center">12800</td><td align="center">25.4 secs</td><td align="center">0.4 mins</td></tr><tr><td align="center">8</td><td align="center">25600</td><td align="center">51.0 secs</td><td align="center">0.9 mins</td></tr><tr><td align="center">9</td><td align="center">51200</td><td align="center">102.2 secs</td><td align="center">1.7 mins</td></tr><tr><td align="center">10</td><td align="center">102400</td><td align="center">204.6 secs</td><td align="center">3.4 mins</td></tr><tr><td align="center">11</td><td align="center">120000</td><td align="center">324.6 secs</td><td align="center">5.4 mins</td></tr><tr><td align="center">12</td><td align="center">120000</td><td align="center">444.6 secs</td><td align="center">7.4 mins</td></tr><tr><td align="center">13</td><td align="center">120000</td><td align="center">564.6 secs</td><td align="center">9.4 mins</td></tr><tr><td align="center">14</td><td align="center">120000</td><td align="center">684.6 secs</td><td align="center">11.4 mins</td></tr><tr><td align="center">15</td><td align="center">120000</td><td align="center">804.6 secs</td><td align="center">13.4 mins</td></tr><tr><td align="center">16</td><td align="center">120000</td><td align="center">924.6 secs</td><td align="center">15.4 mins</td></tr></tbody></table><blockquote><p>总结</p></blockquote><p>如果「客户端进程崩溃」，客户端的进程在发生崩溃的时候，内核会发送 FIN 报文，与服务端进行四次挥手，以此断开 TCP 连接。</p><p>如果「客户端主机宕机」，那么是不会发生四次挥手的，具体后续会发生什么，还要看服务端会不会发送数据。</p><ul><li>如果服务端会发送数据，由于客户端已经不存在，收不到数据报文的响应报文，服务端的数据报文会超时重传，当重传总间隔时长达到一定阈值（内核会根据 <code>tcp_retries2</code> 设置的值计算出一个阈值）后，会断开 TCP 连接；</li><li>如果服务端一直不会发送数据，再看服务端有没有开启 TCP Keepalive 机制？<ul><li>如果服务端有开启 TCP Keepalive 机制，服务端在一段时间没有进行数据交互时，会触发 TCP Keepalive 机制，探测对方是否存在，如果探测到对方已经消亡，则会断开自身的 TCP 连接；</li><li>如果服务端没有开启 TCP Keep 机制，服务端的 TCP 连接会一直存在，并且一直保持在 <code>ESTABLISHED</code> 状态。</li></ul></li></ul>]]></content>
    
    
    <summary type="html">本文主要记录 Linux 网络编程笔记。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="开发随笔" scheme="https://www.techgrow.cn/tags/%E5%BC%80%E5%8F%91%E9%9A%8F%E7%AC%94/"/>
    
    <category term="Linux系统编程" scheme="https://www.techgrow.cn/tags/Linux%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B/"/>
    
    <category term="网络编程" scheme="https://www.techgrow.cn/tags/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>全球最强 AI 编程工具排行榜（2026 版）</title>
    <link href="https://www.techgrow.cn/posts/98d1001d.html"/>
    <id>https://www.techgrow.cn/posts/98d1001d.html</id>
    <published>2026-03-11T14:25:49.000Z</published>
    <updated>2026-03-11T14:25:49.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>由于人工智能（AI）发展迅速、日新月异，文中所列 AI 编程工具排行榜仅供参考，具有一定时效性，可能会随时间变化而失去准确性。截止 2026 年，AI 编程工具已经从早期单纯的 “代码补全工具”（只能基于上下文提示补几行或者一段代码），演进为具备更强自主能力的 AI Agent。这类 AI Agent 不仅能够理解需求，还可以自动完成从方案设计、代码编写、调试测试到迭代优化的一整套开发流程，甚至可以调用工具链、运行代码并根据结果进行自我修正。换句话说，AI 正在从 “辅助写代码” 转变为 “参与甚至主导功能开发” 的角色，极大提升了开发效率，也正在重塑软件工程的工作方式。</p><span id="more"></span><h2 id="AI-编程工具的三种形态"><a href="#AI-编程工具的三种形态" class="headerlink" title="AI 编程工具的三种形态"></a>AI 编程工具的三种形态</h2><table><thead><tr><th>维度</th><th> Copilot（以 GitHub Copilot 为代表）</th><th>AI IDE（以 Cursor 为代表）</th><th>AI Agent（以 Codex 为代表）</th></tr></thead><tbody><tr><td>核心定位</td><td>写代码辅助</td><td>人机协作开发环境</td><td>自动化开发执行者</td></tr><tr><td>产品形态</td><td> IDE 插件</td><td> IDE（VSCode 分支）</td><td>云端 Agent / CLI</td></tr><tr><td> 控制权</td><td>完全在你</td><td>人机协同</td><td> AI 主导</td></tr><tr><td>工作方式</td><td>你写 → 它补全</td><td>你说 → 一起改</td><td>你说 → 它完成</td></tr><tr><td>上下文理解</td><td>低（局部代码）</td><td>中（项目级）</td><td>高（全项目 + 任务）</td></tr><tr><td>是否能自主规划</td><td>不会</td><td>有一定能力（弱 Agent）</td><td>强（任务拆解 + 执行）</td></tr><tr><td>是否自动执行命令</td><td>不支持</td><td>部分支持</td><td>支持（跑测试 / 改代码 / 提 PR）</td></tr><tr><td>典型场景</td><td>写函数、补代码</td><td>重构、调试、解释代码</td><td>从 0 开发模块、自动修复</td></tr><tr><td>学习成本</td><td>低</td><td>中等</td><td>较高</td></tr><tr><td>可控性</td><td>极强</td><td>较强</td><td>较弱</td></tr><tr><td>效率提升点</td><td>提速写代码</td><td>提升开发体验</td><td>替你完成开发任务</td></tr><tr><td>类比行为</td><td>代码自动补全</td><td>副驾驶</td><td>代驾司机</td></tr></tbody></table><hr><table><thead><tr><th>类型</th><th>类比角色</th><th>行为</th><th>使用场景</th></tr></thead><tbody><tr><td> Copilot</td><td> 实习生</td><td>你写一句，他帮你补一句</td><td>日常写代码 → Copilot 就够</td></tr><tr><td> Cursor</td><td> 高级同事</td><td>你们一起改代码、讨论方案</td><td>复杂项目开发 → Cursor 最舒服</td></tr><tr><td> Codex</td><td> 外包团队</td><td>你提需求，它直接交付结果</td><td>自动生成系统 / 批量任务 → Codex 最强</td></tr></tbody></table><h2 id="全球-Top-6-AI-编程工具"><a href="#全球-Top-6-AI-编程工具" class="headerlink" title="全球 Top 6 AI 编程工具"></a>全球 Top 6 AI 编程工具</h2><table><thead><tr><th>全球排名</th><th>名称</th><th>公司</th><th>类型</th><th>使用场景</th></tr></thead><tbody><tr><td> 1</td><td>Claude Code</td><td>Anthropic</td><td> 超级 Agent</td><td> 复杂系统设计：架构级开发、深度推理</td></tr><tr><td> 2</td><td>Codex</td><td>OpenAI</td><td> 工程 Agent</td><td>PR 执行 / 并行任务 / 自动化开发</td></tr><tr><td> 3</td><td>Cursor</td><td>Anysphere</td><td>IDE 型</td><td>日常开发主力：项目级编码 + AI IDE</td></tr><tr><td>4</td><td>GitHub Copilot</td><td>Microsoft</td><td> 工具型</td><td>代码补全 / 企业标准开发工具</td></tr><tr><td> 5</td><td>Windsurf</td><td>Codeium</td><td> 工程型</td><td>超大代码库维护 / 团队工程</td></tr><tr><td> 6</td><td>Replit AI</td><td>Replit</td><td> 产品型</td><td>快速做产品 / 一键生成并部署项目</td></tr></tbody></table><h3 id="Claude-Code（Anthropic）"><a href="#Claude-Code（Anthropic）" class="headerlink" title="Claude Code（Anthropic）"></a>Claude Code（Anthropic）</h3><p>最强 “AI 程序员” 本体（Agent 级别）</p><ul><li>综合能力：<ul><li>目前在多项编程与 Agent 评测中排名第一梯队</li><li>可独立完成复杂软件开发任务</li></ul></li><li>特点：<ul><li>直接 “接管任务”：能够自动写功能、改代码、跑测试</li><li>支持超大上下文（10 万 + 行代码）</li><li>多文件重构能力非常强</li><li>本质：不是辅助，而是 “替你干活”</li><li> 更偏 “系统级开发 Agent” ，而不是代码补全工具</li></ul></li><li>优点：<ul><li>极强的任务执行能力（从需求到代码闭环）</li><li>复杂逻辑推理能力强，适合架构级问题</li><li>多文件协同修改能力领先</li><li>对工程任务自动化支持很强</li></ul></li><li>缺点：<ul><li>对 UI / 产品体验类任务不如 Replit AI 直观</li><li>对任务描述质量要求较高（需要清晰拆解）</li><li>使用成本相对较高（学习 Agent 工作方式）</li></ul></li><li>适合：<ul><li>架构设计</li><li>大型项目重构</li><li> AI Agent 开发</li><li>复杂工程任务自动化</li></ul></li></ul><h3 id="Codex（OpenAI）"><a href="#Codex（OpenAI）" class="headerlink" title="Codex（OpenAI）"></a>Codex（OpenAI）</h3><p>最强 “AI 软件工程 Agent”（云端并行开发系统）</p><ul><li>综合能力：<ul><li>面向真实工程任务的 AI 编程 Agent</li><li> 可并行处理多个开发任务（PR / Feature / Bug Fix）</li><li>在复杂代码库中表现稳定（跨任务 PR 通过率较高）</li></ul></li><li>特点：<ul><li>云端沙盒执行：在独立环境中写代码、跑测试、提交 PR</li><li> 支持多 Agent 并行工作（Worktree / 多线程任务）</li><li>可自动完成：功能开发 / Bug 修复 / 代码迁移 / 重构</li><li>内置测试驱动执行（会跑测试直到通过）</li><li>面向 “端到端软件交付”，而不是单次代码生成</li><li>已支持 IDE / CLI / 云端多入口使用</li><li>可在后台自动执行长期工程任务</li></ul></li><li>优点：<ul><li>工程执行能力极强（可直接产出可合并 PR）</li><li>支持大规模代码库任务拆解与并行处理</li><li>自动化程度高，可持续运行在后台</li><li>非常适合真实团队开发流程（Git Workflow 友好）</li></ul></li><li>缺点：<ul><li>对非结构化需求（创意 / 产品想法）不如 Replit AI 直接</li><li>需要较规范的代码仓库结构才能发挥最佳效果</li><li>更偏工程协作，不适合 “零基础快速做 Demo” </li><li>对任务拆解能力要求较高</li></ul></li><li>适合：<ul><li>大型软件项目开发</li><li>工程级重构 / 迁移 / Bug 修复</li><li>团队并行开发（多任务 PR 管理）</li><li>AI 驱动的软件工程自动化</li><li>复杂后端 / 系统级项目维护</li></ul></li></ul><h3 id="Cursor（AI-原生-IDE）"><a href="#Cursor（AI-原生-IDE）" class="headerlink" title="Cursor（AI 原生 IDE）"></a>Cursor（AI 原生 IDE）</h3><p>最强 AI 原生 IDE（开发者体验天花板）</p><ul><li>综合排名：<ul><li>常年排名 Top 1 ~ 2（AI IDE 领域）</li></ul></li><li>综合能力：<ul><li>基于 VSCode 深度重构的 AI IDE</li><li>AI 深度融入编辑器，而不是插件形式</li><li>支持全代码库理解与跨文件编辑</li><li>内置多模型能力（Claude Code / GPT / Gemini 等）</li></ul></li><li>特点：<ul><li>支持 Agent 模式（自动改代码 + 重构 + 任务执行）</li><li>预测 “下一步修改”（不仅是单行补全）</li><li>可理解整个项目结构</li><li>支持多文件级别编辑与系统性重构</li><li> IDE + AI 深度融合，交互体验非常流畅</li></ul></li><li>优点：<ul><li>项目理解能力强（比 Copilot 更深入）</li><li>开发体验极佳（接近 “AI 操作 IDE” ）</li><li>非常适合全栈开发与高频编码场景</li><li>对中小型项目效率提升非常明显</li></ul></li><li>缺点：<ul><li>对超大规模工程（百万行级）不如 Windsurf</li><li> 自动执行复杂工程任务能力不如 AI Agent（如 Claude Code / Codex）</li><li>企业级权限与合规能力相对较弱</li></ul></li><li>适合：<ul><li>日常开发主力工具</li><li>独立开发者 / 一人公司</li><li>全栈工程师</li><li>中小型项目开发</li><li>高效率迭代型开发场景</li></ul></li></ul><h3 id="GitHub-Copilot（编程助手）"><a href="#GitHub-Copilot（编程助手）" class="headerlink" title="GitHub Copilot（编程助手）"></a>GitHub Copilot（编程助手）</h3><p>最普及、最稳定的 AI 编程助手</p><ul><li>使用率：<ul><li>市场使用率全球第一（行业标准工具）</li></ul></li><li>功能：<ul><li>代码补全</li><li>代码生成</li><li>代码重构</li><li>代码解释</li></ul></li><li>特点：<ul><li>最强 IDE 兼容性（VSCode / JetBrains / Neovim）</li><li>GitHub 全流程集成（PR、CI、代码评审）</li><li>自动补全速度最快</li><li>学习成本最低，上手最快</li></ul></li><li>优点：<ul><li>稳定可靠，企业接受度最高</li><li>集成生态完善（GitHub 工作流）</li><li>响应速度快</li></ul></li><li>缺点：<ul><li>上下文理解能力不如 Claude Code / Cursor</li><li> 更偏 “代码补全型工具”，而非任务型 Agent</li></ul></li><li> 适合：<ul><li>企业开发</li><li>多语言项目</li><li>稳定优先</li><li>代码补全为主的开发场景</li></ul></li></ul><h3 id="Windsurf（超大代码库）"><a href="#Windsurf（超大代码库）" class="headerlink" title="Windsurf（超大代码库）"></a>Windsurf（超大代码库）</h3><p>超大代码库处理王者</p><ul><li>特点：<ul><li>支持 “百万行代码级别” 理解</li><li>面向大型工程与团队协作设计</li><li>多模型支持（Claude Code / GPT 等）</li><li>强调代码库级别的理解与改造能力</li></ul></li><li>优点：<ul><li>超大仓库理解能力强</li><li>更偏工程级（系统维护能力强）</li><li>适合复杂代码重构</li></ul></li><li>缺点：<ul><li>学习成本略高</li><li>产品体验不如 Cursor 轻量直观</li></ul></li><li>适合：<ul><li>大厂项目</li><li>微服务 / 单体巨型仓库</li><li>团队协作开发</li><li>超大规模工程维护</li></ul></li></ul><h3 id="Replit-AI（云端开发）"><a href="#Replit-AI（云端开发）" class="headerlink" title="Replit AI（云端开发）"></a>Replit AI（云端开发）</h3><p>零环境（在线 IDE） + AI 自动生成项目</p><ul><li>特点：<ul><li>浏览器内直接开发，无需本地环境配置（零环境）</li><li>内置在线 IDE，支持实时编写、运行和调试代码</li><li>一句话生成完整项目（类似 Bolt / V0）</li><li>AI 自动完成依赖安装、构建与部署</li><li>提供从开发 → 运行 → 发布的一体化云端流程</li><li>集成 AI Agent，可自动生成完整应用（前端 + 后端 + 数据库）</li></ul></li><li>优点：<ul><li>极快完成 MVP（从想法到上线仅需几分钟）</li><li>大幅降低开发门槛（无需环境配置和运维）</li><li>适合快速试错与产品验证</li><li>全流程（开发 + 运行 + 部署一体化）在线完成，只需浏览器</li></ul></li><li>缺点：<ul><li>不适合大型复杂工程长期维护</li><li>自定义底层能力较弱（受平台限制）</li><li>AI 生成项目在复杂场景下可控性一般</li></ul></li><li>适合：<ul><li>初学者（无需复杂环境即可上手）</li><li>快速原型开发（Prototype / Demo）</li><li>SaaS / Web 小型项目快速上线</li><li>独立开发者 / 副业项目 / 验证想法</li></ul></li></ul><h2 id="国内-Top-4-AI-编程工具"><a href="#国内-Top-4-AI-编程工具" class="headerlink" title="国内 Top 4 AI 编程工具"></a>国内 Top 4 AI 编程工具</h2><table><thead><tr><th>国内排名</th><th>名称</th><th>公司</th><th>类型</th><th>使用场景</th></tr></thead><tbody><tr><td> 1</td><td> 通义灵码</td><td>阿里</td><td>工具 + IDE</td><td> 企业级开发：Java / 后端工程 / 代码补全标准化</td></tr><tr><td> 2</td><td>Trae</td><td> 字节跳动</td><td> AI IDE</td><td>AI 原生开发：项目级编码 / Agent 自动开发</td></tr><tr><td> 3</td><td>CodeBuddy</td><td> 腾讯</td><td>工程平台</td><td>企业研发：DevOps / 代码审查 / 流程化开发</td></tr><tr><td> 4</td><td> 文心快码</td><td>百度</td><td>工具型</td><td>代码补全 / 企业内部开发 / 规范化工程支持</td></tr></tbody></table><h3 id="通义灵码（阿里）"><a href="#通义灵码（阿里）" class="headerlink" title="通义灵码（阿里）"></a>通义灵码（阿里）</h3><p>国内最主流的 AI 编程助手（企业级使用率最高）</p><ul><li>产品形态：<ul><li>通义灵码插件（当前主力）<ul><li>写代码助手</li><li>类似 GitHub Copilot</li></ul></li><li> 通义灵码 AI IDE（新一代形态）<ul><li>自带 Agent 能力</li><li>类似 Cursor</li></ul></li><li>Qwen Code（独立 Agent 工具链）<ul><li>真正执行任务的 AI Agent</li><li> 类似 Codex</li></ul></li></ul></li><li> 综合能力：<ul><li>基于 Qwen 编码模型（Qwen-Coder 系列）</li><li>深度集成 VSCode / JetBrains / IDEA</li><li> 支持代码补全、生成、解释、单测生成</li></ul></li><li>特点：<ul><li>中文理解能力强（需求转代码能力优秀）</li><li>企业内部代码库适配能力较好</li><li>支持私有化部署（大厂常用）</li><li>与阿里云生态（IDE / DevOps）结合紧密</li></ul></li><li>优点：<ul><li>中文场景体验最好（需求理解准确）</li><li>企业级稳定性强</li><li>对 Java / 后端工程支持非常成熟</li><li>适合团队统一接入</li></ul></li><li>缺点：<ul><li>Agent 能力相对 Claude Code / Cursor 偏弱</li><li>超大代码库理解能力一般</li><li>创意型开发能力不强</li></ul></li><li>适合：<ul><li>企业后端开发（Java / Go）</li><li>国内互联网公司团队开发</li><li>规范化工程项目</li><li>内部系统开发</li></ul></li></ul><h3 id="Trae（字节跳动）"><a href="#Trae（字节跳动）" class="headerlink" title="Trae（字节跳动）"></a>Trae（字节跳动）</h3><p>面向未来 AI Agent 编程方向的 IDE（成长最快）</p><ul><li><p>产品形态：</p><ul><li>MarsCode 插件（早期阶段）<ul><li>写代码助手</li><li>类似 GitHub Copilot</li></ul></li><li>Trae AI IDE（最新阶段）<ul><li>独立 AI 原生 IDE（开发环境）</li><li>类似 Cursor / Windsurf</li></ul></li></ul></li><li><p> 综合能力：</p><ul><li>字节自研 AI IDE + 编程 Agent 系统</li><li>支持自动生成项目 + 修改代码 + 调试</li><li>与云开发环境结合</li></ul></li><li><p>特点：</p><ul><li>强调 AI Agent 自动开发能力</li><li>支持从需求到项目生成</li><li>与字节内部工程体系类似（Monorepo 友好）</li><li>持续强化多文件重构能力</li></ul></li><li><p>优点：</p><ul><li>AI Agent 方向发展最快之一</li><li>对前端 / 全栈项目非常友好</li><li>自动生成项目能力强</li></ul></li><li><p>缺点：</p><ul><li>生态仍在快速演进阶段</li><li>稳定性不如 Cursor / Copilot</li><li> 企业级成熟度较低</li></ul></li><li><p>适合：</p><ul><li>前端开发</li><li>快速原型开发</li><li> AI 驱动产品团队</li><li>探索型开发者</li></ul></li></ul><h3 id="CodeBuddy（腾讯）"><a href="#CodeBuddy（腾讯）" class="headerlink" title="CodeBuddy（腾讯）"></a>CodeBuddy（腾讯）</h3><p>国内新一代企业级 AI 编程平台</p><ul><li>综合能力：<ul><li>腾讯 + 混元模型 + 工程平台结合</li><li>支持代码生成、Review、测试生成</li><li>深度集成腾讯内部研发体系</li></ul></li><li>特点：<ul><li>面向企业研发流程（DevOps 一体化）</li><li>支持代码审查（AI Review）</li><li>与腾讯 CI/CD 强绑定</li><li>强调研发规范与工程质量</li></ul></li><li>优点：<ul><li>企业级流程支持完善</li><li>适合大型研发组织</li><li>安全与权限体系成熟</li></ul></li><li>缺点：<ul><li>IDE 体验不如 Cursor</li><li> 创作型开发能力一般</li><li>对个人开发者吸引力较弱</li></ul></li><li>适合：<ul><li>大型企业研发团队</li><li>金融 / 通信 / 政企系统</li><li>强流程规范开发</li></ul></li></ul><h3 id="文心快码（百度）"><a href="#文心快码（百度）" class="headerlink" title="文心快码（百度）"></a>文心快码（百度）</h3><p>偏向工程补全 + 企业研发工具型 AI 编程助手</p><ul><li>综合能力：<ul><li>基于文心大模型（ERNIE Code）</li><li>支持代码补全、生成、修复</li><li>与百度智能云开发体系集成</li></ul></li><li>特点：<ul><li>强代码补全能力（类似 Copilot）</li><li>支持多语言开发（Java / Python / JS）</li><li>企业内部代码场景适配</li><li>支持安全审计与代码规范检查</li></ul></li><li>优点：<ul><li>稳定可靠（偏传统工程工具）</li><li>在企业内网环境适配较好</li><li>适合规范开发流程</li></ul></li><li>缺点：<ul><li>Agent 能力较弱</li><li> IDE 体验一般</li><li>不适合复杂系统级开发</li></ul></li><li>适合：<ul><li>企业后端开发</li><li>传统研发团队</li><li>代码规范要求较高的项目</li></ul></li></ul><h2 id="国内-AI-编程工具的产品形态"><a href="#国内-AI-编程工具的产品形态" class="headerlink" title="国内 AI 编程工具的产品形态"></a>国内 AI 编程工具的产品形态</h2><h3 id="通义灵码（阿里）-1"><a href="#通义灵码（阿里）-1" class="headerlink" title="通义灵码（阿里）"></a>通义灵码（阿里）</h3><p>阿里的通义灵码有三种产品形态，本质是 “三层进化路线”，从插件工具 → AI IDE（开发环境） → AI Agent 执行系统。</p><ul><li><p><strong>通义灵码插件（当前主力）</strong></p><ul><li>产品形态：<ul><li>VSCode / JetBrains 插件</li></ul></li><li>能力特点：<ul><li>代码补全</li><li>基础代码生成与修复</li><li> IDE 内增强辅助能力</li><li> AI 问答（解释代码 / 调试代码）</li></ul></li><li>产品定位：<ul><li>AI 辅助写代码工具</li><li>类似 GitHub Copilot</li></ul></li></ul></li><li><p><strong> 通义灵码 AI IDE（新一代形态）</strong></p><ul><li>产品形态：<ul><li>基于 VSCode / JetBrains 深度改造的 AI IDE</li><li> 或独立 AI IDE 客户端</li></ul></li><li>能力特点：<ul><li>内置 AI Agent</li><li> 支持跨文件修改</li><li>支持任务式编程</li><li>可理解整个项目结构</li><li>支持 “需求 → 功能实现” 流程</li></ul></li><li>定位：<ul><li>类似 Cursor / Windsurf</li></ul></li><li> 核心变化：<ul><li>AI 参与开发流程，而不是只辅助编码</li><li>从 “写代码工具” 逐渐发展为 “任务执行型开发系统”</li></ul></li></ul></li><li><p><strong>Qwen Code（独立 Agent 工具链）</strong></p><ul><li>产品形态：<ul><li>CLI / 命令行 Agent</li><li> 可以脱离 IDE 独立运行</li><li>更偏向自动化执行系统</li></ul></li><li>能力特点：<ul><li>修改代码文件</li><li>执行 Shell 命令</li><li>运行测试与任务流程</li><li>可用于自动化工程操作</li></ul></li><li>定位：<ul><li>类似 Codex</li></ul></li><li> 核心变化：<ul><li>真正执行任务的 AI Agent</li></ul></li></ul></li><li><p><strong> 通义灵码三种产品形态的关系</strong></p><ul><li>通义灵码（插件）<ul><li>写代码助手</li><li> AI 辅助写代码（GitHub Copilot 模式）</li></ul></li><li>通义灵码 AI IDE<ul><li>AI 开发环境</li><li> AI 主导开发（Cursor 模式）</li></ul></li><li>Qwen Code<ul><li>AI 执行引擎</li><li> AI 执行任务（Agent / CLI 模式）</li></ul></li></ul></li></ul><h3 id="Trae（字节跳动）-1"><a href="#Trae（字节跳动）-1" class="headerlink" title="Trae（字节跳动）"></a>Trae（字节跳动）</h3><ul><li><p><strong>MarsCode（早期插件阶段）</strong></p><ul><li>产品形态：<ul><li>VSCode / JetBrains 插件</li></ul></li><li>核心功能：<ul><li>代码补全</li><li>代码生成</li><li> Bug 修复建议</li></ul></li><li>能力特点：<ul><li>以辅助编码为主</li><li>基于上下文的局部代码理解</li></ul></li><li>定位：<ul><li>类似 GitHub Copilot</li></ul></li><li> 核心模式：<ul><li>人主导，AI 辅助</li></ul></li></ul></li><li><p><strong> Trae（最新 AI IDE 阶段）</strong></p><ul><li>产品形态：<ul><li>独立 AI 原生 IDE（开发环境）</li></ul></li><li>核心功能：<ul><li>全项目代码理解</li><li>多文件级别修改与重构</li><li> AI Agent 自动写功能</li><li> Builder 模式（需求 → 项目生成）</li></ul></li><li>能力特点：<ul><li>支持任务级编程</li><li>可进行跨文件、跨模块的系统性改造</li><li>更接近 “AI 参与完整开发流程”</li></ul></li><li> 定位：<ul><li>类似 Cursor / Windsurf</li></ul></li><li> 核心模式：<ul><li>AI 主导，人类协作</li></ul></li></ul></li></ul>]]></content>
    
    
    <summary type="html">全球和国内最强 AI 编程工具排行榜（2026 版），比如 Claude Code、Codex、Cursor、GitHub Copilot。</summary>
    
    
    
    
    <category term="AI" scheme="https://www.techgrow.cn/tags/AI/"/>
    
    <category term="开发工具" scheme="https://www.techgrow.cn/tags/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
    
  </entry>
  
  <entry>
    <title>电⼦元器件零基础⼊⻔教程之二</title>
    <link href="https://www.techgrow.cn/posts/c1303b78.html"/>
    <id>https://www.techgrow.cn/posts/c1303b78.html</id>
    <published>2026-01-30T13:03:50.000Z</published>
    <updated>2026-01-30T13:03:50.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/14c20f01.html">电⼦元器件零基础⼊⻔教程之一</a></li><li><a href="/posts/c1303b78.html">电⼦元器件零基础⼊⻔教程之二</a></li></ul><span id="more"></span><h2 id="常见电子元器件"><a href="#常见电子元器件" class="headerlink" title="常见电子元器件"></a>常见电子元器件</h2>]]></content>
    
    
    <summary type="html">本文主要介绍电⼦元器件零基础⼊⻔教程。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="嵌入式开发" scheme="https://www.techgrow.cn/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>电⼦元器件零基础⼊⻔教程之一</title>
    <link href="https://www.techgrow.cn/posts/14c20f01.html"/>
    <id>https://www.techgrow.cn/posts/14c20f01.html</id>
    <published>2026-01-11T13:03:50.000Z</published>
    <updated>2026-01-11T13:03:50.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/14c20f01.html">电⼦元器件零基础⼊⻔教程之一</a></li><li><a href="/posts/c1303b78.html">电⼦元器件零基础⼊⻔教程之二</a></li></ul><span id="more"></span><h2 id="前序知识"><a href="#前序知识" class="headerlink" title="前序知识"></a>前序知识</h2><p>这里先了解⼀些原⼦相关的知识 ，会对电学中很多的概念和结论有更好的理解。</p><h3 id="原子（Atomic）"><a href="#原子（Atomic）" class="headerlink" title="原子（Atomic）"></a>原子（Atomic）</h3><ul><li>原⼦是构成物质的最⼩单位，它由原⼦核和电⼦组成，其中原⼦核又由质⼦和中⼦组成。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-1.png"></p><ul><li>下图为碳原⼦的简易模型图：</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-2.png"></p><div class="admonition note"><p class="admonition-title">提示</p><p>这里提及原⼦结构，是为了给后续的：电流、电压、⼆极管、三极管等知识做铺垫，但也会忽略掉⼀些与电学⽆关的知识，⽐如：有些元素（氢的同位素氕）没有中⼦，再⽐如：质⼦和中⼦的⽐例关系，会对元素的放射性有⼀定影响等，这些知识不在本教程中体现。</p></div><h3 id="质⼦（Proton）"><a href="#质⼦（Proton）" class="headerlink" title="质⼦（Proton）"></a>质⼦（Proton）</h3><ul><li>质⼦带⼀个单位的正电荷，且质⼦数量决定着元素身份，改变了质⼦数量，也就改变了原⼦的类型。</li><li>举个例⼦：如果能从铅的原⼦核（82 个质子）⾥移出 3 个质⼦，那就成功的⽤铅原⼦造出了⼀个⾦原⼦（79 个质子）。</li><li>⽽实际上：质⼦之间结合地⾮常紧密，⼏乎⽆法分开，这也是炼⾦术未能实现的原因。</li><li>拓展内容：现代物理学中的核反应堆、粒⼦加速器等可以实现上述过程，但所需能量和成本⾮常⾼，已经远超⻩⾦的价值了，⽽且这种反应通常很难精确控制，产出的黄⾦量可能很少。</li></ul><h3 id="中⼦（Neutron）"><a href="#中⼦（Neutron）" class="headerlink" title="中⼦（Neutron）"></a>中⼦（Neutron）</h3><ul><li>中⼦不带电，且中⼦对原⼦的影响远⼩于质⼦。</li><li>电学领域通常不对中⼦进⾏深⼊探讨，因为它不带电荷，不直接影响电势、电流的形成。</li></ul><h3 id="电⼦（Electron）"><a href="#电⼦（Electron）" class="headerlink" title="电⼦（Electron）"></a>电⼦（Electron）</h3><ul><li>电⼦带⼀个单位的负电荷，它围绕原⼦核做⽆规则的运动。<ul><li>早期的『波尔模型』，将电⼦的运动⽐作成⾏星的 “轨道运动”；</li><li>但随着量⼦⼒学的发展，这种⽐喻并不完全正确，所谓的 “轨道” 其实是电⼦出现的概率区域，并不不是⼀个具体的路径。</li></ul></li><li>通常来说原⼦的质⼦数和电⼦数相等，其内部的正电荷与负电荷互相抵消，这时候原⼦呈电中性。</li></ul><hr><ul><li>电⼦层：电⼦围绕原⼦核活动的范围。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-3.png"></p><ul><li><p>最外层电⼦数为 8 个时，通常是较稳定的状态。</p><ul><li>许多元素在化学反应中，会倾向于通过：获得电⼦、失去电⼦、共享电⼦，来尽可能达到 8 个电⼦的稳定结构，这个现象被称为：⼋隅规则。</li><li>当然也有⼀些例外的元素，例如：氢 (H) 、氦 (He) 只需要 2 个电⼦就能达到稳定状态。</li></ul></li><li><p>不同电荷之间互相吸引，相同电荷之间互相排斥，具体计算参考库伦定律：</p></li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-4.png"></p><ul><li>最外层的电⼦更容易脱离原⼦，因为它们与原⼦核的距离较远，受到的引⼒较弱。</li></ul><h3 id="⽯墨与⾦刚⽯"><a href="#⽯墨与⾦刚⽯" class="headerlink" title="⽯墨与⾦刚⽯"></a>⽯墨与⾦刚⽯</h3><ul><li>⽯墨：每个碳原⼦与周边 3 个碳原⼦分别建⽴共价键，最终外层剩余 1 个 未成键的电⼦，这个电⼦可以在不同的原⼦之间⾃由移动，所以⽯墨是导体。</li><li>⽯墨的层与层之间是靠『范德华⼒』结合在⼀起的，范德华⼒是⼀种弱的分⼦间作⽤⼒，因此⽯墨的各个层之间可以很容易地滑动，所以⽯墨可以作为：润滑剂、铅笔芯等。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-5.png"></p><hr><ul><li>⾦刚⽯：每个碳原⼦与周边 4 个 碳原⼦分别建⽴共价键，形成⼀个⾮常坚硬的⽴体⽹状结构，没有⾃由电⼦，所以⾦刚⽯是绝缘体。</li><li>⾦刚⽯的碳原⼦之间共价键内的电⼦活动范围相对较⼩，且共价键内的电⼦被两个原⼦核共同吸引，所以碳原⼦之间共价键内的电⼦相对稳定，不会轻易脱落，除⾮有⾜够的能量来打破这个键。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-6.png"></p><h2 id="电学基础"><a href="#电学基础" class="headerlink" title="电学基础"></a>电学基础</h2><h3 id="电场、电势、电势能"><a href="#电场、电势、电势能" class="headerlink" title="电场、电势、电势能"></a>电场、电势、电势能</h3><p>为了能更好的理解⼀些抽象的概念，这里⽤重⼒场中的相关概念，去类⽐电场中的相关概念。</p><h4 id="重⼒场、电场"><a href="#重⼒场、电场" class="headerlink" title="重⼒场、电场"></a>重⼒场、电场</h4><ul><li>重⼒场：在地球周围存在，有质量的物体进⼊该区域后，会受到被拉向中⼼的⼒。</li><li>电场：在电荷周围存在，带电的粒⼦进⼊这个区域后，会受到吸引⼒或排斥⼒。<ul><li>沿着电场线⽅向，电场的强度会逐渐减弱，且⽆论正电荷还是负电荷，⽆穷远处的电场强度为 0。</li></ul></li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-9.png"></p><h4 id="重⼒势能、电势能"><a href="#重⼒势能、电势能" class="headerlink" title="重⼒势能、电势能"></a>重⼒势能、电势能</h4><ul><li>重⼒势能：描述物体在重⼒场中所具备的能量，与所处位置、物体质量均有关。</li><li>电势能：描述带电粒⼦在电场中所具备的能量，与所处位置、粒⼦带电量均有关。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-10.png"></p><h4 id="重⼒势、电势"><a href="#重⼒势、电势" class="headerlink" title="重⼒势、电势"></a>重⼒势、电势</h4><p><img data-src="../../../asset/2026/01/embedded-dev-11.png"></p><p><img data-src="../../../asset/2026/01/embedded-dev-12.png"></p><h3 id="电流"><a href="#电流" class="headerlink" title="电流"></a>电流</h3><ul><li><p>电流的概念：</p><ul><li>电荷的定向移动，形成了电流，衡量电流的⼤⼩，要看单位时间内通过导体横截⾯的电荷量。</li><li>注意：<ul><li>电学上定义的『电流⽅向』是正电荷的流动的⽅向，这个概念在 19 世纪初就先被定电学上定义的『电流⽅向』是正电荷的流动的⽅向，这个概念在 19 世纪初就先被定义了；</li><li>后来虽然发现了是电⼦在移动，但为了保持定义的统⼀性，『电流⽅向』仍然⽤正电荷的流动去定义，<strong>所以『电流⽅向』与『电⼦移动⽅向』是相反的</strong>。</li></ul></li></ul></li><li><p>电流的单位：安培 (A)，简称安</p><ul><li>1A 的含义：1 秒钟内有 <code>6.242*10^18</code> 个单位电荷（电⼦、质⼦、都属于单位电荷）通过了导体横截⾯。</li><li><code>6.242*10^18</code> 个单位电荷所带的电荷总量，为 1 库伦 (C)，所以 1A 的含义也能这样描述：在 1 秒钟内，有 1 库仑的电荷通过了导体的横截⾯。</li></ul></li><li><p>电流的单位换算：</p><ul><li>进位为 1000，例如 1A = 1000mA</li><li> 常⽤的单位是：安 (A)、毫安 (mA)<br><img data-src="../../../asset/2026/01/embedded-dev-7.png"></li></ul></li><li><p>常⻅电器的⼯作电流：<br>  <img data-src="../../../asset/2026/01/embedded-dev-8.png"></p></li></ul><h3 id="电压"><a href="#电压" class="headerlink" title="电压"></a>电压</h3><ul><li><p>电压的概念：</p><ul><li>⼜称电势差，是两点之间电势的差值，衡量电压的⼤⼩，要看两点电势差的⼤⼩。</li><li>有了电压，电子才能持续且定向地移动起来，所以电压是形成电流的必要条件之一。</li><li>电压越大，能定向移动起来的电子就越多，电流就会越大。</li></ul></li><li><p>电压的单位：伏特 (V)，简称伏</p><ul><li>1V 的含义是：电场 对 1 库伦 (C) 电荷做了 1 焦⽿ (J) 的功。<br><img data-src="../../../asset/2026/01/embedded-dev-17.png"></li></ul></li><li><p>电压的单位换算：</p><ul><li>进位为 1000，例如 1kV = 1000V<br><img data-src="../../../asset/2026/01/embedded-dev-13.png"></li></ul></li><li><p>常⻅电源的电压：<br>  <img data-src="../../../asset/2026/01/embedded-dev-14.png"></p></li></ul><h3 id="电阻"><a href="#电阻" class="headerlink" title="电阻"></a>电阻</h3><ul><li><p>电阻的概念：</p><ul><li>电阻是指材料或元器件对电流流动的阻碍程度。</li><li>电阻的大小不仅与材料有关，还与材料的粗细、长短等因素有关。</li></ul></li><li><p>电阻的单位：欧姆（简称欧），符号是 Ω。</p><ul><li>1Ω 的含义：给导体施加 1V 电压，此时如果导体的电流为 1A，那这个导体的电阻就是 1Ω。</li></ul></li><li><p>电阻的单位换算：</p><ul><li>进位为 1000，例如 1KΩ = 1000Ω<br><img data-src="../../../asset/2026/01/embedded-dev-15.png"></li></ul></li><li><p>电阻的决定式：</p><ul><li>$R = \rho \cdot \frac{L}{A}$<ul><li><strong>R</strong>：电阻</li><li><strong> L</strong>：导体的⻓度</li><li><strong> A</strong>：导体的横截⾯积</li><li><strong> ρ</strong>：材料的电阻率，表示材料对电流的阻碍能⼒</li></ul></li></ul></li><li><p>电阻的补充内容：</p><ul><li>后续会讲解⼀种电⼦元器件 — 『电阻器』，它的简称也叫『电阻』，是⼀种专⻔⽤于控制电流大小的电⼦元器件（如下图所示），其的作⽤是：为电路提供电阻，来限制电流的⼤⼩。<br><img data-src="../../../asset/2026/01/embedded-dev-16.png"></li></ul></li></ul><h3 id="电路"><a href="#电路" class="headerlink" title="电路"></a>电路</h3><ul><li>电源：物理学中将提供电能的装置叫做电源。</li><li>⽤电器：将灯泡、电动机、等这类消耗电能的装置叫做⽤电器。</li><li>电路：电源、⽤电器，再加上导线、开关等，就组成了电流可以流过的路径，这就是电路。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-18.png"></p><ul><li><p>电路图：</p><ul><li>在绘制电路图时，为了绘制简单且⽅便研究，通常⽤图形符号来表示元件。</li><li>此处先只列举了三个电路符号（如下图所示），其他的电路符号以后会随着教程讲解。<br><img data-src="../../../asset/2026/01/embedded-dev-19.png"></li></ul></li><li><p>电路状态：</p><ul><li>断路：电路中某处被切断，电路中没有电流流过。</li><li>通路：正常接通的电路，⽤电器能够正常⼯作的电路。</li><li>短路：比如直接⽤导线将电源的正、负极连接起来（⚠️危险操作）。<br><img data-src="../../../asset/2026/01/embedded-dev-20.png"></li></ul></li></ul><h3 id="欧姆定律"><a href="#欧姆定律" class="headerlink" title="欧姆定律"></a>欧姆定律</h3><ul><li><p>概念：</p><ul><li>导体中的电流，与导体两端的电压成正⽐，与导体的电阻成反⽐。</li><li>在电阻不变的情况下，电压越⼤，电流也会越⼤。</li></ul></li><li><p>公式：</p><ul><li>$I = \frac{U}{R}$<ul><li><strong>U</strong> 为电压，单位为 <strong>V</strong></li><li><strong>I</strong> 为电流，单位为 <strong>A</strong></li><li><strong>R</strong> 为电阻，单位为 <strong>Ω</strong></li></ul></li><li> 变形公式一：<ul><li>$R = \frac{U}{I}$</li></ul></li><li> 变形公式二：<ul><li>$U = IR$</li></ul></li></ul></li><li><p> 决定式 vs 定义式：<br>  <img data-src="../../../asset/2026/01/embedded-dev-21.png"></p></li><li><p>使⽤场景：<br>  <img data-src="../../../asset/2026/01/embedded-dev-22.png"></p></li><li><p>局限性：</p><ul><li><strong>欧姆定律有局限性，仅适用于纯电阻电路（或者说纯电阻元器件、纯电阻设备）！</strong></li><li>纯电阻电路：消耗的电能仅转化为热能，没有其他形式的能量转换。</li></ul></li></ul><h3 id="直流电-VS-交流电"><a href="#直流电-VS-交流电" class="headerlink" title="直流电 VS 交流电"></a>直流电 VS 交流电</h3><h4 id="直流电"><a href="#直流电" class="headerlink" title="直流电"></a>直流电</h4><ul><li><p>定义：</p><ul><li>有固定的正负极，且电流⽅向始终『不变』的电流，全称：Direct Current，简称 DC。</li></ul></li><li><p>举例：</p><ul><li>⼲电池、锂电池、光伏发电板等。<br><img data-src="../../../asset/2026/01/embedded-dev-23.png"></li></ul></li></ul><h4 id="交流电"><a href="#交流电" class="headerlink" title="交流电"></a>交流电</h4><ul><li><p>定义：</p><ul><li>电流⽅向随时间做『周期性变化』的电流，全称：Alternating Current，简称 AC。</li><li>交流电的电流⽅向⼀定做周期性变化，但电流⼤⼩不⼀定做周期性变化！</li></ul></li><li><p>举例：</p><ul><li>家庭⽤电<br><img data-src="../../../asset/2026/01/embedded-dev-24.png"></li></ul></li></ul><h4 id="扩展内容"><a href="#扩展内容" class="headerlink" title="扩展内容"></a>扩展内容</h4><p>家庭⽤电为的 220V / 50Hz 的正弦交流电，下⾯是关于家⽤交流电的⼀些总结性内容。</p><ul><li><p>周期：</p><ul><li>交流电完成⼀个完整波动循环所需的时间（参考下图）。<br><img data-src="../../../asset/2026/01/embedded-dev-25.png"></li></ul></li><li><p>零线与⽕线：</p><ul><li>交流电⽆正负极之分，但有零⽕线之分。</li><li>零线的电势始终为 0，⽕线的电势不断变化，有时⾼于 0，有时低于 0，这样就形成了交变电流。</li></ul></li><li><p>对于 220V / 50Hz 的理解：</p><ul><li>220V 指的是交流电的有效电压<ul><li>什么是有效电压？—— 让交流电和直流电，分别通过同⼀个电阻，在交流电的⼀个周期内，若⼆者产⽣的热量相等，那么这个直流电的电压，就是交流电的有效电压。</li><li>我们平时聊天所说到的、⽤电器说明书上写的、电表所测到的，都是有效电压，即：220V。家庭用电的 220V 指的是交流电的有效电压。</li><li>什么是峰值电压？—— 交流电在⼀个周期中达到的最⼤电压值，220V 交流电的峰值电压为：$200\sqrt {2}$，约为：311V，<a href="../../../asset/2026/01/embedded-dev-30.png">如图所示</a>。</li></ul></li><li>50hz 指的是交流电的频率<ul><li> 50hz 的含义是交流电在 1 秒钟内完成了 50 个周期（每个周期包含两次电流⽅向变化，所以对于 50Hz 的交流电来说，1 秒钟内电流的⽅向变化了 100 次）。</li></ul></li></ul></li><li><p>整流与逆变：</p><ul><li>什么是整流？—— 交流（AC） → 直流（DC）</li><li>什么是逆变？—— 直流（DC） → 交流（AC）</li><li>在⽣活中很多的电器，都是将交流转换成直流后⼯作的（参考下图），例如：电脑、⼿机、路由器等，因为直流电更加稳定，适合精密电路。<br><img data-src="../../../asset/2026/01/embedded-dev-26.png"></li></ul></li></ul><h3 id="弱电与强电"><a href="#弱电与强电" class="headerlink" title="弱电与强电"></a>弱电与强电</h3><ul><li><p>弱电：</p><ul><li>弱电的电压⼀般较低，⾏业规定安全电压为不⾼于 36V。</li><li>弱电通常⽤于直流电路（3.3V、5V、12V），弱电也能于信号传递，例如：⾳频和视频线路、⽹络线路、电话线等（参考下图）。<br><img data-src="../../../asset/2026/01/embedded-dev-27.png"></li></ul></li><li><p>强电：</p><ul><li>强电的电压⼀般很⾼，对人体危害较大，必须采取防护措施，例如：220V 的家⽤电、1000V 及以上的⾼压电。</li><li>强电常⽤于能源传输或者高功率供电（参考下图）。<br><img data-src="../../../asset/2026/01/embedded-dev-28.png"></li></ul></li><li><p>通过人体电流的大小，直接决定了电击的危险性：</p><ul><li>感知电流：能引起人感觉的最小电流，约 <strong>0.7mA ~ 1.1mA</strong> 左右，有很轻微的刺痛感，但不会造成伤害。</li><li>摆脱电流：能自主摆脱的最大电流，约 <strong>10mA ~ 16mA</strong> 左右，会有明显的肌肉收缩，不要持续接触。</li><li>致命电流：能在短时间内危及生命的最小电流，约为 <strong>50mA</strong> 左右，明显的呼吸困难、心脏产生室颤。</li></ul></li></ul><div class="admonition warning"><p class="admonition-title">特别注意</p><p><strong>⼤家⼀定要注意⽤电安全，⼀般弱电平台⽤⼿直接触摸不会造成⼈身危险；但是强电平台 ⼀定不能⽤⼿直接触摸，会有⽣命危险！</strong></p></div><h3 id="家庭电路"><a href="#家庭电路" class="headerlink" title="家庭电路"></a>家庭电路</h3><p>本节属于电⼯学内容，但也属于电学相关的常识性内容，不必深究，了解⼀下就可以。</p><p><img data-src="../../../asset/2026/01/embedded-dev-29.png"></p><ul><li>家庭电路中的『保护接地』，是如何保护我们的？<ul><li>(1) 发电⼚系统会通过⼀个接地点，将零线连接到⼤地，这是电⼒系统中⼀个重要的设计， 其⽬的是：<ul><li>保证电压稳定：将零线电压锁定为地电位（0V）。</li><li>提供保护：在发⽣故障时，接地可以快速引导电流回到地，触发保护装置（如断路器）。</li></ul></li><li>(2) 为什么触摸⽕线很危险？<ul><li>⽕线为 220V，地为 0V，身体与地之间构成回路，导致电流流过身体，很危险！</li><li>人体触电时，电流的整体路径是：⽕线（220V）→ 人体 → 地 → 零线（0V，接地）→ 发电⼚。</li></ul></li></ul></li></ul><hr><ul><li>无地线 + 人体触电（致命危险）</li></ul><p><img data-src="/gif/alternating-current-1.gif"></p><ul><li>接地线 + 人体触电（非致命危险）</li></ul><p><img data-src="/gif/alternating-current-2.gif"></p><ul><li>断路器 + 接地线 + 人体触电（安全）</li></ul><p><img data-src="/gif/alternating-current-3.gif"></p><div class="admonition warning"><p class="admonition-title">特别注意</p><p><strong>特别注意，家庭⽤电属于强电！禁⽌⾃⼰操作强电！也禁⽌⽤⾃⼰的身体去验证各种结论！</strong></p></div><h3 id="串联与并联"><a href="#串联与并联" class="headerlink" title="串联与并联"></a>串联与并联</h3><h4 id="串联电路"><a href="#串联电路" class="headerlink" title="串联电路"></a>串联电路</h4><p><img data-src="../../../asset/2026/01/embedded-dev-31.png"></p><h4 id="并联电路"><a href="#并联电路" class="headerlink" title="并联电路"></a>并联电路</h4><p><img data-src="../../../asset/2026/01/embedded-dev-32.png"></p><h4 id="串并联练习"><a href="#串并联练习" class="headerlink" title="串并联练习"></a>串并联练习</h4><ul><li>电路图 1，请分析 A、B、C 三点电压分别是多少？答案是：A 点 9V，B 点 3V，C 点 0V，<a href="../../../asset/2026/01/embedded-dev-41.png">点击查看图解分析</a>。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-33.png"></p><ul><li>电路图 2，请分析 A、B、C 三点电压分别是多少？答案是：A 点 4V，B 点 1V，C 点 0V，<a href="../../../asset/2026/01/embedded-dev-42.png">点击查看图解分析</a>。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-34.png"></p><ul><li>电路图 3，请分析 A、B、C 三点电压分别是多少？答案是：A 点 10V，B 点 8V，C 点 4V，<a href="../../../asset/2026/01/embedded-dev-44.png">点击查看图解分析</a>。</li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-43.png"></p><div class="admonition note"><p class="admonition-title">电路仿真软件</p><ul><li>串联电路和并联电路可以使用开源电路仿真软件 <a href="https://github.com/sharpie7/circuitjs1">CircuitJS1</a> 进行模拟和分析。CircuitJS1 是基于 Paul Falstad 的 <a href="https://www.falstad.com/circuit/">Circuit Simulator</a> 开发的 JavaScript 版本。</li><li>其他的 CircuitJS1 开源衍生版本有：<a href="https://github.com/pfalstad/circuitjs1">pfalstad/circuitjs1</a>、<a href="https://github.com/SEVA77/circuitjs1">SEVA77/circuitjs1</a>。</li></ul></div><h3 id="电功率"><a href="#电功率" class="headerlink" title="电功率"></a>电功率</h3><ul><li><p>概念：</p><ul><li>电功率⽤于衡量电流在单位时间内所做的功（⽤来表示⽤电器消耗电能快慢的物理量）。</li></ul></li><li><p>单位：</p><ul><li>瓦特（简称瓦），符号是 W。</li></ul></li><li><p>单位换算：</p><ul><li>进位为 1000，例如： 1kW = 1000W。<br><img data-src="../../../asset/2026/01/embedded-dev-36.png"></li></ul></li><li><p>公式：</p><ul><li>$P = U I$<ul><li><code>U</code> 为电压，单位为 <code>V</code>（伏）</li><li><code>I</code> 为电流，单位为 <code>A</code>（安）</li><li><code>P</code> 为功率，单位为 <code>W</code>（瓦）</li></ul></li></ul></li><li><p>常⻅⽤电器的功率</p></li></ul><p><img data-src="../../../asset/2026/01/embedded-dev-37.png"></p><hr><ul><li><p>瓦时 VS 千瓦时</p><ul><li>⼆者都是表示电能的⼀种单位。</li><li>瓦时：<ul><li>瓦时是功率（W）与时间（h）的乘积，即：<code>瓦时 = 功率 × 时间</code></li><li>⼀个 1W 的⽤电器，⼯作 1h ，它消耗的电量就是 1 瓦时（1Wh）。</li></ul></li><li>千瓦时：<ul><li>千瓦时是瓦时的千倍，通常⽤于描述⼤型电⼒设备，如电表读数、新能源汽⻋等。</li><li>⼀个 1kW 的电器⼯作 1h ，它消耗的电量就是 1 千瓦时（1kWh，俗称 1 度电）。<br><img data-src="../../../asset/2026/01/embedded-dev-38.png"></li></ul></li></ul></li><li><p>安时 VS 毫安时</p><ul><li>⼆者都是描述电池容量的单位。</li><li>安时：<ul><li>表示该电池能以 1A 的电流持续⼯作 1h，通常⽤于描述⼤容量的电池。</li></ul></li><li>毫安时：<ul><li>安时的千分之⼀，通常⽤于描述⼩容量的电池。</li></ul></li><li>例如：<ul><li>华为『Mate70 Pro』⼿机电池的容量为 5700mAh ，充电宝的电池容量为 20000mAh。</li><li>有些时候我们也省略 <code>h</code>，直接说：⼿机的电池容量和为 5700mA，充电宝电池容量为：20000mA。<br><img data-src="../../../asset/2026/01/embedded-dev-39.png"></li></ul></li></ul></li></ul><h3 id="焦⽿定律"><a href="#焦⽿定律" class="headerlink" title="焦⽿定律"></a>焦⽿定律</h3><ul><li><p>概念：</p><ul><li>电流通过导体产⽣的热量与电流的平⽅成正⽐，跟导体的电阻成正⽐，跟通电时间成正⽐。</li></ul></li><li><p>单位：</p><ul><li>单位：焦⽿（简称焦），简称 J。</li></ul></li><li><p>公式：</p><ul><li>$Q = I^2 R t$<ul><li><code>Q</code> 为热量，单位为：<code>J</code>（焦⽿）</li><li><code>I</code> 为电流，单位为：<code>A</code>（安培）</li><li><code>R</code> 为电阻，单位为：<code>Ω</code>（欧姆）</li><li><code>t</code> 为时间，单位为：<code>s</code>（秒）</li></ul></li></ul></li><li><p>例如：</p><ul><li>⼀根 60Ω 的电阻丝接在 36V 的电源两端，在 5 分钟内共产⽣多少热量？答案是：$ \left (\frac {36\ \text {V}}{60\ \Omega} \right)^2 \times 60\ \Omega \times 5\ \times 60\ = 6480\ \text {J} $<br>  <img data-src="../../../asset/2026/01/embedded-dev-40.png"></li></ul></li><li><p>注意：</p><ul><li>电路通过导体时，如果电能全部转化为内能，⽽没有同时转化为其他形式的能量，那么电流产⽣的热量就等于消耗的电能，但实际上：<strong>只有纯电阻电路，才能把电能完全⽤于产⽣热量</strong>！</li><li>对于<strong>纯电阻电路</strong>来说，消耗的电能全部⽤于转化热量，所以可以推导出如下公式：<ul><li>功率计算公式：$P = \frac {U^2}{R}$，$P = I^2 R$</li><li> 热量计算公式：$Q = U I t$，$Q = P t$</li></ul></li></ul></li></ul><h3 id="公式总结"><a href="#公式总结" class="headerlink" title="公式总结"></a>公式总结</h3><p><img data-src="../../../asset/2026/01/embedded-dev-45.png"></p><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li>功率计算公式（$P = U I$）与焦耳定律（$Q = I^2 R t$）是原始公式，适用于所有电路。</li><li>欧姆定律（$I = \frac {U}{R}$）有局限性，仅适用于纯电阻电路（或者说纯电阻元器件、纯电阻设备）。</li></ul></div>]]></content>
    
    
    <summary type="html">本文主要介绍电⼦元器件零基础⼊⻔教程。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="嵌入式开发" scheme="https://www.techgrow.cn/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>基于 ESP32、SI4732 开发收音机</title>
    <link href="https://www.techgrow.cn/posts/c350c4dd.html"/>
    <id>https://www.techgrow.cn/posts/c350c4dd.html</id>
    <published>2026-01-10T13:03:50.000Z</published>
    <updated>2026-01-10T13:03:50.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="学习路线"><a href="#学习路线" class="headerlink" title="学习路线"></a>学习路线</h2><p>基于 ESP32 + SI4732 + ATS MINI 的生态开发收音机，开发者并不需要从 51 单片机、寄存器操作等传统嵌入式底层知识开始学习，而可以利用现有的开源项目成果，以 “应用集成和修改” 为核心来切入，这会大大降低门槛。</p><table><thead><tr><th>个人的兴趣 / 目标</th><th>推荐入口</th><th>需要优先掌握的核心技能</th><th>学习难度</th><th>可能成果</th></tr></thead><tbody><tr><td>只想使用 / 小改现有功能（如改界面、调参数）</td><td>作为高级用户</td><td><br> - 1. Arduino IDE 基础使用 <br> - 2. 代码编译与烧录 <br> - 3. 配置文件修改</td><td>⭐⭐ 入门</td><td>成功编译并烧录官方固件，修改频率范围、显示语言等。</td></tr><tr><td>想深度定制功能（如增加新模块、改交互逻辑）</td><td>作为项目开发者</td><td><br> - 1. Arduino 框架下 C++ 基础 <br> - 2. ESP32 基础外设使用（I2C， 旋钮编码器，显示屏）<br> - 3. 阅读理解开源代码的能力</td><td>⭐⭐⭐ 中等</td><td>为收音机增加蓝牙音频输出、修改菜单结构、支持新的显示屏。</td></tr><tr><td>想彻底重写 / 从底层理解（如研究算法、追求极致性能）</td><td>作为系统开发者</td><td><br> - 1. 扎实的 C/C++ 语言和数据结构 <br> - 2. ESP-IDF 框架体系 <br> - 3. 嵌入式操作系统基础 <br> - 4. 射频电路基础知识</td><td>⭐⭐⭐⭐⭐ 困难</td><td>独立架构收音机软件，优化 DSP 音频处理算法，或移植到其他平台。</td></tr></tbody></table><span id="more"></span><div class="admonition note"><p class="admonition-title">学习路线选择</p><ul><li>对于绝大多数爱好者来说，前两个入口（"高级用户" 和 "项目开发者"）是最实际、最有成就感的起点。</li></ul></div><h2 id="必备知识"><a href="#必备知识" class="headerlink" title="必备知识"></a>必备知识</h2><p>基于 ESP32 + SI4732 + ATS MINI 的生态开发收音机，开发者不需要学习 “底层嵌入式全家桶”（寄存器、启动流程、裸机 RTOS 移植等），会 Arduino API 就能开发，但理解 I2C、GPIO、库结构、编译烧录流程是必须的。</p><ul><li><p>必须掌握的知识（重点）</p><ul><li>ESP32 基础使用<ul><li>会刷固件（USB / 串口）</li><li>会选开发板（ESP32 / ESP32-S3）</li><li>会看 GPIO 引脚定义</li></ul></li><li> Arduino 开发方式<ul><li>开发工具：Arduino IDE / PlatformIO-IDE</li><li> 开发框架：Arduino Core for ESP32</li><li> 使用 <code>setup()</code> / <code>loop()</code></li><li>编译 → 烧录 → 串口调试</li><li>修改现有 <code>.ino</code> / <code>.cpp</code></li></ul></li><li>I2C 基本概念<ul><li> SDA / SCL</li><li>SI4732 是 I2C 设备</li><li>会填正确的引脚即可（不需要写 I2C 驱动）</li></ul></li><li>读文档与改配置<ul><li>屏幕分辨率</li><li>按键 / 编码器引脚</li><li> FM / AM / SW 频段参数</li></ul></li><li>到这里，就已经能跑 ATS MINI 固件了</li></ul></li><li><p>不需要掌握的知识（忽略）</p><ul><li>裸机开发</li><li>寄存器级 GPIO / I2C</li><li>FreeRTOS 调度原理</li><li>链接脚本 / 启动代码</li><li>芯片 DataSheet 逐页啃</li></ul></li><li><p>只有在下面情况之一，才建议系统学习嵌入式</p><ul><li>自己写 SI4732 驱动</li><li>自己做低功耗管理</li><li>不用 Arduino IDE，改用 ESP-IDF</li><li> 做商业级产品 / 深度定制</li></ul></li></ul><div class="admonition note"><p class="admonition-title">ESP32 与 SI4732 的关系</p><ul><li>ESP32 是主控芯片（MCU），SI4732 是收音机芯片（射频 + 解调）。</li><li>ESP32：负责程序逻辑、按键处理、屏幕显示、存储、UI 界面、控制流程等。</li><li>SI4732：负责真正的收音工作（AM / FM / SW / SSB 的射频接收与解调等）。</li><li>两者之间，通过 I²C 通信，ESP32 向 SI4732 发送控制命令（调频、切台、模式切换等），SI4732 返回状态和信号质量数据。</li></ul></div><h2 id="开发工具"><a href="#开发工具" class="headerlink" title="开发工具"></a>开发工具</h2><ul><li>ESP32 开发的工具链：<ul><li>IDE：Arduino IDE</li><li>SDK：Arduino Core for ESP32</li><li> 硬件：ESP32 开发板 + SI4732 芯片 + 屏幕</li></ul></li></ul><hr><ul><li> Arduino 官方提供的 Arduino Core for ESP32 支持以下芯片：</li></ul><table><thead><tr><th>芯片</th><th> Arduino Core for ESP32 的支持</th><th>说明</th></tr></thead><tbody><tr><td> ESP32</td><td>✅ 完全支持</td><td>最成熟、资料最多</td></tr><tr><td> ESP32-S2</td><td>✅</td><td>USB 原生</td></tr><tr><td> ESP32-S3</td><td>✅ 完全支持</td><td>新一代，常用于 ATS MINI 项目</td></tr><tr><td> ESP32-C3</td><td>✅</td><td>RISC-V</td></tr></tbody></table><ul><li>Arduino Core for ESP32 对比 ESP-IDF（乐鑫官方原生 SDK）</li></ul><table><thead><tr><th>对比</th><th> Arduino Core for ESP32</th><th>ESP-IDF</th></tr></thead><tbody><tr><td> 上手速度</td><td>⭐⭐⭐⭐⭐</td><td>⭐⭐</td></tr><tr><td>ATS MINI 项目适配</td><td>⭐⭐⭐⭐⭐</td><td>❌</td></tr><tr><td>驱动成熟度</td><td>⭐⭐⭐⭐</td><td>⭐⭐⭐⭐⭐</td></tr><tr><td>学习成本</td><td>低</td><td>高</td></tr></tbody></table><div class="admonition note"><p class="admonition-title">ESP-IDF 是什么</p><ul><li>ESP-IDF（Espressif IoT Development Framework）是乐鑫官方提供的 ESP32 系列底层开发框架（SDK）。</li><li>ESP-IDF 的特点是官方原生（乐鑫自己用），直接使用 FreeRTOS，多任务控制更细，代码更复杂。</li><li>ESP-IDF 适用于专业、灵活、产品级开发，比如做商业级产品 / 深度定制才会使用 ESP-IDF。</li></ul></div><div class="admonition note"><p class="admonition-title">Arduino IDE + ESP32 是怎么连起来的？</p><ul><li>使用 Arduino IDE + ESP32 进行开发时，使用的硬件仍然是 ESP32 开发板，并不是 Arduino 开发板。Arduino IDE 只是一个开发工具（IDE），用于编写、编译和烧录程序，ESP32 才是真正运行程序的硬件平台。</li><li>Arduino IDE 中使用的 Arduino Core for ESP32 相当于一层 "适配层 / 抽象层"，它基于 ESP-IDF 实现，将 ESP32 的底层能力封装为 Arduino 风格的 API，使开发者可以通过熟悉的 Arduino API 来开发 ESP32，而无需直接接触 ESP-IDF。</li><li>在 Arduino IDE 里，选的是 ESP32 开发板，写的是 Arduino API 风格代码，跑的是 ESP32 开发板（芯片）。</li><li>Arduino IDE 完全支持 ESP32 开发板，而且 ATS MINI、SI4732 项目本身就是基于 Arduino 体系开发的。</li></ul></div><h2 id="入门步骤"><a href="#入门步骤" class="headerlink" title="入门步骤"></a>入门步骤</h2><ul><li><p>第一步：搭建环境，跑通示例</p><ul><li>行动：安装 Arduino IDE，添加 ESP32 开发板支持；导入 ATS MINI 的源代码，尝试在电脑上成功编译。</li><li>学习点：嵌入式开发的第一步就是工具链的搭建，这能让你熟悉最基本的流程。</li></ul></li><li><p>第二步：购买开发板，点亮第一盏灯</p><ul><li>行动：购买一块 ESP32 开发板（如 ESP32-DevKitC），不接 SI4732，先学习如何控制一个 LED 闪烁，读取一个按键。</li><li>学习点：这是嵌入式开发的 <code>Hello World</code>，让你建立 “编写代码 -&gt; 硬件响应” 的直接概念，消除对硬件的陌生感。</li></ul></li><li><p>第三步：连接核心部件，理解通信</p><ul><li>行动：将 SI4732 模块通过 I2C 接口连接到 ESP32。不要急于用 ATS MINI 完整代码，而是先寻找 SI4732 的 Arduino 基础库，尝试写几行代码，让 ESP32 命令 SI4732 收一个已知的 FM 电台，并读出信号强度。</li><li>学习点：这是项目的核心，你将学会如何通过查阅芯片数据手册和使用现有驱动库来控制关键器件。I2C 通信是嵌入式开发中最常见的技能之一。</li></ul></li><li><p>第四步：深入研究 ATS MINI 源码</p><ul><li>行动：在完成了前三步的基础上，再打开 ATS MINI 的工程。此时你看代码的心态会完全不同。你能找到初始化 SI4732 的代码、扫描频段的函数、处理编码器输入的逻辑。</li><li>学习点：从 “读天书” 变成 “看图说话”。你能结合自己的实践经验，理解开源项目的架构设计，并知道在哪里修改以实现自己的功能。</li></ul></li></ul><div class="admonition note"><p class="admonition-title">学习顺序推荐</p><ul><li>对于 ESP32 + SI4732 + ATS MINI 开源收音机项目，"在实践中遇到问题，然后针对性地学习理论" 是最高效的方法。推荐的学习顺序如下：</li><li>购买硬件 -&gt; 搭建开发环境 -&gt; 运行 Demo -&gt; 修改小功能 -&gt; 遇到具体问题（如：I2C 通信失败、显示乱码）-&gt; 搜索问题、学习相关知识 -&gt; 解决问题 -&gt; 继续迭代。</li></ul></div><h2 id="学习资源"><a href="#学习资源" class="headerlink" title="学习资源"></a>学习资源</h2><ul><li>ESP32：乐鑫官方文档是最好资源，重点关注 Arduino Core for ESP32 部分。</li><li>SI4732：Silicon Labs 官方的 AN383（应用笔记） 和 SI4732 数据手册是必读文档，它详细说明了所有命令。</li><li>ATS MINI 项目：仔细阅读其 GitHub 仓库的 README 和 Wiki（如果有），这里面通常包含了硬件连接图、编译说明和常见问题。</li></ul><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="/posts/32f038a6.html">开源收音机开发技术调研</a></li><li><a href="https://esp32-si4732.github.io/ats-mini/hardware.html">esp32-si4732/ats-mini 官方文档</a></li></ul>]]></content>
    
    
    <summary type="html">基于 ESP32、SI4732 开发收音机。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="嵌入式开发" scheme="https://www.techgrow.cn/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>开源收音机开发技术调研</title>
    <link href="https://www.techgrow.cn/posts/32f038a6.html"/>
    <id>https://www.techgrow.cn/posts/32f038a6.html</id>
    <published>2026-01-09T13:03:50.000Z</published>
    <updated>2026-01-09T13:03:50.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="技术路线"><a href="#技术路线" class="headerlink" title="技术路线"></a>技术路线</h2><p>为了开发一款 DIY 收音机，非嵌入式开发者通常可以选择以下任意一种技术路线。</p><h3 id="三种技术路线"><a href="#三种技术路线" class="headerlink" title="三种技术路线"></a>三种技术路线</h3><ul><li>技术路线一：模块化开发（推荐起点）<ul><li>怎么做：购买调频（FM）收音机模块（如 RDA5807、TEA5767）或网络收音机模块，用 Arduino 或 ESP32 这类开发板通过 I2C 等简单接口控制，甚至可以用树莓派做网络收音机。</li><li>学习重点：基础的电子连接、Arduino IDE 的使用、调用现成的库函数。</li><li>优点：最快出成果，能建立信心，适合所有初学者。</li></ul></li></ul><span id="more"></span><ul><li><p>技术路线二：基于开源项目二次开发（平衡学习与成果）</p><ul><li>怎么做：在 GitHub 等平台找到开源收音机项目（例如，搜索 “Open Source Radio ESP32”）。下载其原理图、PCB 文件、源代码进行研究、焊接、烧录，并尝试修改功能（如增加显示屏、改变 UI）。</li><li>学习重点：阅读和理解他人代码、学习使用 PCB 设计软件（如 KiCad）、掌握基本的焊接和调试技能。</li><li>优点：在真实项目中高效学习，是成为合格开发者的最佳途径。</li></ul></li><li><p>技术路线三：从零开始（不推荐新手直接尝试）</p><ul><li>涉及内容：从芯片 DataSheet 开始设计收音机高频头、中放电路；为微控制器编写底层寄存器代码驱动音频解码芯片；实现复杂的用户界面和网络协议栈。</li><li>挑战：需要深厚的模拟 / 射频电路知识和嵌入式系统开发能力，极易因调试困难导致项目流产。</li></ul></li></ul><h3 id="技术路线推荐"><a href="#技术路线推荐" class="headerlink" title="技术路线推荐"></a>技术路线推荐</h3><p>对于绝大多数个人开发者，强烈推荐从路线一或者路线二开始。<strong>尤其是路线二，它让你在一个高起点上站上巨人的肩膀，既能做出有成就感的作品，又能通过实践快速学习嵌入式开发中最实用的部分。</strong>一个有效的行动思路是：</p><ul><li>(1) 明确核心：先想清楚最想要的是 “一台自己做的收音机”，还是 “深入掌握嵌入式开发全过程”。如果是前者，绝对不要从零开始。</li><li>(2) 寻找项目：立刻去 GitHub 等平台，用 “open source radio pcb” 或 “arduino radio” 等关键词搜索，找一个 star 数多、文档齐全的项目。</li><li>(3) 动手复刻：按照项目指导，购买现成的 PCB（或打样）、采购元件、焊接、烧录程序，这个过程本身就是最好的学习。</li><li>(4) 迭代升级：成功运行后，再尝试修改代码、增加功能，逐步深入到嵌入式开发中。</li></ul><h3 id="技术路线总结"><a href="#技术路线总结" class="headerlink" title="技术路线总结"></a>技术路线总结</h3><table><thead><tr><th>项目目标</th><th>推荐技术路线</th><th>嵌入式开发需求</th><th>难度评估</th><th>核心工作重心</th></tr></thead><tbody><tr><td>快速实现一个能用的收音机，验证想法</td><td>使用现成模块 / 开发板（如 ESP32 + 网络模块）</td><td>极低。主要进行拼接和配置，类似搭积木。</td><td>★☆☆☆☆（入门）</td><td>软件配置、外壳设计、系统集成</td></tr><tr><td>制作一个功能完整、有学习价值的收音机</td><td>基于成熟开源项目进行修改和优化</td><td>中等。需要理解、修改现有代码和硬件。</td><td>★★☆☆☆（进阶）</td><td>代码阅读与调试、电路调试、功能增删</td></tr><tr><td>从晶体管 / 芯片级打造，追求极致控制或学术研究</td><td>从零设计电路并编写底层驱动</td><td>极高。需精通电路设计、MCU 架构、通信协议、嵌入式 C 语言。</td><td>★★★★★（专业）</td><td>芯片选型、原理图 / PCB 设计、固件开发</td></tr></tbody></table><h2 id="开源固件项目"><a href="#开源固件项目" class="headerlink" title="开源固件项目"></a>开源固件项目</h2><div class="admonition note"><p class="admonition-title">提示</p><p>在 GitHub、Gitee 等代码托管平台，使用 <code>SI4732</code>、<code>ESP32</code>、<code>mini radio</code> 等关键词组合搜索，能找到更多优秀的开源收音机项目。</p></div><h3 id="ATS-MINI"><a href="#ATS-MINI" class="headerlink" title="ATS MINI"></a>ATS MINI</h3><h4 id="项目简单介绍"><a href="#项目简单介绍" class="headerlink" title="项目简单介绍"></a>项目简单介绍</h4><ul><li>在开源硬件与软件领域，ATS MINI 项目无疑是一个值得关注的技术瑰宝。它是一款专为 SI4732（ESP32-S3）Mini/Pocket Receiver 设计的固件，旨在为用户提供一个高度集成、易于使用的收音机解决方案。</li><li>ATS MINI 项目的核心是一个功能齐全的收音机固件，它基于多个开源项目的经验与智慧，例如 Volos Projects、PU2CLR、Ralph Xavier、Goshante 以及 G8PTN 等。这些开源项目的合并，使得 ATS MINI 在性能与稳定性上有着坚实的基础。</li></ul><h4 id="项目主要特点"><a href="#项目主要特点" class="headerlink" title="项目主要特点"></a>项目主要特点</h4><ul><li>高度集成<ul><li> ATS MINI 项目将 ESP32-S3 与 SI4732 模块的优势高度集成，用户无需担心复杂的硬件兼容性问题，只需关注于功能的实现和优化。</li></ul></li><li>灵活配置<ul><li>项目支持多种硬件配置，用户可以根据自己的需求自由搭配，实现个性化定制。</li></ul></li><li>开源共享<ul><li> ATS MINI 项目遵循开源协议，用户可以自由使用、修改和分发，促进了技术的交流与共享。</li></ul></li><li>完善文档<ul><li>项目提供了详细的硬件、软件以及刷机文档，即使是新手也能快速上手，降低了使用门槛。</li></ul></li></ul><h4 id="项目技术分析"><a href="#项目技术分析" class="headerlink" title="项目技术分析"></a>项目技术分析</h4><p>ATS MINI 项目采用 ESP32-S3 芯片作为主控制器，结合 SI4732 收音机模块，实现了 AM/FM/DAB+ 等多种广播信号的接收。其技术亮点如下：</p><ul><li>高性能处理器：ESP32-S3 提供了强大的处理能力和丰富的接口，使得收音机在处理信号和用户交互时游刃有余。</li><li>模块化设计：项目支持多种硬件配置，用户可以根据自己的需求选择不同的硬件模块，实现个性化定制。</li><li>丰富接口支持：ESP32-S3 的丰富接口使得 ATS MINI 能够支持屏幕显示、按键输入等多种交互方式。</li></ul><h4 id="项目应用场景"><a href="#项目应用场景" class="headerlink" title="项目应用场景"></a>项目应用场景</h4><p>ATS MINI 项目不仅适用于个人爱好者进行 DIY 创作，也适用于教育、科普等领域。以下是几个典型的应用场景：</p><ul><li>个人娱乐：用户可以自定义收音机的外观和功能，打造独一无二的个人娱乐设备。</li><li>教育工具：ATS MINI 可以作为一个教学工具，帮助学生了解无线信号传输原理，提高实践操作能力。</li><li>科普展示：在科技馆、展览馆等场所，ATS MINI 可以作为展示无线信号传输技术的展品，供参观者体验。</li></ul><h4 id="项目开源地址"><a href="#项目开源地址" class="headerlink" title="项目开源地址"></a>项目开源地址</h4><h5 id="上游开源项目"><a href="#上游开源项目" class="headerlink" title="上游开源项目"></a>上游开源项目</h5><table><thead><tr><th>名称</th><th> GitHub 地址</th><th>描述</th></tr></thead><tbody><tr><td> Volos Projects — TEmbedFMRadio</td><td><a href="https://github.com/VolosR/TEmbedFMRadio">https://github.com/VolosR/TEmbedFMRadio</a></td><td> 一个围绕 SI473x 系列芯片的嵌入式 FM 收音机示例项目，主要实现基础 FM 收音功能，是多个 ATS MINI 及相关固件项目参考和借鉴的早期实现之一。</td></tr><tr><td>PU2CLR SI4735 Library for Arduino</td><td><a href="https://github.com/pu2clr/SI4735">https://github.com/pu2clr/SI4735</a></td><td> 面向 SI473x 系列收音芯片的 Arduino 库与 Radio 实现，支持 AM、FM、SSB 等多种模式，接口设计清晰，是 SI473x 生态中被广泛引用的基础项目。</td></tr><tr><td>Goshante — ats20_ats_ex</td><td><a href="https://github.com/goshante/ats20_ats_ex">https://github.com/goshante/ats20_ats_ex</a></td><td> 面向 ATS-20 接收机的增强固件项目，基于 SI4732 / SI4735 芯片，对界面和功能进行了扩展，是 ATS MINI 系列固件的重要参考来源之一。</td></tr></tbody></table><h5 id="下游开源项目"><a href="#下游开源项目" class="headerlink" title="下游开源项目"></a>下游开源项目</h5><h6 id="调频收音机"><a href="#调频收音机" class="headerlink" title="调频收音机"></a>调频收音机</h6><ul><li><p>ATS MINI 原始版本 / 老版本固件：<a href="https://github.com/G8PTN/ATS_MINI">G8PTN/ATS_MINI</a></p><ul><li><code>G8PTN/ATS_MINI</code> 是最早的原始 ATS MINI 固件来源之一。</li><li>这是由作者 <em>Dave (G8PTN)</em> 发布的最初或早期的 ATS MINI 固件代码，围绕 ESP32-S3 + SI4732 搭建的固件实现。</li><li>该项目包含基础的功能、菜单扩展、电池电压显示等，可作为项目的一个核心实现版本。</li></ul></li><li><p>ATS MINI 社区 Fork 版本 / 发展版本固件：<a href="https://github.com/esp32-si4732/ats-mini">esp32-si4732/ats-mini</a></p><ul><li><code>esp32-si4732/ats-mini</code> 是一个 “ATS MINI Firmware Fork（ATS MINI 固件派生版）”，基于包括 <code>G8PTN/ATS_MINI</code> 在内的多个开源项目来开发，例如 Volos Projects、PU2CLR、Ralph Xavier、Goshante 以及 G8PTN 自己的 ATS_MINI。</li><li>该项目围绕 ESP32-S3 + SI4732 搭建的固件实现，它集成了更多贡献者的改善、修复、特性扩展和新功能，代码更活跃、更新频率更高，并且形成了自己的版本体系（比如 <code>v2.x</code> 系列）。</li></ul></li><li><p>SI4732 Radio 固件：<a href="https://github.com/joaquimorg/si4732-radio">si4732-radio</a></p><ul><li><code>si4732-radio</code> 是基于 <code>G8PTN/ATS_MINI</code> 的派生实现，偏向个人维护 / 定制化方向，围绕 <strong>ESP32-S2</strong> + SI4732 搭建的固件实现。</li><li>该项目由 <em>Joaquim Org</em> 维护，定位更偏向 通用型 / 工程示例型固件，适合作为 SI4732 芯片的软件参考实现与二次开发基础。</li></ul></li><li><p>SI4732 / SI4735 Radio 固件：<a href="https://github.com/ralphxavier/SI4735">ralphxavier/SI4735</a></p><ul><li><code>ralphxavier/SI4735</code> 是一个面向 SI4732 / SI4735 收音芯片的开源固件与示例项目，旨在为基于这些芯片的收音机开发提供功能实现和驱动参考。</li><li>该项目支持在 ESP32 系列 MCU（包括 ESP32-S2 / ESP32-S3）平台上运行，通过 I²C 接口控制 SI4732 / SI4735，实现 FM、AM、SW 等广播波段的接收与基础操作，并提供基本界面交互示例代码。</li><li>项目主要特点包括对硬件模块的收音控制逻辑封装、示例功能展示及对常见显示与按键硬件的支持，是一个适合作为 SI473x 系列芯片学习、移植与二次开发的基础实现仓库。</li></ul></li><li><p>SI4732 Mini Radio 资源汇总：<a href="https://github.com/Ramsin/SI4732-Mini-Radio-Files">Ramsin/SI4732-Mini-Radio-Files</a></p><ul><li>SI4732 Mini Radio 的固件代码、硬件设计（PCB）、资源链接等汇总。</li></ul></li></ul><h6 id="网络收音机"><a href="#网络收音机" class="headerlink" title="网络收音机"></a>网络收音机</h6><ul><li><p>yoRadio / 网络收音机固件: <a href="https://github.com/e2002/yoradio">e2002/yoradio</a></p><ul><li><code>e2002/yoradio</code> 是一个基于 ESP32 + I2S DAC 或 VS1053 音频解码模块的网络互联网电台播放器固件项目，用于构建可播放网络电台流（Web Radio）的硬件网络收音机，不支持 FM 调频，完全依赖 Wi-Fi 网络流媒体。</li><li>该项目定位为高度可定制的通用网络收音机固件，支持多种 ESP32 开发板和音频输出方案，适合 DIY 桌面网络收音机、智能音箱类设备。</li><li>功能特点包括：<ul><li>支持 MP3、AAC 等常见网络电台流格式，通过 I2S DAC（如 PCM5102、ES9023 等）或 VS1053 输出音频。</li><li>支持多种显示屏（如 SSD1306、ST7735、ILI9341 等），可显示电台名称、播放状态、时间、菜单等信息。</li><li>内置 Web 管理界面，可通过浏览器配置 Wi-Fi、电台列表、系统参数，无需反复刷固件。</li><li>支持多种人机交互方式：按键、旋转编码器、触摸屏等，硬件适配性强。</li><li>支持 OTA 在线升级、电台列表导入导出、配置持久化存储。</li><li>可选支持 MQTT / Home Assistant 集成，适合智能家居场景使用。</li></ul></li></ul></li><li><p>ESP32 Radio Evo3 / 网络收音机固件: <a href="https://github.com/dzikakuna/ESP32_radio_evo3">dzikakuna/ESP32_radio_evo3</a></p><ul><li><code>dzikakuna/ESP32_radio_evo3</code> 是一个基于 ESP32-S3 + PCM5102A 音频 DAC 的网络互联网电台播放器项目，称为 Evo (Evolution 3)，主要用于构建可播放全球在线电台流的硬件网络无线电设备，不支持 FM 调频，完全依赖 Wi-Fi 网络流媒体。</li><li>该项目支持 MP3、AAC、VORBIS 和 FLAC（最高 1.5 Mbit/s）等多种流媒体格式，并集成了 OLED 显示、旋转编码器、IR 遥控等交互控制功能，让用户能方便进行调台、调音量等操作。</li><li>功能特点包括：<ul><li>从预设的 “电台库” 文件获取网络电台流（支持 SD 卡或 GitHub 下载）。</li><li>使用 I2S 与 PCM5102A DAC 解码音频并输出。</li><li>内置完整的 Web 服务器，可通过浏览器在电脑或手机上控制电台、音量等设置。</li><li>OLED 显示屏显示当前电台信息、音量、VU 表等。</li><li>使用单旋转编码器实现菜单操作（包括音量、台号、库切换等），同时支持 NEC 标准红外遥控器操作。</li><li>支持 OTA（网页直接更新）、SD 卡浏览、3 种显示模式、睡眠定时器、3 点均衡器 等扩展功能。</li></ul></li></ul></li></ul><h6 id="二合一收音机"><a href="#二合一收音机" class="headerlink" title="二合一收音机"></a>二合一收音机</h6><h2 id="开源硬件资源"><a href="#开源硬件资源" class="headerlink" title="开源硬件资源"></a>开源硬件资源</h2><h3 id="硬件博客资源"><a href="#硬件博客资源" class="headerlink" title="硬件博客资源"></a>硬件博客资源</h3><table><thead><tr><th>资源名称</th><th>资源链接</th><th>描述</th></tr></thead><tbody><tr><td> esp32-si4732/ats-mini Documentation</td><td><a href="https://esp32-si4732.github.io/ats-mini/hardware.html">https://esp32-si4732.github.io/ats-mini/hardware.html</a></td><td><code>esp32-si4732/ats-mini</code> 项目的官方文档，包含硬件架构说明、引脚定义、参考设计等相关硬件资料。</td></tr><tr><td>Xtronic - ESP32 S3 SI4732 Pocket Multiband Receiver</td><td><a href="https://xtronic.org/circuit/rf/radio-receiver/esp32-s3-si4732-pocket-multiband-receiver/">https://xtronic.org/circuit/rf/radio-receiver/esp32-s3-si4732-pocket-multiband-receiver/</a></td><td>Xtronic 网站上的硬件资料，提供 ESP32 S3 SI4732 口袋多波段接收机相关电路说明、部件清单和设计参考。</td></tr><tr><td>PCBWay - ESP32 S3 SI4732 Pocket Multiband Radio Receiver</td><td><a href="https://www.pcbway.com/project/shareproject/ESP32_S3_SI4732_pocket_multiband_radio_receiver_40ce2b94.html">https://www.pcbway.com/project/shareproject/ESP32_S3_SI4732_pocket_multiband_radio_receiver_40ce2b94.html</a></td><td>PCBWay 社区分享的 ESP32 S3 SI4732 项目，展示 PCB 设计截图、硬件资料及项目概览，可用于了解板子外观与布局。</td></tr><tr><td>MakerWorld - SI4732 Project</td><td><a href="https://makerworld.com/en/models/785921">https://makerworld.com/en/models/785921</a></td><td>MakerWorld 上的 SI4732 项目模型，包含 3D 打印视图及硬件模型，有助于从机械和装配角度理解项目结构。</td></tr></tbody></table><h3 id="立创硬件资源"><a href="#立创硬件资源" class="headerlink" title="立创硬件资源"></a>立创硬件资源</h3><h4 id="调频收音机-1"><a href="#调频收音机-1" class="headerlink" title="调频收音机"></a>调频收音机</h4><table><thead><tr><th>资源名称</th><th>资源链接</th><th>描述</th><th>开源固件源码</th></tr></thead><tbody><tr><td> OSHWHub - ESP32S3 + SI4732 多波段收音机 </td><td><a href="https://oshwhub.com/sunnygold/esp32s3-si4732-shou-yin-ji">sunnygold/esp32s3-si4732-shou-yin-ji</a></td><td> 基于 ESP32-S3 + SI4732，提供完整的原理图、PCB、BOM 等可下载资源，适合 DIY 和二次开发。</td><td><a href="https://github.com/ralphxavier/SI4735">ralphxavier/SI4735</a></td></tr></tbody></table><h4 id="网络收音机-1"><a href="#网络收音机-1" class="headerlink" title="网络收音机"></a>网络收音机</h4><table><thead><tr><th>资源名称</th><th>资源链接</th><th>描述</th><th>开源固件源码</th></tr></thead><tbody><tr><td> OSHWHub - ESP32 + Wi-Fi 网络收音机 </td><td><a href="https://oshwhub.com/twist_66/i-can-listen-to-the-undamaged-in">twist_66/i-can-listen-to-the-undamaged-in</a></td><td> 基于 ESP32 + PCM5102 的网络播放器开源项目，支持蓝牙、流媒体网络电台，可播放 FLAC / AAC / MP3 等格式，通过 Web 页面进行控制，兼具实用性与可玩性。</td><td><a href="https://github.com/dzikakuna/ESP32_radio_evo3">dzikakuna/ESP32_radio_evo3</a></td></tr><tr><td>OSHWHub - ESP32 + I2S DAC 网络收音机 </td><td><a href="https://oshwhub.com/hudiesudie/chao-ji-jian-dan-ban-wang-luo-shou-yin-ji-2025-nian-1-yue-6-ri-ban">hodiesudie / 超级简单版网络收音机</a></td><td>极简 ESP32 网络收音机开源硬件项目，侧重 “低成本 + 易制作”，通常搭配 I2S DAC 使用，用于播放网络电台流，不包含 FM/AM 硬件。</td><td><a href="https://github.com/e2002/yoradio">yoradio</a></td></tr></tbody></table><h4 id="二合一收音机-1"><a href="#二合一收音机-1" class="headerlink" title="二合一收音机"></a>二合一收音机</h4><table><thead><tr><th>资源名称</th><th>资源链接</th><th>描述</th><th>开源固件源码</th></tr></thead><tbody><tr><td> OSHWHub - ESP32S3 + SI4732 多波段 + 网络二合一收音机 </td><td><a href="https://oshwhub.com/abcdef2022/full-band-network-2-in--1-radio">abcdef2022/full-band-network-2-in–1-radio</a></td><td> 在传统 SI4732 多波段收音机基础上，增加了 <strong>WiFi 网络功能</strong>，提供原理图、PCB、BOM 等完整硬件资料。其 v2.2 版本增加了网络收音机解码和 PCM5102 音频部分等改进。</td><td></td></tr><tr><td>OSHWHub - ESP32S3 + SI4732 多波段 + 网络二合一收音机 </td><td><a href="https://oshwhub.com/yaobaling/yoradio-radio">yaobaling/yoradio-radio</a></td><td> 基于 ESP32-S3 和 SI4732 的开源多波段 / 网络收音机项目，在 SI4732 多波段收音机设计上增加了耳放和网络收音机相关元件，并适配了网络收音机功能（固件需另行配合）。</td><td></td></tr></tbody></table>]]></content>
    
    
    <summary type="html">开源收音机开发技术调研。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="嵌入式开发" scheme="https://www.techgrow.cn/tags/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>近期开发计划</title>
    <link href="https://www.techgrow.cn/posts/29d74d90.html"/>
    <id>https://www.techgrow.cn/posts/29d74d90.html</id>
    <published>2026-01-03T14:38:21.000Z</published>
    <updated>2026-01-03T14:38:21.000Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="密码错误, 请重新输入." data-whm="文章校验失败, 但不影响阅读解密后的内容.">  <script id="hbeData" type="hbeData" data-hmacdigest="7aa038b85c2e84e72ab1db689533070f5573ed05f84a9c955d7977b52ef5538e">4cfab7e4fdfd9afdbf1592f9652b8a199f3cb3b67d053a3a2b7db0e886465ca1ea14179f28e40c4d184809b24d2f909058d2606747febc969140bf1db3dbbb1a7542b98eb3d7de26ec2c86ec81828b35913853b3ca6e01e7cdd106a9462dcbff705b54ac505b28447538b61c3ec4400b8cb84079a99816fe59006d71ef364f10dfb4d6a52fa3c40030b50b35a957f58eeaf772e9c44236a3270411d3be05e2b07d30b9bac2b2c9e4a597c4d551dde647547269d613d69e329a68cd1dcba6a2bae36bd33e5e11d2a5d518899e43e6fbe0524ed9b9c309564739dcbd3851cc07f0cf45fdf977054a7183a76498682d3bcbe26762ba095458de76d41fd18e8353e46f9e6a744945ca97a1e13148bb3f9423d01ee13a6b4834facb1fd3008b9bc0b23815838f409203118497fae9e3c50332fdcc1f781ba863ad73325b42ab0feec4ff52515efcaae7b170dbd491c656284a85ddf836e73fcccf8e7ca5401292a93019511a0678c1a62a4f9d524748fa16eb444d4ba9b9709af6db0fd8358faffb7d0c03a8d01f503646851d8ec0c9f69f5e075714dcc796357f1870f6f0dca899a9bf358860381dd91e102a52d8499852d404fc84364601414cf25f113952a4e8453feca610d559c44f8a12d16bfedd77987b81e1710f415778084a5bb8f4a5c6c2aab5f7ed9a2212789eea24995e31f21d19c0f60d3d46829a35a209ad5a64ba59a81bdcf152f450d14cd1a48d46a1387566700476d2ecdb9d8353d4cbf63e855586f66ae706f65dc4955184b9a65a836128c63f24689bd56daa7a178ce5757d9d01dc84779600b82e6a988dab8b740a61c2ff52336f31143d91d739de64ac0009731caf857040616087028acc4ed0f44ce7db4b06867cc89fbeb38e768587f29842144388051bf5a9d0e6d50c34f302fd7baa802f9f9c168ffda3ebac1d2050694c4497144e66768873510e3bd1c5331c36ce81277ca2b8b28a332997ea2d4a0c033d8734e7f53a93fd0f7d93befa1e2ca440424342540b5fb5dafb58bae19439fc52008da933c4ab1c09c9397180751c729410b4e33e4b42e2ca464123092a013681dc189d3c6c9b568c9ef209dd27aa322816b62fdb4d438cbb725b076f448c90ab1226f482c58e4a439618e5544ad0b035ece5da84f39b6e91cc187ad85bba9ad3d1df678e401029e9de1b70b3eb57536d7dd879dfcf8c3e624a38256e95a99af821624daa673e38bd8747b26b94d5348d0d27779b447f343f87368acb8e537276216e2e5e6561d9ccaa46b82bd680bce7c4f4cc56d82439b2024cb26c26c284a9d8f5ce24f35b52d335f8b62fc257b04b8ebac321d9785d90e835961fd2c723e3eaf1e9e9f98ee5db63e0521cac3e574a777bb5f1b4b5dbb8ba3d93a4168cc2dfcc0b9a0ae2442d97a01fa711ef780b41f63d893f1f9f6e88ed61953c66ef74636546b60e5f3b74dedf7826adb9aafc045a4d081cc0c6c46f7d99ecc3ac20e73e1b55a401ba5aa22f530c892de0f7961c766fc616288952010e6da9b886d01b932b09af54700ca58efd3d810c1b6e44c3d4f523cd5bf484023309b809e90de2882acb79c237a7a53594e54cd663039a5c74d97b93a5552c24ef2db394e2bca465056620ea92087125ac05f012fe376adaad2684a73e829406a0537e0c721990efafb49abc2d2573fef95878899b9b3bc40aebb4dacfc78dedfe5f8c510f95fc8cb0c645e44a85a438a33e3abfae5b91404b5baaa760e533120ae51b8a1b45d03db0bb7de2afeb3c68cd14915e3aa2cf82d4006b4925c5c061e4e67f779c38b0398feb23efe1b4af81aa7c13957cee35779cb2baf9aaef4eb0074ba9856601131144f3f32b06b8576ebaeaea8467d965e248dec371e7458f974280e534d7df0ed9d0bd01e8d8c8699fab51b573418372ce4c097c62b155d034eab1fb62d37e66d1931e99c8c49eff493573b2f81085af4de971a5b6c8798d3aae32abc0058307e4eacb6d4c20a714d8025ef4a7aabca790b41248b65894b64b5844136de8c10b9a51a96137dfdae9fbe2f8aa1bec41ec1b99bd9e00772946d236131f0e967f002172ee52b95cd5294ee434c95d941684c440616e2d692c46ce12482b1b975fea4822b6c403a9044b94723fb0f8c340d69c4280cdc99c7ad5bfd27a961d195ab077396fb346fbf60a83f70fd162efc7dea1b6b0aab01b08a18fd862d646ecee92cc5b4aac63b70a87e5374a6b2ad3732c63be3b7fec49225659ec8661145100469d3368056e1c5059b25129d28f297492af8aa0d955560b368ccaf6da9b4b74726223aa8713bc5d07c0683dec0ecdd18c1eb5758f6c9fb7f2b378bb0f0e9158c5b1f4e9965ea2dd715b871e0a4b17b315e9212509bb190ba53daefac614109d530a6c6a72a403193252f64cde247028c19e65b05e2aa5a628efed935c6ac9c288ea9b946bebbcfea053b7abf03b3fa9a8bbf594f4a364281fdddbb6404a71011f4bf85ad4eb9042d591b74b9b827ef705ea7fb47c6503cba8dd79b7d1a33e8e3a15bb2d8e2ca8a36eb685c94b2d78215b71c98b2c0b3ee37e035bafbbafef30c0983a823efe8e7117435dd296db8e79ee3f3a744a2538dfde388f61d0a6d130734c4e44be686c870afdb7a1caa863bdabc994fce857ebf7972073d4426ebf9405f5926b4a25db7461119b55bd9ec4bd07931eb8c7d447e022e17a2df094a1dc00dcacd1fc064f1054276eed105893619605c48d576be245c5b87c97301e98a03f4e7214bb91ccb684917f6d96f2fd3488a7eca5ee845751e15fa94e41092d5e55efeab282dcdd9e11323b8a3946b92b5a59100d1e1491e329296363557a9af9262b745f52b40442e617efb7736c99a740dca8af13ebddcd673eb4a534f72e933973592758b63bbd367081a651b5caa7693a2a0ebda3c9f381a2ba519351c4c1d716001a1848bdd3202c31f564397ceb9eb3e4e70041673ccfaa66bd757cea040a014a4391e18d126faec37a2ee964edd824d8c5d2de2c3ead3f410961b608cad747e2bc14687da33756d6de87cc5f8dff3547d7e63964d64eafa0cb410cf2690800747f38b1a7921cfb554ea6bfff93e4207b42461762ef62c1bdc669e1899d1bb55eacf78d9996258735b9a96d7632068e0736297bda947ac28572609247d9f0e1c88c57b891ae7e97a36b4332161e91c23a37f07780aeb2ec85d85e9dea91110a395c7b162dcc3995b684d10eb28d8c55aa78a45b50171f6b23658357003d77d76c00f69027d812e5a3278649dd7f510af0d5eb652abdc305daab3743709e2dad44b171ddc5be2200541a923d4fb3b83785916e92604a89b9f17fc4b838abb234a80e9e037601fc00ef31188b3464ae33435dfb10ab5c973090ac8f01badd332ec2e904e605cc4a3385be2648132d8718fa5f8fb37c035144517ab367a704133c9644c5d1dbcccd2cb46bf88d63527a0116c8282a3f90f9c7a142d29ee89e95e272aa7063df9143ab4ddba067e8d9fb8e84e1ed83e648e0d238ee55f89ac10c0d62477d0f7c123e9c3d88d6e94e2fdc0b162ac345a16e8a23a0c02f82b4e358a37eca0f550842074acdf047d912df3390745af62d280f49d6292bf2f2c09c0a824f6b8636cdb1638a362e2652aea98475ad25b689189ed0940d539736cadb54cb971531dd44ccb04dc1ae651569af62e117d836931dca4e0895fd6eeb4bca0d59f7c421dbe4e44b4cb69b59b67356219451c11b8d7dd5bf20e013a1ffe68d983d1f722c28bc1d7919d49391c48bf1f7ad94a8950d4cc574db236fdd0bae29273aff2931b6b0b981d3e8b6031c1e6f29a5bc4bbd788723eb8b77b8d311fbf17f848d7709988df6130a9050950aa3dea53fee05c55ce0129f5d50dc4bb80243fd3dcfd8ea416b51808c78d2b2a48f9295cb0c48d850f4d9ea9754f770976bcf621dd13892580768a72d15472a8cdc8025628dfbd380c5be7a4f9d36dc72cc0a5a5281119618c23394885a86b6e0fcf909f2fe8964b41f729a21ea6fe351b1057ae08b3e1afc0d3ef3e0a8e6ea83f57ffcf9269dcd4e5a25369630477074a1243e17561171b83664124025196b8581d3daacf8bf6630fe9bc0026fd074b6661e53781f90a757827dff0df9294f141d088c3209794d875c4fac17e0d476f8a760608d272c6e44cb59ba224e27b2df0bf92d167b9c88d7654110876536879bd3fff1db399662dbbe65bb8451b63dfefba2c0cc6511f4d0bd7135cd679d571b0f8278fb6d16883a843ac8feef8a31fe0484d3a70ae7e9e264437e831c618323b273e75b07cc94a4942b7abee0e629f1d1afefdff8bb8d1e92b9d256a9b8270f7ed297bae1cecd45422e80e518f7911d1a0fd78b969d016e08d330fa115d88ce949c137b9cd4f094428fdb499bf6d554a1bf5a89e15cff3a2ca75b42c4e45593dec554558a68993f38206a93ed9f0108dda3554077915127d450d5517f8b63e2274614f61f8df13b408a990f9946d710005fe09a1ee277c47cf38c4358e2d3263791a611a975c8d10ab60b427b5cb5a8551f564bbf59daab665729f836398fdf8dead68c8a2f6c540a3b70fbac21f8730386c18d0e24212731760555c6f60b23e4bc8054b59868ff124046064aedb65b38d9c17f7cffe6efe3c9dbc79f95771675913101fecb789a17a362750f9d1d3f23d0293554e9a5364a3f57a57f33924e50a836a1e448f0074c7c1d99a42bfb695109fdd3462da7fe1c0f953a6028ba576dc5f0bd3b8b69782888a326b4c23d48d9ef531ff8a74cc96f7ae17b926bb76e45598d4f4d186485d107985561cbc8f7eb5361ea6ea16dabfa2506c0bf67b316d547735432ae0c1d549c359d932b3866f0d366c9be1ded3c3073af4943144b8f8f5a81a51e9ed6a812b6bfe57c9288a01360741989c98f4a5f814da3bf74171fa964c630020d35fe88e46467f9dd37c0bcdd9c54ad21b5630df463bcffdc9b5086439fbd90cd5feebd21fbf19858a0bff3f959cf4c8aa546625becce865924cbca2047d82cb3451a2054d8896936256978eab4d6708896210809c05f87d4a1f6412ea838e6a22ae451e61c7a0cc0d15dca8897b8c04e8a8f9555ab8c26f8bea71412c44ee056cb8b1be5878d616b5127f581ea01974ac9e8765f48a3d2291685f878cfe2c710c1224aea84f4544b60fe75e20747ea69d919e5438ee2c46f09a3a829a33f8d344cdba5a0d4f8a6be912ee9dc2073e64ca73bb6cf8a18df311c05c8e1cadff10de8bf4284b4195c05729b5daa7d27077042fc43e20b4aa2271d673cfab972be594036dd6add32bfdd752066ba423c74fc701ef431b52eef8908df6526566f8f3e9662dc00f198ee669d37676b2a538b5ca4299f8e982cae65320161f6e43f38a25115091bed6696b1f2d7b2cc5e0199ffb5dbee9b63f219e1ba13da38735677e30cf4336cd8656efac6dc6afade3773ba1104d218d8f3b2020835336c8d7ac1e15e5ce10dc5811c6f52efb493fc5e4dc69304e44b4f753d9e3beebe013acea970a32144a91e53f2049b786e68389162b6337a4bb41e367c15389458e1af8ac7da2d943bd7ca97c8a3b17ce7538b095f15a86b6159e68709b65e196b5d4b44cf844bc6176a4b8ef844464f3987a211a93e24dfa199f8f34b19166e626cce8c8e9dbc116b0fcb62c4cd88b4aeeb0119b76eb38f624cd9b8a1cbfd5f43214f8f45034aeed30c89435f44bdfcd2adf832089f9716f02808677410dad61df3da105822adec82a11bf601a2b14289cbf8fce26b01ae6802dfcd8d0592027b83135734e4147bb453814b708231a72d6d12a58022dc7c8e546fc90f95b6102c5d650925e9613efa2b7a520ffb62bc526cd6602b6d30178ca55c18efc7b058c64d9c3f199390f507c18429710b5d52cf5a3fc655e2eb7688c11df912756dd978dd262293f702824caf505fcd6e551ceef8282d230e2768a63457ecadd3a7f8597dbf914ac64e1235003b652037d5045dddec13e1e31192cbd3072744e593e4f2bb49b136ada88e09ba5576ce8d38e8335f8f4f3290dd837de1c8a51e3acb0877936f072366c1151dc70033c28816f6e9a6efc83ad3425f88f78d62ce100cb5a7723f24374567f5653c46cb8323c69571aa468834974110f5dc82a30875bab673be32e95062212ff48ee5e856672f35710fcefcf29362ea983f223d5ca3d01789ff6450a19f93a521acdb652884c351c56f222a03e9c49e45319c9942d717c30101654f3c51031bb677fbcd5a7a4355c62ccb933b7ce3c3611dfb12234b152bcc270d5d3a57f6ce3bfa9a910f4ff1e2799dcfea466e5ac7f9365acc05ac0c2e9edfa3d0b8f084b5f402dbc4f9941cdbcb8ccf46e1776333e5a2bb6df995be4e3b87f10ec5bb4f04c538d087fc9f5dbc9eb683f1971e53016afc07fa5ecaf402068e19b7b2029215d7b8b2defef4a172968a7ab3dee2cb19cef50825e0e915ebd73dbec0cce52d1944e6aad535af0957f425d5ffe17d51638f241ff13bebb8f1358333bd5f1c9e9d9a560611cfd917e14b53e2a5d653980f581504b29f6ead070e64230ab9dae5410176b8744855e1c165235bffa9b3460dc763afca48d7c41aa892fcf38cf6b3d20ba7a20dad3f604894d404d83e74dcae2f9144843c1a81d6767f55ad82c55b3556d81301baf67529a1de8b33b18822c2a7338059682b196ec7eba1a3baf268ec6869f127437ee6d6ef4a5c518b40523e0674e0ffbf1917dc5ca27deeef6d41a6600b1193e0f0ebe6bd93d6b1bea9943617596c4726e9d9f3aa7cbab91c5a9fc9049c84b4bb9ea41d0c6f1cdfe3f72b1a03ce13344e61e9f8b1f19311d9a7d907243ed69be76f6331fa81f69b09b7533748d7b2a05c9f1474e014c06436d960c68263dd2b6901762151d628e06526bf38fda01d348277169cca7394c10737bce7df1a1336c517683f6a905f0e2bf5e0f9bdea98c4ac933345fc74560d88c4ecd2f4776ffec9b599d5b4366a2dcbf9e8ba78fd2f258141e900ad8f3de5c491595fedb26fd1a4fd4bc88de3735c0390ab8bfd211cde9f6575fdfe227bd67e9cff80241f75cd27bf2d549bb4c03d5887becd8345bee94b40770f09678175698a3d67bf8afaeecdcd2ac2a7e1a9db5fdc81b837cf76b964fdca7c35e527a41f3c32be359c4ad173df3085b78986b28100865a760d7ff0fb80d93eb470a57e9a1667d64c576da0898479cc8f54b74b2c895289fc321081df00400a686d75a116754ea4cb29ce5b6e5917cd7db174df72c4f7cc5ceca1e0b53cdce9779ad6bd511c31e7d5bb73b8f0cb2452b0f4f7d1bf73740aa1806ab113df1c82901d59db26690a388a3d17be3bc88c880cb92501e2dfdd76d1681c7e30a34e4469f06b67670cf626da64b3bdc160c82d4f4427217e1bb3baac2f6004ffe4f8da116e1336eb2c217957defd6c15af86195715fc5ac9132d339e754d00b308fd090316c37439e743277ab213484e5a68f4fcb2c65be736e26fda553648ba748f1a17b21001bfd5f02d54d395b50dc4ed81302c5cedd11bd5d9b75ba57cfe4a358a0ad3ca5e1952d402c72a7647322c931a2d913bb537d331ac1858694a7611d07e5154783d6d1c18efe984d82294203c3278d59f5d6a524e295ecc6120714350b9989be5c49fce14982491cd3ad781bedbd82e</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-surge">      <input class="hbe hbe-input-field hbe-input-field-surge" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-surge" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-surge" data-content="请 输 入 阅 读 密 码.">请 输 入 阅 读 密 码.</span>      </label>      <svg class="hbe hbe-graphic hbe-graphic-surge" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none">        <path d="M1200,9c0,0-305.005,0-401.001,0C733,9,675.327,4.969,598,4.969C514.994,4.969,449.336,9,400.333,9C299.666,9,0,9,0,9v43c0,0,299.666,0,400.333,0c49.002,0,114.66,3.484,197.667,3.484c77.327,0,135-3.484,200.999-3.484C894.995,52,1200,52,1200,52V9z"></path>      </svg>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">本文主要记录近期的近期开发计划。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="加密博客" scheme="https://www.techgrow.cn/tags/%E5%8A%A0%E5%AF%86%E5%8D%9A%E5%AE%A2/"/>
    
  </entry>
  
  <entry>
    <title>近期学习计划</title>
    <link href="https://www.techgrow.cn/posts/860c6c02.html"/>
    <id>https://www.techgrow.cn/posts/860c6c02.html</id>
    <published>2026-01-02T14:38:21.000Z</published>
    <updated>2026-01-02T14:38:21.000Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="密码错误, 请重新输入." data-whm="文章校验失败, 但不影响阅读解密后的内容.">  <script id="hbeData" type="hbeData" data-hmacdigest="c8f00ac8144051377ba307fe38694385a15024411505406615450724ca4bd16a">4cfab7e4fdfd9afdbf1592f9652b8a199f3cb3b67d053a3a2b7db0e886465ca1ea14179f28e40c4d184809b24d2f909084075e7e2b3392f33fc9e77db5e7d5c8c75c9f5edb502c44c97d2936037834eaab784e9ab6bd875cbc56679f666329099d4ab89202683abbb75c823d1c8424df846096a5e220e8d947e3237c2079fccffd2ebb407025d74a55454dc8b9b915621dc936a1785358ea4563fc1d860f7b27f4f2efc4497cc955ee3c3c79ef2468b8f5f705ef1c1b0146ff27d98aadb9f9b269a7fea8268b36b7a8eafdcec14f94cf3bdaa5d35f236a25c23f0a8ec54a67b6768c47cc3a69f53ca3630b03cdda249c1862a61de00df4546ed43a441e16c36e2c05a9e0f8cd4e144c55b719c7a697bac7e90957c47087d36dc98afc80b75e748562c6d0e6b4e47ca91575f1758619420cd697b1e042ace49b1e60bd9e1269bf493a3cd90be137138bfa5ff2ba9dbf9c7e88905743edd9faf92b2d8a5726b56afeb5a5eb15f7734a4c4af977045589863cf1dea44b3aae59cc9c1fd517822a14c8e47e7cadd5aa24669b2d02d01078fcbeda47f35d2da2a353ca716ac21de1f4691e5136d7463bec82cb5b58a72e8dfe86153d1893c17369835af18bd66501e6d4559dee343028c34f87525b36beddfd38053a4a2833546621a8845c1132d4808ad89133bed187f171916da2091b4973511701c16b37d026e1c67f27cb0e8046579ccdee9b44f014f413d952254ffc173125f214dde79cbb04f0556811bf311086ed694d656a234bcb8f983ac41836ba4aa20363eba544121d736809b5fe9d80a1c17c99b0d1651b885321a4ae62a288af89150c66fe9dd6f9ddccd7a5fd78ae15f994e65743948fe8b46a5230d20258a412d3ecc33800abeffa33f4b3408f197e06adb2eee6c10b227ff486e3f94818c8069957f8d50128216cede6220ae004687cfe759c7ae4a7e52c5f3a372374a34c7141e248b768167747c31ca73251a4077ab7f7da1bdaa81b30dbfa534b700543a34f32454debaacd801d2359437bcb8269dba80e630ed619e5ea3c57b6b1c896bf5fce44a78b6e42bde3bbde6932dc73af25db63da5ad65ad6eafe1b6f00331f135da50e8e0fc2987275e05d41a86b007cf771746971c5cf4308b9e743949a89b54449b96a7db81e014363c4b29167d86a31373163e92919c114e6d3d0ff60c248f78dc7aa400545d28aad057f381f1a104c9d4becf6c42e8074baddc87c0711efd811bedc215fef0ffe19e9f767d582cae3089d68a0f539301341b62b70ae509b56f7bc46f6d6b7de2d4131b51ac5a280bc7807db539bb8ba4a3f33d9935df2ed6c44a8844eb9d33cb882c97ee5a84315691c86b8dfc111bb1902f9b89b1c3f369af52feece8baa7d4b7ed8cc8e664fa33e7bd646dd1ee1f1fce68c4d9771e9917953e4efeb5278dd11328d6db2f2a58682c6ac8cca3a8e13f9d1bd907a5916d03ca29d243a4f75855c7d5fc8e05dd97b2021e4c0ef4d26646edb0d493588bc22e915b3cadad638336b1f947f4f4d77f2a63858570d82276af131c7a81c1b11ce162dd214da3d73f4a75f2d76a730fa360f3659e2092caa6e1207a1e134d77b28e3b5ad3c09f48de0ed3ff31d28348b42e74b81a76fec467e5d8e792c660407f5c89a16ba5fe34bbb1e22d15aa15df6d7cdeb7fe6bb888d636da0dfba1457d687917f45cf271b863e8009046c7d6f1a6e91478d9bf08bf4036631086a19b52d750828ba3ddc049dc1d90507624f7e1f27d247617fa052bd2c6ed14cdfd025c431f8a384733142d57f2175de452b541b7164f909ee751212434d8c807b2cb23b59bae401422103c8212e9b6b7e4a13862e084306ec88c8f9e361ac1166d73d7fcf545cf98982e47fb3dfc6d04a47b3eaaaf3d48146a93609583380a0fef09b4043f73e8c6b6ffd2717785b7b2d7bfe497019bdc80d1c2efad129c6c00ccc80e9e915effacddd182f033324e01bf08631d07aadb692f5eb1cf99b13e0bbd3fa9fe170ab3a1a07f7c2cc7a22300c6dfb7ce27499a9e0dadb21351ab7fbd802b11b113d7b3618c4bbf2a484fed5c4c341ea07716fcbd2246535ab9077f28b018081d2452f79b8f2930f50d2e85f572a81546099e30575e1a357f22596e8db39f8e3a802f8cc32b4d1e81130d984e5b6f74f4d8e47a98b522dcb4d6e9a1713177e1b10c8d794649564e0829ad73cf2c890b4aff2d3f4a95f3f57ed16257261ed14b8fe8698d32a3fda73df5a20c2ba3695eac5eeae2b1497d13622b05353d5a63e20668f3632253524c81f6e9e5b376529630fe169774ed4f6098383010b01688d0123636c4bc4df0892d0c9978bf892c5d07950d1080c405e8f9edf6facc0cb0720bb0b4ea69b7490fae6d809c2c4b647724ca5cca621890d654e0054d2d9482ff380cb9ce02564d9c4cb83367bedb156a34c65ae7dba1b3a7030c98d51098b4a90b93b941d9f6d941a7fc9a56e96c8281f70ffc59230bab686b0ff94dd052d96672f83e6e1f20240394a89de78a563e055d3c1e2230789270edbc6e4871b30e038eb6e2d70c9756739f9abeafb4cf4763dd401afaab6e8b7d28eaa946938ef2224fba7ebee3c0955e0bfc177b64826dbda44291765079976b352dd800f2ec099d9d96a41ba984363b8ccc03cd7c1e67618c1f916759cadb1795b396e81a03591dd0cb88d06bbd935bf96819a0b3f899677ee20545543b2b62705b187a710e3f1e558403ee7e8885c3d60a31d53ccef36f7b80ef2d98beb0cf864f40f64b6b35af29e8842b350cc55520ec31b0f98382ff7d5e1396b0f835096e6da4f44e293f860d9a61fcd874c3515d0e9c970d9c2849f8feea51b6c5ad300165bc2904f84961d01d0d56fc2d9c725c43b17f94d9f1bb882b9a9bb4d5fd94c25423e9490f36a607bae8c1545f85e3fed4f4f437be53dc368d55cb4785cfc504c5ee0c78b4aec8fb67d84c9dcddaaa6f706eba6fc22377879827930942c2e5d49c6c35a2c1036e081dbf4bd6df0a2d4dfe492a434e85278f945ff84cf170519d33a5d4913823a7610a1a06d4c08e6fb56e634f6dff45835193185de8c44d78e4e9941c93ced089324fd70bd38b13864f83482c0e01ea36f55f90f1b2f752cac8178ca5439505630710a3bdda6b37c5e2227c73e53da2bb1b827f018bdfd6ce135a6e71b377580536e84e861caff81872d9541f3d864263c4c2d94aab9246a7b15a762116966ffb7f656ec1bc0af79d17a8c5459ded5bb7be392abba048e005748614da06452ad7c17ab4a0d7a71fa7f92b45ad1c06d637bbf674c4c7134fae11086c52b88429f728f20df23c76402e1ec3ef8ffd4edb6da08d19aa9af550e2600f633766a146e0ab5e1aa7ae5c5d6f2d22ba68f8e156b69218faaa613305b79fe226afb2b76c1d8f9ffc513379d588d11ca2183fdd232e69514029dd16e81fd94d852c333ac4c4491f83eb39731014679fae09d06a990c9fef72e9d3a2ca4bec17e2d9c186c3f41e8b8efbdbbb96f3b3f515bd70599c10b2dc7ed621342cb852f2d8c63d614d68d963dc3ae9afb0f233c7db2557f2c231c6f94fd7f482ecdda94a58d965b76e10c16b7b00efbc92768b5fe59fdb8a7f763e7947352d955ee12b71bbccf5d01728e73dca3baca3d5093d8aafb686bcce50933f5398f2342d010645014ed95de5371fb611e472746a9fa5346aaf497b2f2768dc628e433edd2769db9ea95882b6da8448d5cd87c2efed530d92511309352986d39a289c8d8db256516f6c37731d432c6702dc7dc577ad7d9386509f3fc885fe3d6327ac962b59d204376db69924cef74a7a9973d4f752667066ed47d53f35fa65c4bcbd025452a4adc8349679ef411646fed07c4dca3ea3399c61fe0f16c3deb7c13b8ef6bad453b4fb495bb3d79f59d70497773eecdb6eefe158f4634b7dc9e0e1ddc07aeae3f7d49ad0e96483c9319a8062eb7a4299b0835cd3fdd5875aa10ab484ff3a5a86c0e25f1e434e1fcd1f0a421aefba4c2ac26a5377c8fb069492b72533638cfa473c5bfd2111a6c277e07248d77e647f23b7c250853c6ea7c1af148a756184cf07acd171cb2b97778a0f8a8dbcf6c0442775a238ad39782eddd9079b224bd533411f3308fce227a2f837a598feea0cbd90867cc94b6245ffc5b21e2029c18bfe2561e0352706c717750e649e14da517742a5a3656b0bff2a7df3453042580b703f6c6cdf45743ef0e34eecc7a4204898eb6cb8c1a9a71aa60ba433f0438d4a977198186f69d854ee762c0d498d1cc6000ec3b0e441eafc89ec9f70af36dc73bb6d9030cbd97f915bcd11e268bc04ab69068ee9039a79ea2f114eebb195d04c77e729f24274db59c6ad3cba01d95aa14330a1e4f4f88ba13aa6b5463a6164fc4cffa1d526286cfba1b1b421f56ce42ca90fff2f2cfb70dfa11ffde21712103d74e19a2809b0f8b75da921d19ea91e6f71152f7b04d406bbbcecea68c51c1d46b045f78f84b0f464d41341bab2d456ed189585e4d5816572b7105f55bef91a2fe1b29bc8966703007b5a542b785146dae149d2ade3340989919359a5e89a496a42a9451cc82a343a9d6e8b545c6efb68618a7515fc7484b07b69825457883fbac5b59f962cb010643260d1043432e7dd71094a26a8f59d79cbec99a99ba805d81fe3386edf76acfc45fea7ea66dff9808d58c92b7cadb9b577f33472faa31457cb085d0245587a11c59d562678f30be18b2863d2f49a62274d520f57a3abf17f8dc60053f8e29671cd0b650f360b08d670d9f44c563f118412a2fd7cecf7fdf87e5a4e75ae4ab54ce62a251305148d73b2417a7b5431523c0169e9b4662d7e84aad66a904e5cec46ff61b1f70a31f87dd0f2a3e613cd4a0b167275bff2bc1117e7ee17e2e87070b9cbb9fd4255f6219513597566d15065d8bebf9f59d7d1ffb0e513f57c703bbe80c54b66a4859343cdb9b411fc2e67375b893cde020168a4ba4fca30c2cbba96bda18a27f91df60c25336a1ecd707668d1dd840b63ff62aff5f6be2d817bd62f155a07ef084d52c571ada2bd6f5114855dacf11b26de64b09f3121919bf017a3f045d09f52c5c05bd5098647f9efeb2b13bdd6cff7457460d9e8fc832de10c3c0b9dc8e9f039dde61c300567200fcd797a660e08518c64c08ff97e2339969b02d8d015053d8b087c5aaa06e4f60478d20418106802e29903205d07527daca79010151e3ea989748f9d00301904edeb40f816b0c9d8cce77c5e3d14c0df244697ed942f68f4c1fb52d9610419b5c980ad72caaa4cb6a0a0523ff61b619a2594267719ec686c67261cfad3d56f2abbdd50e97e1f6fd028e066eab86b1ce99ad3b28fdb05284e6fae46ce8f145391b0f6aeba63cd60e1b53df6ccd8cf2a9428d7845f45bc44ec9d1e6f20838775cdff18d4d4369919390dfb20f9c0de9c7a2cfd30992f7173943c43c6a16e41a93820481584362574165e8589b3e38425585eb124d186e9ae401ab9c1740568d362abf892696064c76e3f91faf49dfae8bb8a8260e0c33b18da0bd4e78fe7fdfc3f167e4968a94d878bcb9904758c8b6f2ecdcbe122ca21394bc5f2dbd185d3dcc3f16b2b7397ad4dfc40594dbe4040dd14f83ddd9f48dee8bb20a123b07ceeff8d93f24b5ee6a5cd0a3762d2f324a7a459a9a044f95d25e8b3027973082da46cd8cbce165e1eabef80464ece16e9b846ed51430d643d8c8cd34db43e4e925f2a4c560d5290316dcf8e42ff8325ebecc87e4e8bb8b553a4a81b16c7e31efa5514d3493c8919c633055699e567d6e36c032d278742f2af37fe198796a9b72d3da605e7d95773935aa8fb984f66026cf82356619d7bda944560684bcb29b82214cc75f0d2ed2e072ad212004e20ba8b9b92632edcfd9d54bc19f65c805fa05aeac1c77bd00281a30e86898d7ba53d58be2c4d353245f5c3bd096f330cb7cfef320fbe271c587b8401f0af8b28aa9a08892d265d814ed27f196b114da6efc60be8cfcadff4f960bc109421245382d46eb4c082eed695478dd93f4a32c72f912950ea48785787872bbc51c8dc2dfd592300183637260d94683e4340c2831bb8ca202dd8718283551258985bbfbf0edc4500eab33d833cbbe937cdcab566805b1906e1162a47b9258f4bd17bc3c62098d4ffdad7d0c0e2fb069a850ee08af5376cf2ac6319ee141049d650ea3ba0bb4e4bfc5adba802130556919a3c1d94112307750fbbb8776166f625aa8731fd9f56dfbb9d611e06106b944c0052db30496ad5526521688be65bfb889abb861cb0769b1eaab2a50e6073b2504ec5e9e497249c3027d2861f71cd2c13e47dd5bf1cab33a3f4a5a4e708c6fdbb7e9fb48fb96ddcebe084375edfbc45c5b8f1bb1dfd965e0a099d347c5aeaf76f8a47c86df1be7f3d7e2ab276b68912cb45067a423fffb720cfbe1908c6d2b0f4cb8c68c093eb8cedc3b852d535e90b63cd3b26e12de71fcaf30dfc0e7ddb9bdb4dcd2defaaf5a66150e2e6e6555289238beb8e899f8a319e239f051d470fe2cc881d5aa763c9d4acc235d3a1fb5463cf21a190aaee42c78fd4290c70aa7c86b57513d6b80d3dd66cbb8312a17632037e482bc90983c691639674a76935ca8db1023bd29018490281be92aeb3f69a7ac4d0b0c3733b27022a41b4022949e398d6f3904454bed1c6febe19278665b5ab25aa1bf3782b30c1a9b7c3027b490dbf0b661c9d8c6be9f8c5904ce44de817653ab99cc7eec09c941eece83bf9bc21a0cfc6fa929ac7979f3840c6805d30fc3cbb8f49bc4977992dc044aec8640354b2a3e2aa9899aa338f4dd9670f1aac84988a2e55df90c40cc3ddefb8e78da73b94d57342851e232e1bc38b912ed4dac037fd9fa2317ffe5bfd8344e3877e93d3b0fc86c3e69e87facd4f787fd508d656f414dd10d2ac2e803f95f0373abcd84793b379a2e530f3e851828dbc7ba5d77f847c1fa493999137e81ffc9f03311773b8c9d8bdded185f0b98d727fe339f68a80becdd48738100c41f53da3ff5f35f346d33902409896b288eb2c148885de93ff037017f068f7683dae067d882ca1935a6ea77486a848dbebae30000d765511d5bd4ac5ef0ec0438ce09bc8a48ab1eb5e3f9e9c4e504f8c99d1720e38fd5a62a93ab75c76ce760eef31c2adc7bc0025c6766847f51275ccd6d315f8377c93c69b0b1c31b0eee40da41b0ae782a61ca91eb9a305c6ee5117195cb99e7f3648ae49bd584f8156e798681f559aa28f58348724839d56d8b5167ef9424ba08555687e04fad27fa6abc29a8b77f7e0143e9634bbd7fa22a1213614e71e9323e79aa6c59538b866a05f07898d05eaba0f41de2812b9a2d71a5d0f36d6bc0c72d73b8c81cbc6b108dbc3c777bb801a7a98c20cfa930a39f90cce56830056790860e04c9f79418411acabbcaf8c630146841bc322b913b591cd73e380b3d7a3fe1c9d7e3c5b76356a56541ffae0982b0bdf4ccd06eae3af86fb30a4382acc456ec202c2f6546c0c818cde7b519691e4e74469864b7b2894ff7c46ab9850c9a52f690a84a7231ae191310cb41052704a519ff9358846d4f3387042fcbeef9b4b6253eb987b718b6d84b1c871e9dd9d317c4275a6d79619aa6d4626910dc75b49e34b2e2499755544476d79cc5129fbe6015676ff2c7ca0a924079987baa140055361a807541f26a5163231e80f687d55f90f9308831650f6ea646bfa45b93a4b454f69976930300f7d7a675195bfcefa3da9b4163dfb58bc00ee5162924547215e6c9b60a0b1d7d457ddf0d2a31706fdd4c4952d8df34242477045dac7d13d24cb0f0475de1afe6b4f709d68996e1694ed6a57b99bd8097a04eb7d4050bbec524486b040e1d7b6f3c7f57c2b79c2ae6c89f31a75dbc7d028cd10c3a60b19edd5e9a6afd6d34799001a1d477573467c7acf9589c09c0769e3f63373e3d4f6cfff7a46b25709b4effd5f0f6da2de9b8d8926c99fc5548ca035cccefac170eb542f7c42a6e0f99fc0a139b16be64e6485c2632ad17d5300952309fec2c43c8e098be2ae3ad544e8531e8b24c98afc57b19f1aa2b3af647e4bd35a5d88f8a42595c5a640d116ea14d8ea60dcc986775085adccc01b2ad520e516342e0a22b24d45fd367691d87bf8d8166193f5d16424a18829526c184a27a169e5b1b347da391b47d055a5f40a9d9d012f718278cbf60c59173fa60c9ba868d15aabdbdb15a26f71ab52df240a920943235db5209346c55916884722c3069ab2e085790de378bcb970246e0ab8f5058fdcd792dbd4ed25dccfcb84f345ff3000e24cc5369ea2c74a2b46e029e3ba172e27892f8cd858d9953347f8481ab8e6d714343e26b126aa3182aa7436c8d02fd4026cb19915731a4015ae85607e50173981aee3a86dddffbfea02521f35f8948dfe3569420ed17469f8288ac1cf824ff208248d18df6729c098061a5ac9a5b8970b879cf533abdfa57e0bf36e1aacfee7a18959a1c09477dc28b120bc2544698031e63818c038628858dbf66c35ef1146f03f6f146fbf3994347eb070f2dd481ae889c7b5a17335ff5760b56dfc5a46fef3dcd3cfe72cd677f93548a4e7f454b120de3c01e8dae8379b0901f30c3561cdf122a9ddb2ab7f53b0ec4d8de5b75b05c7ae34d1f013eb64afb9a1339143abbb8d449b862c42e1c040f99a370927a2ad564765d790cd4b271e3e7ed63b87b8438d7de8896e7d5cab351953311fa82e5b5651784a3eabbd2b9acc0704aed12796715cfa0b8b48a0f64cf3b78292ba025d0b9306fb0cd7856d4d0907c1b89aecb976540c4ae849190ba61c0a66f196c616348a37a30f5e2e2c7ea6623401daab7701f5d17ccf854eae1c1947041bea46e06312d75061c3382e84ded73348e8683b23f8ea5f69e1e8e767775b077e0f6b477c05f766fe85938dbe96d50ce0a543f59d1ecea4f82d81a4c6143dc5634c3558f184c225ff1ef0b6f5642438515a1f83eb65cc33a6aa16e3ef67584f2e23970575a09e8b97eaa92bf0f2567841839a86738ec5df90e5985678e768ffc2cf68407210a580ae5eb69d925af254fa7818b140083f21a127c8dd01b43c85ce11c4582001cd1b6b6a648e5d416d593ac4599cf57a9ae85fb409054715572e4d65c01d40b199642ed7e57cf99e1f0c67a33a7b681b87349b158b1ebb63fd54a71e35f02f2124d96edbe72279410037e5af67717a522f9ad0ee6f9612c017463b494e3f2ce00b63ba960c3b9e339fd512a3f1de4e4ae678fdb42092178a1e88a09b0ed8d3958922a1f8ab0a7e69339d0e9d0aee0858a9b796661a5ca3a3766857209f41648428041bb01e3c81051cb4bf20edf787ad658e137a143250972a377081585adcb7f4860ec28f05a1973f6f2878d53b4b60d7c70863f937f2a9044d9b717c9dd935e1672fd2c22972cc49f365c988b5f3df8c7a1acfe09dc79b7cb7180dfc45894414dc2e6850d597abaef8e3dfc541d6ee0248950501014024f840d37008b0efddcbd72d53013e654d6accf49a4b02551ce8ab52e65f44b665a6a3e621a3f1e4e1e212dbf9ed134c27769c35e3888c7249556ab425f164afd78937fad13ff93f20ae82a5d2b8da9174a610eddf0683d84ec3a816016f7781a5c50298eac9f8018ba06ef6cd9fa87e046405dc235e02424d2bd2a3e80672aeff775126c24c64afeac789297a62ae0bfe3cfbe78747d8d4f8e1f6ca21bdb4430d059cf11d3d94437a3969e03643c074846f9e1426d7f4ab4a4761d638838801b9cca8e0e9580a066cabdcdf1d9f99bfa74a893bd9fc9500a5da15174a6cd661bf926bc1d87b3cde4744c64457aed0685f86a0b9512f4cd1cf9504d97297b4492a87817979535bcce1a59126ce5943eb52dca082794d110844b6a8ce0c0c0169edcfc641a597b8d3506b8eb440ff6a3e1d2450b119a6db87c856304bb03bbd4cbc0174395429c95271d9fdd70448cfa607a982566417b7feec128e76948cd0543f4843cfff09e47c5281d193409180f465d19d92fda3e77918639ea166673d917680e9d15cdc6dae5f6ef3c824cc3ec631ac509369edba38497b075f6aa6da83b79d83c29aba7cbae1429f75385b15d035570bad7ba305c8be662a18236b2de5f26bff643dfa5fc5644f81192dcc0b8d2b4a85116de94a96cff9623906cd2b754f43264b46f2c5e76895478876c3586cd607fb09b0409cfbfb14ce25b0017eb516fa4b23f7929d41dfc2040095e29076f36d694a528346452911d10c3dbe2d8532ae8fe9ab98f62f9590eec28b96356cc6ab1ae9ba765188ba699ed4cfcc1a21980ec8c37c61ea7c961fb6d58fe7fa70cc14f23180ab8590e8c94fec43b128043064d407cd95481370622385291cc54485230eba4d22185eb548484a82c6d6496551936fd8446b2a3c23d04364fd36b743f5f43fc2a5a658ed327af36e5fa94ac69082cd572695dc5b02d00b12b6ce8c3e6e12a4bd5a005dbf68bc410fa9ecf84d204af54ae17fc5ce8d0db0cab3675706366d1d3b1c182901f07fb19dbd715322abe95a82a08b2111da057bf3a434e14123cbe6325be562861d1ead9d8e9b0bf140b0effe18f044218d31dccd72f9ef8f8d8587a60955885635942854b04e4106e69c6d04f57e7f7a4f8e86ff435c1f76e506e845decf2a488c4875f24d1c8781a9904533ac2c7892fa68ad32dce7bf8a93950b923a0de00594b1cb1c3c4cb01b1b1976cc15538c6fa60f05350738f4165068d7c13ef4404d6ff15fcc195b2e29fd2a9801345d0da4d037ace24bd639c90f8648334ac5364b30dbe58e341d6c695866c8c1ae57f9f4bb134cc330f5f717f2927a65c73a9ce764bd6a91dfea104805da8b5d65d44b44f728028889054eeaa035e81fe4b6ed92003aa08a61d57e783b360ff8885a1840beb19a7f984051fefd10c75ebc0da910267d1f7e197cfa93e1ee7587dbbba860b6fc9dce5f15e93a4ddebfc96ba785cc61f3e5fabc972654ffebd078b453d87aba5d6e2908d9ea107726a70cd04c6b59f601899b11c8714aeea998065dee17b0d194ed5dbf4c430258cb879e743f7bc8dddfc3ccd572281990ed68b9d225e3a12d92638c2e79efd909d4a2211d6897c36780e94266188de5eb243574ea0ebc7ffcc2621788aef7c30ea125f3dbc4d0d597ca1cac266b8e6d463f579fd7dbda5bbe6471f121ed08444620d479c0260a748c9811c61b48e74a404c3879be8e3a6fb99e1f253428f0d0eb67a0abb247dcfcdd906b4ef64f637851990be7c407e142ac3858c1d0fc874982f5d810deae418854df9ff16c1fa29023491acdc334f3fa775b48f86c1a7f2252acd4924e39b7db5fb97cdd829e0371e12d4b93c7d3a01a35dacf4b1a47a94f988ee42cb0d2e57e655d04aab5a7f8059668012caa4734e99124a7d0a6e24c7b2edea4766d4820c3d7413f2a4c7452af1bb663a11815819002e9ad6de9ed21bc02a147e8910bd91c03410ebb2206f8c42fdcd8f04286b58698d54183747d0bbd668c50f1865faa54f287ce24468e03e6414359af25b8ae3d7a9c518b641859511cf0748c454f7eefa0af222899948c4f16cc14ae930f08b40f19e9176c1745600b30a317bb6314705f60584b0e0258e1292d9291d4499dd92593bfa7802cb81415bbd67a4c33e73415018d3f7c19a64b0cf19e0ed0ccea4865e89bb1041af9b039d4edd5d67fdc489914ac619f7f91a8913d310c58287f16cc3ae20198b975085408f39448599cc9cea496fcc4a18f8b8d67cfc5669df03f82fc64f901223b92bc6b3ea687efe23b2e851ad7f454ee0ecfc419b4fd08a86c714369ca092b39ae1c81545f9cb583348a142201e7d8d223ab1fcefe8f8e2dc16ca998fcfa71e935a139a80b4ef6199f421b7f0d38b77696ae04bcd527b49cb4c11f7e344a9b9e4aa5b5f6581d8acbc38938d7b4f0846e789de18759aabe83c663a01956099c47dde52b2f876164e7f8284abcec8e64581add3861a329dab768255198b2400394a40df6f63c07d1db1fb2c1b0ae69f3f687219f73aef361386a5e583e8525052d8ef58485576006eb38267e43f13c339e37d49a000a851bbe5489407a01b0a8539e333d7cf48bab344e3c544922ef234c3ecc6744c62b3095276d907f96d6c0c3720b8a877b5f73581fd5f3906d5fbcebf00f3295160c5834ed48b602caa497883141a77bc358afafa2dec6e5c25d92ec89fb52e7433b976c624ca805871b6ad482d4fb62c0ba89a3607de08f7051d1397bb21b1421f4ea2a1c2c0f44419f0b365b43c29f3393ffa7e9967d9c4ae973766ba62aa3fc0e5500db4aea871fe8ad0ab8d801ae027fcf2071d751a84cfbc9a4c7bcb206cb71df3474648fbd3126995f9bc70fd8889f239ada3cadb92d4e16bb4a801908211f60d28700b49e3de012f63f4f85badb7770af4dc126575d395ba13b699953c38d9c33aa6237d23df42cbb2490cfc19e381a68cc1d63f22ffb092c2b7bcea2c897f94f270fa2779de75e0d9e169a8cead160c4b37e76cbfc5e1ae55ea0ae8f19d7630f07569c301e1e33d24a244d8729a0842aeb67eae9bd0521e7b970f38923edc9c215e8e17f3a948a4e94a27758bf175a993387a0d961e9e664cf0711de03b4e986d8b9f72f203163ab42a38e5dc572b17b6366da9ce824c8137bd3dea9644d5c97d02ed9624754e34c267c1c1801fb988ff1b24ad57a085f267d04c024bc38c1fcb144697e6001048ffbfee4cf21c11af71daaf859a230712a39f6aeba56e072d6a68ada249cdcc9ed47925c39095fb7b945e7b81482ef664be37e1710f534c588307a5a643bcc2537cc64fdfa83029509d94ad75c9075497782d7d2bf8f22ae8950c6b90c631b35ae617d04af0e7d03d5d6ec03b4b9564566d1519378d5d9e982169f156459098f9bc7bdb6383bfb92e94f0a2d159085114763429e38fcf203943b881b0dc22280f535107da0464e668f855938592d6c92caf83ebfc81ccd58729689c399ac3a9988c41eea25ecb81ed95e26998abe99cd23a02b8c60c11adc8cebb9dec6c6b208dcd36dc8943c0172538123fa9486b3f264d4c7900f10ea9cb3e75a92b165ddbdbf558615b31ae20d94c405177759bbcac043b687ab68cebcefdeb5f9e656ed109d5ae7dc6fc72fa7ecd4d328177b17b77d7152bf0cc2a3aba78420728d8c8e85462471c4757fec32a1fd5bcd27922ef3a6c43177633169a5866a09173fb592c9301b772eda2b65657bf966b9e56374bbae071d37f7427ac9743a9d24f23a63d872c12f1c6b0b5514e7fe7b54928b41b78dd2636173221cd931f9fb26978c664528cb1e5a68e895961c78312cbd394d12965789e471ad01d1da3ad21c0bbef2902b9ba96086f3cf70652b9993db25ef15b20fff90b2dd87665bcced620d501951d5a0ac4e27bb26259784bd6afc2b40102b64ede24eabea82d2adf931d500fcb581eccde05a7c0ecf29cf5e5c1f052d25c1a10a6d8059bd4894a19f51d2da05632a43b45883fbc6846e85c5ac6a983ba88c9b7bc96142f642ce70cec5bbec388adb3aa0df85bc611346c4c3bbd908132bbe8b9709dc3b25aa6ff9b6a28c46e7d00bac7e6d5751c2d2e819da1bb302df576ca32c2433a7443196c1ab81d8cf091051e448ca31d181ffe6b1f0b4d8d087109edcb68d96835a35beaba4f017963a7728666ae1fa676b85235ac65c113cfdb6c8097d5823452d6b786b992027ece31553186a17a1a89303e22d8b6ac1c995394cff2cf5a23662cf6036da1d5043189f66efdc9a3569e91f4f806c52aad17e037123a622a9bc5e3753bea0b12900d053f950fcb6ed2d0c6bdfb515d289e77b6b3284077f3b1fc8cdf1ef5ffe423c468612f8bef45af8782ad84c9d8389357e6dc78381c97f185a035d9f6fa6ee7ed832ea16efb0c8c0278ce61416980c9f867171528240208c671e22e664716d0224cb738897c7d91ca929d5165d4879ce5025a67b87502b1e7783dfa9392612c757d7fcb8027eee9fd74d0c24d37f4d509a58c0e15731a433c306ca7786d6fe613ee9ab1fa73382f616c6d2530225d6530373c050a5e34ce060dae83100ee1de76a5a10c6c8b9cbd241e7b1ed02cc0ff820b4be6be1bebce82b5fc429d977aa3d297fc5f69dcd62caf6dfe612fbf7dd1d2afec3f519552df9a6f475ff3a21fd7d49f20ce6f826d476fa56d922132aedc99ff0ea3bc75999019dccc74c6befdad121ef5b80c3609498aad2dded07d2c755cffde617403e1f418404126e1fb29a875a0cffff899bad3af0c1790de2a9bea8fb041462c6698840ec0a351a47f1ef09d7282d30f11dfa45155d439179619fc6914c03f8ec04553792b6d219de622912147504e7b45b957c5868838129a3af2301f27b95e812993ddcba68141677d652b1f21b179a51cbd66d98596d52e65a9788d8baf08d1e26503a1e85d9f4752d719b0d543556a1a1c8b431cc7cbd5983e5aa9b728dc685a45be754bd1fa2aa20225ef8f773264386bfce4f601807b94976055c5a0248f30ddfae4f903858bf06b797abf5e849b34e6d49a2486bc646759cb6b7786dde89588a95153ea7b90efceb0597740e72a8ce589645786d903259ef3a61ffa0eb363c330ef2b0384a906c84cb732854b6605775b10a2725c2f342c263dc9af26cd07cef3c8f09de64480a984f6faf780b8fbad17f8e2d9163a9e3cca6c0b41138bc0eca36e8ff6d0f20dc295ee065977cc2aedef603f64f03fef72ad37c7a4dc645b08d64f9b25f812333ce4252fd2ade441f6c7d0ccd0acc18909085b14455bbcc1a5ed9065fc5d5b1096b71dc03f73d1d7d7c6d4c13a5f6a9b145ce25d1574d597439d8b791b6da4d1bebf3949c8de1e0b864cca80b4e49388d57fd5279c09e496b9a9a34148b72fe18653ebec72cfce20f8a1c96e060f23165335b1ef8e1e952eb0b092ad2e54d0a094e40e7b805cfe9e039b3d1c79a1eccd243b05711cf435cc675b4decbea012744b5fa05b10f43491ad2fc929ec5f9edc688784fa72d152625b29cada2d8f5fffa94af4ff492787d4b5d7386de8dfabdeb02d788e1224b5111e5452d5ba1475d2e83e7e6a0e7e501db0cfe417a368bd0098f3a7dc2eb80bdfece6c0b35ccd8a96c17d4da33ca9b3cbc9dd7bda9fef32fce5af74186c863e0de11bfd8da6cd230aaf497528ea380addc41c82e56156f55ecd728a46b544e15d1cdf58059886805c563292939697cbafccdcf944e81f9e29f38781e5c09929291dc150e3b125b2c537bfbb5362c94a0b0a673382cc8406d9e3be373b4a2e945f0dd824a99da75b3e271a6af34eccbe0c24ef6354b602402b147493cadcc802b532bbecd0258d7e421920faf71113557a43fd19ef7a1ee2201702420610cc7c89b0a9b1784150986b31483c03f46613a57eb2e313c32272ae28dcfa360198e237f4329b9515580c67a4c277014cf79fed64842e4ab9b2685baecc8b68f4f7034bb0e6500e58f244c923386600ec158f21855288a02714561ef8c10a97d92f4ce2b69a5c74b0d8beed74a93aa2878b23f5fa5c271d3ec8137f06d92981974b486e2cb103cca3042de53c5eab4b8adef7669c0aeaefcd1eda848111a411ff79b9a26ecfff0e001a296cb108da8be3cade81456c22b27c5e35545ea13ee02358a29a0eac56b14c977793bbc1b0c245b4e99a1593024b555c92ce9263d9a847ab8b29d65ab45b20830ebc8ace237502cff34572361d0117d2940c282e4ceff6d9f17a86fc633d553e47fca0fd18efd79923bcc05dd84ba13a254da1e507867250d6834951e1a50ba30a76b52b7f3aee234d2ecfa07147d7e43598806bb7f771feba0d478b6e916da6f5b16d5788141c3f43b99b53b3f6b96e7fdb9a043cb78b3d447f79231206e323c58f616776b4ca096da1cab696afbc3c0e34e31929ab01e6ed7275ecf2de44f3ba13423f86d16b71241d010548078649260e75fce7cf45a48d08c54f6f8d836b79967aa8405cfed6a78f16d76e2cfc0de3dc608e9b03db3aeb088864c46834c9bcf351c09aaeabbffe3b5a40501b31ad570a637ca2cad1e027673304a6b25f1f3157bf3fb375980303acda0532cbfe4aae7331e6debb3a2c8eff75ffbfd189aae49fe5c99c6152ad3edb421f5263695665221ce4d155a2861d6ccd999c3e1e00b803ae1a036f7bcb9394e2262434f86445fb4e4f07ab73a0bcd857c5afc1b034ff0b1e51d4d679206052f8c0acceb3c043e3f677a7c5acf6a6690a9523bb009373926d1e234ca4126aa425f26a1bc225543992c7a328421016b3d671cdc627fa506bcaadddb08a7deba6a2f33129fc3f67b0fc44a9a9e4b690bd0244b436e60b0ea3e89b0c41771d441c671812a441a1e770ffbe1e94585bd40f18c3459e4cb6f098fcb168c850426fb9943d1b76684f9383e53a12075b4d6072b18e76148ad6b67871b80d2f96de7a63851c801a193e316724ba06b96c1903d614c71ff0cc32e72443dec9dcc674837cf286c56b5799a83f406cabb969a33a4652f546c0b9ee41736bdecc63ccd78a1d566acb50cd10d0ca6cc4a203a6c9841d2c74576fdad0c2cff519d277ef17f7cd7b4246f3f37b36200f4959925facd3ae75ce9ebc3f3bda24c6febc53aaf15228d4ee32955297d48af0386d7b66916a4b78838d2224468d06d0057d544ed4238ba368e1bb9f22f1ac9b1a0d8d3787e553373cdefc51356615070812694e84eda750312979bdcca940f1e35edf483659c47443f0eaceb864802b0117458dfc791b044806f69088fc457e250b6071ad53253ddf60e1161f63452b52111cd11c81628f98115a97116d6c8a96d19d5812dc1d1b87d02716207c9fe4c144d09b29bd5bdff5102357121182b8703b1d3d86b397f7f546471d271d08d0af0888f20bc80fb6e80f95f2fa12a6324b5df7573afa31b50c5ffe1267d89ba390a2ece50e0d3b783ef7b208266f2d32eb8695b113201f9976ace0cb261b0de4c3e65e86de12b80f703d503f41eb29a1d44f3fc660284bead92b2c9cfa4b63f442929dd21c1cb57d1f7eca476f2806b63c6703c419317cdf4f18efe87b6ea67fca742d43dc77b838f0d92519380b29aeb6a4391d0c01181c7ecafc67dbc1a62919afb7c6da78e613560e548a317ae45d70e680d4ac79824a56473558bd0f56607f60d19c6f0558e62151b7e07d60a10b0e5b77d18c61f4bda0a5478e8e9d6e3a3fdd223543decd900c1bf679a7fd4e3d0f64e12d11b11902fa300b3f679cdb91c1879f2c2445a9c93163b7f1d874b345796b3e0dfe66759c42875e65bc2f16850664c2773b508e34377453f4b1a27fe6ab3dc75c054a76b8cc6ec89629cfa605be5d9cd94e5974bdf012131faaa7df9c542e0adb2444cda5f203a5256b189d8b41a9e7f4b86a900a05892e1ea1a682a689eba570ab7767d0394ecbbbc32f56161e2e60cce2776ac18bd090020d78451ac86f7063689fa3af68dfefc153b44aae16fb28ff47cd41a321f6f09d568033f53943806ba0f8d06ae87210adb65bfbb0794653438a51b52aa915fa5c10bdf23571f8f3a5592fba7f4c7a8556279d0c72c5eb9e05927320a2ed572395224a6ace58f131f9a05531aa0111bddf8b13f418881033f406266a546a662872e477f48ff2b0250bc8f908558f319a59fabca25c0926a5f258f9ae8982bf4087655faa2b856fa0ed8f15c39a0011d3b79b22381bab09eaf9280d7b68a1889d6223d728267db0b7660ec62ce3d2c082c8353198a96ac84c411d5eec7b56b2f4a2628cc2dc1b2bf7b1059c7cef181bae3cdc0d626c1c9c3a0d4f2f56f0042d551d92f57463be1aaa402f41623d9b612bdacac779e8fbd94fe6cc020e85b2241f10504dbcfba78595e98454919123ae1a62ec8591c2e6dc5ed3c6643fd7da75864118146b4ff94fe3835afea72dfb98cdd6d3659b8e7c067c4635b9b230fad51f1362f5644177fa9a721ba70b0a9ec27d411400d795fda1f59b7ebfb59940ab2441551803f94318509c944e463afc9e30dd1a9406d22c340e92e133acacf0f8c920aa46a16dff02e50238e0f5e9435e2a4e135681b2d4c65264320d01ad72b4320fb3009b73ec80523d79bb234454f40e9b3e40381ae30c160da96d6cde44d5fa5c87a60ac4bfd8ded9561c52059ae826725c02c6d4fb04684dc576f3acb76440e33771f85ef4d620b478ffdb712d374d7132bc9dd45df314541cea11dd3596c76896041cca3d9a100c6188d5b5aebba2e7a066a849cc06e6a2373c737ef7d5e96c57e94bd9537d1e619f3cd837def7d114e1f36e62e6df49685ee955d71c4fd7c38c949663dc5cf4d51192dd8893d98e9d2a712423d93a299c31411d2f40f4e5aeaea0a82cd0a7b7c53fcb1868c053d0b80962f1e3af2c2252610a76331ad58d5dc737c6509c5274dc2c60e6d8d6160d0761d1de0fc52e2b805e414345d22b4384287935654480e38f1c9e2fd815470af6279158ee2d897a4534bf8cb3fb5e4261db86ba8708c2d741446a53291d8cbd2997807d2adb44fc72a08eb10c4fd13a872ab8cc89f05785f8f2d26c26e78689794b2e032ecbe4087cbe9c213c3be632a52d0281526da3ec29e2fdd2782c0e007ac4c3649ffa3ee63c955665f9d1accc77db03b4a004793b79cd672ea71ce42ca246e0d29ee5e901c2aed7f7b06811fe2c062e5096a62e6f52d35499c4</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-surge">      <input class="hbe hbe-input-field hbe-input-field-surge" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-surge" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-surge" data-content="请 输 入 阅 读 密 码.">请 输 入 阅 读 密 码.</span>      </label>      <svg class="hbe hbe-graphic hbe-graphic-surge" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none">        <path d="M1200,9c0,0-305.005,0-401.001,0C733,9,675.327,4.969,598,4.969C514.994,4.969,449.336,9,400.333,9C299.666,9,0,9,0,9v43c0,0,299.666,0,400.333,0c49.002,0,114.66,3.484,197.667,3.484c77.327,0,135-3.484,200.999-3.484C894.995,52,1200,52,1200,52V9z"></path>      </svg>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">本文主要记录近期开发相关的学习计划。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="加密博客" scheme="https://www.techgrow.cn/tags/%E5%8A%A0%E5%AF%86%E5%8D%9A%E5%AE%A2/"/>
    
  </entry>
  
  <entry>
    <title>Linux 使用内存缓存减少磁盘 I/O</title>
    <link href="https://www.techgrow.cn/posts/3d17ec47.html"/>
    <id>https://www.techgrow.cn/posts/3d17ec47.html</id>
    <published>2025-12-30T11:50:21.000Z</published>
    <updated>2025-12-30T11:50:21.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在 Linux 系统中，磁盘 I/O 往往是性能瓶颈。当应用程序频繁读取大文件（例如数据库文件、日志文件、多媒体文件）时，系统可能因为 I/O 等待而变得非常卡顿。为了减少磁盘压力、提升性能，Linux 提供了多种利用内存作为缓存的机制，例如：tmpfs、ramfs、Page Cache（页缓存）。本文将详细介绍这些机制的使用方法及区别，并介绍常用的磁盘 I/O 诊断工具。</p><span id="more"></span><h2 id="磁盘-I-O-诊断工具"><a href="#磁盘-I-O-诊断工具" class="headerlink" title="磁盘 I/O 诊断工具"></a>磁盘 I/O 诊断工具</h2><p>在 Linux 下，查看磁盘的吞吐量（每秒读 / 写的数据量）或者负载（IOPS - 每秒读 / 写的请求数），最常用、最直观的工具有下面几种。</p><table><thead><tr><th>工具</th><th>是否需要安装</th><th>能看具体磁盘</th><th>是否推荐使用</th></tr></thead><tbody><tr><td><code>iostat</code></td><td>需要</td><td>✅</td><td>⭐⭐⭐⭐⭐</td></tr><tr><td><code>iotop</code></td><td>需要</td><td>间接</td><td>⭐⭐⭐⭐</td></tr><tr><td><code>vmstat</code></td><td>不需要</td><td>❌</td><td>⭐⭐⭐</td></tr></tbody></table><h3 id="iostat"><a href="#iostat" class="headerlink" title="iostat"></a>iostat</h3><p><code>iostat</code> 是最快速的磁盘工具（强烈推荐）。</p><h4 id="iostat-安装"><a href="#iostat-安装" class="headerlink" title="iostat 安装"></a>iostat 安装</h4><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword"># CentOS</span> / RHEL</span><br><span class="line">sudo yum install sysstat</span><br><span class="line"><span class="keyword"></span></span><br><span class="line"><span class="keyword"># Debian</span> / Ubuntu</span><br><span class="line">sudo apt install sysstat</span><br></pre></td></tr></tbody></table></figure><h4 id="iostat-使用"><a href="#iostat-使用" class="headerlink" title="iostat 使用"></a>iostat 使用</h4><ul><li><code>iostat</code> 的使用 </li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">iostat<span class="params"> -x</span> 1</span><br></pre></td></tr></tbody></table></figure><ul><li>输出示例如下：</li></ul><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">avg-cpu:  %user   %nice %system %iowait  %steal   %idle</span><br><span class="line">           3.59    0.02    1.32    0.33    0.00   94.74</span><br><span class="line"></span><br><span class="line">Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util</span><br><span class="line">sdc               0.09     0.67   33.60   14.91  2100.28  1028.30   128.99     0.32    6.53    2.01   16.73   0.43   2.09</span><br><span class="line">sdb               0.00     0.00    0.17    0.00     9.39     0.00   110.24     0.00    6.75    6.75    0.00   4.22   0.07</span><br><span class="line">sda               0.03     0.31    5.14    0.97   403.47    30.99   142.36     0.09   15.25   11.69   34.10   4.71   2.88</span><br></pre></td></tr></tbody></table></figure><ul><li>重点关注字段</li></ul><table><thead><tr><th>字段</th><th>含义</th><th>如何判断</th></tr></thead><tbody><tr><td><code>%util</code></td><td>磁盘忙碌时间百分比</td><td><strong>接近 100% = 磁盘 I/O 被打满（最重要）</strong>，<code>%util</code> 最高的磁盘 = 当前 I/O 压力最大的磁盘</td></tr><tr><td><code>await</code></td><td>平均 I/O 等待时间（ms）</td><td>等待时间高 = I/O 排队严重</td></tr><tr><td><code>r/s</code></td><td>每秒读的次数</td><td>读的频率，高表示随机读多</td></tr><tr><td><code>w/s</code></td><td>每秒写的次数</td><td>写的频率，高表示随机写多</td></tr><tr><td><code>rkB/s</code></td><td>每秒读取的数据量</td><td>读吞吐量，顺序读场景通常很高</td></tr><tr><td><code>wkB/s</code></td><td>每秒写入的数据量</td><td>写吞吐量，顺序写场景通常很高</td></tr></tbody></table><ul><li>其他字段说明</li></ul><table><thead><tr><th>字段</th><th>含义</th><th>如何判断</th></tr></thead><tbody><tr><td><code>rrqm/s</code></td><td>每秒被合并的读请求数</td><td>值高说明内核在合并顺序读</td></tr><tr><td><code>wrqm/s</code></td><td>每秒被合并的写请求数</td><td>值高说明顺序写、写合并效果好</td></tr><tr><td><code>avgrq-sz</code></td><td>平均每次 I/O 请求大小（KB）</td><td>值小（如 &lt; 16KB）多为随机 I/O；值大多为顺序 I/O</td></tr><tr><td><code>avgqu-sz</code></td><td>平均 I/O 队列长度</td><td>值大于 1 则说明开始排队；持续偏大 = 磁盘处理不过来</td></tr><tr><td><code>r_await</code></td><td>读请求平均等待时间（ms）</td><td>读延迟高，说明读取受阻（数据库、随机读场景重点看）</td></tr><tr><td><code>w_await</code></td><td>写请求平均等待时间（ms）</td><td>写延迟高，常见于日志、落盘、fsync 场景</td></tr><tr><td><code>svctm</code></td><td>平均 I/O 服务时间（ms）</td><td>单次 I/O 被磁盘处理的时间（老指标，仅作参考）</td></tr></tbody></table><ul><li>实战判断口诀<ul><li>磁盘是否被打满？<ul><li>看 <code>%util</code></li></ul></li><li>磁盘为什么慢？<ul><li><code>%util</code> + <code>await</code> + <code>avgqu-sz</code></li></ul></li><li>随机 I/O 还是顺序 I/O？<ul><li><code>avgrq-sz</code> + <code>r/s</code> / <code>w/s</code></li></ul></li><li>是读慢还是写慢？<ul><li><code>r_await</code> vs <code>w_await</code></li></ul></li><li>吞吐瓶颈还是 IOPS 瓶颈？<ul><li><code>rkB/s</code> / <code>wkB/s</code> vs <code>r/s</code> / <code>w/s</code></li></ul></li></ul></li></ul><h3 id="iotop"><a href="#iotop" class="headerlink" title="iotop"></a>iotop</h3><p><code>iotop</code> 是磁盘交互式神器（看进程 + 磁盘）。</p><h4 id="iotop-安装"><a href="#iotop-安装" class="headerlink" title="iotop 安装"></a>iotop 安装</h4><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword"># CentOS</span> / RHEL</span><br><span class="line">sudo yum install iotop</span><br><span class="line"><span class="keyword"></span></span><br><span class="line"><span class="keyword"># Debian</span> / Ubuntu</span><br><span class="line">sudo apt install iotop</span><br></pre></td></tr></tbody></table></figure><h4 id="iotop-使用"><a href="#iotop-使用" class="headerlink" title="iotop 使用"></a>iotop 使用</h4><ul><li><code>iotop</code> 的使用（需要 <code>root</code> 权限）</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo iotop</span><br></pre></td></tr></tbody></table></figure><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 或者</span></span><br><span class="line"><span class="comment"># -o 只显示有 I/O 的进程</span></span><br><span class="line"><span class="comment"># -p 显示进程而非线程</span></span><br><span class="line"><span class="comment"># -a 累计 I/O</span></span><br><span class="line">sudo iotop<span class="params"> -oPa</span></span><br></pre></td></tr></tbody></table></figure><ul><li>输出示例如下：</li></ul><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Total DISK READ:  12.34 M/s | Total DISK WRITE: 56.78 M/s</span><br><span class="line">Actual DISK READ: 12.10 M/s | Actual DISK WRITE: 55.90 M/s</span><br><span class="line">  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN   IO&gt;    COMMAND</span><br><span class="line">12345 be/4  mysql    1.20 M/s    35.60 M/s    0.00 %  92.34 %  mysqld</span><br><span class="line">23456 be/4  root     8.50 M/s     0.00 B/s    0.00 %  48.12 %  tar czf backup.tar.gz</span><br><span class="line">34567 be/4  www-data 0.00 B/s     15.80 M/s   0.00 %  61.45 %  php-fpm</span><br><span class="line">  987 be/4  root     0.00 B/s     2.10 M/s    0.00 %   5.23 %  jbd2/sda1-8</span><br></pre></td></tr></tbody></table></figure><ul><li>字段说明</li></ul><table><thead><tr><th>字段</th><th>含义</th><th>说明</th></tr></thead><tbody><tr><td><code>Total DISK READ</code></td><td>总读请求速率</td><td>所有进程发起的磁盘读取速率（总读请求速率）</td></tr><tr><td><code>Total DISK WRITE</code></td><td>总写请求速率</td><td>所有进程发起的磁盘写入速率（总写请求速率）</td></tr><tr><td><code>Actual DISK READ</code></td><td>实际读速率</td><td>实际落盘的磁盘读取速率（受页缓存命中情况影响）</td></tr><tr><td><code>Actual DISK WRITE</code></td><td>实际写速率</td><td>实际落盘的磁盘写入速率（受页缓存、写合并等机制影响）</td></tr><tr><td><code>TID</code></td><td>Thread ID</td><td> 线程 ID（比 PID 更细粒度）</td></tr><tr><td><code>PRIO</code></td><td>I/O 优先级</td><td><code>be</code> = best effort（普通优先级）</td></tr><tr><td><code>USER</code></td><td>进程所属用户</td><td>进程运行的用户</td></tr><tr><td><code>DISK READ</code></td><td>磁盘读取速率</td><td>该进程每秒从磁盘读取的数据量</td></tr><tr><td><code>DISK WRITE</code></td><td>磁盘写入速率</td><td>该进程每秒写入磁盘的数据量</td></tr><tr><td><code>SWAPIN</code></td><td>换页占比</td><td>因内存不足导致的换页 I/O 百分比</td></tr><tr><td><code>IO&gt;</code></td><td>I/O 等待占比</td><td><strong>最重要：进程被磁盘 I/O 阻塞的时间比例</strong></td></tr><tr><td><code>COMMAND</code></td><td>命令名</td><td>进程 / 线程名称</td></tr></tbody></table><ul><li>排查磁盘 I/O 瓶颈时重点关注的字段<ul><li><code>IO&gt;</code>（最重要）<ul><li>举例：  <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">IO&gt;  92.34 %</span><br></pre></td></tr></tbody></table></figure></li><li>含义：<ul><li>进程 92% 的时间在等磁盘 I/O</li><li> 几乎可以确定：磁盘是瓶颈</li></ul></li><li>判断规则：<ul><li><code>IO&gt; &gt; 30%</code> → I/O 明显慢</li><li><code>IO&gt; &gt; 60%</code> → 严重 I/O 阻塞</li><li><code>IO&gt; &gt; 80%</code> → 磁盘被打爆</li></ul></li></ul></li><li><code>DISK READ / DISK WRITE</code><ul><li>举例：  <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DISK WRITE 35.60 M/s</span><br></pre></td></tr></tbody></table></figure></li><li>用来判断：<ul><li>磁盘是读取多，还是写入多</li><li>磁盘是否有异常的大流量进程</li></ul></li><li>特别注意：<ul><li>磁盘吞吐量高 ≠ 磁盘一定慢</li><li>必须结合 <code>IO&gt;</code> 来判断才有意义</li></ul></li></ul></li><li><code>Total / Actual</code> 顶部汇总行（系统级）<ul><li>举例：  <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Total DISK READ:  12.34 M/s | Total DISK WRITE: 56.78 M/s</span><br><span class="line">Actual DISK READ: 12.10 M/s | Actual DISK WRITE: 55.90 M/s</span><br></pre></td></tr></tbody></table></figure></li><li>字段：<ul><li><code>Total DISK READ</code>：所有进程发起的磁盘读取速率（总读请求速率）</li><li><code>Total DISK WRITE</code>：所有进程发起的磁盘写入速率（总写请求速率）</li><li><code>Actual DISK READ</code>：实际落盘的磁盘读取速率（受页缓存命中情况影响）</li><li><code>Actual DISK WRITE</code>：实际落盘的磁盘写入速率（受页缓存、写合并等机制影响）</li></ul></li><li><code>Total</code> 与 <code>Actual</code> 两者差距大的可能原因<ul><li> Page Cache（页缓存）命中高</li><li>写请求被缓存 / 合并</li></ul></li></ul></li></ul></li></ul><h3 id="vmstat"><a href="#vmstat" class="headerlink" title="vmstat"></a>vmstat</h3><p><code>vmstat</code> 无需安装，缺点只能看整体的磁盘 I/O 吞吐量，看不到具体的磁盘。</p><h4 id="vmstat-概述"><a href="#vmstat-概述" class="headerlink" title="vmstat 概述"></a>vmstat 概述</h4><ul><li><code>vmstat</code> 适合用来判断的事情</li></ul><table><thead><tr><th>适用场景</th><th>用法</th></tr></thead><tbody><tr><td>是否有磁盘读写行为</td><td>看 <code>bi</code> / <code>bo</code> 是否为 0</td></tr><tr><td> 是否突然爆发 I/O</td><td> 看是否瞬间飙升</td></tr><tr><td>写入型 / 读取型的吞吐量</td><td>对比 <code>bi</code> vs <code>bo</code></td></tr></tbody></table><ul><li><code>vmstat</code> 不适合用来判断的事情</li></ul><table><thead><tr><th>不适用场景</th><th>原因</th></tr></thead><tbody><tr><td>哪块磁盘 I/O 最高</td><td>看不到具体磁盘</td></tr><tr><td>磁盘是否打满</td><td>没有 <code>%util</code></td></tr><tr><td>I/O 是否排队</td><td>没有 <code>await</code></td></tr></tbody></table><h4 id="vmstat-使用"><a href="#vmstat-使用" class="headerlink" title="vmstat 使用"></a>vmstat 使用</h4><ul><li><code>vmstat</code> 的使用 </li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vmstat 1</span><br></pre></td></tr></tbody></table></figure><ul><li>输出示例如下：</li></ul><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----</span><br><span class="line"> r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st</span><br><span class="line"> 1  0      0 24642772 6851660 22099404    0    0    50    16  133   71  3  1 95  0  0</span><br><span class="line"> 0  0      0 24644776 6851660 22096364    0    0     0    37 10080 9298  1  0 98  0  0</span><br><span class="line"> 0  0      0 24644308 6851660 22096384    0    0     0   409 3063 8706  1  0 99  0  0</span><br><span class="line"> 0  0      0 24652476 6851660 22096388    0    0     0   226 3145 8780  1  0 99  0  0</span><br><span class="line"> 0  0      0 24640804 6851660 22096400    0    0     0     0 4060 10007  1  1 98  0  0</span><br></pre></td></tr></tbody></table></figure><ul><li><p>重点关注字段</p><ul><li><code>bi</code>（Block In）<ul><li>每秒从块设备读取的数据量（磁盘读流量），单位：KB/s</li><li> 不是针对某一块磁盘，而是所有块设备的读吞吐量总和</li><li><code>bi</code> 高，表示系统正在大量从磁盘读取数据</li></ul></li><li><code>bo</code>（Block Out）<ul><li>每秒写入块设备的数据量（磁盘写流量），单位：KB/s</li><li> 不是针对某一块磁盘，而是所有块设备的写吞吐量总和</li><li><code>bo</code> 高，表现系统正在大量向磁盘写数据</li></ul></li></ul></li><li><p>使用注意事项</p><ul><li><code>vmstat</code> 关注的是「磁盘吞吐量」，而不是「磁盘负载」</li><li><code>bi / bo</code> 高 ≠ 磁盘一定忙</li><li>磁盘顺序读写时：<ul><li><code>bi / bo</code> 很高</li><li>但磁盘可能很轻松</li></ul></li><li>磁盘随机读写时：<ul><li><code>bi / bo</code> 不高</li><li>磁盘却可能被打满</li></ul></li><li>不能用 <code>vmstat</code> 来判断磁盘 I/O 瓶颈<ul><li><code>bi / bo</code> 是全系统汇总值</li><li>只能判断 “有没有磁盘读写”</li><li> 不能判断 “哪个磁盘最忙”</li><li> 不能判断 “某个磁盘是否打满”</li></ul></li></ul></li></ul><h2 id="磁盘-I-O-优化方案"><a href="#磁盘-I-O-优化方案" class="headerlink" title="磁盘 I/O 优化方案"></a>磁盘 I/O 优化方案</h2><h3 id="tmpfs"><a href="#tmpfs" class="headerlink" title="tmpfs"></a>tmpfs</h3><h4 id="tmpfs-的概述"><a href="#tmpfs-的概述" class="headerlink" title="tmpfs 的概述"></a>tmpfs 的概述</h4><ul><li><p><code>tmpfs</code> 是一种基于内存的文件系统：</p><ul><li>文件实际存储在内存 + 交换分区（Swap）中</li><li>支持容量限制（挂载时可指定大小）</li><li>内存不足时，tmpfs 文件可以被交换到 Swap</li><li>tmpfs 的文件内容本质上就是 Page Cache</li><li> 不会导致 OOM（Out of Memory）</li><li>适合存放临时文件、缓存数据</li><li>典型的挂载点：<code>/dev/shm</code>、<code>/run</code></li></ul></li><li><p><code>tmpfs</code> 的主要特点</p><ul><li>支持创建目录（无限层级）</li><li>支持普通文件</li><li>支持符号链接、硬链接</li><li>支持权限、UID/GID</li><li> 支持文件删除（立即释放内存）</li><li>支持调整大小（动态扩缩）</li><li><strong>系统重启后会丢失所有数据</strong></li></ul></li><li><p><code>tmpfs</code> 的适用场景</p><ul><li>程序临时文件</li><li>加速频繁读写的小文件</li><li>构建系统（<code>make</code>、<code>npm</code>、<code>cargo</code> 等）</li><li>OBS、FFmpeg 等读取多媒体文件减少磁盘压力</li></ul></li></ul><h4 id="tmpfs-的使用"><a href="#tmpfs-的使用" class="headerlink" title="tmpfs 的使用"></a>tmpfs 的使用</h4><ul><li>创建挂载点 </li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo mkdir<span class="params"> -p</span> /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><ul><li>挂载 tmpfs</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 比如限制 4GB 容量</span></span><br><span class="line">sudo mount<span class="params"> -t</span> tmpfs<span class="params"> -o</span> size=4G tmpfs /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><ul><li>验证挂载 </li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">df<span class="params"> -h</span> /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><ul><li>开机自动挂载（可选）</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 编辑 fstab 系统配置文件，添加以下配置内容</span></span><br><span class="line">sudo vim /etc/fstab</span><br><span class="line"></span><br><span class="line"><span class="comment"># 挂载 fstab 中所有自动挂载的文件系统，并检测配置是否正确</span></span><br><span class="line">sudo mount<span class="params"> -a</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tmpfs /mnt/ramdisk tmpfs defaults,size=4G 0 0</span><br></pre></td></tr></tbody></table></figure><ul><li>卸载挂载（可选）</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo umount /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><h3 id="ramfs"><a href="#ramfs" class="headerlink" title="ramfs"></a>ramfs</h3><h4 id="ramfs-的概述"><a href="#ramfs-的概述" class="headerlink" title="ramfs 的概述"></a>ramfs 的概述</h4><ul><li><p><code>ramfs</code> 是最简单的内存文件系统：</p><ul><li>数据存储在纯内存中</li><li>没有容量限制</li><li>内存写满后系统不会阻止继续写入，会持续占用内存</li><li>内存耗尽就会 OOM，最终导致系统宕机</li><li>更像一个危险，但读写速度极快的文件系统</li></ul></li><li><p><code>ramfs</code> 的主要特点</p><ul><li>支持创建目录（无限层级）</li><li>支持普通文件</li><li>支持符号链接、硬链接</li><li>支持权限、UID/GID</li><li> 支持文件删除（立即释放内存）</li><li>不支持大小限制（不能设置 Size，容量可无限增长）</li><li>数据页不可回收（不会被内核回收）</li><li>不支持 Swap Out（永远驻留物理内存中）</li><li>容易导致系统 OOM（写入越多，内存占用越多）</li><li><strong>系统重启后会丢失所有数据</strong></li></ul></li><li><p><code>ramfs</code> 的适用场景</p><ul><li>极端场景下的高性能缓存</li><li>只用于开发 / 研究，不建议生产环境使用</li></ul></li></ul><h4 id="ramfs-的使用"><a href="#ramfs-的使用" class="headerlink" title="ramfs 的使用"></a>ramfs 的使用</h4><ul><li>创建挂载点 </li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo mkdir<span class="params"> -p</span> /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><ul><li>挂载 ramfs</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo mount<span class="params"> -t</span> ramfs ramfs /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><ul><li>开机自动挂载（可选）</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 编辑 fstab 系统配置文件，添加以下配置内容</span></span><br><span class="line">sudo vim /etc/fstab</span><br><span class="line"></span><br><span class="line"><span class="comment"># 挂载 fstab 中所有自动挂载的文件系统，并检测配置是否正确</span></span><br><span class="line">sudo mount<span class="params"> -a</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ramfs   /mnt/ramdisk   ramfs   defaults   0   0</span><br></pre></td></tr></tbody></table></figure><ul><li>卸载挂载（可选）</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo umount /mnt/ramdisk</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p>由于 ramfs 没有容量限制，如果程序写入大量数据会导致系统 OOM。</p></div><h3 id="Page-Cache"><a href="#Page-Cache" class="headerlink" title="Page Cache"></a>Page Cache</h3><h4 id="Page-Cache-的概述"><a href="#Page-Cache-的概述" class="headerlink" title="Page Cache 的概述"></a>Page Cache 的概述</h4><p>即使用户不主动使用 tmpfs 或 ramfs 文件系统，Linux 内核本身也会自动利用内存作为 “磁盘缓存”。当用户读取一个文件时：</p><ul><li>内核将数据加载到操作系统的 Page Cache（页缓存）</li><li>下次读取同一个文件时，不再触发磁盘 I/O，直接从内存返回结果</li></ul><h4 id="Page-Cache-的使用"><a href="#Page-Cache-的使用" class="headerlink" title="Page Cache 的使用"></a>Page Cache 的使用</h4><ul><li>将文件写入 Page Cache</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat video.mp4 &gt; /dev/null</span><br></pre></td></tr></tbody></table></figure><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">上述命令会：</span><br><span class="line">- 强制把 `video.mp4` 文件从磁盘加载进内存</span><br><span class="line">- 但不输出任何内容</span><br><span class="line">- 后续程序读取 `video.mp4` 文件会命中缓存，不再产生磁盘 I/O</span><br></pre></td></tr></tbody></table></figure><ul><li>查看缓存使用情况 </li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看内存使用情况，会看到 buff/cache 部分增大</span></span><br><span class="line">free<span class="params"> -h</span></span><br></pre></td></tr></tbody></table></figure><ul><li>清除缓存（<strong>慎用，仅测试使用</strong>）</li></ul><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 将所有文件系统的缓存数据写回磁盘</span></span><br><span class="line">sudo sync</span><br><span class="line"></span><br><span class="line"><span class="comment"># 清理页缓存、目录缓存和 inode 缓存</span></span><br><span class="line">sudo sh<span class="params"> -c</span> <span class="string">'echo 3 &gt; /proc/sys/vm/drop_caches'</span></span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在 Linux 生产环境中，不建议随意清除缓存，否则会影响系统性能。</p></div><h3 id="优化方案总结"><a href="#优化方案总结" class="headerlink" title="优化方案总结"></a>优化方案总结</h3><ul><li><code>tmpfs</code>、<code>ramfs</code>、Page Cache 的优缺点</li></ul><table><thead><tr><th>技术</th><th>优点</th><th>缺点</th><th>适用场景</th></tr></thead><tbody><tr><td> tmpfs</td><td> 快、可限制容量、安全</td><td>占用系统内存</td><td>缓存文件、临时空间、多媒体文件读取</td></tr><tr><td> ramfs</td><td> 极快</td><td>不限容量，OOM 风险高</td><td>测试、高速缓存（不推荐生产环境使用）</td></tr><tr><td>Page Cache</td><td> 自动、透明、无需修改程序</td><td>缓存不可控、可能被挤掉</td><td>文件预加载（热加载）</td></tr></tbody></table><ul><li><code>tmpfs</code> 与 <code>ramfs</code> 的主要区别</li></ul><table><thead><tr><th>特性</th><th> tmpfs</th><th>ramfs</th></tr></thead><tbody><tr><td> 存储位置</td><td>内存 + Swap</td><td> 纯内存（永远不使用 Swap）</td></tr><tr><td>内存限制</td><td>可限制大小（推荐）</td><td>不可限制，体积可无限增长</td></tr><tr><td>系统内存压力大时</td><td>会将部分数据 Swap 出去</td><td>继续占用内存，最终导致系统 OOM</td></tr><tr><td> 默认缓存行为</td><td>文件内容存放在 Page Cache 中，这些页是可回收的，内核在内存压力大时可以回收或 Swap Out</td><td> 文件内容同样存放在 Page Cache 中，但被标记为不可回收，不会 Swap Out，也不会被回收，因此会永久占用物理内存</td></tr><tr><td> OOM 风险</td><td>低（可回收）</td><td>极高（不可回收）</td></tr><tr><td>性能</td><td>与 ramfs 速度差异极小，几乎相同</td><td>比如 tmpfs 略快，但差距可以忽略</td></tr><tr><td>适用场景</td><td>安全缓存、高性能临时存储</td><td>特殊场景、嵌入式、需要可预测行为</td></tr></tbody></table>]]></content>
    
    
    <summary type="html">本文主要介绍 Linux 使用内存缓存减少磁盘 I/O，包括 tmpfs、ramfs、Page Cache 的使用。</summary>
    
    
    
    
    <category term="Linux运维" scheme="https://www.techgrow.cn/tags/Linux%E8%BF%90%E7%BB%B4/"/>
    
    <category term="性能调优" scheme="https://www.techgrow.cn/tags/%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98/"/>
    
  </entry>
  
  <entry>
    <title>Windows 11 专业版 KMS 命令激活教程</title>
    <link href="https://www.techgrow.cn/posts/6ca05db7.html"/>
    <id>https://www.techgrow.cn/posts/6ca05db7.html</id>
    <published>2025-12-25T13:55:33.000Z</published>
    <updated>2025-12-25T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<div class="hbe hbe-container" id="hexo-blog-encrypt" data-wpm="密码错误, 请重新输入." data-whm="文章校验失败, 但不影响阅读解密后的内容.">  <script id="hbeData" type="hbeData" data-hmacdigest="44669a3bfe031a82b1f0741d9447db27eb4bfc2c287884322c5483d3c9e55c2f">4cfab7e4fdfd9afdbf1592f9652b8a199f3cb3b67d053a3a2b7db0e886465ca10dd5417753553decd4afc2f563848bbec165c496b7c4f6f669a163d8bbf4c0c9dd1007308880f552e5a0c1bb0012666302e2ec3dfba5e1f5ea9934ea655c381d6425cb4dff9b8a09eca652ab3b851ab8a381bdac1ea568d948ba8dd46c6da9488480c04a492454201728ecc1e372953f1242e8a61b4322691feffbc3437ea780f63426fe7ccb3e0cddf820b581807a64a7aae014a8ecac090d03948c516484292424e671e6d60c671c144919b5907c7abf863be31c7535520f221e38fcfe9c7c08e2aec17edabe7f8ea581ef18b9c0d3000de266b02ac4cd8113db080b216b72dcf485884e5cb93c0cd31e8320f773fc3895b1d187256f3e0517b8a6c0928c0d03274e34b86292c09f1ad54988e35f17efe2ca396183de1cfa19c7442c181436661d881883e6614fd3275d2b304f69c0aaa66028d298c2dd2a2886c8cb8831dbb9d038732ef0649d2c086ca036ecea16e065ec3d77632f184df5d0ea9a01678581e40dc7a88332870b74891186be0f341343e58fad4d9b4364e057336440e51fad9b79863229e57f640692be06254d5205038a9812b3e467de38ad3b2114e2b9fd1be014042b058f4a768f4ecf55b992f6c476d49c56432c6d3b6e2d6bc4763024746cd7c3b399827bc5e2a36c44048393114be765006fc7b11a7877d9fdd22466503a90d9a9eeffe06bbeab00e224c5966012bcb417abafdbe29a38aa0c67154636ae29f0da45fb2db987615f40f4d12e937ba7733961cfcffb5bce3cdafe872f62a102462e480b43f9c46be2cab02fe7aca30a93a18fc52fe7b88f3f39fabcc56fd4176592c41bb0e31816f51e37f3a5434453b28f033af6b05e1378bbc22ddc7262295b69d3fd6646142dfd903c11312bd0c4eceb864601bb5b43f101452d12c4d3d8aa4de16cdd83e08ea271c0b4d9ff3003a5303ed6a8a6ac4b065c213eda73acda5b9afd229d7946f4f8b6edb7e96190d00ae6c5ddc0e9ed2c0474dfe9d2b75ab4a15ac1562a8e9c7a4cecf804fc195bc87f3e7c8476ee9ecd9e42757f43671c80291ac7fa6c10309c8e9634e9384eba408ab17340931ddaa10b27616d2bfa2cdb7a321e3be91d63b70626a4077eee68b4ac3094dc2a71cb4c9c13d7ab9ceb217ff53e574360bad6f25c191fbcb5c37013b51179e6227244d51cd69fa987104711d19d6e550ebfc6cbf800c1607816eecc28b37055318e9ebdf65aba33d43547a64586e1df132d6d2f21d499eefe15525d94597cd9af5d73aec16b77945afd19eb7fac9ff04f93f91bdbeff5cf</script>  <div class="hbe hbe-content">    <div class="hbe hbe-input hbe-input-surge">      <input class="hbe hbe-input-field hbe-input-field-surge" type="password" id="hbePass">      <label class="hbe hbe-input-label hbe-input-label-surge" for="hbePass">        <span class="hbe hbe-input-label-content hbe-input-label-content-surge" data-content="请 输 入 阅 读 密 码.">请 输 入 阅 读 密 码.</span>      </label>      <svg class="hbe hbe-graphic hbe-graphic-surge" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none">        <path d="M1200,9c0,0-305.005,0-401.001,0C733,9,675.327,4.969,598,4.969C514.994,4.969,449.336,9,400.333,9C299.666,9,0,9,0,9v43c0,0,299.666,0,400.333,0c49.002,0,114.66,3.484,197.667,3.484c77.327,0,135-3.484,200.999-3.484C894.995,52,1200,52,1200,52V9z"></path>      </svg>    </div>  </div></div><script data-pjax src="/lib/hbe.js"></script><link href="/css/hbe.style.css" rel="stylesheet" type="text/css">]]></content>
    
    
    <summary type="html">本文主要介绍如何通过 KMS 命令激活 Windows 11 专业版系统。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="加密博客" scheme="https://www.techgrow.cn/tags/%E5%8A%A0%E5%AF%86%E5%8D%9A%E5%AE%A2/"/>
    
    <category term="Windows系统运维" scheme="https://www.techgrow.cn/tags/Windows%E7%B3%BB%E7%BB%9F%E8%BF%90%E7%BB%B4/"/>
    
  </entry>
  
  <entry>
    <title>C++ 从入门到精通之十</title>
    <link href="https://www.techgrow.cn/posts/badb9ea0.html"/>
    <id>https://www.techgrow.cn/posts/badb9ea0.html</id>
    <published>2025-11-28T13:55:33.000Z</published>
    <updated>2025-11-28T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/332ad818.html">C++ 从入门到精通之一</a>、<a href="/posts/68a580cf.html">C++ 从入门到精通之二</a>、<a href="/posts/9ce17bf9.html">C++ 从入门到精通之三</a></li><li><a href="/posts/d198d85d.html">C++ 从入门到精通之四</a>、<a href="/posts/7246a640.html">C++ 从入门到精通之五</a>、<a href="/posts/80bc7e12.html">C++ 从入门到精通之六</a></li><li><a href="/posts/e1f4608d.html">C++ 从入门到精通之七</a>、<a href="/posts/a9b9dd7e.html">C++ 从入门到精通之八</a>、<a href="/posts/14325b2f.html">C++ 从入门到精通之九</a></li><li><a href="/posts/badb9ea0.html">C++ 从入门到精通之十</a>、<a href="/posts/c961bdb.html">C++ 从入门到精通之十一</a></li></ul><span id="more"></span><h2 id="C-并发编程"><a href="#C-并发编程" class="headerlink" title="C++ 并发编程"></a>C++ 并发编程</h2><div class="admonition note"><p class="admonition-title">扩展阅读</p><ul><li><a href="/posts/a2a7ad9b.html">C++ 多线程编程之一</a></li></ul></div><h3 id="线程开始和结束运行"><a href="#线程开始和结束运行" class="headerlink" title="线程开始和结束运行"></a>线程开始和结束运行</h3><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li>进程是否执行完毕，是以主线程是否执行完为标志的。<strong>主线程一旦结束，整个进程就会终止，未执行完的子线程也会被操作系统强制结束</strong>。因此，若希望子线程继续运行，那么就必须保持主线程不结束。</li><li>值得一提的是，只有当子线程被设计为必须完成的核心任务时，主线程提前结束导致子线程被操作系统强杀，这才属于程序缺陷或不合格的程序设计；若子线程设计为<strong>守护线程</strong>或完成辅助性任务，其随主线程终止属于正常行为。</li></ul></div><blockquote><p><strong>thread 与 join () 的使用</strong></p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">5</span>; ++i) {</span><br><span class="line">        std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">    }</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动一个普通线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 阻塞主线程，让主线程等待子线程执行结束，然后再往下执行</span></span><br><span class="line">    <span class="comment">// 注意：如果这里不让主线程阻塞等待子线程执行结束，会导致程序意外退出（直接崩溃），除非子线程调用 detach()</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">sub thread start.</span><br><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">sub thread end.</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><blockquote><p><strong>thread 与 detach () 的使用</strong></p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">5</span>; ++i) {</span><br><span class="line">        std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">    }</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动一个普通线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将主线程与子线程分离（即子线程变为守护线程），主线程无需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下，可以发现在子线程还未执行完，进程（主线程）就已经正常退出：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">main thread end.</span><br><span class="line">sub thread start.</span><br><span class="line">1</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在 C++ 中，子线程调用 <code>detach()</code> 后，<code>std::thread</code> 对象与底层线程的关联会被切断，子线程转为后台运行（即变成守护线程），由 C++ 运行时接管。子线程结束运行后，C++ 运行时会自动清理其资源。<strong>特别注意，子线程调用 <code>detach()</code> 后，不能再调用 <code>join()</code>，否则程序会异常退出（崩溃）；子线程是否可以调用 <code>join()</code>，可以调用 <code>joinable()</code> 来判断</strong>。</p></div><h3 id="线程创建的其他方式"><a href="#线程创建的其他方式" class="headerlink" title="线程创建的其他方式"></a>线程创建的其他方式</h3><h4 id="使用函数对象创建线程"><a href="#使用函数对象创建线程" class="headerlink" title="使用函数对象创建线程"></a>使用函数对象创建线程</h4><blockquote><p>使用函数对象（仿函数）创建线程</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp; mc) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重载 () 操作运算符</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">operator</span><span class="params">()</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">5</span>; ++i) {</span><br><span class="line">            std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义函数对象（仿函数）</span></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动一个普通线程</span></span><br><span class="line">    <span class="comment">// 注意：这里底层会将 mc 对象复制到子线程里面去，因此即使 mc 对象提前销毁，也不会影响子线程的执行</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 阻塞主线程，让主线程等待子线程执行结束，然后再往下执行</span></span><br><span class="line">    <span class="comment">// 注意：如果这里不让主线程阻塞等待子线程执行结束，会导致程序意外退出（直接崩溃），除非子线程调用 detach()</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">MyClass()</span><br><span class="line">MyClass(const MyClass &amp;)   // 将主线程中的 MyClass 对象复制进子线程中</span><br><span class="line">sub thread start.</span><br><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">sub thread end.</span><br><span class="line">~MyClass()            // 这里析构的是复制进子线程的 MyClass 对象</span><br><span class="line">main thread end.</span><br><span class="line">~MyClass()            // 这里析构的是主线程中定义的 MyClass 对象 mc</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在上面的代码中，会额外调用一次类对象的拷贝构造函数，导致多拷贝一个类对象。若希望避免多拷贝一份类对象，可以改为使用 <code>std::thread t(std::ref(mc));</code> 来创建子线程。</p></div><blockquote><p>使用函数对象创建线程时，应避免引用局部变量</p></blockquote><p>在下面这段代码中，最核心问题是：<code>MyClass</code> 的构造函数接收 <code>int&amp;</code> 引用，该引用绑定到 <code>main</code> 函数的局部变量 <code>i</code>。当子线程执行 <code>detach()</code> 后，主线程可能先于子线程结束运行，导致局部变量 <code>i</code> 被销毁，而子线程中的 <code>m_i</code> 仍持有该悬空引用，后续在 <code>operator()</code> 中访问 <code>m_i</code> 时将产生未定义行为（通常是程序崩溃、数据错乱或看似正常等）。<strong>特别注意，在使用函数对象（仿函数）创建线程时，应避免引用局部变量，否则可能会导致悬空引用（发生未定义行为）</strong>。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> &amp;i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重载 () 操作运算符</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">operator</span><span class="params">()</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">5</span>; ++i) {</span><br><span class="line">            std::cout &lt;&lt; i &lt;&lt; <span class="string">", "</span> &lt;&lt; m_i &lt;&lt; std::endl;</span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">int</span> &amp;m_i;  <span class="comment">// 引用</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> i = <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义函数对象（引用了局部变量）</span></span><br><span class="line">    <span class="function">MyClass <span class="title">mc</span><span class="params">(i)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动一个普通线程</span></span><br><span class="line">    <span class="comment">// 注意：这里底层会将 mc 对象复制到子线程里面去，因此即使 mc 对象提前销毁，也不会影响子线程的执行</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将主线程与子线程分离（即子线程变为守护线程），主线程无需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下，可以发现在子线程还未执行完，进程（主线程）就已经正常退出，并且 <code>m_i</code> 成员变量输出的值可能是错误的：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">MyClass()</span><br><span class="line">MyClass(const MyClass &amp;)    // 将主线程中的 MyClass 对象复制进子线程中</span><br><span class="line">main thread end.</span><br><span class="line">~MyClass()    // 这里析构的是主线程中定义的 MyClass 对象 mc</span><br><span class="line">sub thread start.</span><br><span class="line">sub thread start.</span><br><span class="line">1, 22088    // 由于在主线程中，MyClass 对象引用了局部变量，因此这里输出的 m_i 成员变量的值可能是错误的；因为主线程先结束运行，局部变量 i 所在的栈内存会跟着提前被释放掉</span><br><span class="line">1, 22088    // 由于在主线程中，MyClass 对象引用了局部变量，因此这里输出的 m_i 成员变量的值可能是错误的；因为主线程先结束运行，局部变量 i 所在的栈内存会跟着提前被释放掉</span><br></pre></td></tr></tbody></table></figure><h4 id="使用-Lambda-创建线程"><a href="#使用-Lambda-创建线程" class="headerlink" title="使用 Lambda 创建线程"></a>使用 Lambda 创建线程</h4><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义 Lambda 表达式</span></span><br><span class="line">    <span class="keyword">auto</span> lambda = [] {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">5</span>; ++i) {</span><br><span class="line">            std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    };</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动一个普通线程</span></span><br><span class="line">    <span class="comment">// 注意：这里底层会将 mc 对象复制到子线程里面去，因此即使 mc 对象提前销毁，也不会影响子线程的执行</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(lambda)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 阻塞主线程，让主线程等待子线程执行结束，然后再往下执行</span></span><br><span class="line">    <span class="comment">// 注意：如果这里不让主线程阻塞等待子线程执行结束，会导致程序意外退出（直接崩溃），除非子线程调用 detach()</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">sub thread start.</span><br><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">sub thread end.</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h4 id="使用成员函数指针创建线程"><a href="#使用成员函数指针创建线程" class="headerlink" title="使用成员函数指针创建线程"></a>使用成员函数指针创建线程</h4><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 成员函数</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">thread_run</span><span class="params">(MyClass &amp;mc)</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">        <span class="comment">// 主线程中使用 std::ref() 强制传递引用作为线程函数参数后，这里修改的是主线程中的 m_c 对象</span></span><br><span class="line">        mc.m_i = <span class="number">300</span>;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="function">MyClass <span class="title">m_c</span><span class="params">(<span class="number">200</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 在创建子线程时，使用 std::ref() 强制传递引用作为线程函数参数</span></span><br><span class="line">    <span class="comment">// 第一个参数是成员函数指针，第二个参数是类对象（可以是指针或者引用），第三个参数是真正传递给线程函数的参数</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(&amp;MyClass::thread_run, &amp;m_c, std::ref(m_c))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等价于上面的写法</span></span><br><span class="line">    <span class="comment">// std::thread t(&amp;MyClass::thread_run, std::ref(m_c), std::ref(m_c));</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注意：如果使用下面这种写法（合法），又会多拷贝一份 m_c 对象，导致线程函数里修改的不是主线程中的 m_c 对象</span></span><br><span class="line">    <span class="comment">// std::thread t(&amp;MyClass::thread_run, m_c, std::ref(m_c));</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 主线程需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">MyClass()</span><br><span class="line">sub thread start.</span><br><span class="line">sub thread end.</span><br><span class="line">main thread end.</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><h3 id="线程传递参数的方式"><a href="#线程传递参数的方式" class="headerlink" title="线程传递参数的方式"></a>线程传递参数的方式</h3><h4 id="传递临时对象作为线程参数"><a href="#传递临时对象作为线程参数" class="headerlink" title="传递临时对象作为线程参数"></a>传递临时对象作为线程参数</h4><h5 id="参数传递实战"><a href="#参数传递实战" class="headerlink" title="参数传递实战"></a>参数传递实战</h5><p>这段代码是安全的（正确的），因为主线程必须等待子线程执行结束，然后再往下执行。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">char</span> *buf)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; buf &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">char</span> m_buf[] = <span class="string">"this is a string"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程，并传递参数</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, m_buf)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 阻塞主线程，让主线程等待子线程执行结束，然后再往下执行</span></span><br><span class="line">    <span class="comment">// 注意：如果这里不让主线程阻塞等待子线程执行结束，会导致程序意外退出（直接崩溃），除非子线程调用 detach()</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">sub thread start.</span><br><span class="line">1</span><br><span class="line">this is a string</span><br><span class="line">sub thread end.</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h5 id="要避免的陷阱一"><a href="#要避免的陷阱一" class="headerlink" title="要避免的陷阱一"></a>要避免的陷阱一</h5><p>在下面的这段代码中，存在一个严重的悬垂指针问题，这是 C++ 多线程开发中非常经典的错误。核心问题是：参数 <code>m_buf</code> 传递的是指针，但在子线程执行 <code>detach()</code> 后，主线程可能先结束运行，导致局部变量 <code>m_buf</code> 被销毁，最终子线程会访问已释放的内存。</p><ul><li><p>(1) 代码安全的部分：<code>int &amp;</code> 参数</p><ul><li>虽然函数签名是 <code>const int &amp;i</code>，但实际上 <code>std::thread</code> 构造函数会对参数做<strong>按值拷贝</strong>（除非显式使用 <code>std::ref</code>）</li><li>即使 <code>detach()</code> 后主线程销毁了 <code>m_i</code>，子线程持有的也是独立副本，访问是安全的</li></ul></li><li><p> (2) 代码不安全的部分：<code>char *</code> 参数</p><ul><li><code>m_buf</code> 是局部变量，存储在<strong>栈</strong>上</li><li><code>std::thread</code> 只是传递了数组首地址（指针值被拷贝），并没有拷贝字符串内容</li><li>当主线程执行完 <code>return 0</code> 后，<code>m_buf</code> 的内存会被释放 / 覆盖</li><li>子线程若此时继续访问 <code>buf</code>，将发生<strong>未定义行为</strong>（程序可能会崩溃、输出乱码、看似正常等）</li></ul></li></ul><blockquote><p>问题代码</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">char</span> *buf)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; buf &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">char</span> m_buf[] = <span class="string">"this is a string"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程，并传递参数</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, m_buf)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将主线程与子线程分离（即子线程变为守护线程），主线程无需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><blockquote><p>解决方案</p></blockquote><ul><li>解决方案一：使用 <code>join()</code> 替代 <code>detach()</code>，让主线程等待子线程结束 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">char</span> *buf)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; buf &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">char</span> m_buf[] = <span class="string">"this is a string"</span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, m_buf)</span></span>;</span><br><span class="line">    t.<span class="built_in">join</span>();   <span class="comment">// 主线程等待子线程结束</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>解决方案二：传递字符串副本 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">char</span> *buf)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; buf &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">char</span> m_buf[] = <span class="string">"this is a string"</span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, std::string(m_buf))</span></span>;   <span class="comment">// 使用临时对象</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>解决方案三：使用智能指针 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">char</span> *buf)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; buf &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">char</span> m_buf[] = <span class="string">"this is a string"</span>;</span><br><span class="line">    <span class="keyword">auto</span> sp_buf = std::make_shared&lt;<span class="keyword">char</span>[]&gt;(<span class="built_in">strlen</span>(m_buf) + <span class="number">1</span>);</span><br><span class="line">    <span class="built_in">strcpy</span>(sp_buf.<span class="built_in">get</span>(), m_buf);</span><br><span class="line"></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">([m_i, sp_buf]() { </span></span></span><br><span class="line"><span class="params"><span class="function">        func(m_i, sp_buf.get()); </span></span></span><br><span class="line"><span class="params"><span class="function">    })</span></span>;</span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>解决方案四：使用值传递而不是引用专递（<strong>最佳实践</strong>）</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 最安全的写法（不使用引用或指针，直接传递值）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> i, <span class="keyword">const</span> std::string buf)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; buf &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">char</span> m_buf[] = <span class="string">"this is a string"</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 传递字符串副本（使用临时对象）</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, std::string(m_buf))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 现在执行 detach() 安全了，因为 string 已经被拷贝到线程内部</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><div class="admonition note"><p class="admonition-title">总结</p><ul><li>本质问题：代码将指向局部栈数组的指针传递给分离线程（子线程），主线程退出后子线程访问已销毁的内存，导致未定义行为。</li><li>类似风险：传递局部对象的引用、指向局部对象的指针、迭代器等给调用过 <code>detach()</code> 的子线程（分离线程）。</li><li>最佳实践：除非明确知道对象生命周期大于线程生命周期，否则应避免在调用过 <code>detach()</code> 的子线程中使用指针或者引用，尽量传递值类型或智能指针；<code>join()</code> 比 <code>detach()</code> 更安全，能明确控制生命周期。</li></ul></div><h5 id="要避免的陷阱二"><a href="#要避免的陷阱二" class="headerlink" title="要避免的陷阱二"></a>要避免的陷阱二</h5><p>在下面的这段代码中，存在一个严重的悬空引用问题，这是 C++ 多线程开发中非常经典的错误。核心问题是：由于 <code>m_c</code> 局部变量通过隐式类型转换构造 MyClass 临时对象的时机不确定，因此代码是不安全的。</p><ul><li><p>(1) 隐式类型转换的细节：</p><ul><li><code>func()</code> 期望第二个参数是 <code>const MyClass &amp;</code>，但传递的是 <code>int</code>，这里会发生隐式类型转换，也就是编译器会用 <code>m_c</code>（200）调用 <code>MyClass(int)</code> 构造函数创建一个临时对象。</li></ul></li><li><p>(2) 代码安全的部分：<code>int &amp;</code> 参数</p><ul><li>虽然函数签名是 <code>const int &amp;i</code>，但实际上 <code>std::thread</code> 构造函数会对参数做<strong>按值拷贝</strong>（除非显式使用 <code>std::ref</code>）</li><li>即使 <code>detach()</code> 后主线程销毁了 <code>m_i</code>，子线程持有的也是独立副本，访问是安全的</li></ul></li><li><p> (3) 代码不安全的部分：<code>MyClass &amp;</code> 参数</p><ul><li><code>m_c</code>（200）会通过隐式类型转换构造一个 <code>MyClass</code> 临时对象</li><li>但是 <code>std::thread</code> 并不是立即执行线程函数，它会先拷贝或移动参数到内部存储</li><li>当线程函数真正执行时，<code>MyClass</code> 临时对象的构造时机可能已经晚于主线程中 <code>m_c</code> 的销毁</li><li>因此 <code>const MyClass &amp;mc</code> 可能绑定到一个未正确构造的 <code>MyClass</code> 对象</li><li>这会导致悬空引用，访问 <code>mc</code> 属于未定义行为（通常是程序崩溃、数据错乱或看似正常等）</li></ul></li></ul><blockquote><p>问题代码（使用引用 + 隐式类型转换）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">get</span><span class="params">()</span> <span class="keyword">const</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> m_i;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">const</span> MyClass &amp;mc)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; mc.<span class="built_in">get</span>() &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">int</span> m_c = <span class="number">200</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程，并传递参数（这里的 m_c 会通过隐式类型转换构造一个 MyClass 临时对象）</span></span><br><span class="line">    <span class="comment">// 注意：这里的 MyClass 临时对象是在子线程中构造出来的，而且有可能是在主线程结束运行之后才构造，因此是不安全的</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, m_c)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将主线程与子线程分离（即子线程变为守护线程），主线程无需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><blockquote><p>解决方案</p></blockquote><ul><li>解决方案一：使用引用 + 手动构造栈对象 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">get</span><span class="params">()</span> <span class="keyword">const</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> m_i;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">const</span> MyClass &amp;mc)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; mc.<span class="built_in">get</span>() &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="function">MyClass <span class="title">m_c</span><span class="params">(<span class="number">200</span>)</span></span>;  <span class="comment">// 栈上的对象（临时对象）</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程，并传递参数</span></span><br><span class="line">    <span class="comment">// 在创建子线程时，会拷贝参数 m_c 到子线程的内部存储中，因此在执行 detach() 后继续访问 m_c 是安全的</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, m_c)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将主线程与子线程分离（即子线程变为守护线程），主线程无需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>解决方案二：使用引用 + 手动构造临时对象（<strong>最佳实践</strong>）</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">get</span><span class="params">()</span> <span class="keyword">const</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> m_i;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> &amp;i, <span class="keyword">const</span> MyClass &amp;mc)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; mc.<span class="built_in">get</span>() &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">int</span> m_c = <span class="number">200</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程，并传递参数（构造一个 MyClass 临时对象）</span></span><br><span class="line">    <span class="comment">// 注意：这里的 MyClass 临时对象是在主线程中构造出来的，而且是在主线程结束运行之前构造出来，因此是安全的</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, MyClass(m_c))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将主线程与子线程分离（即子线程变为守护线程），主线程无需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="参数传递的最佳实践"><a href="#参数传递的最佳实践" class="headerlink" title="参数传递的最佳实践"></a>参数传递的最佳实践</h5><ul><li>建议不要使用 <code>detach()</code>，只使用 <code>join()</code>。这样可以避免因局部变量失效而导致子线程访问非法内存的问题。  </li><li>如果确实要使用 <code>detach()</code>，应遵守以下规则：<ul><li>传递基础类型（如 <code>int</code>）给线程时，建议使用<strong>值传递</strong>，不要使用引用或指针。  </li><li>传递类对象（如 <code>MyClass</code>）给线程时：<ul><li>(1) 应避免隐式类型转换</li><li> (2) 建议在创建线程的同一行代码中直接构建临时对象，并在线程函数的参数列表中使用<strong>引用</strong>来接收该对象，这样做可以避免一次额外的类对象拷贝构造（示例代码如下）<figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自定义类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        </span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        </span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">get</span><span class="params">()</span> <span class="keyword">const</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> m_i;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 基础类型使用值传递，类对象使用引用传递</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> i, <span class="keyword">const</span> MyClass &amp;mc)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; i &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; mc.<span class="built_in">get</span>() &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> m_i = <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">int</span> m_c = <span class="number">200</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建线程时，在创建线程的同一行代码中直接构建临时对象</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_i, MyClass(m_c))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置主线程无需等待子线程执行结束才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">detach</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul></li></ul><h4 id="传递类对象、智能指针作为线程参数"><a href="#传递类对象、智能指针作为线程参数" class="headerlink" title="传递类对象、智能指针作为线程参数"></a>传递类对象、智能指针作为线程参数</h4><p>下面这段代码是安全的（因为使用 <code>join()</code> 而不是 <code>detach()</code>），但线程函数里面修改的不是主线程中的 <code>m_c</code> 对象。因为在子线程创建时，已经将主线程中的 <code>m_c</code> 对象拷贝到子线程的内部存储中，这里的 <code>MyClass &amp;mc</code> 引用绑定的是这份拷贝，所以修改不会影响主线程中的 <code>m_c</code> 对象。<strong>思考问题：如何才能够在子线程中正确修改主线程中的 <code>mc</code> 对象呢？</strong></p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">mutable</span> <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">const</span> MyClass &amp;mc)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 注意：子线程创建时已经将 m_c 对象拷贝到子线程的内部存储中，这里的 mc 引用绑定的是这份拷贝，因此修改不会影响主线程中的 m_c 对象</span></span><br><span class="line">    mc.m_i = <span class="number">300</span>;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="function">MyClass <span class="title">m_c</span><span class="params">(<span class="number">200</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 在创建子线程时，会拷贝 m_c 对象到子线程的内部存储中</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, m_c)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 主线程需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">MyClass()</span><br><span class="line">MyClass(const MyClass &amp;)</span><br><span class="line">sub thread start.</span><br><span class="line">sub thread end.</span><br><span class="line">~MyClass()</span><br><span class="line">main thread end.</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><blockquote><p>解决方案一，使用 <code>std::ref()</code> 强制传递引用作为线程函数参数，避免多拷贝一份类对象</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(MyClass &amp;mc)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 主线程中使用 std::ref() 强制传递引用后，这里修改的是主线程中的 m_c 对象</span></span><br><span class="line">    mc.m_i = <span class="number">300</span>;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="function">MyClass <span class="title">m_c</span><span class="params">(<span class="number">200</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 在创建子线程时，使用 std::ref() 强制传递引用作为线程函数参数</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, std::ref(m_c))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 主线程需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">MyClass()</span><br><span class="line">sub thread start.</span><br><span class="line">sub thread end.</span><br><span class="line">main thread end.</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><blockquote><p>解决方案二，传递智能指针作为线程函数参数，避免多拷贝一份类对象</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;memory&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">int</span> i) : <span class="built_in">m_i</span>(i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass &amp;mc) : <span class="built_in">m_i</span>(mc.m_i) {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass(const MyClass &amp;)"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数（带参数）</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(std::unique_ptr&lt;MyClass&gt; up)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread start."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="comment">// 主线程中传递智能指针后，这里修改的是主线程中的 m_c 对象</span></span><br><span class="line">    up-&gt;m_i = <span class="number">300</span>;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    <span class="function">std::unique_ptr&lt;MyClass&gt; <span class="title">m_c</span><span class="params">(<span class="keyword">new</span> MyClass(<span class="number">200</span>))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 在创建子线程时，传递智能指针作为线程函数参数</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(func, std::move(m_c))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 主线程需等待子线程执行完成才继续往下执行</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">MyClass()</span><br><span class="line">sub thread start.</span><br><span class="line">sub thread end.</span><br><span class="line">~MyClass()</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h3 id="并发与多线程"><a href="#并发与多线程" class="headerlink" title="并发与多线程"></a>并发与多线程</h3><h4 id="创建和等待多个线程"><a href="#创建和等待多个线程" class="headerlink" title="创建和等待多个线程"></a>创建和等待多个线程</h4><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">int</span> num)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">100</span>));</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::vector&lt;std::thread&gt; threads;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 10 个线程，同时这 10 个线程会立即执行（多个线程之间的启动顺序是不确定的）</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) {</span><br><span class="line">        threads.<span class="built_in">emplace_back</span>(std::<span class="built_in">thread</span>(func, i));</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 主线程等待所有线程执行完毕，然后才继续往下执行</span></span><br><span class="line">    <span class="keyword">for</span> (std::vector&lt;std::thread&gt;::iterator it = threads.<span class="built_in">begin</span>(); it != threads.<span class="built_in">end</span>(); ++it) {</span><br><span class="line">        it-&gt;<span class="built_in">join</span>();</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">sub thread 0 start.</span><br><span class="line">sub thread 1 start.</span><br><span class="line">sub thread 2 start.</span><br><span class="line">sub thread 3 start.</span><br><span class="line">sub thread 5 start.</span><br><span class="line">sub thread 4 start.</span><br><span class="line">sub thread 6 start.</span><br><span class="line">sub thread 8 start.</span><br><span class="line">sub thread 7 start.</span><br><span class="line">sub thread 9 start.</span><br><span class="line">sub thread 0 end.</span><br><span class="line">sub thread 1 end.</span><br><span class="line">sub thread 2 end.</span><br><span class="line">sub thread 3 end.</span><br><span class="line">sub thread 5 end.</span><br><span class="line">sub thread 4 end.</span><br><span class="line">sub thread 6 end.</span><br><span class="line">sub thread 8 end.</span><br><span class="line">sub thread 7 end.</span><br><span class="line">sub thread 9 end.</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h4 id="数据共享问题的分析"><a href="#数据共享问题的分析" class="headerlink" title="数据共享问题的分析"></a>数据共享问题的分析</h4><h5 id="只读的数据"><a href="#只读的数据" class="headerlink" title="只读的数据"></a>只读的数据</h5><div class="admonition note"><p class="admonition-title">关键点</p><ul><li>在多线程环境下，只读的共享数据是不存在线程安全问题的，不需要特殊的处理手段，直接读就可以。</li></ul></div><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 共享数据（只读）</span></span><br><span class="line">std::vector&lt;<span class="keyword">int</span>&gt; g_v = {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">int</span> num)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" start."</span> &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" read "</span> &lt;&lt; g_v[<span class="number">0</span>] &lt;&lt; <span class="string">", "</span> &lt;&lt; g_v[<span class="number">1</span>] &lt;&lt; <span class="string">", "</span> &lt;&lt; g_v[<span class="number">2</span>] &lt;&lt; std::endl;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">50</span>));</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::vector&lt;std::thread&gt; threads;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 5 个线程，同时这 5 个线程会立即执行（多个线程之间的启动顺序是不确定的）</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) {</span><br><span class="line">        threads.<span class="built_in">emplace_back</span>(std::<span class="built_in">thread</span>(func, i));</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待所有线程执行完毕</span></span><br><span class="line">    <span class="keyword">for</span> (std::vector&lt;std::thread&gt;::iterator it = threads.<span class="built_in">begin</span>(); it != threads.<span class="built_in">end</span>(); ++it) {</span><br><span class="line">        it-&gt;<span class="built_in">join</span>();</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">sub thread 0 start.</span><br><span class="line">sub thread 0 read 1, 2, 3</span><br><span class="line">sub thread 1 start.</span><br><span class="line">sub thread 1 read 1, 2, 3</span><br><span class="line">sub thread 2 start.</span><br><span class="line">sub thread 2 read 1, 2, 3</span><br><span class="line">sub thread 3 start.</span><br><span class="line">sub thread 3 read 1, 2, 3</span><br><span class="line">sub thread 4 start.</span><br><span class="line">sub thread 4 read 1, 2, 3</span><br><span class="line">sub thread 2 end.</span><br><span class="line">sub thread 1 end.</span><br><span class="line">sub thread 0 end.</span><br><span class="line">sub thread 3 end.</span><br><span class="line">sub thread 4 end.</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h5 id="有读有写"><a href="#有读有写" class="headerlink" title="有读有写"></a>有读有写</h5><div class="admonition note"><p class="admonition-title">关键点</p><ul><li>在多线程环境下，有读有写的共享数据是存在线程安全问题的（比如 3 个线程同时读数据，2 个线程同时写数据），需要特殊的处理手段。</li><li>常见的处理手段：针对共享数据，使用互斥锁（如 <code>std::mutex</code>）或读写锁（<code>std::shared_mutex</code>）来保证读读不互斥、读写和写写互斥。</li></ul></div><ul><li>问题代码的简单分析<ul><li>以下代码展示了在多线程环境下，多个线程同时读写共享数据 <code>std::vector&lt;int&gt; g_v</code> 而没有使用任何同步机制，从而产生线程安全问题（数据竞争）。</li><li>前 2 个线程执行写操作（修改 <code>vector</code> 内容），后 3 个线程执行读操作（读取 <code>vector</code> 内容）。由于读写同时发生，可能会导致未定义行为（比如读取到不一致的值、程序崩溃等）。</li></ul></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 共享数据（有读有写）</span></span><br><span class="line">std::vector&lt;<span class="keyword">int</span>&gt; g_v = {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">int</span> num)</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 前两个线程：执行写操作</span></span><br><span class="line">    <span class="keyword">if</span> (num &lt; <span class="number">2</span>) {</span><br><span class="line">        <span class="comment">// 写操作：添加一个新元素</span></span><br><span class="line">        g_v.<span class="built_in">push_back</span>(num);</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" write value "</span> &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line">    <span class="comment">// 后三个线程：执行读操作</span></span><br><span class="line">    <span class="keyword">else</span> {</span><br><span class="line">        <span class="comment">// 读操作：读取 vector 的所有元素</span></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" reads: "</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> val : g_v) {</span><br><span class="line">            std::cout &lt;&lt; val &lt;&lt; <span class="string">" "</span>;</span><br><span class="line">        }</span><br><span class="line">        std::cout &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 模拟一些工作耗时</span></span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">50</span>));</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"sub thread "</span> &lt;&lt; num &lt;&lt; <span class="string">" end."</span> &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    std::vector&lt;std::thread&gt; threads;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建 5 个线程，其中 2 个线程执行写操作，3 个线程执行读操作</span></span><br><span class="line">    <span class="comment">// 由于读写同时发生，可能会导致未定义行为（比如读取到不一致的值、程序崩溃等）</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) {</span><br><span class="line">        threads.<span class="built_in">emplace_back</span>(func, i);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待所有线程执行完毕</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; t : threads) {</span><br><span class="line">        t.<span class="built_in">join</span>();</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>存在的线程安全问题<ul><li> (1) 数据竞争：写线程修改 <code>g_v</code>（例如 <code>push_back</code> 可能会引发 <code>vector</code> 重新分配内存）时，读线程同时遍历 <code>g_v</code>，会导致读线程访问到无效的内存或未更新的状态。</li><li>(2) 不一致的读取：读线程可能在写线程修改过程中读取到部分旧数据、部分新数据，甚至因迭代器失效导致程序崩溃。</li><li>(3) 未定义行为：根据 C++ 标准，并发读写同一非原子对象且无同步机制时，程序存在未定义行为，可能的表现如下：<ul><li>程序可能正常输出混乱的结果</li><li>程序可能抛出异常（如 <code>vector</code> 迭代器失效）</li><li>程序可能直接崩溃（段错误等）</li></ul></li></ul></li></ul><h4 id="共享数据的保护案例"><a href="#共享数据的保护案例" class="headerlink" title="共享数据的保护案例"></a>共享数据的保护案例</h4><div class="admonition note"><p class="admonition-title">案例背景说明</p><p>在网络游戏服务器的设计中，共享数据的保护是一个典型案例：可以创建两个线程，其中一个线程负责收集玩家的命令并将命令数据写入队列，另一个线程则从队列中取出玩家发来的命令，进行解析并执行玩家所需的动作。值得一提的是，建议使用生产者消费者模型来实现，并使用 <code>list</code> 容器作为队列。</p></div><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;condition_variable&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 将收到的玩家命令写入队列（生产者）</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">inMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; i++) {</span><br><span class="line">            {</span><br><span class="line">                <span class="comment">// 获取互斥锁（作用域结束后会自动释放锁）</span></span><br><span class="line">                <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 将玩家命令写入队列</span></span><br><span class="line">                msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 唤醒等待操作队列的其他线程</span></span><br><span class="line">            queueNotEmptyCondition.<span class="built_in">notify_one</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟网络收包间隔</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 通知消费者退出</span></span><br><span class="line">        {</span><br><span class="line">            <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line">            stop = <span class="literal">true</span>;</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 唤醒所有等待线程</span></span><br><span class="line">        queueNotEmptyCondition.<span class="built_in">notify_all</span>();</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从队列中读取玩家命令（消费者）</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">outMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line">            <span class="comment">// 获取互斥锁（作用域结束后会自动释放锁）</span></span><br><span class="line">            <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 当前线程等待队列不为空或者收到退出信号</span></span><br><span class="line">            queueNotEmptyCondition.<span class="built_in">wait</span>(lock, [<span class="keyword">this</span>]() { <span class="keyword">return</span> stop || !msgRecvQueue.<span class="built_in">empty</span>(); });</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 收到退出信号且队列为空</span></span><br><span class="line">            <span class="keyword">if</span> (stop &amp;&amp; msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 取出队列元素</span></span><br><span class="line">            <span class="keyword">int</span> command = msgRecvQueue.<span class="built_in">front</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 移除队列元素</span></span><br><span class="line">            msgRecvQueue.<span class="built_in">pop_front</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 尽早释放锁，提高并发性能</span></span><br><span class="line">            lock.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 解析并执行玩家命令</span></span><br><span class="line">            <span class="built_in">handleCommand</span>(command);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 模拟处理玩家命令</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">handleCommand</span><span class="params">(<span class="keyword">int</span> command)</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"已处理玩家命令: "</span> &lt;&lt; command &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::list&lt;<span class="keyword">int</span>&gt; msgRecvQueue;                     <span class="comment">// 消息队列（共享数据）</span></span><br><span class="line">    std::mutex msgRecvQueueMutex;                    <span class="comment">// 保护消息队列线程安全的互斥锁</span></span><br><span class="line">    std::condition_variable queueNotEmptyCondition;  <span class="comment">// 消息队列不为空的条件</span></span><br><span class="line">    <span class="keyword">bool</span> stop = <span class="literal">false</span>;                               <span class="comment">// 线程退出标志</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动写线程（生产者）</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_write</span><span class="params">(&amp;MyClass::inMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动读线程（消费者）</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_read</span><span class="params">(&amp;MyClass::outMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待写线程执行完毕</span></span><br><span class="line">    t_write.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待读线程执行完毕</span></span><br><span class="line">    t_read.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">已处理玩家命令: 0</span><br><span class="line">已处理玩家命令: 1</span><br><span class="line">已处理玩家命令: 2</span><br><span class="line">已处理玩家命令: 3</span><br><span class="line">......</span><br><span class="line">已处理玩家命令: 996</span><br><span class="line">已处理玩家命令: 997</span><br><span class="line">已处理玩家命令: 998</span><br><span class="line">已处理玩家命令: 999</span><br></pre></td></tr></tbody></table></figure><blockquote><p><strong>list 容器和 vector 容器的适用场景</strong></p></blockquote><table><thead><tr><th>容器</th><th>适用场景（效率高）</th><th>不适用场景（效率低）</th></tr></thead><tbody><tr><td><code>list</code></td><td>- 任意位置插入 / 删除（给定迭代器）<br>- 从头部 / 尾部操作<br>- 合并两个列表</td><td> - 随机访问（不支持 <code>[]</code>）<br>- 查找元素<br>- 遍历（缓存不友好）</td></tr><tr><td><code>vector</code></td><td>- 尾部插入 / 删除<br>- 随机访问（下标访问）<br>- 遍历（缓存友好）</td><td>- 头部 / 中间插入 / 删除<br>- 插入导致重新分配内存</td></tr></tbody></table><div class="admonition note"><p class="admonition-title">提示</p><ul><li>C++ 的 <code>list</code> 容器在任意位置频繁插入和删除数据时效率高（尤其是中间位置），因为它只需调整前后节点的指针，不需要移动其他元素。</li><li>C++ 的 <code>vector</code> 容器在尾部频繁插入和删除数据时效率高（平均时间复杂度 <code>O(1)</code>），但在头部或中间插入 / 删除数据时效率低，因为需要移动后续所有元素。</li></ul></div>]]></content>
    
    
    <summary type="html">本文主要介绍 C++ 从入门到精通的内容。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="并发编程" scheme="https://www.techgrow.cn/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    <category term="C++" scheme="https://www.techgrow.cn/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>C++ 从入门到精通之十</title>
    <link href="https://www.techgrow.cn/posts/c961bdb.html"/>
    <id>https://www.techgrow.cn/posts/c961bdb.html</id>
    <published>2025-11-28T13:55:33.000Z</published>
    <updated>2025-11-28T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/332ad818.html">C++ 从入门到精通之一</a>、<a href="/posts/68a580cf.html">C++ 从入门到精通之二</a>、<a href="/posts/9ce17bf9.html">C++ 从入门到精通之三</a></li><li><a href="/posts/d198d85d.html">C++ 从入门到精通之四</a>、<a href="/posts/7246a640.html">C++ 从入门到精通之五</a>、<a href="/posts/80bc7e12.html">C++ 从入门到精通之六</a></li><li><a href="/posts/e1f4608d.html">C++ 从入门到精通之七</a>、<a href="/posts/a9b9dd7e.html">C++ 从入门到精通之八</a>、<a href="/posts/14325b2f.html">C++ 从入门到精通之九</a></li><li><a href="/posts/badb9ea0.html">C++ 从入门到精通之十</a>、<a href="/posts/c961bdb.html">C++ 从入门到精通之十一</a></li></ul><span id="more"></span><h2 id="C-并发编程"><a href="#C-并发编程" class="headerlink" title="C++ 并发编程"></a>C++ 并发编程</h2><div class="admonition note"><p class="admonition-title">扩展阅读</p><ul><li><a href="/posts/a2a7ad9b.html">C++ 多线程编程之一</a></li></ul></div><h3 id="互斥量"><a href="#互斥量" class="headerlink" title="互斥量"></a>互斥量</h3><h4 id="互斥量的概念"><a href="#互斥量的概念" class="headerlink" title="互斥量的概念"></a>互斥量的概念</h4><p>C++ 中的互斥量（<code>std::mutex</code>）是一种用于保护共享数据免受并发访问影响的同步原语。它的核心机制是 “独占锁定”：同一时刻，只有一个线程能成功锁定（lock）互斥量并进入临界区执行代码，其他试图进入的线程必须等待，直到持有锁的线程调用解锁（unlock）释放所有权。这种机制有效防止了数据竞争（Data Race）和未定义行为。C++ 11 标准库将互斥量纳入了线程支持库，并提供了 <code>std::lock_guard</code>、<code>std::unique_lock</code> 等 RAII 包装器，用于自动管理锁的获取与释放，避免因异常或忘记解锁而导致的死锁问题。</p><h4 id="互斥量的使用"><a href="#互斥量的使用" class="headerlink" title="互斥量的使用"></a>互斥量的使用</h4><h5 id="lock-、unlock"><a href="#lock-、unlock" class="headerlink" title="lock()、unlock()"></a>lock()、unlock()</h5><p>互斥量（<code>std::mutex</code>）的基本用法遵循显式的加锁与解锁配对原则：线程必须先调用 <code>lock()</code> 获取互斥量，然后安全地操作共享数据，最后调用 <code>unlock()</code> 释放所有权。</p><div class="admonition warning"><p class="admonition-title">特别注意</p><p><strong>互斥量（<code>std::mutex</code>）的 <code>lock()</code> 和 <code>unlock()</code> 必须严格成对使用，每次 <code>lock()</code> 对应一次 <code>unlock()</code>，既不能多调用一次 <code>unlock()</code>，也不能少调用一次，否则会导致未定义行为或线程死锁等严重问题。</strong></p></div><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 将收到的玩家命令写入队列</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">inMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; ++i) {</span><br><span class="line">            <span class="comment">// 加锁</span></span><br><span class="line">            msgRecvQueueMutex.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 插入队列</span></span><br><span class="line">            msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 解锁</span></span><br><span class="line">            msgRecvQueueMutex.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟网络收包间隔</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 更新程序停止标记</span></span><br><span class="line">        stop = <span class="literal">true</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从队列中读取玩家命令</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">outMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line">            <span class="keyword">int</span> command = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 加锁</span></span><br><span class="line">            msgRecvQueueMutex.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 判断程序停止标记</span></span><br><span class="line">            <span class="keyword">if</span> (stop &amp;&amp; msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                <span class="comment">// 解锁</span></span><br><span class="line">                msgRecvQueueMutex.<span class="built_in">unlock</span>();</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 操作队列</span></span><br><span class="line">            <span class="keyword">if</span> (!msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                <span class="comment">// 取出队列元素</span></span><br><span class="line">                command = msgRecvQueue.<span class="built_in">front</span>();</span><br><span class="line">                <span class="comment">// 移除队列元素</span></span><br><span class="line">                msgRecvQueue.<span class="built_in">pop_front</span>();</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 解锁</span></span><br><span class="line">            msgRecvQueueMutex.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 打印</span></span><br><span class="line">            <span class="keyword">if</span> (command != <span class="number">-1</span>) {</span><br><span class="line">                std::cout &lt;&lt; <span class="string">"已处理玩家命令: "</span> &lt;&lt; command &lt;&lt; std::endl;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟业务执行耗时</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::list&lt;<span class="keyword">int</span>&gt; msgRecvQueue;   <span class="comment">// 消息队列（共享数据）</span></span><br><span class="line">    std::mutex msgRecvQueueMutex;  <span class="comment">// 保护消息队列线程安全的互斥锁</span></span><br><span class="line">    std::<span class="keyword">atomic_bool</span> stop{<span class="literal">false</span>};  <span class="comment">// 程序停止标记</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动写线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_write</span><span class="params">(&amp;MyClass::inMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动读线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_read</span><span class="params">(&amp;MyClass::outMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待写线程执行完毕</span></span><br><span class="line">    t_write.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待读线程执行完毕</span></span><br><span class="line">    t_read.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">已处理玩家命令: 0</span><br><span class="line">已处理玩家命令: 1</span><br><span class="line">已处理玩家命令: 2</span><br><span class="line">已处理玩家命令: 3</span><br><span class="line">......</span><br><span class="line">已处理玩家命令: 996</span><br><span class="line">已处理玩家命令: 997</span><br><span class="line">已处理玩家命令: 998</span><br><span class="line">已处理玩家命令: 999</span><br></pre></td></tr></tbody></table></figure><h5 id="std-lock-guard-模板"><a href="#std-lock-guard-模板" class="headerlink" title="std::lock_guard 模板"></a>std::lock_guard 模板</h5><p>C++ 11 标准库将互斥量纳入了线程支持库，并提供了 <code>std::lock_guard</code>、<code>std::unique_lock</code> 等 RAII 包装器，用于自动管理锁的获取与释放，避免因异常或忘记解锁而导致的死锁问题。值得一提的是，<code>std::lock_guard</code> 通过 RAII（资源获取即初始化）机制实现自动加锁和解锁：在构造时，它的构造函数会接收一个互斥量对象并立即调用该互斥量的 <code>lock()</code> 方法完成加锁；在析构时，它的析构函数会自动调用互斥量的 <code>unlock()</code> 方法完成解锁。这样，当 <code>lock_guard</code> 对象被创建时锁就被获取，当该对象离开作用域（无论是正常结束、<code>return</code>、<code>break</code> 还是抛出异常）时，析构函数会被自动触发，从而保证锁一定会被释放，避免了因忘记调用 <code>unlock()</code> 或异常导致死锁的问题。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 将收到的玩家命令写入队列</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">inMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; ++i) {</span><br><span class="line">            {</span><br><span class="line">                <span class="comment">// 加锁（出了作用域后会自动解锁）</span></span><br><span class="line">                <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 插入队列</span></span><br><span class="line">                msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟网络收包间隔</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 更新程序停止标记</span></span><br><span class="line">        stop = <span class="literal">true</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从队列中读取玩家命令</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">outMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line">            <span class="keyword">int</span> command = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">            {</span><br><span class="line">                <span class="comment">// 加锁（出了作用域后会自动解锁）</span></span><br><span class="line">                <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 判断程序停止标记</span></span><br><span class="line">                <span class="keyword">if</span> (stop &amp;&amp; msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                }</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 操作队列</span></span><br><span class="line">                <span class="keyword">if</span> (!msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                    <span class="comment">// 取出队列元素</span></span><br><span class="line">                    command = msgRecvQueue.<span class="built_in">front</span>();</span><br><span class="line">                    <span class="comment">// 移除队列元素</span></span><br><span class="line">                    msgRecvQueue.<span class="built_in">pop_front</span>();</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 打印</span></span><br><span class="line">            <span class="keyword">if</span> (command != <span class="number">-1</span>) {</span><br><span class="line">                std::cout &lt;&lt; <span class="string">"已处理玩家命令: "</span> &lt;&lt; command &lt;&lt; std::endl;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟业务执行耗时</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::list&lt;<span class="keyword">int</span>&gt; msgRecvQueue;   <span class="comment">// 消息队列（共享数据）</span></span><br><span class="line">    std::mutex msgRecvQueueMutex;  <span class="comment">// 保护消息队列线程安全的互斥锁</span></span><br><span class="line">    std::<span class="keyword">atomic_bool</span> stop{<span class="literal">false</span>};  <span class="comment">// 程序停止标记</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动写线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_write</span><span class="params">(&amp;MyClass::inMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动读线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_read</span><span class="params">(&amp;MyClass::outMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待写线程执行完毕</span></span><br><span class="line">    t_write.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待读线程执行完毕</span></span><br><span class="line">    t_read.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">已处理玩家命令: 0</span><br><span class="line">已处理玩家命令: 1</span><br><span class="line">已处理玩家命令: 2</span><br><span class="line">已处理玩家命令: 3</span><br><span class="line">......</span><br><span class="line">已处理玩家命令: 996</span><br><span class="line">已处理玩家命令: 997</span><br><span class="line">已处理玩家命令: 998</span><br><span class="line">已处理玩家命令: 999</span><br></pre></td></tr></tbody></table></figure><h3 id="线程死锁"><a href="#线程死锁" class="headerlink" title="线程死锁"></a>线程死锁</h3><p>C++ 中的线程死锁是指两个或多个线程在执行过程中，因互相等待对方持有的资源（如互斥锁）而永远阻塞的状态。典型场景是线程 A 持有锁 1 并等待锁 2，而线程 B 持有锁 2 并等待锁 1，双方都无法继续推进。死锁的本质是资源竞争和循环依赖，会导致程序完全停滞，既不会崩溃也不会自动恢复。避免死锁的常见策略包括：按固定顺序获取锁、使用超时机制（如 <code>try_lock()</code>）、采用分层锁或使用 RAII 管理锁的持有时间，从而打破循环等待条件。</p><table><thead><tr><th>死锁的四个必要条件</th><th>代码中的体现</th></tr></thead><tbody><tr><td>互斥</td><td><code>std::mutex</code> 保证同一时间只有一个线程能持有锁</td></tr><tr><td>持有并等待</td><td>线程 1 持有 lock1 等待 lock2；线程 2 持有 lock2 等待 lock1</td></tr><tr><td> 不可抢占</td><td><code>std::mutex</code> 不能被强制释放，只能由持有者主动 unlock（解锁）</td></tr><tr><td>循环等待</td><td>线程 1 → lock1 → lock2，线程 2 → lock2 → lock1，形成环路</td></tr></tbody></table><h4 id="触发线程死锁的案例"><a href="#触发线程死锁的案例" class="headerlink" title="触发线程死锁的案例"></a>触发线程死锁的案例</h4><p>下面这段代码会触发线程死锁，因为<strong>两个线程以相反的顺序获取锁</strong>：线程 1 先锁 lock1 再等待 lock2，线程 2 先锁 lock2 再等待 lock1，导致两个线程各自持有一个锁并等待对方释放另一个锁，形成循环等待，结果谁也无法继续执行，程序永久阻塞。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func1</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 wait to get locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 1</span></span><br><span class="line">        lock1.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already got locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待一段时间再获锁 2</span></span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 wait to get locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 2</span></span><br><span class="line">        lock2.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already got locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 2</span></span><br><span class="line">        lock2.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already released locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 1</span></span><br><span class="line">        lock1.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already released locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func2</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 wait to get locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 2</span></span><br><span class="line">        lock2.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already got locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待一段时间再获锁 1</span></span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 wait to get locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 1</span></span><br><span class="line">        lock1.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already got locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 1</span></span><br><span class="line">        lock1.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already released locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 2</span></span><br><span class="line">        lock2.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already released locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::mutex lock1;  <span class="comment">// 锁 1</span></span><br><span class="line">    std::mutex lock2;  <span class="comment">// 锁 2</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 1</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(&amp;MyClass::func1, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 2</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(&amp;MyClass::func2, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 1 执行结束</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 2 执行结束</span></span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">thread 1 wait to get locker 1</span><br><span class="line">thread 1 already got locker 1</span><br><span class="line">thread 2 wait to get locker 2</span><br><span class="line">thread 2 already got locker 2</span><br><span class="line">thread 1 wait to get locker 2</span><br><span class="line">thread 2 wait to get locker 1</span><br><span class="line">（程序卡住，永远不会打印后续的 "already got" 消息）</span><br></pre></td></tr></tbody></table></figure><h4 id="解决线程死锁的案例"><a href="#解决线程死锁的案例" class="headerlink" title="解决线程死锁的案例"></a>解决线程死锁的案例</h4><blockquote><p>解决线程死锁的方案</p></blockquote><p>在 C++ 中，解决线程死锁的方案有以下几种：</p><ul><li><p>(1) 破坏互斥条件</p><ul><li>使用读写锁（<code>std::shared_mutex</code>）允许多个读</li><li>使用无锁编程（<code>std::atomic</code>）</li></ul></li><li><p>(2) 破坏持有并等待条件</p><ul><li><code>std::lock()</code> 一次性获取所有锁</li><li>预先分配所有资源后再执行</li></ul></li><li><p> (3) 破坏不可抢占条件</p><ul><li>使用 <code>std::timed_mutex</code> + <code>try_lock_for()</code> 设置超时</li><li>超时后释放已持有锁，重试</li></ul></li><li><p> (4) 破坏循环等待条件</p><ul><li>所有线程以相同顺序获取锁（最常用）</li><li>为锁分配层级，按层级顺序锁定</li></ul></li><li><p> (5) 辅助手段</p><ul><li>最小化临界区代码</li><li>减少锁的数量，合并临界区</li><li>使用 <code>std::lock_guard</code> / <code>std::unique_lock</code> 自动释放锁</li><li>使用死锁检测工具（静态分析）</li></ul></li></ul><table><thead><tr><th>避免死锁的最常见方法</th><th>破坏的条件</th><th>复杂度</th><th>性能</th><th>适用场景</th></tr></thead><tbody><tr><td>统一获取锁的顺序</td><td>循环等待</td><td>低</td><td>高</td><td>锁数量少，顺序明确</td></tr><tr><td>一次性获取所有锁（<code>std::lock()</code>）</td><td>循环等待</td><td>低</td><td>中</td><td>多锁场景，推荐优先使用</td></tr><tr><td>加入超时等待机制（<code>std::timed_mutex</code> + <code>try_lock_for()</code>）</td><td>持有并等待</td><td>高</td><td>低</td><td>实时系统，要求响应性</td></tr></tbody></table><div class="admonition warning"><p class="admonition-title">使用 std::lock () 一次性获取多个锁</p><p>在 C++ 中，<code>std::lock()</code> 能够一次性锁住两个或两个以上的互斥量（<code>std::mutex</code>），至少两个，一个不行。它的核心优势是<strong>从根本上避免了因锁的顺序不一致而导致的死锁风险</strong>。工作原理是：如果其中某个互斥量没锁住，它就会一直等待，直到所有互斥量都锁住才会继续执行；关键是它保证了要么全部锁住，要么全部没锁住；如果只锁住了其中一个互斥量，而另一个互斥量没锁成功，它会立即把已经锁住的互斥量释放掉。</p></div><h5 id="线程死锁解决案例一"><a href="#线程死锁解决案例一" class="headerlink" title="线程死锁解决案例一"></a>线程死锁解决案例一</h5><ul><li>线程死锁解决方案：统一获取锁的顺序 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func1</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 wait to get locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 1</span></span><br><span class="line">        lock1.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already got locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待一段时间再获锁 2</span></span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 wait to get locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 2</span></span><br><span class="line">        lock2.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already got locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 2</span></span><br><span class="line">        lock2.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already released locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 1</span></span><br><span class="line">        lock1.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already released locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func2</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 wait to get locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 1</span></span><br><span class="line">        lock1.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already got locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待一段时间再获锁 2</span></span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 wait to get locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 等待获取锁 2</span></span><br><span class="line">        lock2.<span class="built_in">lock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already got locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 2</span></span><br><span class="line">        lock2.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already released locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放锁 1</span></span><br><span class="line">        lock1.<span class="built_in">unlock</span>();</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already released locker 1"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::mutex lock1;  <span class="comment">// 锁 1</span></span><br><span class="line">    std::mutex lock2;  <span class="comment">// 锁 2</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 1</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(&amp;MyClass::func1, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 2</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(&amp;MyClass::func2, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 1 执行结束</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 2 执行结束</span></span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">thread 1 wait to get locker 1</span><br><span class="line">thread 1 already got locker 1</span><br><span class="line">thread 2 wait to get locker 1</span><br><span class="line">thread 1 wait to get locker 2</span><br><span class="line">thread 1 already got locker 2</span><br><span class="line">thread 1 already released locker 2</span><br><span class="line">thread 1 already released locker 1</span><br><span class="line">thread 2 already got locker 1</span><br><span class="line">thread 2 wait to get locker 2</span><br><span class="line">thread 2 already got locker 2</span><br><span class="line">thread 2 already released locker 2</span><br><span class="line">thread 2 already released locker 1</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h5 id="线程死锁解决案例二"><a href="#线程死锁解决案例二" class="headerlink" title="线程死锁解决案例二"></a>线程死锁解决案例二</h5><div class="admonition warning"><p class="admonition-title">特别注意</p><p>使用 <code>std::lock()</code> 锁住多个互斥量（<code>std::mutex</code>）时，如果不使用 <code>std::lock_guard</code> 或 <code>std::unique_lock</code>，则必须手动调用 <code>std::mutex</code> 的 <code>unlock()</code> 释放锁。强烈建议采用 RAII 方式管理锁，此时需要配合 <code>std::adopt_lock</code> 参数，这个参数的作用是告诉 <code>std::lock_guard</code> 或 <code>std::unique_lock</code>：互斥量已经被 <code>std::lock()</code> 锁住了，不要在尝试构造时加锁，只需在析构时自动解锁即可。这样做不仅能在作用域结束时自动释放锁，避免因忘记解锁而引发的资源泄漏，还能在临界区抛出异常时保证锁被正确释放，从而大幅提升代码的安全性和健壮性，同时减少手动管理带来的出错风险。</p></div><ul><li>线程死锁解决方案：使用 <code>std::lock()</code> 一次性获取所有锁 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func1</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 wait to get locker 1 and locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 一次性锁住两个互斥量</span></span><br><span class="line">        std::<span class="built_in">lock</span>(lock1, lock2);</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already got locker 1 and locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 使用 std::adopt_lock 表明锁已经持有</span></span><br><span class="line">        <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">g1</span><span class="params">(lock1, std::adopt_lock)</span></span>;</span><br><span class="line">        <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">g2</span><span class="params">(lock2, std::adopt_lock)</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 模拟业务执行耗时（临界区代码）</span></span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 1 already released locker 1 and locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// std::lock_guard 析构时会自动释放锁</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func2</span><span class="params">()</span> </span>{</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 wait to get locker 1 and locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 一次性锁住两个互斥量（顺序与 func1 不同也没关系）</span></span><br><span class="line">        std::<span class="built_in">lock</span>(lock1, lock2);</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already got locker 1 and locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 使用 std::adopt_lock 表明锁已经持有</span></span><br><span class="line">        <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">g1</span><span class="params">(lock1, std::adopt_lock)</span></span>;</span><br><span class="line">        <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">g2</span><span class="params">(lock2, std::adopt_lock)</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 模拟业务执行耗时（临界区代码）</span></span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line"></span><br><span class="line">        std::cout &lt;&lt; <span class="string">"thread 2 already released locker 1 and locker 2"</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// std::lock_guard 析构时会自动释放锁</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::mutex lock1;  <span class="comment">// 锁 1</span></span><br><span class="line">    std::mutex lock2;  <span class="comment">// 锁 2</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 1</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(&amp;MyClass::func1, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动线程 2</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(&amp;MyClass::func2, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 1 执行结束</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待线程 2 执行结束</span></span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">main thread start.</span><br><span class="line">thread 1 wait to get locker 1 and locker 2</span><br><span class="line">thread 1 already got locker 1 and locker 2</span><br><span class="line">thread 2 wait to get locker 1 and locker 2</span><br><span class="line">thread 1 already released locker 1 and locker 2</span><br><span class="line">thread 2 already got locker 1 and locker 2</span><br><span class="line">thread 2 already released locker 1 and locker 2</span><br><span class="line">main thread end.</span><br></pre></td></tr></tbody></table></figure><h3 id="unique-lock"><a href="#unique-lock" class="headerlink" title="unique_lock"></a>unique_lock</h3><p><code>unique_lock</code> 是一个通用互斥锁（<code>std::mutex</code>）包装器，通过 RAII（资源获取即初始化）机制自动管理锁的获取与释放，从根本上避免因异常或返回导致的死锁。与简单的 <code>lock_guard</code> 相比，它提供了延迟加锁、尝试加锁、提前解锁和所有权转移等灵活特性，但这也带来稍大的内存与性能开销。其最关键的专属用途是必须配合 <code>condition_variable</code> 使用，因为 <code>wait()</code> 操作需要动态释放和重新获取锁。简言之，<code>unique_lock</code> 是复杂锁定场景与线程同步通信的核心工具，而在只需基础互斥保护的简单场景下，优先考虑轻量的 <code>lock_guard</code> 或 <code>scoped_lock</code>（C++ 17 开始支持）。</p><h4 id="unique-lock-的核心作用"><a href="#unique-lock-的核心作用" class="headerlink" title="unique_lock 的核心作用"></a>unique_lock 的核心作用</h4><ul><li><p>核心作用：RAII（资源获取即初始化）锁管理</p><ul><li><code>unique_lock</code> 的核心思想是利用对象的构造函数和析构函数，自动管理互斥量的加锁与解锁。<ul><li>构造时：调用 <code>mutex.lock()</code>（或尝试加锁）。</li><li>析构时：自动调用 <code>mutex.unlock()</code>。</li></ul></li><li>这带来了最直接的好处：即使代码在执行过程中抛出异常或提前 <code>return</code>，<code>unique_lock</code> 的析构函数也一定会被执行，从而彻底避免死锁（因为锁一定会被释放）。</li></ul></li><li><p>相较于 <code>lock_guard</code> 的增强（独特优势）</p><ul><li>虽然 <code>lock_guard</code> 也能做到 RAII，但 <code>unique_lock</code> 更加灵活。它弥补了 <code>std::mutex</code> 原生接口和 <code>lock_guard</code> 的不足，提供了以下关键特性：</li><li>延迟加锁（Deferred Locking）：构造时可以不立即加锁，而是稍后在需要时手动调用 <code>lock()</code>。这在需要将锁的创建与加锁分离时非常有用。</li><li>尝试加锁（Try Locking）：支持 <code>try_lock()</code>, <code>try_lock_for()</code>, <code>try_lock_until()</code>，允许线程在尝试获取锁失败时去做其他事情，而不是阻塞等待，提高了程序的响应性。</li><li>所有权转移（Move Semantics）：<code>unique_lock</code> 是可移动的（但不可拷贝）。你可以将锁的所有权从一个对象转移给另一个对象，或者从函数中返回一个已加锁的 <code>unique_lock</code>，这极大地方便了在函数间传递锁的管控权。</li><li>提前解锁（Manual Unlock）：在锁的作用域结束前，可以手动调用 <code>unlock()</code>。这在需要保护一段临界区极短的代码，且后续还有耗时操作时非常有用 —— 提前释放锁以减少锁持有的时间，提高并发度。</li></ul></li></ul><hr><ul><li><p>与条件变量（<code>condition_variable</code>）配合的强制性要求</p><ul><li>这是 <code>unique_lock</code> 最重要的专属职责。C++ 标准规定，<code>condition_variable</code> 的 <code>wait()</code> 系列函数只能接受 <code>unique_lock</code>，不能接受 <code>lock_guard</code>。</li><li>原因在于 <code>wait()</code> 内部需要原子的释放锁并进入阻塞状态，并且在被唤醒后需要重新获取锁。只有 <code>unique_lock</code> 支持这种动态的加锁 / 解锁 / 所有权转移操作，而 <code>lock_guard</code> 过于死板，无法满足这一需求。</li></ul></li><li><p><code>unique_lock</code> 的性能与开销权衡</p><ul><li>需要注意的是，灵活性是有代价的。<code>unique_lock</code> 内部通常需要维护一个标志位来记录当前是否持有锁（以便在析构时决定是否解锁），因此它的内存占用比 <code>lock_guard</code> 稍大，性能开销也稍高。</li><li>如果只需要简单的 RAII 加锁 / 解锁，且不需要上述灵活性：建议使用 <code>lock_guard</code>（或 C++17 的 <code>scoped_lock</code>），它们更轻量且意图更明确。</li><li>如果需要延迟加锁、尝试加锁、移动所有权或配合条件变量：请使用 <code>unique_lock</code>。</li></ul></li></ul><h4 id="unique-lock-的构造参数"><a href="#unique-lock-的构造参数" class="headerlink" title="unique_lock 的构造参数"></a>unique_lock 的构造参数</h4><p>在 C++ 中，<code>unique_lock</code> 的构造函数最常用的参数主要有以下几种：</p><ul><li><p><code>std::adopt_lock_t</code>（接管锁）</p><ul><li>假设当前线程已经成功获取了互斥量的锁（比如使用 <code>std::lock()</code>），<code>unique_lock</code> 在构造时不再尝试加锁，而是直接接管该锁的所有权，并在析构时自动解锁。</li><li>示例：<code>std::unique_lock&lt;std::mutex&gt; lk(mtx, std::adopt_lock);</code></li></ul></li><li><p><code>std::defer_lock_t</code>（延迟加锁）</p><ul><li><code>unique_lock</code> 在构造时不加锁，仅持有互斥量的引用。后续需要时再通过调用 <code>lk.lock()</code> 手动加锁。常用于将对象创建与加锁时机分离。  </li><li>示例：<code>std::unique_lock&lt;std::mutex&gt; lk(mtx, std::defer_lock);</code></li></ul></li><li><p><code>std::try_to_lock_t</code>（尝试加锁）</p><ul><li><code>unique_lock</code> 在构造时调用 <code>mutex.try_lock()</code> 尝试加锁。加锁结果可通过 <code>lk.owns_lock()</code> 判断，即使加锁失败也不会阻塞线程，允许程序做其他处理。  </li><li>示例：<code>std::unique_lock&lt;std::mutex&gt; lk(mtx, std::try_to_lock);</code></li></ul></li><li><p>超时时间（时间点或等待时长）</p><ul><li><code>unique_lock</code> 在构造时尝试加锁，但若无法立即获取，则阻塞等待直至超时。支持相对时长（<code>std::chrono::duration</code>）或绝对时间点（<code>std::chrono::time_point</code>）。</li><li>示例：<ul><li><code>std::unique_lock&lt;std::timed_mutex&gt; lk(mtx, std::chrono::seconds(1));</code>（等待 1 秒）</li><li><code>std::unique_lock&lt;std::timed_mutex&gt; lk(mtx, std::chrono::steady_clock::now() + std::chrono::seconds(1));</code></li></ul></li></ul></li><li><p>默认情况（无参数）：</p><ul><li><code>unique_lock</code> 在构造时立即调用 <code>mutex.lock()</code>，阻塞当前线程直到获取锁为止。</li></ul></li></ul><div class="admonition warning"><p class="admonition-title">特别注意</p><p>使用 <code>unique_lock</code> 时需注意，除默认构造函数外，以上参数必须与互斥量类型（<code>std::mutex</code>）匹配（例如，超时参数要求互斥量支持 <code>try_lock_for</code> 或 <code>try_lock_until</code>，即 <code>std::timed_mutex</code> 等）。</p></div><h4 id="unique-lock-的成员函数"><a href="#unique-lock-的成员函数" class="headerlink" title="unique_lock 的成员函数"></a>unique_lock 的成员函数</h4><p><code>unique_lock</code> 的常用成员函数主要分为锁操作、状态查询和所有权管理三类：</p><ul><li><p>锁操作（加锁 / 解锁）</p><ul><li><code>lock()</code>：手动加锁。若互斥量已被其他线程持有，则阻塞等待，需在未持有锁时调用。</li><li><code>try_lock()</code>：尝试加锁。立即返回，成功返回 <code>true</code>，失败返回 <code>false</code>，不阻塞线程。</li><li><code>try_lock_for(duration)</code>：尝试加锁，等待指定的相对时长（需互斥量支持 <code>try_lock_for()</code>，如 <code>std::timed_mutex</code>）。</li><li><code>try_lock_until(time_point)</code>：尝试加锁，等待直到指定的绝对时间点（需互斥量支持 <code>try_lock_until</code>）。</li><li><code>unlock()</code>：手动解锁。通常用于提前释放锁，减少临界区持有时间，需确保当前线程持有锁。</li></ul></li><li><p>锁状态查询</p><ul><li><code>owns_lock()</code>：返回 <code>bool</code>，表示当前是否持有锁。常用于 <code>try_lock()</code> 系列操作后判断是否成功获取锁。</li><li><code>operator bool()</code>：与 <code>owns_lock()</code> 功能相同，允许在条件判断中直接使用对象（如 <code>if (lk) { ... }</code>）。</li><li><code>mutex()</code>：返回指向所管理互斥量的指针（不常用，一般用于代码调试）。</li></ul></li><li><p>所有权管理</p><ul><li><code>release()</code>：释放所有权，即解除 <code>unique_lock</code> 与互斥量（<code>std::mutex</code>）的关联，但不解锁互斥量。<strong>返回互斥量指针，调用者需负责后续手动解锁</strong>。常用于转移锁管理责任。</li><li>移动语义（非成员函数风格）：<code>unique_lock</code> 可移动（包括移动构造函数和移动赋值运算符），允许将锁的所有权从一个对象转移给另一个对象。</li></ul></li></ul><hr><ul><li>成员函数的使用案例 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">std::timed_mutex mtx;</span><br><span class="line"><span class="function">std::unique_lock&lt;std::timed_mutex&gt; <span class="title">lk</span><span class="params">(mtx, std::defer_lock)</span></span>; <span class="comment">// 延迟加锁</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 尝试获取锁，最多等待 100 毫秒</span></span><br><span class="line"><span class="keyword">if</span> (lk.<span class="built_in">try_lock_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">100</span>))) {</span><br><span class="line">    <span class="comment">// 成功获取锁，操作共享数据</span></span><br><span class="line">    lk.<span class="built_in">unlock</span>();  <span class="comment">// 提前释放锁</span></span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line">    <span class="comment">// 超时等待未获得锁，做其他处理</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li><code>unique_lock</code> 的 <code>lock()</code> 和 <code>unlock()</code> 必须成对使用，且调用前需确认当前锁状态，否则可能导致未定义行为（如重复加锁或解锁未持有的锁）。</li><li><code>unique_lock</code> 调用 <code>release()</code> 后，<code>unique_lock</code> 析构时不会再自动解锁，调用者需负责后续手动解锁，使用时需格外谨慎。</li><li><code>unique_lock</code> 在大部分场景下，开发者无需手动调用 <code>unlock()</code>，利用 RAII 自动析构即可；手动解锁主要用于优化性能（提前释放锁）。</li></ul></div><h4 id="unique-lock-的所有权传递"><a href="#unique-lock-的所有权传递" class="headerlink" title="unique_lock 的所有权传递"></a>unique_lock 的所有权传递</h4><p>在 C++ 中，<code>unique_lock</code> 遵循移动语义，其互斥量（<code>std::mutex</code>）的所有权<strong>可以转移，但不能复制</strong>。这意味着一个 <code>unique_lock</code> 对象可以通过移动构造函数或移动赋值运算符将其对互斥量的管理权交给另一个 <code>unique_lock</code> 对象，转移后原 <code>unique_lock</code> 对象不再持有锁，从而避免多个 <code>unique_lock</code> 对象同时管理同一互斥量。这种设计既保证了资源管理的唯一性，又提供了在不同作用域或函数间传递锁所有权的灵活性。</p><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li><code>unique_lock</code> 对象不能被复制：如果试图写 <code>std::unique_lock&lt;std::mutex&gt; lock2 = lock;</code>（拷贝），编译器会报错。</li><li><code>unique_lock</code> 所有权转移后，原 <code>unique_lock</code> 对象会变为空状态（不持有任何锁），其 <code>owns_lock()</code> 返回 <code>false</code>，析构时不会尝试解锁。</li><li><code>unique_lock</code> 所有权转移后，不能再对原 <code>unique_lock</code> 对象调用 <code>lock()</code> 和 <code>unlock()</code> 等方法，否则会导致未定义行为（程序崩溃、运行时抛出异常等）。</li></ul></div><ul><li>案例代码一 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::mutex mx;</span><br><span class="line">    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock1</span><span class="params">(mx)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 非法代码（编译出错），所有权是不允许复制的</span></span><br><span class="line">    <span class="comment">// std::unique_lock&lt;std::mutex&gt; lock2(lock1);</span></span><br><span class="line">    <span class="comment">// std::unique_lock&lt;std::mutex&gt; lock2 = lock1;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 合法代码（编译通过），所有权是允许转移的</span></span><br><span class="line">    <span class="comment">// 所有权转移后，lock1 的互斥量指向为空（不持有任何锁），而 lock2 的互斥量指向为 mx</span></span><br><span class="line">    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock2</span><span class="params">(std::move(lock1))</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>案例代码二 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">getLock</span><span class="params">(std::mutex &amp;mx)</span> </span>{</span><br><span class="line">    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(mx)</span></span>;</span><br><span class="line">    <span class="comment">// 函数结果返回 unique_lock 局部对象是可以的，系统会生成临时的 unique_lock 对象，并调用 unique_lock 的移动构造函数</span></span><br><span class="line">    <span class="keyword">return</span> lock;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::mutex mx;</span><br><span class="line">    std::unique_lock&lt;std::mutex&gt; lock = <span class="built_in">getLock</span>(mx);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="unique-lock-的完整使用案例"><a href="#unique-lock-的完整使用案例" class="headerlink" title="unique_lock 的完整使用案例"></a>unique_lock 的完整使用案例</h4><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 将收到的玩家命令写入队列</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">inMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; ++i) {</span><br><span class="line">            {</span><br><span class="line">                <span class="comment">// 加锁（出了作用域后会自动解锁）</span></span><br><span class="line">                <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 插入队列</span></span><br><span class="line">                msgRecvQueue.<span class="built_in">push_back</span>(i);</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟网络收包间隔</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 更新程序停止标记</span></span><br><span class="line">        stop = <span class="literal">true</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从队列中读取玩家命令</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">outMsgRecvQueue</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line">            <span class="keyword">int</span> command = <span class="number">-1</span>;</span><br><span class="line"></span><br><span class="line">            {</span><br><span class="line">                <span class="comment">// 加锁（出了作用域后会自动解锁）</span></span><br><span class="line">                <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(msgRecvQueueMutex)</span></span>;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 判断程序停止标记</span></span><br><span class="line">                <span class="keyword">if</span> (stop &amp;&amp; msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                }</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 操作队列</span></span><br><span class="line">                <span class="keyword">if</span> (!msgRecvQueue.<span class="built_in">empty</span>()) {</span><br><span class="line">                    <span class="comment">// 取出队列元素</span></span><br><span class="line">                    command = msgRecvQueue.<span class="built_in">front</span>();</span><br><span class="line">                    <span class="comment">// 移除队列元素</span></span><br><span class="line">                    msgRecvQueue.<span class="built_in">pop_front</span>();</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 打印</span></span><br><span class="line">            <span class="keyword">if</span> (command != <span class="number">-1</span>) {</span><br><span class="line">                std::cout &lt;&lt; <span class="string">"已处理玩家命令: "</span> &lt;&lt; command &lt;&lt; std::endl;</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟业务执行耗时</span></span><br><span class="line">            std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1</span>));</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::list&lt;<span class="keyword">int</span>&gt; msgRecvQueue;   <span class="comment">// 消息队列（共享数据）</span></span><br><span class="line">    std::mutex msgRecvQueueMutex;  <span class="comment">// 保护消息队列线程安全的互斥锁</span></span><br><span class="line">    std::<span class="keyword">atomic_bool</span> stop{<span class="literal">false</span>};  <span class="comment">// 程序停止标记</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread start."</span> &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 局部变量</span></span><br><span class="line">    MyClass mc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动写线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_write</span><span class="params">(&amp;MyClass::inMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建并启动读线程</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t_read</span><span class="params">(&amp;MyClass::outMsgRecvQueue, &amp;mc)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待写线程执行完毕</span></span><br><span class="line">    t_write.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待读线程执行完毕</span></span><br><span class="line">    t_read.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"main thread end."</span> &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">已处理玩家命令: 0</span><br><span class="line">已处理玩家命令: 1</span><br><span class="line">已处理玩家命令: 2</span><br><span class="line">已处理玩家命令: 3</span><br><span class="line">......</span><br><span class="line">已处理玩家命令: 996</span><br><span class="line">已处理玩家命令: 997</span><br><span class="line">已处理玩家命令: 998</span><br><span class="line">已处理玩家命令: 999</span><br></pre></td></tr></tbody></table></figure><h3 id="单例设计模式"><a href="#单例设计模式" class="headerlink" title="单例设计模式"></a>单例设计模式</h3><p>在 C++ 中使用单例模式时，一般不用管单例对象的内存释放问题，也就是不需要编写自定义的析构函数。</p><div class="admonition note"><p class="admonition-title">扩展阅读</p><ul><li><a href="/posts/66192ea1.html">C++ 设计模式使用教程之一</a></li><li><a href="/posts/bcb943df.html">C++ 设计模式使用教程之二</a></li></ul></div><h4 id="单例设计模式实现"><a href="#单例设计模式实现" class="headerlink" title="单例设计模式实现"></a>单例设计模式实现</h4><h5 id="饿汉单例模式"><a href="#饿汉单例模式" class="headerlink" title="饿汉单例模式"></a>饿汉单例模式</h5><p>饿汉单例模式是指还没有获取单例对象，单例对象就已经创建完成（初始化）了。值得一提的是，<strong>饿汉单例模式是线程安全的</strong>，因为单例对象在 <code>main()</code> 函数执行前就已经初始化完成（静态初始化），所以是线程安全的（因为对象已经存在，只是返回指针）。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除赋值操作运算符</span></span><br><span class="line">    MyClass&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 获取单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> MyClass* <span class="title">getInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> m_instance;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 静态成员变量</span></span><br><span class="line">    <span class="keyword">static</span> MyClass* m_instance;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（单例对象），分配内存空间</span></span><br><span class="line">MyClass* MyClass::m_instance = <span class="keyword">new</span> <span class="built_in">MyClass</span>();</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 获取单例对象</span></span><br><span class="line">    MyClass* mc = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">    MyClass* mc2 = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断地址是否相同</span></span><br><span class="line">    cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc2 &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">"equals: "</span> &lt;&lt; (mc == mc2 ? <span class="string">"true"</span> : <span class="string">"false"</span>) &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MyClass()</span><br><span class="line">address: 0x55a0fe5b4eb0</span><br><span class="line">address: 0x55a0fe5b4eb0</span><br><span class="line">equals: true</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">资源泄漏问题</p><ul><li>上面饿汉单例模式的代码存在资源泄漏问题，不推荐使用（建议使用静态局部变量实现单例模式），原因如下：</li><li>(1) 对象在堆上分配内存，但从未被 <code>delete</code>，内存不会被释放；</li><li>(2) 由于析构函数是 <code>private</code>，即使开发者想手动 <code>delete</code> 也做不到；</li><li>(3) 当程序退出时，对象的析构函数不会被调用，资源永远无法得到释放。</li></ul></div><h5 id="懒汉单例模式"><a href="#懒汉单例模式" class="headerlink" title="懒汉单例模式"></a>懒汉单例模式</h5><p>懒汉单例模式是指在第一次被使用时才创建单例对象，而不是程序启动时就创建。<strong>特别注意，懒汉单例模式不一定是线程安全的，由具体的代码实现决定。</strong>线程安全的懒汉单例模式有以下几种实现方式：</p><table><thead><tr><th>实现方式</th><th>是否线程安全</th><th>防止指令重排</th><th>推荐指数</th></tr></thead><tbody><tr><td>静态局部变量</td><td>✅（需要 C++ 11 及以上的编译器）</td><td>✅</td><td>⭐⭐⭐⭐（最推荐，最简洁）</td></tr><tr><td><code>mutex</code> + <code>atomic</code> + <code>DCL</code></td><td>✅（兼容 C++ 11 以下的编译器，如 C++ 98 / C++ 03</td><td>✅</td><td>⭐⭐⭐（适合对底层控制有要求的场景）</td></tr></tbody></table><blockquote><p>C++ 基于静态局部变量实现懒汉单例模式</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除赋值操作运算符</span></span><br><span class="line">    MyClass&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 获取单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> MyClass* <span class="title">getInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="comment">// 静态局部变量（线程安全）</span></span><br><span class="line">        <span class="keyword">static</span> MyClass instance;</span><br><span class="line">        <span class="keyword">return</span> &amp;instance;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 获取单例对象</span></span><br><span class="line">    MyClass* mc = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">    MyClass* mc2 = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断地址是否相同</span></span><br><span class="line">    cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc2 &lt;&lt; endl;</span><br><span class="line">    cout &lt;&lt; <span class="string">"equals: "</span> &lt;&lt; (mc == mc2 ? <span class="string">"true"</span> : <span class="string">"false"</span>) &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">MyClass()</span><br><span class="line">address: 0x55b578666192</span><br><span class="line">address: 0x55b578666192</span><br><span class="line">equals: true</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><blockquote><p>C++ 基于 DCL（双重检查锁）实现懒汉单例模式</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除赋值操作运算符</span></span><br><span class="line">    MyClass&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 获取单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> MyClass* <span class="title">getInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="comment">// 第一次获取单例对象</span></span><br><span class="line">        MyClass* ptr = m_instance.<span class="built_in">load</span>(std::memory_order_acquire);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 第一次检测单例对象是否为空</span></span><br><span class="line">        <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">            <span class="comment">// 获取互斥锁</span></span><br><span class="line">            <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第二次获取单例对象</span></span><br><span class="line">            ptr = m_instance.<span class="built_in">load</span>(std::memory_order_relaxed);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第二次检测单例对象是否为空</span></span><br><span class="line">            <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">                <span class="comment">// 初始化单例对象</span></span><br><span class="line">                ptr = <span class="keyword">new</span> <span class="built_in">MyClass</span>();</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 设置单例对象</span></span><br><span class="line">                m_instance.<span class="built_in">store</span>(ptr, std::memory_order_release);</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 返回单例对象</span></span><br><span class="line">        <span class="keyword">return</span> ptr;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 销毁单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">destroyInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="comment">// 获取互斥锁</span></span><br><span class="line">        <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取单例对象</span></span><br><span class="line">        MyClass* ptr = m_instance.<span class="built_in">exchange</span>(<span class="literal">nullptr</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 释放单例对象</span></span><br><span class="line">        <span class="keyword">if</span> (ptr != <span class="literal">nullptr</span>) {</span><br><span class="line">            <span class="keyword">delete</span> ptr;</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">static</span> std::mutex m_mutex;                <span class="comment">// 互斥锁（静态变量）</span></span><br><span class="line">    <span class="keyword">static</span> std::atomic&lt;MyClass*&gt; m_instance;  <span class="comment">// 单例对象（静态变量）</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（互斥锁）</span></span><br><span class="line">std::mutex MyClass::m_mutex;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（单例对象）</span></span><br><span class="line"><span class="function">std::atomic&lt;MyClass*&gt; <span class="title">MyClass::m_instance</span><span class="params">(<span class="literal">nullptr</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 获取单例对象</span></span><br><span class="line">    MyClass* mc = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">    MyClass* mc2 = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断地址是否相同</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc2 &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"equals: "</span> &lt;&lt; (mc == mc2 ? <span class="string">"true"</span> : <span class="string">"false"</span>) &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放单例对象</span></span><br><span class="line">    MyClass::<span class="built_in">destroyInstance</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">MyClass</span>()</span><br><span class="line">address: <span class="number">0x55ec9d5bbeb0</span></span><br><span class="line">address: <span class="number">0x55ec9d5bbeb0</span></span><br><span class="line">equals: <span class="literal">true</span></span><br><span class="line">~<span class="built_in">MyClass</span>()</span><br></pre></td></tr></tbody></table></figure><hr><p>在写单例模式时，很多开发者喜欢提供一个类似 <code>destroyInstance()</code> 的方法来手动释放内存。开发者可能觉得自己在里面加了互斥锁（Mutex）和原子操作（Atomic）就已经万无一失了，但实际上，这在多线程环境下埋下了一个致命的隐患。</p><ul><li><p>假设有以下并发场景：</p><ul><li>(1) 线程 A 调用 <code>getInstance()</code>，通过了空指针检查，成功拿到了单例对象的指针 <code>ptr</code>。</li><li>(2) 线程 B 此时突然调用了 <code>destroyInstance()</code>。因为有锁保护，它成功将内部指针置空，并无情地执行了 <code>delete ptr;</code>。</li><li>(3) 线程 A 根本不知道单例对象已经被销毁。它兴高采烈地拿着手里的 <code>ptr</code> 去调用成员函数（例如 <code>ptr-&gt;doSomething()</code>）。</li></ul></li><li><p>错误原因分析：</p><ul><li>错误结果：线程 A 访问了已经被释放内存的单例对象指针（野指针 / 悬空指针），引发未定义行为，导致程序直接崩溃。</li><li>核心矛盾：锁和原子操作只能保护单例 “指针本身” 的修改是安全的，但它们无法保护其他线程手中已经持有的、指向单例内存的存根。</li><li>最佳实践：在现代 C++（C++11 及以后）中，永远不要尝试手动销毁单例对象。最优雅、最安全的做法是使用静态局部变量来实现单例模式。</li></ul></li><li><p>正常释放单例对象的内存：</p></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除赋值操作运算符</span></span><br><span class="line">    MyClass&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 获取单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> MyClass* <span class="title">getInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="comment">// 第一次获取单例对象</span></span><br><span class="line">        MyClass* ptr = m_instance.<span class="built_in">load</span>(std::memory_order_acquire);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 第一次检测单例对象是否为空</span></span><br><span class="line">        <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">            <span class="comment">// 获取互斥锁</span></span><br><span class="line">            <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第二次获取单例对象</span></span><br><span class="line">            ptr = m_instance.<span class="built_in">load</span>(std::memory_order_relaxed);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第二次检测单例对象是否为空</span></span><br><span class="line">            <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">                <span class="comment">// 初始化单例对象</span></span><br><span class="line">                ptr = <span class="keyword">new</span> <span class="built_in">MyClass</span>();</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 静态局部变量（用于自动释放单例对象的内存）</span></span><br><span class="line">                <span class="keyword">static</span> GcClass gc;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 设置单例对象</span></span><br><span class="line">                m_instance.<span class="built_in">store</span>(ptr, std::memory_order_release);</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 返回单例对象</span></span><br><span class="line">        <span class="keyword">return</span> ptr;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 内部定义的 GC 类，用于自动释放单例对象的内存</span></span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">GcClass</span> {</span></span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">        <span class="comment">// 构造函数</span></span><br><span class="line">        <span class="built_in">GcClass</span>() {</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 析构函数</span></span><br><span class="line">        ~<span class="built_in">GcClass</span>() {</span><br><span class="line">            MyClass* ptr = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">            <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">                <span class="comment">// 释放单例对象的内存</span></span><br><span class="line">                <span class="keyword">delete</span> ptr;</span><br><span class="line">                ptr = <span class="literal">nullptr</span>;</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">    };</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">static</span> std::mutex m_mutex;                <span class="comment">// 互斥锁（静态变量）</span></span><br><span class="line">    <span class="keyword">static</span> std::atomic&lt;MyClass*&gt; m_instance;  <span class="comment">// 单例对象（静态变量）</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（互斥锁）</span></span><br><span class="line">std::mutex MyClass::m_mutex;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（单例对象）</span></span><br><span class="line"><span class="function">std::atomic&lt;MyClass*&gt; <span class="title">MyClass::m_instance</span><span class="params">(<span class="literal">nullptr</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 获取单例对象</span></span><br><span class="line">    MyClass* mc = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">    MyClass* mc2 = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断地址是否相同</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; mc2 &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"equals: "</span> &lt;&lt; (mc == mc2 ? <span class="string">"true"</span> : <span class="string">"false"</span>) &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">MyClass()</span><br><span class="line">address: 0x55c7c52d6eb0</span><br><span class="line">address: 0x55c7c52d6eb0</span><br><span class="line">equals: true</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><h4 id="共享数据问题分析"><a href="#共享数据问题分析" class="headerlink" title="共享数据问题分析"></a>共享数据问题分析</h4><p>在多线程环境中，下面的单例设计模式代码存在严重的线程安全问题（共享数据问题）。这是因为在多线程环境下，下述代码的线程安全问题核心在于 <code>getInstance()</code> 中的 “检查 - 然后 - 操作” 竞态条件：当多个线程同时首次调用 <code>getInstance()</code> 并检测到 <code>m_instance == nullptr</code> 时，它们都会通过 <code>new MyClass()</code> 创建各自独立的实例，导致单例规则被破坏，并且由于析构函数私有，这些多余的对象永远无法被正确释放，造成内存泄漏；同时，<code>new</code> 操作本身涉及内存分配和构造两个步骤，若无内存屏障保护，其他线程可能看到一个未完全构造完成的对象，进而引发未定义行为。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除赋值操作运算符</span></span><br><span class="line">    MyClass&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 获取单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> MyClass* <span class="title">getInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">if</span> (m_instance == <span class="literal">nullptr</span>) {</span><br><span class="line">            <span class="comment">// 初始化单例对象</span></span><br><span class="line">            m_instance = <span class="keyword">new</span> <span class="built_in">MyClass</span>();</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 返回单例对象</span></span><br><span class="line">        <span class="keyword">return</span> m_instance;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">static</span> MyClass* m_instance;  <span class="comment">// 单例对象（静态变量）</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（单例对象）</span></span><br><span class="line"><span class="function">MyClass* <span class="title">MyClass::m_instance</span><span class="params">(<span class="literal">nullptr</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 线程函数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line">    MyClass* ptr = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; ptr &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(run)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(run)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t3</span><span class="params">(run)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待子线程执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">    t3.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下（可以发现 MyClass 类的构造函数被调用了多次，破坏了单例规则）：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">MyClass()</span><br><span class="line">address: 0x7f84e4000b70</span><br><span class="line">MyClass()</span><br><span class="line">address: 0x7f84dc000b70</span><br><span class="line">MyClass()</span><br><span class="line">address: 0x7f84ec000b70</span><br></pre></td></tr></tbody></table></figure><h4 id="共享数据问题解决"><a href="#共享数据问题解决" class="headerlink" title="共享数据问题解决"></a>共享数据问题解决</h4><p>在多线程环境中，可以使用互斥量（<code>mutex</code>）+ 原子操作（<code>atomic</code>） + DCL（双重检查锁）来解决上面单例设计模式代码存在的线程安全问题（共享数据问题）。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;atomic&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;thread&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 私有构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        std::cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; std::endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除赋值操作运算符</span></span><br><span class="line">    MyClass&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyClass&amp;) = <span class="keyword">delete</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 获取单例对象（静态方法）</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> MyClass* <span class="title">getInstance</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="comment">// 第一次获取单例对象</span></span><br><span class="line">        MyClass* ptr = m_instance.<span class="built_in">load</span>(std::memory_order_acquire);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 第一次检测单例对象是否为空</span></span><br><span class="line">        <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">            <span class="comment">// 获取互斥锁</span></span><br><span class="line">            <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(m_mutex)</span></span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第二次获取单例对象</span></span><br><span class="line">            ptr = m_instance.<span class="built_in">load</span>(std::memory_order_relaxed);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 第二次检测单例对象是否为空</span></span><br><span class="line">            <span class="keyword">if</span> (ptr == <span class="literal">nullptr</span>) {</span><br><span class="line">                <span class="comment">// 初始化单例对象</span></span><br><span class="line">                ptr = <span class="keyword">new</span> <span class="built_in">MyClass</span>();</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 设置单例对象</span></span><br><span class="line">                m_instance.<span class="built_in">store</span>(ptr, std::memory_order_release);</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 返回单例对象</span></span><br><span class="line">        <span class="keyword">return</span> ptr;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">static</span> std::mutex m_mutex;                <span class="comment">// 互斥锁（静态变量）</span></span><br><span class="line">    <span class="keyword">static</span> std::atomic&lt;MyClass*&gt; m_instance;  <span class="comment">// 单例对象（静态变量）</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（互斥锁）</span></span><br><span class="line">std::mutex MyClass::m_mutex;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类外初始化静态变量（单例对象）</span></span><br><span class="line"><span class="function">std::atomic&lt;MyClass*&gt; <span class="title">MyClass::m_instance</span><span class="params">(<span class="literal">nullptr</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 线程函数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line">    MyClass* ptr = MyClass::<span class="built_in">getInstance</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">"address: "</span> &lt;&lt; ptr &lt;&lt; std::endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(run)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(run)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t3</span><span class="params">(run)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 等待子线程执行完成</span></span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">    t3.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行的结果如下（可以发现 MyClass 类的构造函数不会被调用多次，单例规则没有被破坏）：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">MyClass()</span><br><span class="line">address: 0x7f44e0000b70</span><br><span class="line">address: 0x7f44e0000b70</span><br><span class="line">address: 0x7f44e0000b70</span><br></pre></td></tr></tbody></table></figure>]]></content>
    
    
    <summary type="html">本文主要介绍 C++ 从入门到精通的内容。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="并发编程" scheme="https://www.techgrow.cn/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    <category term="C++" scheme="https://www.techgrow.cn/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>C++ 从入门到精通之九</title>
    <link href="https://www.techgrow.cn/posts/14325b2f.html"/>
    <id>https://www.techgrow.cn/posts/14325b2f.html</id>
    <published>2025-11-23T13:55:33.000Z</published>
    <updated>2025-11-23T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/332ad818.html">C++ 从入门到精通之一</a>、<a href="/posts/68a580cf.html">C++ 从入门到精通之二</a>、<a href="/posts/9ce17bf9.html">C++ 从入门到精通之三</a></li><li><a href="/posts/d198d85d.html">C++ 从入门到精通之四</a>、<a href="/posts/7246a640.html">C++ 从入门到精通之五</a>、<a href="/posts/80bc7e12.html">C++ 从入门到精通之六</a></li><li><a href="/posts/e1f4608d.html">C++ 从入门到精通之七</a>、<a href="/posts/a9b9dd7e.html">C++ 从入门到精通之八</a>、<a href="/posts/14325b2f.html">C++ 从入门到精通之九</a></li><li><a href="/posts/badb9ea0.html">C++ 从入门到精通之十</a>、<a href="/posts/c961bdb.html">C++ 从入门到精通之十一</a></li></ul><span id="more"></span><h2 id="C-智能指针"><a href="#C-智能指针" class="headerlink" title="C++ 智能指针"></a>C++ 智能指针</h2><h3 id="weak-ptr-智能指针"><a href="#weak-ptr-智能指针" class="headerlink" title="weak_ptr 智能指针"></a>weak_ptr 智能指针</h3><p><code>weak_ptr</code> 是 C++ 中的一个类模板，属于智能指针的一种。<strong>它指向一个由 <code>shared_ptr</code> 管理的对象，但与 <code>shared_ptr</code> 不同的是，<code>weak_ptr</code> 不控制所指向对象的生命周期</strong>。换句话说，<strong>将一个 <code>weak_ptr</code> 绑定到 <code>shared_ptr</code> 上，不会增加引用计数</strong>。因此，<code>weak_ptr</code> 常用于解决 <code>shared_ptr</code> 可能导致的循环引用问题，同时也可以用来监视 <code>shared_ptr</code> 所管理对象的生命周期。如果需要访问 <code>weak_ptr</code> 所指向的对象，可以调用其 <code>lock()</code> 成员函数，它会返回一个有效的 <code>shared_ptr</code>（如果原对象还存在）；若对象已被释放，则返回一个空的 <code>shared_ptr</code>。<strong>值得注意的是，<code>weak_ptr</code> 的构造和析构不会增加或减少所指向对象的引用计数。因此，当最后一个管理该对象的 <code>shared_ptr</code> 被销毁时，对象会照常释放，而不会因为仍有 <code>weak_ptr</code> 指向它而延迟或阻止释放</strong>。在 C++ 的术语中，通常将 <code>shared_ptr</code> 称为强智能指针（强引用），而将 <code>weak_ptr</code> 称为弱智能指针（弱引用、弱共享）。这种 “弱” 正体现在它不参与对象的生命周期管理上。</p><div class="admonition warning"><p class="admonition-title">特别注意</p><p><code>weak_ptr</code> 不是一种独立的智能指针，不能用于操作所指向的对象，所以它看起来像是一个 <code>shared_ptr</code> 的助手（旁观者），能够监视到所指向的对象是否存在。</p></div><h4 id="weak-ptr-的使用语法"><a href="#weak-ptr-的使用语法" class="headerlink" title="weak_ptr 的使用语法"></a>weak_ptr 的使用语法</h4><ul><li><code>weak_ptr</code> 的创建方式 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 直接使用 shared_ptr 来初始化 weak_ptr</span></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line"><span class="function">weak_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">wp</span><span class="params">(sp)</span></span>;</span><br></pre></td></tr></tbody></table></figure><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 或者将 shared_ptr 赋值给 weak_ptr</span></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line">weak_ptr&lt;<span class="keyword">int</span>&gt; wp;</span><br><span class="line">wp = sp;</span><br></pre></td></tr></tbody></table></figure><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 或者将 weak_ptr 赋值给 weak_ptr</span></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line"><span class="function">weak_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">wp</span><span class="params">(sp)</span></span>;</span><br><span class="line">weak_ptr&lt;<span class="keyword">int</span>&gt; wp2;</span><br><span class="line">wp2 = wp;</span><br></pre></td></tr></tbody></table></figure><h4 id="weak-ptr-的常用操作"><a href="#weak-ptr-的常用操作" class="headerlink" title="weak_ptr 的常用操作"></a>weak_ptr 的常用操作</h4><h5 id="lock"><a href="#lock" class="headerlink" title="lock()"></a>lock()</h5><ul><li><code>lock()</code>：作用是检查 <code>weak_ptr</code> 所指向的对象是否存在<ul><li>如果存在，则 <code>lock()</code> 会返回一个指向该对象的 <code>shared_ptr</code>（即所指向对象的强引用计数会加一）</li><li>如果不存在，则 <code>lock()</code> 会返回一个空的 <code>shared_ptr</code>（空指针）</li></ul></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line"><span class="function">weak_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">wp</span><span class="params">(sp)</span></span>;</span><br><span class="line"></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp2 = wp.<span class="built_in">lock</span>();</span><br><span class="line"><span class="keyword">if</span> (sp2 != <span class="literal">nullptr</span>) {</span><br><span class="line">    cout &lt;&lt; <span class="string">"object value is "</span> &lt;&lt; *sp2 &lt;&lt; endl;</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line">    cout &lt;&lt; <span class="string">"object not exist"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">object value is 100</span><br></pre></td></tr></tbody></table></figure><hr><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line"><span class="function">weak_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">wp</span><span class="params">(sp)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 释放 shared_ptr 所管理的对象</span></span><br><span class="line">sp.<span class="built_in">reset</span>();</span><br><span class="line"></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp2 = wp.<span class="built_in">lock</span>();</span><br><span class="line"><span class="keyword">if</span> (sp2 != <span class="literal">nullptr</span>) {</span><br><span class="line">    cout &lt;&lt; <span class="string">"object value is "</span> &lt;&lt; *sp2 &lt;&lt; endl;</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line">    cout &lt;&lt; <span class="string">"object not exist"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">object not exist</span><br></pre></td></tr></tbody></table></figure><h5 id="use-count"><a href="#use-count" class="headerlink" title="use_count()"></a>use_count()</h5><ul><li><code>use_count()</code>：返回有多少个 <code>shared_ptr</code> 指向某个对象（即获取与该 <code>weak_ptr</code> 一起共享对象的其他 <code>shared_ptr</code> 的数量），主要用于代码调试目的 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line"><span class="function">weak_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">wp</span><span class="params">(sp)</span></span>;</span><br><span class="line"><span class="keyword">int</span> count = wp.<span class="built_in">use_count</span>();</span><br><span class="line">cout &lt;&lt; count &lt;&lt; endl;   <span class="comment">// 输出 1</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line"><span class="function">weak_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">wp</span><span class="params">(sp)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 释放 shared_ptr 所管理的对象</span></span><br><span class="line">sp.<span class="built_in">reset</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> count = wp.<span class="built_in">use_count</span>();</span><br><span class="line">cout &lt;&lt; count &lt;&lt; endl;   <span class="comment">// 输出 0</span></span><br></pre></td></tr></tbody></table></figure><h5 id="expired"><a href="#expired" class="headerlink" title="expired()"></a>expired()</h5><ul><li><code>expired()</code>：监视的对象是否已过期<ul><li>已过期是指 <code>weak_ptr</code> 的 <code>use_count()</code> 返回 0（表示该弱智能指针所指向的对象已经不存在）</li></ul></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line"><span class="function">weak_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">wp</span><span class="params">(sp)</span></span>;</span><br><span class="line"><span class="keyword">bool</span> expired = wp.<span class="built_in">expired</span>();</span><br><span class="line">cout &lt;&lt; expired &lt;&lt; endl;  <span class="comment">// 输出 0，表示 false（未过期）</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line"><span class="function">weak_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">wp</span><span class="params">(sp)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 释放 shared_ptr 所管理的对象</span></span><br><span class="line">sp.<span class="built_in">reset</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">bool</span> expired = wp.<span class="built_in">expired</span>();</span><br><span class="line">cout &lt;&lt; expired &lt;&lt; endl;  <span class="comment">// 输出 1，表示 true（已过期）</span></span><br></pre></td></tr></tbody></table></figure><h5 id="reset"><a href="#reset" class="headerlink" title="reset()"></a>reset()</h5><ul><li><code>reset()</code>：将 <code>weak_ptr</code> 设置为空指针，不影响所指向对象的强引用计数（即 <code>shared_ptr</code> 所指向对象的引用计数），但指向该对象的弱引用计数会减少一 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line"><span class="function">weak_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">wp</span><span class="params">(sp)</span></span>;</span><br><span class="line">wp.<span class="built_in">reset</span>();</span><br></pre></td></tr></tbody></table></figure><h4 id="weak-ptr-的指针大小"><a href="#weak-ptr-的指针大小" class="headerlink" title="weak_ptr 的指针大小"></a>weak_ptr 的指针大小</h4><p>在 C++ 中，<code>weak_ptr</code> 和 <code>shared_ptr</code> 的大小是一样的，是裸指针（通过 <code>new</code> 得到的指针）大小的两倍；其中包括两个指针（如下图所示），第一个是指向 T 类型对象的指针，第二个是指向控制块的指针。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> *p;</span><br><span class="line">weak_ptr&lt;<span class="keyword">int</span>&gt; wp;</span><br><span class="line"><span class="keyword">int</span> length1 = <span class="built_in"><span class="keyword">sizeof</span></span>(p);   <span class="comment">// 8 个字节（64 位系统）</span></span><br><span class="line"><span class="keyword">int</span> length2 = <span class="built_in"><span class="keyword">sizeof</span></span>(wp);  <span class="comment">// 16 个字节（64 位系统）</span></span><br></pre></td></tr></tbody></table></figure><p><img data-src="../../../asset/2026/04/weak_ptr_size.png"></p><h3 id="unique-ptr-智能指针"><a href="#unique-ptr-智能指针" class="headerlink" title="unique_ptr 智能指针"></a>unique_ptr 智能指针</h3><p>C++ 中的 <code>unique_ptr</code> 是一种独占所有权的智能指针，同一时刻只能有一个 <code>unique_ptr</code> 指向某个对象；当该 <code>unique_ptr</code> 被销毁时，它所指的对象也会随之自动销毁。</p><h4 id="unique-ptr-的使用语法"><a href="#unique-ptr-的使用语法" class="headerlink" title="unique_ptr 的使用语法"></a>unique_ptr 的使用语法</h4><h5 id="正确的使用语法"><a href="#正确的使用语法" class="headerlink" title="正确的使用语法"></a>正确的使用语法</h5><ul><li><code>unique_ptr</code> 的正确用法<ul><li>第一种正确用法：<ul><li><code>unique_ptr&lt;int&gt; up(new int(100));</code></li><li><code>unique_ptr&lt;MyClass&gt; up(new MyClass());</code></li></ul></li><li>第二种正确用法：<ul><li><code>unique_ptr&lt;int&gt; up = make_unique&lt;int&gt;(100);</code></li><li><code>unique_ptr&lt;MyClass&gt; up = make_unique&lt;MyClass&gt;();</code></li><li>函数模板 <code>make_unique()</code> 是 C++ 14 才开始引入的，C++ 11 并不支持</li></ul></li><li>第三种正确用法：<ul><li><code>unique_ptr&lt;int&gt; up;</code></li><li><code>up</code> 是指向 <code>int</code> 的智能指针，但目前指向的内存地址为空，即属于空指针</li></ul></li></ul></li></ul><h5 id="错误的使用语法"><a href="#错误的使用语法" class="headerlink" title="错误的使用语法"></a>错误的使用语法</h5><ul><li><code>unique_ptr</code> 的错误用法<ul><li>第一种错误用法（代码编译失败）<ul><li><code>unique_ptr&lt;int&gt; up = new int(100);</code></li><li>智能指针是 <code>explicit</code>，不可以进行隐式类型转换，必须用直接初始化形式</li></ul></li><li>第二种错误用法（代码可以正常运行，但不推荐使用）<ul><li><code>int *pi = new int(100); unique_ptr&lt;int&gt; upi(pi);</code></li><li>裸指针与智能指针尽量不要混用，否则会影响代码的健壮性</li></ul></li></ul></li></ul><h4 id="unique-ptr-的错误操作"><a href="#unique-ptr-的错误操作" class="headerlink" title="unique_ptr 的错误操作"></a>unique_ptr 的错误操作</h4><p>在 C++ 中，<code>unique_ptr</code> 是独占式的，不支持拷贝操作（包括拷贝构造函数和拷贝赋值运算符）。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up1</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>))</span></span>;</span><br><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up2</span><span class="params">(up1)</span></span>;   <span class="comment">// 错误写法（编译失败），unique_ptr 是独占式的，不支持拷贝构造函数</span></span><br><span class="line">unique_ptr&lt;<span class="keyword">int</span>&gt; up2 = up1;  <span class="comment">// 错误写法（编译失败），unique_ptr 是独占式的，不支持拷贝构造函数</span></span><br><span class="line"></span><br><span class="line">unique_ptr&lt;<span class="keyword">int</span>&gt; up3;</span><br><span class="line">up3 = up1;  <span class="comment">// 错误写法（编译失败），unique_ptr 是独占式的，不支持拷贝赋值运算符</span></span><br></pre></td></tr></tbody></table></figure><h4 id="unique-ptr-的常用操作"><a href="#unique-ptr-的常用操作" class="headerlink" title="unique_ptr 的常用操作"></a>unique_ptr 的常用操作</h4><h5 id="get"><a href="#get" class="headerlink" title="get()"></a>get()</h5><ul><li><code>get()</code>：返回 <code>unique_ptr</code> 存储的裸指针（通过 <code>new</code> 得到的原始指针）<ul><li><code>unique_ptr</code> 返回的裸指针，千万不要手动 <code>delete</code>，否则会导致程序运行崩溃（未定义行为）</li><li>注意：如果 <code>unique_ptr</code> 释放了所指向对象的内存，那么 <code>get()</code> 返回的裸指针也会变得无效 </li></ul></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>))</span></span>;</span><br><span class="line"><span class="keyword">int</span>* p = up.<span class="built_in">get</span>();</span><br><span class="line">*p = <span class="number">50</span>;</span><br><span class="line">cout &lt;&lt; *p &lt;&lt; endl;  <span class="comment">// 输出 50</span></span><br></pre></td></tr></tbody></table></figure><h5 id="swap"><a href="#swap" class="headerlink" title="swap()"></a>swap()</h5><ul><li><code>swap()</code>：交换两个智能指针所指向的对象 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up1</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>))</span></span>;</span><br><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up2</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">200</span>))</span></span>;</span><br><span class="line">up1.<span class="built_in">swap</span>(up2);  <span class="comment">// 交换两个智能指针指向的对象</span></span><br></pre></td></tr></tbody></table></figure><h5 id="release"><a href="#release" class="headerlink" title="release()"></a>release()</h5><p><code>unique_ptr</code> 的 <code>release()</code> 函数用于放弃智能指针对其所指向对象的控制权，切断两者之间的联系。该函数调用后会返回原始的裸指针，同时会将 <code>unique_ptr</code> 自身置为空指针；返回的裸指针需要由程序员手动管理，可以对其执行 <code>delete</code> 释放内存，也可以将其用来初始化另一个智能指针，或者给另一个智能指针赋值。<strong>特别注意的是，如果调用 <code>release()</code> 后不对返回的裸指针进行任何处理（既不 <code>delete</code>，也不交给另一个智能指针管理），则原本由 <code>unique_ptr</code> 管理的对象将再也无法被自动释放内存，从而导致内存泄漏。</strong></p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up1</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>))</span></span>;</span><br><span class="line"><span class="keyword">int</span>* p = up1.<span class="built_in">release</span>();  <span class="comment">// 释放完成后，up1 置为空指针</span></span><br><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up2</span><span class="params">(p)</span></span>;  <span class="comment">// up2 指向 up1 原来所指向的对象，不会发生内存泄漏</span></span><br></pre></td></tr></tbody></table></figure><h5 id="reset-1"><a href="#reset-1" class="headerlink" title="reset()"></a>reset()</h5><ul><li><code>reset()</code>：用于改变或释放 <code>unique_ptr</code> 所管理的对象<ul><li><code>reset()</code> 不带参数：<ul><li>释放当前 <code>unique_ptr</code> 所指向对象的内存，然后将当前 <code>unique_ptr</code> 置为空指针</li></ul></li><li><code>reset(newPtr)</code> 带参数：<ul><li>释放当前 <code>unique_ptr</code> 所指向对象的内存，然后将当前 <code>unique_ptr</code> 指向新对象（<code>newPtr</code>）</li><li>注意：参数 <code>newPtr</code> 是裸指针（通过 <code>new</code> 得到的原始指针），另外还有两个参数的重载版本 <code>reset(newPtr, deleter)</code>，用于替换并指定自定义删除器</li></ul></li><li>如果 <code>reset()</code> 抛出异常（如自定义删除器在释放内存时抛出异常），原对象保持原样（强异常保证）</li></ul></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 情况一（不带参数）</span></span><br><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up1</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>))</span></span>;</span><br><span class="line">up1.<span class="built_in">reset</span>();  <span class="comment">// up1 所指向对象的内存会被释放，up1 置为空指针</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 情况二（带参数）</span></span><br><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up2</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>))</span></span>;</span><br><span class="line"><span class="keyword">int</span>* p = <span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">200</span>);</span><br><span class="line">up2.<span class="built_in">reset</span>(p);  <span class="comment">// up2 原来所指向对象的内存会被释放，up2 指向最新的对象 p</span></span><br></pre></td></tr></tbody></table></figure><h5 id="nullptr"><a href="#nullptr" class="headerlink" title="= nullptr"></a>= nullptr</h5><ul><li><code>= nullptr</code> 会释放 <code>unique_ptr</code> 所指向对象的内存，并将 <code>unique_ptr</code> 置为空指针。</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">test04</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>))</span></span>;</span><br><span class="line">    up = <span class="literal">nullptr</span>;  <span class="comment">// up 所指向对象的内存会被释放，up 置为空指针</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="解引用"><a href="#解引用" class="headerlink" title="* 解引用"></a>* 解引用</h5><ul><li>通过 <code>*</code> 获取 <code>unique_ptr</code> 所指向的对象 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">sp</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>))</span></span>;</span><br><span class="line">cout &lt;&lt; *sp &lt;&lt; endl;  <span class="comment">// 输出 100</span></span><br></pre></td></tr></tbody></table></figure><h5 id="移动语义"><a href="#移动语义" class="headerlink" title="移动语义"></a>移动语义</h5><p><code>unique_ptr</code> 支持移动语义，可以通过移动构造函数或移动赋值运算符将所有权从一个 <code>unique_ptr</code> 转移到另一个 <code>unique_ptr</code>。当 <code>unique_ptr</code> 移动后，原来的 <code>unique_ptr</code> 会变为空指针，不再指向任何任何对象。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up1</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>))</span></span>;</span><br><span class="line">unique_ptr&lt;<span class="keyword">int</span>&gt; up2 = <span class="built_in">move</span>(up1);  <span class="comment">// 移动完成后，up1 置为空指针，up2 指向 up1 原来所指向的对象</span></span><br></pre></td></tr></tbody></table></figure><h5 id="作为判断条件"><a href="#作为判断条件" class="headerlink" title="作为判断条件"></a>作为判断条件</h5><ul><li>智能指针（如 <code>unique_ptr</code>）重载了 <code>operator bool</code>，因此它可以被直接用作条件判断：当智能指针指向一个有效对象时，条件为 <code>true</code>；当它为空（即未指向任何对象）时，条件为 <code>false</code>。</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>))</span></span>;</span><br><span class="line"><span class="keyword">if</span> (up != <span class="literal">nullptr</span>) {</span><br><span class="line">    cout &lt;&lt; <span class="string">"not nullptr"</span> &lt;&lt; endl;  <span class="comment">// 执行输出</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">up.<span class="built_in">reset</span>();</span><br><span class="line"><span class="keyword">if</span> (up != <span class="literal">nullptr</span>) {</span><br><span class="line">    cout &lt;&lt; <span class="string">"not nullptr"</span> &lt;&lt; endl;</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line">    cout &lt;&lt; <span class="string">"nullptr"</span> &lt;&lt; endl;  <span class="comment">// 执行输出</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="返回-unique-ptr-类型"><a href="#返回-unique-ptr-类型" class="headerlink" title="返回 unique_ptr 类型"></a>返回 unique_ptr 类型</h5><ul><li>虽然 <code>unique_ptr</code> 是独占式的，不支持拷贝操作（包括拷贝构造函数和拷贝赋值运算符）；但是，当 <code>unique_ptr</code> 即将被销毁的时候，它是可以拷贝的。最常见的用法就是从函数返回一个 <code>unique_ptr</code>。</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">func</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>))</span></span>;</span><br><span class="line">    <span class="keyword">return</span> up;  <span class="comment">// 返回一个局部对象，编译器会生成一个临时对象，并调用 unique_ptr 的移动构造函数</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    unique_ptr&lt;<span class="keyword">int</span>&gt; up = <span class="built_in">func</span>();  <span class="comment">// 接收函数返回的 unique_ptr，调用 unique_ptr 的拷贝构造函数</span></span><br><span class="line"></span><br><span class="line">    unique_ptr&lt;<span class="keyword">int</span>&gt; up2;  <span class="comment">// 接收函数返回的 unique_ptr</span></span><br><span class="line">    up2 = <span class="built_in">func</span>();         <span class="comment">// 调用 unique_ptr 的赋值运算符</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="转换为-shared-ptr-类型"><a href="#转换为-shared-ptr-类型" class="headerlink" title="转换为 shared_ptr 类型"></a>转换为 shared_ptr 类型</h5><ul><li><p>如果 <code>unique_ptr</code> 是右值，可以将其转换为 <code>shared_ptr</code> 类型。<code>shared_ptr</code> 提供了一个显式构造函数来接收右值 <code>unique_ptr</code>，并接管原来归 <code>unique_ptr</code> 管理的对象。</p></li><li><p>(1) 代码案例一</p></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">func</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">return</span> unique_ptr&lt;<span class="keyword">int</span>&gt;(<span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">100</span>));  <span class="comment">// 返回一个右值（临时对象都是右值）</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    shared_ptr&lt;<span class="keyword">int</span>&gt; sp = <span class="built_in">func</span>();  <span class="comment">// 转换为 shared_ptr 类型，这里会额外创建一个控制块（用于存储引用计数、删除器等）</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>(2) 代码案例二 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">up</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>))</span></span>;  <span class="comment">// 左值</span></span><br><span class="line">    shared_ptr&lt;<span class="keyword">int</span>&gt; sp = <span class="built_in">move</span>(up);     <span class="comment">// 将左值转换为右值</span></span><br><span class="line">                                       <span class="comment">// 移动完成后，up 置为空指针，sp 指向 up 原来所指向的对象</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="unique-ptr-的删除器"><a href="#unique-ptr-的删除器" class="headerlink" title="unique_ptr 的删除器"></a>unique_ptr 的删除器</h4><h5 id="自定义删除器"><a href="#自定义删除器" class="headerlink" title="自定义删除器"></a>自定义删除器</h5><ul><li><code>unique_ptr</code> 会在一定的时机自动删除所指向的对象，并且底层使用 <code>delete</code> 操作符作为默认的资源析构方式。</li><li><code>unique_ptr</code> 还支持指定自定义的删除器来取代 C++ 编译器提供的默认删除器，语法格式为：<code>unique_ptr&lt;对象类型, 删除器类型&gt; 智能指针变量名</code>。</li><li><code>unique_ptr</code> 的删除器可以是任何可调用对象，例如普通函数、重载了 <code>operator()</code> 的类，或者 Lambda 表达式。</li></ul><blockquote><p>第一种写法（使用普通函数自定义删除器）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义普通函数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">myDeleter</span><span class="params">(<span class="keyword">int</span> *p)</span> </span>{</span><br><span class="line">    <span class="keyword">delete</span> p;</span><br><span class="line">    p = <span class="literal">nullptr</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">"delete int *"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 使用 typedef 定义一个函数指针类型，类型名称是 func</span></span><br><span class="line">    <span class="function"><span class="keyword">typedef</span> <span class="title">void</span> <span class="params">(*func)</span><span class="params">(<span class="keyword">int</span> *)</span></span>;</span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>, func&gt; <span class="title">up</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>), myDeleter)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义普通函数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">myDeleter</span><span class="params">(<span class="keyword">int</span> *p)</span> </span>{</span><br><span class="line">    <span class="keyword">delete</span> p;</span><br><span class="line">    p = <span class="literal">nullptr</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">"delete int *"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 或者使用 using 定义一个函数指针类型，类型名称是 func</span></span><br><span class="line">    <span class="keyword">using</span> func = <span class="built_in"><span class="keyword">void</span></span> (*)(<span class="keyword">int</span> *);</span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>, func&gt; <span class="title">up</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>), myDeleter)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义普通函数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">myDeleter</span><span class="params">(<span class="keyword">int</span> *p)</span> </span>{</span><br><span class="line">    <span class="keyword">delete</span> p;</span><br><span class="line">    p = <span class="literal">nullptr</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">"delete int *"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 或者直接省去函数指针类型的定义</span></span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>, <span class="title">void</span> <span class="params">(*)</span><span class="params">(<span class="keyword">int</span> *)</span>&gt; <span class="title">up</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>), myDeleter)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义普通函数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">myDeleter</span><span class="params">(<span class="keyword">int</span> *p)</span> </span>{</span><br><span class="line">    <span class="keyword">delete</span> p;</span><br><span class="line">    p = <span class="literal">nullptr</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">"delete int *"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 或者配合使用 typedef 和 decltype</span></span><br><span class="line">    <span class="function"><span class="keyword">typedef</span> <span class="title">decltype</span><span class="params">(myDeleter)</span> *func</span>;</span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>, func&gt; <span class="title">up</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>), myDeleter)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 或者配合使用 using 和 decltype</span></span><br><span class="line">    <span class="keyword">using</span> func2 = <span class="keyword">decltype</span>(myDeleter) *;</span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>, func2&gt; <span class="title">up2</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>), myDeleter)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 或者直接使用 decltype 获取具体的类型</span></span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>, <span class="title">decltype</span><span class="params">(myDeleter)</span> *&gt; <span class="title">up3</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>), myDeleter)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><blockquote><p>第二种写法（使用 Lambda 表达式自定义删除器）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义 Lambda 表达式</span></span><br><span class="line"><span class="keyword">auto</span> myDelLambda = [](<span class="keyword">int</span> *p) {</span><br><span class="line">    <span class="keyword">delete</span> p;</span><br><span class="line">    p = <span class="literal">nullptr</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">"delete int *"</span> &lt;&lt; endl;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 配合使用 decltype 和 Lambda 表达式</span></span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>, <span class="title">decltype</span><span class="params">(myDelLambda)</span>&gt; <span class="title">up</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>), myDelLambda)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 或者直接使用 Lambda 表达式</span></span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>, <span class="title">void</span> <span class="params">(*)</span><span class="params">(<span class="keyword">int</span> *)</span>&gt; <span class="title">up</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>), [](<span class="keyword">int</span> *p) {</span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">delete</span> p;</span></span></span><br><span class="line"><span class="params"><span class="function">        p = <span class="literal">nullptr</span>;</span></span></span><br><span class="line"><span class="params"><span class="function">        cout &lt;&lt; <span class="string">"delete int *"</span> &lt;&lt; endl;</span></span></span><br><span class="line"><span class="params"><span class="function">    })</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="使用数组的问题"><a href="#使用数组的问题" class="headerlink" title="使用数组的问题"></a>使用数组的问题</h5><blockquote><p>如果 <code>unique_ptr</code> 指向的是数组，那么就需要自定义删除器或者特殊语法，否则 C++ 编译器不会使用 <code>delete []</code> 正确释放数组的内存，而是默认使用 <code>delete</code> 导致内存泄漏</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自定义类（带自定义的析构函数）</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">    }</span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解决使用数组的问题</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 如果 unique_ptr 指向的是数组，那么就需要自定义删除器，否则不会使用 delete [] 正确释放数组的内存</span></span><br><span class="line">    <span class="function">unique_ptr&lt;MyClass[], <span class="title">void</span> <span class="params">(*)</span><span class="params">(MyClass *)</span>&gt; <span class="title">sp2</span><span class="params">(<span class="keyword">new</span> MyClass[<span class="number">3</span>], [](MyClass *p) {</span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">delete</span>[] p;</span></span></span><br><span class="line"><span class="params"><span class="function">        cout &lt;&lt; <span class="string">"delete MyClass []"</span> &lt;&lt; endl;</span></span></span><br><span class="line"><span class="params"><span class="function">    })</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br><span class="line">delete MyClass []</span><br></pre></td></tr></tbody></table></figure><blockquote><p>还可以使用数组特化版本 <code>unique_ptr&lt;T[]&gt;</code>，同样可以正确释放数组内存（<strong>从 C++ 17 开始支持以下写法</strong>）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自定义类（带自定义的析构函数）</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">    }</span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解决使用数组的问题</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 从 C++ 17 开始正式支持以下写法，使用数组特化版本 unique_ptr&lt;T[]&gt;，可以正确释放数组内存</span></span><br><span class="line">    <span class="function">unique_ptr&lt;MyClass[]&gt; <span class="title">up</span><span class="params">(<span class="keyword">new</span> MyClass[<span class="number">3</span>])</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><blockquote><p>还可以使用 <code>std::default_delete</code> 来做删除器，<code>std::default_delete</code> 是标准库（STL）里的模板类，同样可以正确释放数组的内存（<strong>从 C++ 17 开始支持以下写法</strong>）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自定义类（带自定义的析构函数）</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">    }</span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解决使用数组的问题</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 如果 unique_ptr 指向的是数组，还可以使用 default_delete 来正确释放数组的内存</span></span><br><span class="line">    unique_ptr&lt;MyClass[], default_delete&lt;MyClass[]&gt;&gt; <span class="built_in">up</span>(<span class="keyword">new</span> MyClass[<span class="number">3</span>]);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><h5 id="不是同一种类型的说明"><a href="#不是同一种类型的说明" class="headerlink" title="不是同一种类型的说明"></a>不是同一种类型的说明</h5><ul><li>如果两个 <code>shared_ptr</code> 指定了不同的删除器，只要它们所指向的对象类型是相同的，那么这两个 <code>shared_ptr</code> 也属于同一种类型。这就代表只要类型相同，就可以将它们放入到元素类型为该对象类型的容器里面。</li><li>但是，对于 <code>unique_ptr</code> 来说，指定 <code>unique_ptr</code> 的删除器后，会影响 <code>unique_ptr</code> 的类型。</li><li>简而言之，<strong>如果两个 <code>unique_ptr</code> 的删除器类型不同，那么即使它们指向的对象类型相同，这两个 <code>unique_ptr</code> 本身也属于不同的类型，因此无法放入同一个容器中</strong>。</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">auto</span> lambda1 = [](<span class="keyword">int</span> *p) {</span><br><span class="line">        <span class="keyword">delete</span> p;</span><br><span class="line">        cout &lt;&lt; <span class="string">"delete by lambda1"</span> &lt;&lt; endl;</span><br><span class="line">    };</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span> lambda2 = [](<span class="keyword">int</span> *p) {</span><br><span class="line">        <span class="keyword">delete</span> p;</span><br><span class="line">        cout &lt;&lt; <span class="string">"delete by lambda2"</span> &lt;&lt; endl;</span><br><span class="line">    };</span><br><span class="line"></span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>, <span class="title">decltype</span><span class="params">(lambda1)</span>&gt; <span class="title">up1</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>), lambda1)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>, <span class="title">decltype</span><span class="params">(lambda2)</span>&gt; <span class="title">up2</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">200</span>), lambda2)</span></span>;</span><br><span class="line"></span><br><span class="line">    vector&lt;unique_ptr&lt;<span class="keyword">int</span>, <span class="keyword">decltype</span>(lambda1)&gt;&gt; vec;</span><br><span class="line">    vec.<span class="built_in">emplace_back</span>(<span class="built_in">move</span>(up1));</span><br><span class="line">    <span class="comment">// vec.emplace_back(move(up2));  // up1 与 up2 的删除器类型不相同，两者不属于同一种类型，不可以放入同一个容器中</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="unique-ptr-的性能分析"><a href="#unique-ptr-的性能分析" class="headerlink" title="unique_ptr 的性能分析"></a>unique_ptr 的性能分析</h4><h5 id="unique-ptr-的大小"><a href="#unique-ptr-的大小" class="headerlink" title="unique_ptr 的大小"></a>unique_ptr 的大小</h5><p>在 C++ 中，<code>unique_ptr</code> 的大小通常与裸指针（通过 <code>new</code> 得到的指针）相同。但是，如果 <code>unique_ptr</code> 指定了自定义删除器（尤其是带有状态的删除器，如函数对象或 Lambda 表达式捕获了变量），则其大小可能超过裸指针的大小。</p><ul><li>默认删除器（<code>std::default_delete</code>）无状态，通常不增加额外大小。</li><li>无捕获的 Lambda 表达式作为删除器时，由于继承空基类优化（EBO），也通常不增加大小。</li><li>使用函数指针作为删除器时，会额外占用一个指针的大小。</li><li>有捕获的 Lambda 表达式或自定义类删除器带有成员变量时，会增加相应大小。</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> *p;</span><br><span class="line">    unique_ptr&lt;<span class="keyword">int</span>&gt; up;</span><br><span class="line">    <span class="keyword">int</span> length1 = <span class="built_in"><span class="keyword">sizeof</span></span>(p);   <span class="comment">// 8 个字节（64 位系统）</span></span><br><span class="line">    <span class="keyword">int</span> length2 = <span class="built_in"><span class="keyword">sizeof</span></span>(up);  <span class="comment">// 8 个字节（64 位系统）</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">myDeleter</span><span class="params">(<span class="keyword">int</span> *p)</span> </span>{</span><br><span class="line">    <span class="keyword">delete</span> p;</span><br><span class="line">    p = <span class="literal">nullptr</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">"delete int *"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="function">unique_ptr&lt;<span class="keyword">int</span>, <span class="title">void</span> <span class="params">(*)</span><span class="params">(<span class="keyword">int</span> *)</span>&gt; <span class="title">up3</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>), myDeleter)</span></span>;</span><br><span class="line">    <span class="keyword">int</span> length = <span class="built_in"><span class="keyword">sizeof</span></span>(up3);</span><br><span class="line">    cout &lt;&lt; length &lt;&lt; endl;  <span class="comment">// 16 个字节（64位系统）</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="unique-ptr-的选择建议"><a href="#unique-ptr-的选择建议" class="headerlink" title="unique_ptr 的选择建议"></a>unique_ptr 的选择建议</h5><ul><li>如果程序需要多个指针共享同一个对象，应选择 <code>shared_ptr</code>。</li><li>如果程序不需要多个指针共享同一个对象，应优先使用 <code>unique_ptr</code>，性能更高。</li></ul><h3 id="auto-ptr-智能指针"><a href="#auto-ptr-智能指针" class="headerlink" title="auto_ptr 智能指针"></a>auto_ptr 智能指针</h3><h4 id="auto-ptr-为什么会被废弃"><a href="#auto-ptr-为什么会被废弃" class="headerlink" title="auto_ptr 为什么会被废弃"></a>auto_ptr 为什么会被废弃</h4><p><code>auto_ptr</code> 是 C++ 98 时代的智能指针，其被废弃的主要原因是：它使用<strong>拷贝语义</strong>实现移动所有权，导致拷贝后原指针变为空，违反了常规拷贝的直觉，极易引发运行时错误。C++ 11 引入移动语义后，由 <code>unique_ptr</code> 安全替代 <code>auto_ptr</code>。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">auto_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">ap1</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>))</span></span>;</span><br><span class="line">auto_ptr&lt;<span class="keyword">int</span>&gt; ap2 = ap1; <span class="comment">// ap1 置为空指针，ap2 指向 ap1 原来所指向的对象</span></span><br></pre></td></tr></tbody></table></figure>]]></content>
    
    
    <summary type="html">本文主要介绍 C++ 从入门到精通的内容。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="C++" scheme="https://www.techgrow.cn/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>C++ 从入门到精通之八</title>
    <link href="https://www.techgrow.cn/posts/a9b9dd7e.html"/>
    <id>https://www.techgrow.cn/posts/a9b9dd7e.html</id>
    <published>2025-11-18T13:55:33.000Z</published>
    <updated>2025-11-18T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/332ad818.html">C++ 从入门到精通之一</a>、<a href="/posts/68a580cf.html">C++ 从入门到精通之二</a>、<a href="/posts/9ce17bf9.html">C++ 从入门到精通之三</a></li><li><a href="/posts/d198d85d.html">C++ 从入门到精通之四</a>、<a href="/posts/7246a640.html">C++ 从入门到精通之五</a>、<a href="/posts/80bc7e12.html">C++ 从入门到精通之六</a></li><li><a href="/posts/e1f4608d.html">C++ 从入门到精通之七</a>、<a href="/posts/a9b9dd7e.html">C++ 从入门到精通之八</a>、<a href="/posts/14325b2f.html">C++ 从入门到精通之九</a></li><li><a href="/posts/badb9ea0.html">C++ 从入门到精通之十</a>、<a href="/posts/c961bdb.html">C++ 从入门到精通之十一</a></li></ul><span id="more"></span><h2 id="C-智能指针"><a href="#C-智能指针" class="headerlink" title="C++ 智能指针"></a>C++ 智能指针</h2><h3 id="直接内存管理"><a href="#直接内存管理" class="headerlink" title="直接内存管理"></a>直接内存管理</h3><h4 id="直接内存管理的示例代码"><a href="#直接内存管理的示例代码" class="headerlink" title="直接内存管理的示例代码"></a>直接内存管理的示例代码</h4><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 自动内存管理</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> a = <span class="number">0</span>;          <span class="comment">// 局部变量（临时）</span></span><br><span class="line">    MyClass mc;         <span class="comment">// 局部变量（临时）</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">int</span> b = <span class="number">30</span>;  <span class="comment">// 静态局部变量（全局）</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 直接内存管理</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func2</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 初值未定义</span></span><br><span class="line">    <span class="keyword">int</span>* pint = <span class="keyword">new</span> <span class="keyword">int</span>;</span><br><span class="line">    <span class="keyword">delete</span> pint;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初值为0，空括号()表示值初始化</span></span><br><span class="line">    <span class="keyword">int</span>* pint2 = <span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>();</span><br><span class="line">    <span class="keyword">delete</span> pint2;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初值为10</span></span><br><span class="line">    <span class="keyword">int</span>* pint3 = <span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">10</span>);</span><br><span class="line">    <span class="keyword">delete</span> pint3;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初值是空字符串，调用string的默认构造函数</span></span><br><span class="line">    string* pstr = <span class="keyword">new</span> string;</span><br><span class="line">    <span class="keyword">delete</span> pstr;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 调用string的默认构造函数，空括号()表示值初始化</span></span><br><span class="line">    string* pstr2 = <span class="keyword">new</span> <span class="built_in">string</span>();</span><br><span class="line">    <span class="keyword">delete</span> pstr2;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初值为AAAAA</span></span><br><span class="line">    string* pstr3 = <span class="keyword">new</span> <span class="built_in">string</span>(<span class="number">5</span>, <span class="string">'A'</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// auto 配合 new 使用，pstr4 推断的类型是 string **</span></span><br><span class="line">    <span class="keyword">auto</span> pstr4 = <span class="keyword">new</span> <span class="built_in"><span class="keyword">auto</span></span>(pstr3);</span><br><span class="line">    <span class="keyword">delete</span> pstr3;</span><br><span class="line">    <span class="keyword">delete</span> pstr4;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初始值为 1, 2, 3</span></span><br><span class="line">    vector&lt;<span class="keyword">int</span>&gt;* pvec = <span class="keyword">new</span> vector&lt;<span class="keyword">int</span>&gt;{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>};</span><br><span class="line">    <span class="keyword">delete</span> pvec;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 调用MyClass的默认构造函数</span></span><br><span class="line">    MyClass* pmc = <span class="keyword">new</span> MyClass;</span><br><span class="line">    <span class="keyword">delete</span> pmc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 调用MyClass的默认构造函数，空括号()表示值初始化</span></span><br><span class="line">    MyClass* pmc2 = <span class="keyword">new</span> <span class="built_in">MyClass</span>();</span><br><span class="line">    <span class="keyword">delete</span> pmc2;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 动态分配const对象</span></span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span>* pconst = <span class="keyword">new</span> <span class="keyword">const</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">100</span>);</span><br><span class="line">    <span class="keyword">delete</span> pconst;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="同一块内存不能-delete-多次"><a href="#同一块内存不能-delete-多次" class="headerlink" title="同一块内存不能 delete 多次"></a>同一块内存不能 delete 多次</h4><ul><li>不能 <code>delete</code> 多次的根本原因是：<ul><li>设计假设：内存管理器假设每次 <code>delete</code> 的都是正在使用中的内存</li><li>数据结构破坏：第二次 <code>delete</code> 会修改已被修改过的元数据，破坏空闲链表（Free List）</li><li>性能取舍：为了性能，不维护复杂的 “是否已释放” 检测机制</li><li>不可确定性：第一次 <code>delete</code> 的内存可能已被重新分配，导致第二次 <code>delete</code> 释放的是其他对象的内存</li></ul></li></ul><table><thead><tr><th><code>delete</code> 操作</th><th>结果</th></tr></thead><tbody><tr><td><code>delete p; delete p;</code></td><td>❌ 未定义行为（通常崩溃）</td></tr><tr><td><code>delete p; p=nullptr; delete p;</code></td><td>✅ 安全（delete nullptr 无效果）</td></tr><tr><td>两个指针指向同一内存，多次 <code>delete</code></td><td>❌ 未定义行为</td></tr><tr><td><code>delete nullptr</code> 多次</td><td>✅ 安全</td></tr></tbody></table><ul><li>错误写法一：同一指针的重复删除 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span>* p = <span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">10</span>);</span><br><span class="line"><span class="keyword">delete</span> p;</span><br><span class="line">p = <span class="literal">nullptr</span>;  <span class="comment">// ✅ 好习惯</span></span><br><span class="line"><span class="keyword">delete</span> p;     <span class="comment">// ❌ 错误！同一指针重复删除</span></span><br></pre></td></tr></tbody></table></figure><ul><li>错误写法二：两个指针指向同一地址 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span>* p1 = <span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">10</span>);</span><br><span class="line"><span class="keyword">int</span>* p2 = p1;  <span class="comment">// p2 指向同一内存</span></span><br><span class="line"><span class="keyword">delete</span> p1;     <span class="comment">// ✅ 释放内存</span></span><br><span class="line"><span class="keyword">delete</span> p2;     <span class="comment">// ❌ 错误！p1 和 p2 指向同一块内存</span></span><br></pre></td></tr></tbody></table></figure><ul><li>错误写法三：拷贝指针后重复删除 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">badFunction</span><span class="params">(<span class="keyword">int</span>* ptr)</span> </span>{</span><br><span class="line">    <span class="keyword">delete</span> ptr;  <span class="comment">// 删除传入的指针</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span>* p = <span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">42</span>);</span><br><span class="line">    <span class="built_in">badFunction</span>(p);</span><br><span class="line">    <span class="keyword">delete</span> p;    <span class="comment">// ❌ 错误！p 已经被删除</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>特殊情况：<code>delete</code> 空指针是合法的 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span>* p = <span class="literal">nullptr</span>;</span><br><span class="line"><span class="keyword">delete</span> p;  <span class="comment">// ✅ 安全！C++ 标准保证 delete nullptr 不做任何事</span></span><br><span class="line"><span class="keyword">delete</span> p;  <span class="comment">// ✅ 仍然安全，对 nullptr 重复 delete 是安全的</span></span><br></pre></td></tr></tbody></table></figure><h4 id="delete-操作的最佳实践总结"><a href="#delete-操作的最佳实践总结" class="headerlink" title="delete 操作的最佳实践总结"></a>delete 操作的最佳实践总结</h4><ul><li><code>delete</code> 后立即置空 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">delete</span> p;</span><br><span class="line">p = <span class="literal">nullptr</span>;  <span class="comment">// 后续 delete nullptr 是安全的</span></span><br></pre></td></tr></tbody></table></figure><ul><li>使用智能指针（推荐）</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">unique_ptr&lt;<span class="keyword">int</span>&gt; p1 = make_unique&lt;<span class="keyword">int</span>&gt;(<span class="number">42</span>);  <span class="comment">// 自动管理</span></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; p2 = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">42</span>);  <span class="comment">// 引用计数，自动处理</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 不需要手动 delete，不会出现指针双重删除问题</span></span><br></pre></td></tr></tbody></table></figure><ul><li>明确指针所有权 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 明确谁拥有指针，谁负责删除</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line">    <span class="keyword">int</span>* data;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MyClass</span>() : <span class="built_in">data</span>(<span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">42</span>)) {}</span><br><span class="line">    ~<span class="built_in">MyClass</span>() { <span class="keyword">delete</span> data; }  <span class="comment">// 唯一删除点</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 禁止拷贝或使用引用计数</span></span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><div class="admonition note"><p class="admonition-title">最佳实践总结</p><ul><li>每个 <code>new</code> 只对应一个 <code>delete</code>，<code>delete</code> 后立即将指针置为 <code>nullptr</code>，更好的是方案使用智能指针自动管理内存。</li></ul></div><h3 id="new、delete-探秘"><a href="#new、delete-探秘" class="headerlink" title="new、delete 探秘"></a>new、delete 探秘</h3><h4 id="重载-new、delete-运算符"><a href="#重载-new、delete-运算符" class="headerlink" title="重载 new、delete 运算符"></a>重载 new、delete 运算符</h4><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"MyClass() called"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~MyClass() called"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重载 new 运算符，用于对象分配内存</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">void</span>* <span class="keyword">operator</span> <span class="title">new</span><span class="params">(<span class="keyword">size_t</span> size)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"operator new called, size: "</span> &lt;&lt; size &lt;&lt; endl;</span><br><span class="line">        <span class="keyword">void</span>* ptr = <span class="built_in">malloc</span>(size);</span><br><span class="line">        <span class="keyword">if</span> (!ptr) {</span><br><span class="line">            <span class="keyword">throw</span> <span class="built_in">bad_alloc</span>();</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> ptr;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重载 delete 运算符，用于对象释放内存</span></span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="keyword">operator</span> <span class="title">delete</span><span class="params">(<span class="keyword">void</span>* ptr)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"operator delete called"</span> &lt;&lt; endl;</span><br><span class="line">        <span class="keyword">if</span> (ptr) {</span><br><span class="line">            <span class="built_in">free</span>(ptr);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重载 new[] 运算符，用于数组分配内存</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span>[](<span class="keyword">size_t</span> size) {</span><br><span class="line">        cout &lt;&lt; <span class="string">"operator new[] called, size: "</span> &lt;&lt; size &lt;&lt; endl;</span><br><span class="line">        <span class="keyword">void</span>* ptr = <span class="built_in">malloc</span>(size);</span><br><span class="line">        <span class="keyword">if</span> (!ptr) {</span><br><span class="line">            <span class="keyword">throw</span> <span class="built_in">bad_alloc</span>();</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> ptr;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重载 delete[] 运算符，用于数组释放内存</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">void</span> <span class="keyword">operator</span> <span class="keyword">delete</span>[](<span class="keyword">void</span>* ptr) {</span><br><span class="line">        cout &lt;&lt; <span class="string">"operator delete[] called"</span> &lt;&lt; endl;</span><br><span class="line">        <span class="keyword">if</span> (ptr) {</span><br><span class="line">            <span class="built_in">free</span>(ptr);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">int</span> m_i;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 单个对象</span></span><br><span class="line">    MyClass* mc = <span class="keyword">new</span> <span class="built_in">MyClass</span>();</span><br><span class="line">    <span class="keyword">delete</span> mc;</span><br><span class="line"></span><br><span class="line">    cout &lt;&lt; <span class="string">"------------------"</span> &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 对象数组</span></span><br><span class="line">    MyClass* marry = <span class="keyword">new</span> MyClass[<span class="number">3</span>];</span><br><span class="line">    <span class="keyword">delete</span>[] marry;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">operator new called, size: 4</span><br><span class="line">MyClass() called</span><br><span class="line">~MyClass() called</span><br><span class="line">operator delete called</span><br><span class="line">------------------</span><br><span class="line">operator new[] called, size: 20</span><br><span class="line">MyClass() called</span><br><span class="line">MyClass() called</span><br><span class="line">MyClass() called</span><br><span class="line">~MyClass() called</span><br><span class="line">~MyClass() called</span><br><span class="line">~MyClass() called</span><br><span class="line">operator delete[] called</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li>在 C++ 中，<code>new</code> 和 <code>delete</code> 具备对堆上分配的内存进行初始化和释放的能力，这是 <code>malloc()</code>、<code>free()</code> 不具备的。</li><li>对象构造时，先使用 <code>new</code> 分配对象内存，然后再调用对象的构造函数；对象析构时，先调用对象的析构函数，再调用 <code>delete</code> 释放对象内存。</li></ul></div><h4 id="如何记录-new-分配的内存大小供-delete-使用"><a href="#如何记录-new-分配的内存大小供-delete-使用" class="headerlink" title="如何记录 new 分配的内存大小供 delete 使用"></a>如何记录 new 分配的内存大小供 delete 使用</h4><p>C++ 编译器或运行时库在 <code>new</code> 分配内存时，通常会在用户请求的内存前额外多分配一块空间，用于存储内存块的大小信息，然后返回偏移后的指针；<code>delete</code> 根据指针向前访问这个内存块大小信息，从而知道实际分配的内存大小并释放。<code>new</code> 的底层通常调用 <code>malloc()</code> 来分配内存，而 <code>malloc()</code> 自身在内存块前维护了内存块的大小信息，以便 <code>free()</code> 正确释放内存；这些内存块大小信息由底层内存管理器维护，而不是由 <code>new</code> 本身维护。</p><ul><li>理解注意事项：<ul><li>(1) 当调用 <code>malloc(totalSize)</code> 时，底层内存管理器（C 标准库）会记录这块内存的实际大小和管理信息（通常在内存块头部隐藏存储）。</li><li>(2) 当调用 <code>free(ptr)</code> 时，它会读取自己内部的管理信息来知道这块内存的大小，然后回收，不需要用户手动告诉它大小。</li><li>(3) 因此在 <code>operator delete</code> 中，用户只需要传给 <code>free()</code> 原始分配的指针，底层内存管理器会自动处理。</li></ul></li></ul><hr><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 构造函数</span></span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"MyClass() called"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~MyClass() called"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">test01</span><span class="params">()</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"---------- test01() ----------"</span> &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 空类（不包含任何成员变量）占一个字节大小</span></span><br><span class="line">    MyClass mc;</span><br><span class="line">    <span class="keyword">size_t</span> len = <span class="built_in"><span class="keyword">sizeof</span></span>(mc);</span><br><span class="line">    cout &lt;&lt; <span class="string">"size: "</span> &lt;&lt; len &lt;&lt; endl;  <span class="comment">// 输出 1</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">test02</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 分配整型数组</span></span><br><span class="line">    <span class="keyword">int</span>* iarray = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">2</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放整型数组，如果这里不执行 delete[]，会泄露 16 个字节内存（不同平台有差异）</span></span><br><span class="line">    <span class="keyword">delete</span>[] iarray;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">test03</span><span class="params">()</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"---------- test03() ----------"</span> &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 分配对象数组</span></span><br><span class="line">    MyClass* parray = <span class="keyword">new</span> MyClass[<span class="number">2</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放对象数组，如果这里不执行 delete[]，会泄露 18 个字节内存（不同平台有差异）</span></span><br><span class="line">    <span class="keyword">delete</span>[] parray;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>为什么在上述的 <code>test03()</code> 函数中，如果不手动通过 <code>delete[]</code> 释放对象数组时，会泄露 10 个字节呢？</p><ul><li><p>(1) 对象大小</p><ul><li><code>MyClass</code> 类只有构造函数和析构函数，没有成员变量，对象本身大小为 1 字节（空类保证每个对象都有唯一地址）。</li><li>在大多数 64 位编译器下，空类大小通常是 1 字节。</li><li>如果类里有成员变量，例如 <code>int m_i</code>，类的大小通常是 <code>sizeof(int)</code>，按 4 或者 8 字节对齐。</li></ul></li><li><p>(2) 数组长度信息</p><ul><li>编译器在用 <code>new[]</code> 分配数组时，通常会额外在内存块前额外多分配一块空间来存储数组长度信息，用于告诉 <code>delete[]</code> 需要调用多少次析构函数。</li><li>这个数组长度通常是 <code>size_t</code>（在 64 位系统上占 8 字节）。</li></ul></li><li><p>(3) 内存块大小信息</p><ul><li><code>new</code> 底层调用 <code>malloc()</code> 分配内存，<code>malloc</code> 自身会在内存块前额外多分配一块空间，用于存储内存块的大小信息，以便 <code>free()</code> 回收内存。</li><li>这内存块大小信息通常占 8 ~ 16 字节（依赖系统和实现）。</li></ul></li><li><p>(4) 内存布局示意（简化版）</p>  <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">+------------------------+ </span><br><span class="line">| 内存块大小 = 8 ~ 16 字节 |  &lt;-- malloc 内部隐藏头（管理内存块大小）</span><br><span class="line">+------------------------+</span><br><span class="line">| 数组长度 size_t = 8 字节  | &lt;-- 编译器存储数组元素数量</span><br><span class="line">+------------------------+</span><br><span class="line">| 对象 MyClass[0] = 1 字节 |</span><br><span class="line">+------------------------+</span><br><span class="line">| 对象 MyClass[1] = 1 字节 |</span><br><span class="line">+------------------------+</span><br></pre></td></tr></tbody></table></figure></li><li><p>(5) 实际泄露的内存大小</p><ul><li>对象数据本身大小：<code>2 × sizeof(MyClass)</code></li><li>额外存储的数组长度信息：<code>sizeof(size_t)</code>（在 64 位系统上占 8 字节）</li><li>额外存储的内存块大小信息：约占 8 ~ 16 字节（依赖系统和实现）</li></ul></li><li><p>(6) 举例说明</p><ul><li>假设是 64 位系统，空类大小占 1 字节，数组长度（<code>size_t</code>）占 8 字节，内存块大小信息占 8 字节：<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">总的内存泄漏大小 ≈ 8 (内存块大小) + 8 (数组长度) + 2 × 1 (对象大小) = 18 字节</span><br></pre></td></tr></tbody></table></figure></li><li>如果类有一个 <code>int</code> 成员变量（4 字节），并按 4 字节对齐：  <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sizeof(MyClass) = 4字节</span><br><span class="line">总的内存泄漏大小 ≈ 8 (内存块大小) + 8 (数组长度) + 2 × 4 (对象大小) = 24 字节</span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul><hr><p>为什么在上述的 <code>test02()</code> 函数中，如果不手动通过 <code>delete[]</code> 释放对象数组时，会泄露 16 个字节呢？</p><ul><li><p>(1) 对象大小</p><ul><li><code>int</code> 在 64 位系统上通常是占用 4 字节（32 位整数）。</li><li>数组有 2 个元素，所以对象数据占用：<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2 × sizeof(int) = 2 × 4 = 8 字节</span><br></pre></td></tr></tbody></table></figure></li></ul></li><li><p>(2) 数组长度信息</p><ul><li>编译器在用 <code>new[]</code> 分配数组时，通常会额外在内存块前额外多分配一块空间来存储数组长度信息，用于告诉 <code>delete[]</code> 需要调用多少次析构函数。</li><li>对于 <code>int</code> 这种基础类型没有析构函数，数组长度信息通常会被编译器优化掉不存储（也就是说不会占用额外字节）。</li><li>因此数组长度占用 0 字节。</li></ul></li><li><p>(3) 内存块大小信息</p><ul><li><code>new</code> 底层调用 <code>malloc()</code> 分配内存，<code>malloc</code> 自身会在内存块前额外多分配一块空间，用于存储内存块的大小信息，以便 <code>free()</code> 回收内存。</li><li>这内存块大小信息通常占 8 ~ 16 字节（依赖系统和实现）。</li></ul></li><li><p>(4) 内存布局示意（简化版）</p>  <figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">+------------------------+ </span><br><span class="line">| 内存块大小 = 8 ~ 16 字节 |  &lt;-- malloc 内部隐藏头（管理内存块大小）</span><br><span class="line">+------------------------+</span><br><span class="line">| 对象 MyClass[0] = 4 字节 |</span><br><span class="line">+------------------------+</span><br><span class="line">| 对象 MyClass[1] = 4 字节 |</span><br><span class="line">+------------------------+</span><br></pre></td></tr></tbody></table></figure></li><li><p>(5) 实际泄露的内存大小</p><ul><li>对象数据本身大小：<code>2 × sizeof(int)</code></li><li>额外存储的数组长度信息：0 字节（基础类型）</li><li>额外存储的内存块大小信息：约占 8 ~ 16 字节（依赖系统和实现）<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">总的内存泄漏大小 ≈ 8 (内存块大小) + 2 x 4 (对象大小) = 16 字节</span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul><div class="admonition note"><p class="admonition-title">关键点</p><ul><li>数组长度信息：由编译器自己存储，用于告诉 <code>delete[]</code> 需要调用多少次析构函数。</li><li>内存块大小信息：由 <code>malloc()</code> / 底层内存管理器存储，用于告诉 <code>free()</code> 实际回收的内存大小。</li></ul></div><ul><li><p><code>delete[]</code> 需要的额外信息</p><ul><li>当用 <code>new MyClass[2]</code> 分配数组内存时，编译器必须知道数组里有多少个对象，以便告诉 <code>delete[]</code> 需要调用多少次析构函数。</li><li>所以编译器通常会在数组实际内存的前面额外多分配一块空间，用于存储一个 <code>size_t</code>（或者其它形式）记录数组长度，这不是整个内存块的大小，而是数组长度信息。</li><li>这块数组长度信息只为 <code>delete[]</code> 调用析构函数而服务，与底层内存管理器（<code>malloc</code>）内部记录的 “内存块大小信息” 是两回事。</li></ul></li><li><p><code>operator delete</code> / <code>free</code> 需要的额外信息</p><ul><li>底层内存管理器（<code>malloc/free</code>）内部会记录每块内存的大小，但这是给 <code>free()</code> 自己用的，不是编译器额外添加给 <code>delete[]</code> 使用的。</li><li>用户不需要知道每块内存的大小，也不需要将内存大小传给 <code>free()</code>，因为 <code>free()</code> 会自动查到。</li></ul></li></ul><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在 C++ 中，使用 <code>new[]</code> 分配数组内存后，必须使用 <code>delete[]</code> 释放内存；<strong>若误用 <code>delete</code> 释放数组内存，仅当数组元素类型为基础类型或者不含自定义析构函数的类类型时，行为才不是未定义的（通常不会引发错误）</strong>，否则会导致内存泄漏或程序崩溃。正确写法应始终成对使用 <code>new[]</code> 与 <code>delete[]</code>。</p></div><h3 id="智能指针的使用"><a href="#智能指针的使用" class="headerlink" title="智能指针的使用"></a>智能指针的使用</h3><div class="admonition note"><p class="admonition-title">扩展阅读</p><ul><li><a href="/posts/dbff2af9.html">C++ 入门进阶之一 - 智能指针使用</a></li><li><a href="/posts/6e5acd18.html">C++ 巩固进阶之四 - 智能指针使用</a></li></ul></div><h4 id="智能指针的类型"><a href="#智能指针的类型" class="headerlink" title="智能指针的类型"></a>智能指针的类型</h4><p>在 C++ 中，智能指针的类型有以下几种：</p><ul><li>(1) 带引用计数的智能指针：<code>shared_ptr</code></li><li>(2) 不带引用计数的智能指针：<code>auto_ptr</code>、<code>scoped_ptr</code>、<code>unique_ptr</code></li><li>(3) 特殊的智能指针：<code>weak_ptr</code>（不增加引用计数，可以用于避免 <code>shared_ptr</code> 发生循环引用）</li></ul><table><thead><tr><th>智能指针</th><th> C++ 标准</th><th>所有权</th><th>带引用计数</th><th>适用场景</th><th>核心特性</th></tr></thead><tbody><tr><td><code>auto_ptr</code></td><td>C++ 98</td><td> 独占（拷贝时转移）</td><td>否</td><td>⚠ 已废弃，建议改用 <code>unique_ptr</code></td><td>独占所有权，在复制或赋值时会转移所有权，导致原指针变为空（<code>nullptr</code>）</td></tr><tr><td><code>scoped_ptr</code></td><td>Boost</td><td> 独占</td><td>否</td><td>生命周期受限于作用域，适用于简单的场景，避免资源泄漏</td><td>独占所有权，不可复制或赋值，不支持移动语义，即不可以使用 <code>std::move()</code> 函数转移所有权</td></tr><tr><td><code>unique_ptr</code></td><td>C++ 11</td><td> 独占</td><td>否</td><td>资源独占，生命周期明确</td><td>独占所有权，不可复制（拷贝构造和赋值），但可以移动（移动构造和移动赋值），即支持使用 <code>std::move()</code> 函数转移所有权</td></tr><tr><td><code>shared_ptr</code></td><td>C++ 11</td><td> 共享</td><td>是</td><td>资源共享，生命周期不固定</td><td>共享所有权（允许多个智能指针管理同一个资源）</td></tr><tr><td><code>weak_ptr</code></td><td>C++ 11</td><td> 观察 <code>shared_ptr</code></td><td>否</td><td>避免 <code>shared_ptr</code> 发生循环引用</td><td>不增加引用计数，用于避免 <code>shared_ptr</code> 发生循环引用，可以通过 <code>lock()</code> 函数转换为 <code>shared_ptr</code></td></tr></tbody></table><h4 id="shared-ptr-智能指针"><a href="#shared-ptr-智能指针" class="headerlink" title="shared_ptr 智能指针"></a>shared_ptr 智能指针</h4><h5 id="shared-ptr-的使用语法"><a href="#shared-ptr-的使用语法" class="headerlink" title="shared_ptr 的使用语法"></a>shared_ptr 的使用语法</h5><h6 id="正确的使用语法"><a href="#正确的使用语法" class="headerlink" title="正确的使用语法"></a>正确的使用语法</h6><ul><li><code>shared_ptr</code> 的正确用法<ul><li>第一种正确用法<ul><li><code>shared_ptr&lt;int&gt; sp(new int(100));</code></li><li><code>shared_ptr&lt;MyClass&gt; sp(new MyClass());</code></li></ul></li><li>第二种正确用法<ul><li><code>shared_ptr&lt;int&gt; sp = make_shared&lt;int&gt;(100);</code></li><li><code>shared_ptr&lt;MyClass&gt; sp = make_shared&lt;MyClass&gt;();</code></li></ul></li><li>第三种正确用法<ul><li><code>shared_ptr&lt;int&gt; sp;</code></li><li><code>sp</code> 是指向 <code>int</code> 的智能指针，但目前指向的内存地址为空，即属于空指针</li></ul></li></ul></li></ul><h6 id="错误的使用语法"><a href="#错误的使用语法" class="headerlink" title="错误的使用语法"></a>错误的使用语法</h6><ul><li><code>shared_ptr</code> 的错误用法<ul><li>第一种错误用法（代码编译失败）<ul><li><code>shared_ptr&lt;int&gt; sp = new int(100);</code></li><li>智能指针是 <code>explicit</code>，不可以进行隐式类型转换，必须用直接初始化形式</li></ul></li><li>第二种错误用法（代码可以正常运行，但不推荐使用）<ul><li><code>int *pi = new int(100); shared_ptr&lt;int&gt; spi(pi);</code></li><li>裸指针与智能指针尽量不要混用，否则会影响代码的健壮性</li></ul></li></ul></li></ul><h5 id="shared-ptr-的引用计数"><a href="#shared-ptr-的引用计数" class="headerlink" title="shared_ptr 的引用计数"></a>shared_ptr 的引用计数</h5><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在 C++ 中，<code>shared_ptr</code> 的引用计数操作是线程安全的（使用原子操作），但 <code>shared_ptr</code> 指向的对象本身不是线程安全的，需要额外同步处理。</p></div><h6 id="引用计数增加"><a href="#引用计数增加" class="headerlink" title="引用计数增加"></a>引用计数增加</h6><ul><li><code>shared_ptr</code> 引用计数增加的说明<ul><li>移动操作：移动构造 / 移动赋值不会增加原对象的引用计数，只是转移所有权  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; p1 = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">10</span>);  <span class="comment">// 引用计数为 1</span></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; p2 = std::<span class="built_in">move</span>(p1);              <span class="comment">// 移动构造，p1 变为空指针，p1 指向的原对象的引用计数仍为 1</span></span><br><span class="line">p1 = std::<span class="built_in">move</span>(p2);                              <span class="comment">// 移动赋值，同样不增加引用计数</span></span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul><hr><ul><li><code>shared_ptr</code> 引用计数增加的情况<ul><li> (1) 拷贝构造  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; p1 = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">42</span>);  <span class="comment">// 引用计数为 1</span></span><br><span class="line"><span class="function">shared_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">p2</span><span class="params">(p1)</span></span>;                          <span class="comment">// 引用计数为 2</span></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; p3 = p1;                         <span class="comment">// 引用计数为 3</span></span><br></pre></td></tr></tbody></table></figure></li><li>(2) 拷贝赋值  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; p1 = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">10</span>);  <span class="comment">// 引用计数为 1</span></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; p2 = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">20</span>);  <span class="comment">// 引用计数为 1</span></span><br><span class="line">p2 = p1;                                         <span class="comment">// p2 改为指向 p1 的对象，p2 指向原对象的引用计数从 1 减少到 0，释放资源</span></span><br><span class="line">                                                 <span class="comment">// p1 指向的对象引用计数从 1 增加到 2</span></span><br></pre></td></tr></tbody></table></figure></li><li>(3) 从 <code>weak_ptr</code> 构造 <code>shared_ptr</code>  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">5</span>);</span><br><span class="line">std::weak_ptr&lt;<span class="keyword">int</span>&gt; wp = sp;           <span class="comment">// 不影响引用计数</span></span><br><span class="line"></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp2 = wp.<span class="built_in">lock</span>();                 <span class="comment">// 成功时返回 shared_ptr，引用计数加 1</span></span><br><span class="line"><span class="keyword">if</span> (sp2) {</span><br><span class="line">    <span class="comment">// sp2 有效，原对象引用计数变为 2</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li>(4) 智能指针作为函数的形参（按值传递）  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 注意：如果这里的形参是引用（按引用传递），那么智能指针的引用计数不会增加</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(shared_ptr&lt;<span class="keyword">int</span>&gt; sp)</span> </span>{</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);  <span class="comment">// 引用计数为 1</span></span><br><span class="line"><span class="built_in">func</span>(sp);                                    <span class="comment">// 引用计数为 2</span></span><br></pre></td></tr></tbody></table></figure></li><li>(5) 智能指针作为函数的返回值  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">std::shared_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">create</span><span class="params">()</span> </span>{</span><br><span class="line">    shared_ptr&lt;<span class="keyword">int</span>&gt; sp = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">50</span>);  <span class="comment">// 引用计数为 1</span></span><br><span class="line">    <span class="keyword">return</span> sp;                                       <span class="comment">// 返回时可能移动或拷贝，但不一定增加引用计数</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = <span class="built_in">create</span>();  <span class="comment">// 通常会使用移动语义，引用计数保持 1</span></span><br><span class="line">                                <span class="comment">// 但如果编译器没有优化，可能会临时增加引用计数到 2</span></span><br></pre></td></tr></tbody></table></figure></li><li>(6) 容器操作（拷贝元素）  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">std::vector&lt;std::shared_ptr&lt;<span class="keyword">int</span>&gt;&gt; vec;</span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">10</span>);     <span class="comment">// 引用计数为 1</span></span><br><span class="line">vec.<span class="built_in">push_back</span>(sp);                                  <span class="comment">// 拷贝到容器，引用计数为 2</span></span><br><span class="line">vec.<span class="built_in">insert</span>(vec.<span class="built_in">begin</span>(), sp);                        <span class="comment">// 再次拷贝到容器，引用计数为 3</span></span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul><h6 id="引用计数减少"><a href="#引用计数减少" class="headerlink" title="引用计数减少"></a>引用计数减少</h6><ul><li><code>shared_ptr</code> 引用计数减少的说明<ul><li>当引用计数减少到 0 时，<code>shared_ptr</code> 会：<ul><li>(1) 调用删除器释放管理的对象</li><li> (2) 释放控制块（如果没有 <code>weak_ptr</code> 指向它）</li></ul></li><li>移动操作：移动构造 / 移动赋值不会减少原对象的引用计数，只是转移所有权  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; p1 = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">42</span>);    <span class="comment">// 引用计数为 1</span></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; p2 = std::<span class="built_in">move</span>(p1);                <span class="comment">// 移动后 p1 变为空指针，但 p1 原来的引用计数不变（转移而非减少）</span></span><br></pre></td></tr></tbody></table></figure></li><li><code>weak_ptr</code> 不会影响引用计数，但 <code>weak_ptr</code> 可以延长控制块的生存时间  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">10</span>);</span><br><span class="line">std::weak_ptr&lt;<span class="keyword">int</span>&gt; wp = sp;  <span class="comment">// 不影响引用计数</span></span><br><span class="line">sp.<span class="built_in">reset</span>();                  <span class="comment">// 引用计数从 1 减到 0，对象销毁</span></span><br><span class="line">                             <span class="comment">// 但控制块仍存在，直到 wp 也销毁</span></span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul><hr><ul><li><code>shared_ptr</code> 引用计数减少的情况<ul><li> (1) 智能指针对象被销毁  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">()</span> </span>{</span><br><span class="line">    shared_ptr&lt;<span class="keyword">int</span>&gt; p = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">42</span>);  <span class="comment">// 引用计数 = 1</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">} <span class="comment">// 离开作用域，p 被销毁，引用计数减为 0，释放资源</span></span><br></pre></td></tr></tbody></table></figure></li><li>(2) 智能指针指向新的对象  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);  <span class="comment">// 引用计数为 1</span></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp2 = sp;                    <span class="comment">// 引用计数为 2</span></span><br><span class="line">sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">200</span>);                  <span class="comment">// sp 指向新对象，sp 指向的原对象的引用计数减为 1</span></span><br></pre></td></tr></tbody></table></figure></li><li>(3) 智能指针的 <code>reset()</code> 方法被调用  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; p = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">10</span>);     <span class="comment">// 引用计数为 1</span></span><br><span class="line">p.<span class="built_in">reset</span>();                                         <span class="comment">// 引用计数为 0，释放资源</span></span><br><span class="line">p.<span class="built_in">reset</span>(<span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">20</span>));                              <span class="comment">// 先减原引用计数，再管理新资源</span></span><br></pre></td></tr></tbody></table></figure></li><li>(4) 容器中移除或替换元素  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">std::vector&lt;std::shared_ptr&lt;<span class="keyword">int</span>&gt;&gt; vec;</span><br><span class="line">vec.<span class="built_in">push_back</span>(std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">1</span>));    <span class="comment">// 引用计数为 1</span></span><br><span class="line">vec.<span class="built_in">push_back</span>(std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">2</span>));  </span><br><span class="line">vec.<span class="built_in">erase</span>(vec.<span class="built_in">begin</span>());                     <span class="comment">// 第一个元素引用计数减 1</span></span><br><span class="line">vec[<span class="number">0</span>] = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">3</span>);          <span class="comment">// 第二个元素引用计数减 1</span></span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul><h5 id="shared-ptr-的常用操作"><a href="#shared-ptr-的常用操作" class="headerlink" title="shared_ptr 的常用操作"></a>shared_ptr 的常用操作</h5><h6 id="use-count"><a href="#use-count" class="headerlink" title="use_count()"></a>use_count()</h6><ul><li><code>use_count()</code>：返回有多少个智能指针指向某个对象，主要用于代码调试目的 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line"><span class="keyword">int</span> count1 = sp.<span class="built_in">use_count</span>();</span><br><span class="line">cout &lt;&lt; count1 &lt;&lt; endl;  <span class="comment">// 输出 1</span></span><br><span class="line"></span><br><span class="line"><span class="function">shared_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">sp2</span><span class="params">(sp)</span></span>;</span><br><span class="line"><span class="keyword">int</span> count2 = sp.<span class="built_in">use_count</span>();</span><br><span class="line">cout &lt;&lt; count2 &lt;&lt; endl;  <span class="comment">// 输出 2</span></span><br><span class="line"></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp3;</span><br><span class="line">sp3 = sp;</span><br><span class="line"><span class="keyword">int</span> count3 = sp.<span class="built_in">use_count</span>();</span><br><span class="line">cout &lt;&lt; count3 &lt;&lt; endl;  <span class="comment">// 输出 3</span></span><br></pre></td></tr></tbody></table></figure><h6 id="unique"><a href="#unique" class="headerlink" title="unique()"></a>unique()</h6><ul><li><code>unique()</code>：判断该智能指针是否独占某个指向的对象，也就是如果只有一个智能指针指向某个对象，那么 <code>unique()</code> 会返回 <code>1</code>，否则返回 <code>0</code></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line">cout &lt;&lt; sp.<span class="built_in">unique</span>() &lt;&lt; endl;  <span class="comment">// 输出 1，表示 true</span></span><br><span class="line"></span><br><span class="line"><span class="function">shared_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">sp2</span><span class="params">(sp)</span></span>;</span><br><span class="line">cout &lt;&lt; sp.<span class="built_in">unique</span>() &lt;&lt; endl;  <span class="comment">// 输出 0，表示 false</span></span><br></pre></td></tr></tbody></table></figure><h6 id="reset"><a href="#reset" class="headerlink" title="reset()"></a>reset()</h6><ul><li><p><code>reset()</code>：用于改变或释放 <code>shared_ptr</code> 所管理的对象</p><ul><li><code>reset()</code> 不带参数：<ul><li>唯一指向时：若当前 <code>shared_ptr</code> 是唯一指向所管理对象的智能指针（即引用计数为 1），则释放原对象的内存，然后将当前 <code>shared_ptr</code> 置为空指针</li><li>非唯一指向时：若有多个 <code>shared_ptr</code> 共享同一个对象（引用计数 &gt; 1），则不释放原对象的内存，仅将原对象的引用计数减 1，然后将当前 <code>shared_ptr</code> 置为空指针</li><li>无论是哪种情况，调用 <code>reset()</code> 后，当前 <code>shared_ptr</code> 都会变为空指针（<code>nullptr</code>）</li></ul></li><li><code>reset(newPtr)</code> 带参数：<ul><li>唯一指向时：若当前 <code>shared_ptr</code> 是唯一指向所管理对象的智能指针（即引用计数为 1），则释放原对象的内存，然后将当前 <code>shared_ptr</code> 指向新对象（<code>newPtr</code>）</li><li>非唯一指向时：若有多个 <code>shared_ptr</code> 共享同一个对象（引用计数 &gt; 1），则不释放原对象的内存，仅将原对象的引用计数减 1，然后将当前 <code>shared_ptr</code> 指向新对象（<code>newPtr</code>）</li><li>无论是哪种情况，原对象的引用计数都会减少 1（唯一指向时减到 0 则释放原对象的内存，非唯一指向时仅减 1 不释放原对象内存），然后将当前 <code>shared_ptr</code> 转而管理新对象（<code>newPtr</code>）</li><li>注意：参数 <code>newPtr</code> 是裸指针（通过 <code>new</code> 得到的原始指针），另外还有两个参数的重载版本 <code>reset(newPtr, deleter)</code>，用于替换并指定自定义删除器</li></ul></li><li>如果 <code>reset()</code> 抛出异常（如自定义删除器在释放内存时抛出异常），原对象保持原样（强异常保证）</li></ul></li><li><p>(1) 代码案例一</p></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp2 = sp;  <span class="comment">// 引用计数为 2</span></span><br><span class="line">sp.<span class="built_in">reset</span>();                <span class="comment">// 非唯一指向时：不释放原对象的内存，引用计数减为 1，sp 置空，此时 sp2 仍有效</span></span><br><span class="line">sp2.<span class="built_in">reset</span>();               <span class="comment">// 唯一指向时：释放原对象的内存，引用计数减为 0，sp2 置空</span></span><br></pre></td></tr></tbody></table></figure><ul><li>(2) 代码案例二 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">auto</span> p1 = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);  <span class="comment">// 对象 A</span></span><br><span class="line"><span class="keyword">auto</span> p2 = p1;                          <span class="comment">// 对象 A，引用计数为 2</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 情况1：非唯一指向</span></span><br><span class="line">p1.<span class="built_in">reset</span>(<span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">200</span>));  <span class="comment">// 对象 A 的引用计数减为 1（不释放原对象内存），p1 现在指向新对象 B(200)，此时 p2 仍指向原对象 A(100) 且有效</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> p3 = std::make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">300</span>);  <span class="comment">// 对象 C，引用计数为 1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 情况2：唯一指向</span></span><br><span class="line">p3.<span class="built_in">reset</span>(<span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">400</span>));  <span class="comment">// 对象 C 的引用计数减为 0（释放原对象内存），p3 现在指向新对象 D(400)</span></span><br></pre></td></tr></tbody></table></figure><ul><li>(3) 代码案例三 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp;       <span class="comment">// 空指针</span></span><br><span class="line">sp.<span class="built_in">reset</span>(<span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">100</span>));   <span class="comment">// 空指针也可以通过 reset() 来初始化</span></span><br></pre></td></tr></tbody></table></figure><h6 id="解引用"><a href="#解引用" class="headerlink" title="* 解引用"></a>* 解引用</h6><ul><li>通过 <code>*</code> 获取 <code>shared_ptr</code> 所指向的对象 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line">cout &lt;&lt; *sp &lt;&lt; endl;  <span class="comment">// 输出 100</span></span><br></pre></td></tr></tbody></table></figure><h6 id="get"><a href="#get" class="headerlink" title="get()"></a>get()</h6><ul><li><code>get()</code>：返回 <code>shared_ptr</code> 存储的裸指针（通过 <code>new</code> 得到的原始指针）<ul><li><code>shared_ptr</code> 返回的裸指针，千万不要手动 <code>delete</code>，否则会导致程序运行崩溃（未定义行为）</li><li>注意：如果 <code>shared_ptr</code> 释放了所指向对象的内存，那么 <code>get()</code> 返回的裸指针也会变得无效 </li></ul></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line"><span class="keyword">int</span> *p = sp.<span class="built_in">get</span>();</span><br><span class="line">*p = <span class="number">150</span>;</span><br><span class="line">cout &lt;&lt; *p &lt;&lt; endl;  <span class="comment">// 输出 150</span></span><br></pre></td></tr></tbody></table></figure><h6 id="swap"><a href="#swap" class="headerlink" title="swap()"></a>swap()</h6><ul><li><code>swap()</code>：用于交换两个 <code>shared_ptr</code> 所指向的对象 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp2 = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">300</span>);</span><br><span class="line">sp.<span class="built_in">swap</span>(sp2);</span><br><span class="line"></span><br><span class="line">cout &lt;&lt; *sp &lt;&lt; endl;   <span class="comment">// 输出 300</span></span><br><span class="line">cout &lt;&lt; *sp2 &lt;&lt; endl;  <span class="comment">// 输出 100</span></span><br></pre></td></tr></tbody></table></figure><h6 id="nullptr"><a href="#nullptr" class="headerlink" title="= nullptr"></a>= nullptr</h6><ul><li><code>= nullptr</code><ul><li>唯一指向时：若当前 <code>shared_ptr</code> 是唯一指向所管理对象的智能指针（即引用计数为 1），则释放原对象的内存，然后将当前 <code>shared_ptr</code> 置为空指针</li><li>非唯一指向时：若有多个 <code>shared_ptr</code> 共享同一个对象（引用计数 &gt; 1），则不释放原对象的内存，仅将原对象的引用计数减 1，然后将当前 <code>shared_ptr</code> 置为空指针</li><li>无论是哪种情况，执行 <code>= nullptr</code> 后，当前 <code>shared_ptr</code> 都会变为空指针（<code>nullptr</code>）</li></ul></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp2 = sp;  <span class="comment">// 引用计数为 2</span></span><br><span class="line">sp = <span class="literal">nullptr</span>;              <span class="comment">// 非唯一指向时：不释放原对象的内存，引用计数减为 1，sp 置空，此时 sp2 仍有效</span></span><br><span class="line">sp2 = <span class="literal">nullptr</span>;             <span class="comment">// 唯一指向时：释放原对象的内存，引用计数减为 0，sp2 置空</span></span><br></pre></td></tr></tbody></table></figure><h5 id="shared-ptr-的删除器"><a href="#shared-ptr-的删除器" class="headerlink" title="shared_ptr 的删除器"></a>shared_ptr 的删除器</h5><h6 id="自定义删除器"><a href="#自定义删除器" class="headerlink" title="自定义删除器"></a>自定义删除器</h6><ul><li><p><code>shared_ptr</code> 会在一定的时机自动删除所指向的对象，并且底层使用 <code>delete</code> 操作符作为默认的资源析构方式</p></li><li><p><code>shared_ptr</code> 还支持指定自定义的删除器来取代 C++ 编译器提供的默认删除器</p></li><li><p>(1) 代码案例一</p></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">myDeleter</span><span class="params">(<span class="keyword">int</span> *p)</span> </span>{</span><br><span class="line">    <span class="keyword">delete</span> p;</span><br><span class="line">    p = <span class="literal">nullptr</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这里不能使用 make_shared，因为它不支持自定义删除器</span></span><br><span class="line"><span class="function">shared_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">sp</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>), myDeleter)</span></span>;</span><br><span class="line">sp.<span class="built_in">reset</span>();  <span class="comment">// 唯一指向时：释放原对象的内存（会调用自定义删除器），引用计数减为 0，sp 置空</span></span><br></pre></td></tr></tbody></table></figure><ul><li>(2) 代码案例二 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自定义的删除器还可以是 Lambda 表达式</span></span><br><span class="line"><span class="function">shared_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">sp</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>), [](<span class="keyword">int</span> *p) {</span></span></span><br><span class="line"><span class="params"><span class="function">    <span class="keyword">delete</span> p;</span></span></span><br><span class="line"><span class="params"><span class="function">    p = <span class="literal">nullptr</span>;</span></span></span><br><span class="line"><span class="params"><span class="function">    cout &lt;&lt; <span class="string">"delete int *"</span> &lt;&lt; endl;</span></span></span><br><span class="line"><span class="params"><span class="function">})</span></span>;</span><br></pre></td></tr></tbody></table></figure><div class="admonition warning"><p class="admonition-title">make_shared 不支持自定义删除器的原因</p><ul><li>设计目的：<code>make_shared</code> 是为了一次分配内存（控制块和对象内存在一起）和异常安全，而自定义删除器通常用于管理非 <code>new</code> 分配的资源，比如文件句柄、自定义释放逻辑（动态数组）等</li><li>类型推导问题：删除器的类型会改变 <code>shared_ptr</code> 的类型签名，而 <code>make_shared</code> 无法推断这种类型信息</li><li>语义清晰：需要自定义删除器说明资源不是普通方式分配的，此时使用 <code>new</code> 显式创建对象更符合意图</li></ul></div><h6 id="使用数组的问题"><a href="#使用数组的问题" class="headerlink" title="使用数组的问题"></a>使用数组的问题</h6><blockquote><p>如果 <code>shared_ptr</code> 指向的是数组，那么就需要自定义删除器或者特殊语法，否则 C++ 编译器不会使用 <code>delete []</code> 正确释放数组的内存，而是默认使用 <code>delete</code> 导致内存泄漏</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自定义类（带自定义的析构函数）</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">    }</span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解决使用数组的问题</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 如果 shared_ptr 指向的是数组，那么就需要自定义删除器，否则不会使用 delete [] 正确释放数组的内存</span></span><br><span class="line">    <span class="function">shared_ptr&lt;MyClass&gt; <span class="title">sp</span><span class="params">(<span class="keyword">new</span> MyClass[<span class="number">3</span>], [](MyClass *p) {</span></span></span><br><span class="line"><span class="params"><span class="function">        <span class="keyword">delete</span>[] p;</span></span></span><br><span class="line"><span class="params"><span class="function">        cout &lt;&lt; <span class="string">"delete MyClass []"</span> &lt;&lt; endl;</span></span></span><br><span class="line"><span class="params"><span class="function">    })</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br><span class="line">delete MyClass []</span><br></pre></td></tr></tbody></table></figure><blockquote><p>除了上面通过函数或者 Lambda 表达式作为自定义删除器来释放数组内存，还可以使用 <code>std::default_delete</code> 来做删除器，<code>std::default_delete</code> 是标准库（STL）里的模板类，同样可以正确释放数组的内存</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自定义类（带自定义的析构函数）</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">    }</span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解决使用数组的问题</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 如果 shared_ptr 指向的是数组，还可以使用 default_delete 来正确释放数组的内存</span></span><br><span class="line">    <span class="function">shared_ptr&lt;MyClass&gt; <span class="title">sp</span><span class="params">(<span class="keyword">new</span> MyClass[<span class="number">3</span>], default_delete&lt;MyClass[]&gt;())</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><blockquote><p>如果实在不想通过自定义删除器或者 <code>std::default_delete</code> 来正确释放数组的内存，还可以使用数组特化版本 <code>shared_ptr&lt;T[]&gt;</code>，同样可以正确释放数组内存（<strong>从 C++ 17 开始支持以下写法</strong>）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">    }</span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解决使用数组的问题</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 从 C++ 17 开始正式支持以下写法，使用数组特化版本 shared_ptr&lt;T[]&gt;，同样可以正确释放数组内存</span></span><br><span class="line">    <span class="function">shared_ptr&lt;MyClass[]&gt; <span class="title">sp2</span><span class="params">(<span class="keyword">new</span> MyClass[<span class="number">3</span>])</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><blockquote><p>同样的，还可以封装一个函数模板来正确释放数组内存</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">    }</span><br><span class="line">    ~<span class="built_in">MyClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~MyClass()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 函数模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function">shared_ptr&lt;T&gt; <span class="title">make_shared_array</span><span class="params">(<span class="keyword">size_t</span> size)</span> </span>{</span><br><span class="line">    <span class="keyword">return</span> shared_ptr&lt;T&gt;(<span class="keyword">new</span> T[size], default_delete&lt;T[]&gt;());</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解决使用数组的问题</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 调用自定义的函数模板</span></span><br><span class="line">    shared_ptr&lt;MyClass&gt; sp = make_shared_array&lt;MyClass&gt;(<span class="number">3</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br><span class="line">~MyClass()</span><br></pre></td></tr></tbody></table></figure><h6 id="同一种类型的说明"><a href="#同一种类型的说明" class="headerlink" title="同一种类型的说明"></a>同一种类型的说明</h6><div class="admonition warning"><p class="admonition-title">特别注意</p><p>如果两个 <code>shared_ptr</code> 指定了不同的删除器，只要它们所指向的对象类型是相同的，那么这两个 <code>shared_ptr</code> 也属于同一种类型。这就代表只要类型相同，就可以将它们放入到元素类型为该对象类型的容器里面。</p></div><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">auto</span> lambda1 = [](<span class="keyword">int</span> *p) {</span><br><span class="line">        <span class="keyword">delete</span> p;</span><br><span class="line">        cout &lt;&lt; <span class="string">"delete by lambda1"</span> &lt;&lt; endl;</span><br><span class="line">    };</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span> lambda2 = [](<span class="keyword">int</span> *p) {</span><br><span class="line">        <span class="keyword">delete</span> p;</span><br><span class="line">        cout &lt;&lt; <span class="string">"delete by lambda2"</span> &lt;&lt; endl;</span><br><span class="line">    };</span><br><span class="line"></span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">p1</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">100</span>), lambda1)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">p2</span><span class="params">(<span class="keyword">new</span> <span class="keyword">int</span>(<span class="number">200</span>), lambda2)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 类型相同，就可以放入到元素类型为该对象类型的容器里面</span></span><br><span class="line">    <span class="comment">// vector&lt;shared_ptr&lt;int&gt;&gt; vec{p1, p2};</span></span><br><span class="line"></span><br><span class="line">    p2 = p1;  <span class="comment">// p2 先调用 lambda2 释放自己所指向的对象，然后 p2 重新指向 p1 所指向的对象，此时 p1 所指向对象的引用计数为 2</span></span><br><span class="line">              <span class="comment">// 当作用域结束后，最终还会调用 lambda1 释放 p1、p2 共同指向的对象</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">delete by lambda2</span><br><span class="line">delete by lambda1</span><br></pre></td></tr></tbody></table></figure><h5 id="shared-ptr-的使用陷阱"><a href="#shared-ptr-的使用陷阱" class="headerlink" title="shared_ptr 的使用陷阱"></a>shared_ptr 的使用陷阱</h5><h6 id="裸指针与智能指针混合使用"><a href="#裸指针与智能指针混合使用" class="headerlink" title="裸指针与智能指针混合使用"></a>裸指针与智能指针混合使用</h6><p>将一个裸指针绑定到一个 <code>shared_ptr</code> 之后，那内存管理的责任就应该交给 <code>shared_ptr</code> 了，这个时候就不能够再通过裸指针来访问 <code>shared_ptr</code> 所指向的内存。简而言之，<strong>在 C++ 中应该尽量避免同时使用裸指针和智能指针（如 <code>shared_ptr</code>）</strong>。</p><ul><li>错误使用案例 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">proc</span><span class="params">(shared_ptr&lt;<span class="keyword">int</span>&gt; sp)</span> </span>{</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> *p = <span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">100</span>);     <span class="comment">// 裸指针</span></span><br><span class="line">    <span class="built_in">proc</span>(shared_ptr&lt;<span class="keyword">int</span>&gt;(p));  <span class="comment">// 使用 shared_ptr 临时对象作为函数参数</span></span><br><span class="line">    *p = <span class="number">45</span>;                   <span class="comment">// 错误写法（存在未定义行为），因为 proc() 执行结束后，p 指向的内存已经被 shared_ptr 临时对象释放掉</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>正确使用案例 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">proc</span><span class="params">(shared_ptr&lt;<span class="keyword">int</span>&gt; sp)</span> </span>{</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);  <span class="comment">// 定义 shared_ptr 变量，避免使用裸指针</span></span><br><span class="line">    <span class="built_in">proc</span>(sp);                                    <span class="comment">// 直接传递 shared_ptr 变量，而不是临时对象</span></span><br><span class="line">    *sp = <span class="number">45</span>;                                    <span class="comment">// 可以更改 shared_ptr 所指向的对象，不会出现未定义行为</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h6 id="使用裸指针初始化多个智能指针"><a href="#使用裸指针初始化多个智能指针" class="headerlink" title="使用裸指针初始化多个智能指针"></a>使用裸指针初始化多个智能指针</h6><p>C++ 禁止使用同一个裸指针初始化多个独立的 <code>shared_ptr</code>，核心原因是<strong>避免重复释放内存</strong>。每个 <code>shared_ptr</code> 都有自己的控制块，如果从同一个裸指针分别创建，它们会互不知情，导致析构时对同一块内存释放两次，造成未定义行为。正确的做法是先创建一个 <code>shared_ptr</code>，然后用它拷贝构造其他 <code>shared_ptr</code>，这样它们才能共享同一个控制块和引用计数。</p><ul><li>错误使用案例 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> * p = <span class="keyword">new</span> <span class="built_in"><span class="keyword">int</span></span>(<span class="number">100</span>);</span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">sp1</span><span class="params">(p)</span></span>;</span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">sp2</span><span class="params">(p)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    <span class="comment">// 离开作用域后，p 指向的内存会被 delete 两次（因为 sp1 与 sp2 的引用计数是互相独立的，没有任何关联关系），导致程序运行崩溃</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>正确使用案例 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    shared_ptr&lt;<span class="keyword">int</span>&gt; sp1 = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);  <span class="comment">// 避免使用裸指针</span></span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">sp2</span><span class="params">(sp1)</span></span>;                     <span class="comment">// 使用拷贝构造</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h6 id="谨慎使用-get-返回的裸指针"><a href="#谨慎使用-get-返回的裸指针" class="headerlink" title="谨慎使用 get() 返回的裸指针"></a>谨慎使用 get () 返回的裸指针</h6><p>在 C++ 中，需要谨慎使用 <code>shared_ptr</code> 的 <code>get()</code> 返回的裸指针，是因为<strong>这个裸指针不参与引用计数管理</strong>。如果你 <code>delete</code> 这个裸指针，会导致原始内存被释放，但 <code>shared_ptr</code> 仍认为它拥有这块内存，最终在析构时会再次尝试释放，造成<strong>重复释放内存</strong>的未定义行为。此外，<strong>如果用这个裸指针去初始化另一个 <code>shared_ptr</code>，也会造成独立的控制块，同样导致重复释放内存</strong>。<code>get()</code> 返回的裸指针仅适合用于那些不转移所有权、不延长生命周期的只读访问场景。</p><ul><li>错误使用案例 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line">    <span class="keyword">int</span> *p = sp.<span class="built_in">get</span>();</span><br><span class="line">    <span class="function">shared_ptr&lt;<span class="keyword">int</span>&gt; <span class="title">sp2</span><span class="params">(p)</span></span>;  <span class="comment">// 错误写法，不能将 get() 返回的裸指针绑定到其他 shared_ptr，否则会重复释放内存，导致程序运行崩溃</span></span><br><span class="line">    <span class="keyword">delete</span> p;                <span class="comment">// 错误写法，不能将 get() 返回的裸指针 delete 掉，否则会重复释放内存，导致程序运行崩溃</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>正确使用案例 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line">    <span class="keyword">int</span> *p = sp.<span class="built_in">get</span>();</span><br><span class="line">    <span class="comment">// 作用域结束后，p 指向的内存会被 shared_ptr 自动释放掉</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h6 id="不要将-this-作为-shared-ptr-返回"><a href="#不要将-this-作为-shared-ptr-返回" class="headerlink" title="不要将 this 作为 shared_ptr 返回"></a>不要将 this 作为 shared_ptr 返回</h6><p>在 C++ 中，将 <code>this</code>（类对象指针）作为 <code>shared_ptr</code> 返回是危险的，因为 <code>this</code> 本质上是一个原始指针，用它直接构造 <code>shared_ptr</code> 会创建一个独立于现有引用计数体系的新控制块。这会导致同一个对象被多个相互不知晓的 <code>shared_ptr</code> 管理，从而在析构时引发重复释放内存（Double Free）的未定义行为。正确的做法是让类继承自 <code>enable_shared_from_this</code>，然后通过 <code>shared_from_this()</code> 返回安全的智能指针。</p><ul><li>错误使用案例 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">shared_ptr&lt;MyClass&gt; <span class="title">getSelf</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> shared_ptr&lt;MyClass&gt;(<span class="keyword">this</span>);  <span class="comment">// 错误写法（危险操作）</span></span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">shared_ptr&lt;MyClass&gt; p1 = make_shared&lt;MyClass&gt;();</span><br><span class="line">shared_ptr&lt;MyClass&gt; p2 = p1-&gt;<span class="built_in">getSelf</span>();  <span class="comment">// p1 和 p2 独立管理同一对象（不共享引用计数），会导致重复释放内存</span></span><br></pre></td></tr></tbody></table></figure><ul><li>正确使用案例 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> :</span> <span class="keyword">public</span> enable_shared_from_this&lt;MyClass&gt; {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">shared_ptr&lt;MyClass&gt; <span class="title">getSelf</span><span class="params">()</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">shared_from_this</span>();  <span class="comment">// 安全</span></span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">shared_ptr&lt;MyClass&gt; p1 = make_shared&lt;MyClass&gt;();</span><br><span class="line">shared_ptr&lt;MyClass&gt; p2 = p1-&gt;<span class="built_in">getSelf</span>();  <span class="comment">// p1, p2 共享引用计数，不会导致重复释放内存</span></span><br></pre></td></tr></tbody></table></figure><h6 id="应避免-shared-ptr-发生循环引用"><a href="#应避免-shared-ptr-发生循环引用" class="headerlink" title="应避免 shared_ptr 发生循环引用"></a>应避免 shared_ptr 发生循环引用</h6><p>在 C++ 中，循环引用是指两个或多个 <code>shared_ptr</code> 互相持有对方的引用，导致每个对象的引用计数始终无法降为零，从而永远不会被自动析构，造成内存泄漏。这是因为 <code>shared_ptr</code> 基于引用计数机制工作，只有当计数变为零时才会释放对象，而循环引用使得每个对象至少还被一个其他对象强引用着。解决方法是使用 <code>weak_ptr</code> 来打破循环：将其中一个方向的引用改为弱引用，<code>weak_ptr</code> 不会增加引用计数，因此不影响对象的生命周期。当需要访问弱引用所指向的对象时，可以临时将其提升为 <code>shared_ptr</code>，若对象仍存在则操作成功，否则说明对象已被释放。</p><ul><li>错误使用案例 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CB</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CA</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    shared_ptr&lt;CB&gt; m_cb;</span><br><span class="line">    ~<span class="built_in">CA</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~CA()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CB</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    shared_ptr&lt;CA&gt; m_ca;</span><br><span class="line">    ~<span class="built_in">CB</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~CB()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    shared_ptr&lt;CA&gt; sp_ca = make_shared&lt;CA&gt;();</span><br><span class="line">    shared_ptr&lt;CB&gt; sp_cb = make_shared&lt;CB&gt;();</span><br><span class="line">    sp_ca-&gt;m_cb = sp_cb;</span><br><span class="line">    sp_cb-&gt;m_ca = sp_ca;</span><br><span class="line">    <span class="comment">// 离开作用域后，由于存在循环引用，CA 和 CB 的对象都不会自动析构（释放内存），导致内存泄漏</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>正确使用案例 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CB</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CA</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    shared_ptr&lt;CB&gt; m_cb;</span><br><span class="line">    ~<span class="built_in">CA</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~CA()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CB</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    weak_ptr&lt;CA&gt; m_ca;  <span class="comment">// 使用 weak_ptr 打破循环引用</span></span><br><span class="line">    ~<span class="built_in">CB</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~CB()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    shared_ptr&lt;CA&gt; sp_ca = make_shared&lt;CA&gt;();</span><br><span class="line">    shared_ptr&lt;CB&gt; sp_cb = make_shared&lt;CB&gt;();</span><br><span class="line">    sp_ca-&gt;m_cb = sp_cb;</span><br><span class="line">    sp_cb-&gt;m_ca = sp_ca;</span><br><span class="line">    <span class="comment">// 离开作用域后，由于不存在循环引用，CA 和 CB 的对象都会自动析构（释放内存），不会导致内存泄漏</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="shared-ptr-的性能分析"><a href="#shared-ptr-的性能分析" class="headerlink" title="shared_ptr 的性能分析"></a>shared_ptr 的性能分析</h4><h5 id="shared-ptr-的指针大小"><a href="#shared-ptr-的指针大小" class="headerlink" title="shared_ptr 的指针大小"></a>shared_ptr 的指针大小</h5><p>在 C++ 中，<code>weak_ptr</code> 和 <code>shared_ptr</code> 的大小是一样的，是裸指针（通过 <code>new</code> 得到的指针）大小的两倍；其中包括两个指针（如下图所示），第一个是指向 T 类型对象的指针，第二个是指向控制块的指针。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> *p;</span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp;</span><br><span class="line"><span class="keyword">int</span> length1 = <span class="built_in"><span class="keyword">sizeof</span></span>(p);   <span class="comment">// 8 个字节（64 位系统）</span></span><br><span class="line"><span class="keyword">int</span> length2 = <span class="built_in"><span class="keyword">sizeof</span></span>(sp);  <span class="comment">// 16 个字节（64 位系统）</span></span><br></pre></td></tr></tbody></table></figure><p><img data-src="../../../asset/2026/04/shared_ptr_size.png"></p><div class="admonition note"><p class="admonition-title">提示</p><p>控制块是由第一个指向某个对象的 <code>shared_ptr</code> 创建的。</p></div><h5 id="shared-ptr-的移动语义"><a href="#shared-ptr-的移动语义" class="headerlink" title="shared_ptr 的移动语义"></a>shared_ptr 的移动语义</h5><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp = make_shared&lt;<span class="keyword">int</span>&gt;(<span class="number">100</span>);</span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp2 = (<span class="built_in">move</span>(sp));  <span class="comment">// 移动构造一个新的 shared_ptr 对象 sp2</span></span><br><span class="line">cout &lt;&lt; sp2.<span class="built_in">use_count</span>() &lt;&lt; endl;   <span class="comment">// sp 不再指向原对象（置为空），但原对象的引用计数仍然是 1</span></span><br><span class="line"></span><br><span class="line">shared_ptr&lt;<span class="keyword">int</span>&gt; sp3;</span><br><span class="line">sp3 = <span class="built_in">move</span>(sp2);                  <span class="comment">// 移动赋值</span></span><br><span class="line">cout &lt;&lt; sp3.<span class="built_in">use_count</span>() &lt;&lt; endl;  <span class="comment">// sp2 不再指向原对象（置为空），但原对象的引用计数仍然是 1</span></span><br></pre></td></tr></tbody></table></figure><div class="admonition note"><p class="admonition-title">note</p><p>对于 <code>shared_ptr</code>，移动构造函数的效率高过拷贝构造函数，移动赋值运算符的效率高过拷贝赋值运算符，因为拷贝需要增加引用计数，而移动语义不需要增加引用计数。</p></div><h4 id="线程安全问题的解决"><a href="#线程安全问题的解决" class="headerlink" title="线程安全问题的解决"></a>线程安全问题的解决</h4><p>使用智能指针（如 <code>shared_ptr</code>）后，可以解决在多个线程同时访问同一个对象（共享对象）时产生的线程安全问题，详细介绍可以看 <a href="/posts/6e5acd18.html#%E5%A4%9A%E7%BA%BF%E7%A8%8B%E8%AE%BF%E9%97%AE%E5%85%B1%E4%BA%AB%E5%AF%B9%E8%B1%A1%E7%9A%84%E9%97%AE%E9%A2%98">这里</a>。</p>]]></content>
    
    
    <summary type="html">本文主要介绍 C++ 从入门到精通的内容。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="C++" scheme="https://www.techgrow.cn/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>DevOps 的技术选型介绍</title>
    <link href="https://www.techgrow.cn/posts/54626c2e.html"/>
    <id>https://www.techgrow.cn/posts/54626c2e.html</id>
    <published>2025-11-13T13:12:19.000Z</published>
    <updated>2025-11-13T13:12:19.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="主流-DevOps-技术栈"><a href="#主流-DevOps-技术栈" class="headerlink" title="主流 DevOps 技术栈"></a>主流 DevOps 技术栈</h2><span id="more"></span><blockquote><p>GitHub + Jenkins + Docker</p></blockquote><p><img data-src="../../../asset/2025/11/devops-1.png"></p><blockquote><p>GitLab + Jenkins + Harbor + Kubernetes + Docker </p></blockquote><p><img data-src="../../../asset/2025/11/devops-3.png"></p><blockquote><p>基于 Kubernetes 的微服务部署与监控运维架构</p></blockquote><p><img data-src="../../../asset/2025/11/devops-4.png"></p><blockquote><p>Jenkins 作为 CD / DevOps 生态的核心</p></blockquote><p><img data-src="../../../asset/2025/11/devops-2.png"></p>]]></content>
    
    
    <summary type="html">本文主要介绍 DevOps 的技术选型介绍。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="微服务" scheme="https://www.techgrow.cn/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
    <category term="技术选型" scheme="https://www.techgrow.cn/tags/%E6%8A%80%E6%9C%AF%E9%80%89%E5%9E%8B/"/>
    
    <category term="容器化" scheme="https://www.techgrow.cn/tags/%E5%AE%B9%E5%99%A8%E5%8C%96/"/>
    
    <category term="Linux运维" scheme="https://www.techgrow.cn/tags/Linux%E8%BF%90%E7%BB%B4/"/>
    
  </entry>
  
  <entry>
    <title>C++ 从入门到精通之七</title>
    <link href="https://www.techgrow.cn/posts/e1f4608d.html"/>
    <id>https://www.techgrow.cn/posts/e1f4608d.html</id>
    <published>2025-11-03T13:55:33.000Z</published>
    <updated>2025-11-03T13:55:33.000Z</updated>
    
    <content type="html"><![CDATA[<!-- toc --><h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><ul><li><a href="/posts/332ad818.html">C++ 从入门到精通之一</a>、<a href="/posts/68a580cf.html">C++ 从入门到精通之二</a>、<a href="/posts/9ce17bf9.html">C++ 从入门到精通之三</a></li><li><a href="/posts/d198d85d.html">C++ 从入门到精通之四</a>、<a href="/posts/7246a640.html">C++ 从入门到精通之五</a>、<a href="/posts/80bc7e12.html">C++ 从入门到精通之六</a></li><li><a href="/posts/e1f4608d.html">C++ 从入门到精通之七</a>、<a href="/posts/a9b9dd7e.html">C++ 从入门到精通之八</a>、<a href="/posts/14325b2f.html">C++ 从入门到精通之九</a></li><li><a href="/posts/badb9ea0.html">C++ 从入门到精通之十</a>、<a href="/posts/c961bdb.html">C++ 从入门到精通之十一</a></li></ul><span id="more"></span><h2 id="C-模板与泛型"><a href="#C-模板与泛型" class="headerlink" title="C++ 模板与泛型"></a>C++ 模板与泛型</h2><ul><li><p>泛型编程</p><ul><li>泛型编程是一种以独立于任何特定类型的方式编写代码的编程方式。在使用泛型编程时，需要提供具体程序实例所需的类型、习惯或值。</li><li><strong>在 C++ 中，泛型编程（基于模板）可以用于实现静态多态（编译期多态），即在编译阶段根据具体类型生成对应的代码</strong>；与之对应，虚函数机制用于实现动态多态（运行时多态），通过基类指针或引用在运行时决定调用哪个派生类的实现。</li></ul></li><li><p>模板的作用  </p><ul><li>模板是泛型编程的基础。它是一种用于创建类或函数的蓝图或公式，开发者只需为这些蓝图提供足够的信息，模板便能真正执行并生成具体的代码。</li></ul></li><li><p>模板的类型支持</p><ul><li>模板支持将类型作为参数进行程序设计，从而直接支持泛型编程过程。也就是说，C++ 的模板机制允许在定义类或函数时，将类型作为参数传递，实现代码的通用性。</li></ul></li><li><p>模板的两种分类</p><ul><li>函数模板<ul><li>函数模板是用于生成特定类型函数的蓝图。它允许在定义函数时，将参数或返回值的类型作为模板参数，从而实现通用操作（如比较、交换等）。</li><li>比如，基于函数模板编写一个比较两个值大小的函数，可以适用于 <code>int</code>、<code>double</code>、<code>string</code> 等多种类型。</li></ul></li><li>类模板<ul><li>类模板是用于生成特定类型类的蓝图。它允许在定义类时，将成员变量或成员函数的类型作为模板参数，从而实现通用数据结构或容器（如栈、队列、数组等）。</li><li>比如，基于类模板实现一个可存储任意类型元素的栈类，使用时再指定具体类型（如 <code>Stack&lt;int&gt;</code>、<code>Stack&lt;string&gt;</code>）。</li></ul></li></ul></li></ul><div class="admonition note"><p class="admonition-title">扩展阅读</p><ul><li><a href="/posts/779107de.html">C++ 入门进阶之二 - 函数模板与类模板</a></li><li><a href="/posts/418bd270.html">C++ 巩固基础之二 - 函数模板与类模板</a></li></ul></div><h3 id="模板基础"><a href="#模板基础" class="headerlink" title="模板基础"></a>模板基础</h3><h4 id="函数模板"><a href="#函数模板" class="headerlink" title="函数模板"></a>函数模板</h4><h5 id="函数模板的概述"><a href="#函数模板的概述" class="headerlink" title="函数模板的概述"></a>函数模板的概述</h5><ul><li><p>模的板定义以 <code>template</code> 关键字开头</p><ul><li>后面紧跟 <code>&lt;&gt;</code>，其中称为模板参数列表（或模板实参列表）。</li></ul></li><li><p>模板参数列表规则</p><ul><li>至少包含一个模板参数。  </li><li>多个参数用逗号分隔。  </li><li>每个模板参数前需使用 <code>typename</code> 或 <code>class</code> 关键字（此处 <code>class</code> 非类定义用途）。</li></ul></li><li><p>常见写法示例</p><ul><li>单个模板参数<ul><li><code>template&lt;class T&gt;</code></li><li>或者 <code>template&lt;typename T&gt;</code></li></ul></li><li>多个模板参数：<ul><li><code>template&lt;class T, class Q&gt;</code></li><li>或者 <code>template&lt;typename T, typename Q&gt;</code></li></ul></li></ul></li><li><p>模板参数的含义</p><ul><li>表示函数定义中使用的类型或值，类似于函数参数列表的作用。</li></ul></li><li><p>模板实参的指定方式</p><ul><li>显式指定：使用 <code>&lt;&gt;</code> 包裹模板实参，比如 <code>max&lt;int&gt;(3, 5);</code>。</li><li>隐式推断：编译器根据函数调用上下文自动推断模板实参，无需显式指定。</li></ul></li><li><p>类型参数 <code>T</code> 的作用</p><ul><li><code>T</code> 是模板的类型参数，本身只是一个占位符。</li><li><strong>在模板被使用时，编译器会进行模板实例化，根据调用上下文推导出 T 的具体类型。这个过程通常发生在编译期，而不是运行时</strong>。</li></ul></li></ul><h5 id="函数模板的使用"><a href="#函数模板的使用" class="headerlink" title="函数模板的使用"></a>函数模板的使用</h5><div class="admonition note"><p class="admonition-title">关键点</p><p>函数模板的调用与普通函数的调用区别不大。在调用函数模板时，编译器会根据调用时提供的实参，去推断模板参数列表中的参数（即模板形参）的类型。有些时候，光凭函数实参是无法准确推断出模板参数的，这时候就需要使用 <code>&lt;&gt;</code> 来主动提供模板参数了，比如 <code>max&lt;int&gt;(3, 5);</code>。值得注意的是，模板参数有时是由编译器推断出来的，推断的依据是调用函数时传入的实参。当编译器推断出模板的形参类型后，编译器会实例化一个特定版本的函数。<strong>函数模板只有被调用（实例化）时，编译器才会生成对应的代码，仅定义模板编译器是不会生成任何代码的。由于编译器在生成代码的时候，需要找到函数的函数体，所以函数模板的定义通常是写在 <code>.h</code> 头文件中。</strong></p></div><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function">T <span class="title">maxNum</span><span class="params">(T a, T b)</span> </span>{</span><br><span class="line">    <span class="keyword">return</span> (a &gt; b) ? a : b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 调用时，编译器根据实参(10, 20)推断 T 为 int</span></span><br><span class="line">    <span class="keyword">int</span> result1 = <span class="built_in">maxNum</span>(<span class="number">10</span>, <span class="number">20</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 调用时，编译器根据实参(3.14, 2.71)推断 T 为 double</span></span><br><span class="line">    <span class="keyword">double</span> result2 = <span class="built_in">maxNum</span>(<span class="number">3.14</span>, <span class="number">2.71</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 如果实参类型不一致，编译器无法唯一确定 T，会导致模板参数推断失败</span></span><br><span class="line">    <span class="comment">// double result3 = max(10, 3.14);</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 显式指定模板参数</span></span><br><span class="line">    <span class="keyword">double</span> result3 = maxNum&lt;<span class="keyword">double</span>&gt;(<span class="number">10</span>, <span class="number">3.14</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="非类型模板参数"><a href="#非类型模板参数" class="headerlink" title="非类型模板参数"></a>非类型模板参数</h5><div class="admonition note"><p class="admonition-title">关键点</p><p>在定义函数模板时，因为 <code>T</code> 前边有一个 <code>typename</code> / <code>class</code>，这表示 <code>T</code> 代表一个类型，是一个类型参数。那么在模板参数列表里面，还可以定义非类型参数，这里的非类型参数代表的是一个值。既然非类型参数代表一个值，那么就不能用 <code>typename</code> / <code>class</code> 这种关键字来修饰这个值，而是必须要用具体的类型名称来指定非类型参数。比如，非类型参数 <code>S</code> 如果是个整型，就可以写成 <code>template&lt;typename T, int S&gt;</code>。当函数模板实例化时，这种非类型参数的值通常需要由用户显式指定（一般情况下编译器不会自动推断），并且这些值都必须是常量表达式，因为函数模板的实例化是在编译阶段完成的。另外，无论是函数模板还是类模板，非类型模板参数都可以指定默认值，比如 <code>template &lt;typename T, int A = 5&gt;</code>，但需要注意非类型模板参数的默认值必须从右向左连续定义。特别注意，浮点型不能作为非类型模板参数，比如 <code>float</code>、<code>double</code>。</p></div><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cstring&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数模板（使用非类型模板参数）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">int</span> A, <span class="keyword">int</span> B&gt;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sum</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">return</span> A + B;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数模板（使用非类型模板参数）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">unsigned</span> L1, <span class="keyword">unsigned</span> L2&gt;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">charscomp</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span> (&amp;c1)[L1], <span class="keyword">const</span> <span class="keyword">char</span> (&amp;c2)[L2])</span> </span>{</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">strcmp</span>(c1, c2);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数模板（使用类型模板参数和非类型模板参数）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">int</span> A = <span class="number">5</span>&gt;</span><br><span class="line"><span class="keyword">void</span> <span class="built_in">repeatPrint</span>(T value) {</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; A; ++i) {</span><br><span class="line">        cout &lt;&lt; value &lt;&lt; <span class="string">" "</span>;</span><br><span class="line">    }</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">test01</span><span class="params">()</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"========== test01() ======"</span> &lt;&lt; endl;</span><br><span class="line">    <span class="comment">// 显式指定非类型模板参数</span></span><br><span class="line">    <span class="keyword">int</span> result1 = sum&lt;<span class="number">3</span>, <span class="number">5</span>&gt;();</span><br><span class="line">    cout &lt;&lt; result1 &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">test02</span><span class="params">()</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"========== test02() ======"</span> &lt;&lt; endl;</span><br><span class="line">    <span class="comment">// 编译器会自动推断非类型模板参数</span></span><br><span class="line">    <span class="keyword">int</span> result1 = <span class="built_in">charscomp</span>(<span class="string">"test2"</span>, <span class="string">"test"</span>);</span><br><span class="line">    cout &lt;&lt; result1 &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">test03</span><span class="params">()</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"========== test03() ======"</span> &lt;&lt; endl;</span><br><span class="line">    <span class="comment">// T 被推断为 int，而 A 显式指定为 2</span></span><br><span class="line">    repeatPrint&lt;<span class="keyword">int</span>, <span class="number">2</span>&gt;(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// T 被推断为 double，而 A 显式指定为 3</span></span><br><span class="line">    repeatPrint&lt;<span class="keyword">double</span>, <span class="number">3</span>&gt;(<span class="number">3.14</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// T 被推断为 const char*，而 A 显式指定为 4</span></span><br><span class="line">    repeatPrint&lt;<span class="keyword">const</span> <span class="keyword">char</span>*, <span class="number">4</span>&gt;(<span class="string">"Hi"</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// T 被推断为 float，而 A 默认指定为 5</span></span><br><span class="line">    repeatPrint&lt;<span class="keyword">float</span>&gt;(<span class="number">6.7f</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="built_in">test01</span>();</span><br><span class="line">    <span class="built_in">test02</span>();</span><br><span class="line">    <span class="built_in">test03</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">========== test01() ======</span><br><span class="line">8</span><br><span class="line">========== test02() ======</span><br><span class="line">50</span><br><span class="line">========== test03() ======</span><br><span class="line">10 10 </span><br><span class="line">3.14 3.14 3.14 </span><br><span class="line">Hi Hi Hi Hi </span><br><span class="line">6.7 6.7 6.7 6.7 6.7 </span><br></pre></td></tr></tbody></table></figure><h4 id="类模板"><a href="#类模板" class="headerlink" title="类模板"></a>类模板</h4><h5 id="类模板的概述"><a href="#类模板的概述" class="headerlink" title="类模板的概述"></a>类模板的概述</h5><p>类模板可以用来实例化一个特定的类。由于编译器不能为类模板自动推断模板参数类型，因此为了使用类模板，必须在模板名后边使用 <code>&lt;&gt;</code> 来提供额外的信息，这些信息其实就是对应着模板参数的具体类型或值。类模板的核心优势在于：同一套代码，可以应付不同的数据类型，代码精简多了，避免了为每种类型重复编写类似的类。在 C++ 模板中，只要一个名称依赖于模板参数，编译器在第一阶段无法判断它是类型还是变量，默认按非类型解析；因此无论是在类模板还是函数模板中，都必须使用 <code>typename</code> 显式声明它是类型。</p><table><thead><tr><th>对比项</th><th>函数模板</th><th>类模板</th></tr></thead><tbody><tr><td>模板实参推断</td><td>支持自动推断</td><td>不支持（C++17 之前），需显式指定</td></tr><tr><td>使用方式</td><td><code>max(1, 2)</code></td><td><code>vector&lt;int&gt;</code></td></tr><tr><td>实例化时机</td><td>调用时实例化（编译期实例化）</td><td>使用类型时实例化（编译期实例化）</td></tr><tr><td>函数 / 成员函数生成</td><td>调用时生成</td><td>成员函数按需实例化（懒生成）</td></tr></tbody></table><h5 id="类模板的使用"><a href="#类模板的使用" class="headerlink" title="类模板的使用"></a>类模板的使用</h5><div class="admonition note"><p class="admonition-title">关键点</p><p>C++ 类模板的成员函数是指在类模板中定义的函数，这些函数同样依赖于模板参数，因此在类外实现类模板的成员函数时，需要保留模板参数声明（比如 <code>template&lt;typename T&gt;</code>）并使用作用域限定符（如 <code>MyVector&lt;T&gt;::func()</code>）进行定义。由于模板是在编译期进行实例化的，成员函数的实现通常需要放在头文件中，以便编译器在使用时能够生成具体类型的代码。此外，在成员函数中如果使用了依赖于模板参数的类型（如 <code>MyVector&lt;T&gt;::iterator</code>），编译器默认无法判断其是类型还是变量，此时必须使用 <code>typename</code> 进行显式说明（如 <code>typename MyVector&lt;T&gt;::iterator</code>）。总体来说，类模板的成员函数与普通类成员函数在语法上类似，但需要额外注意模板参数、依赖名称解析以及编译期实例化机制。</p></div><ul><li><code>MyVector.hpp</code>，简单模拟实现 STL 的 vector 容器，不考虑扩容、移动语义等实现 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">pragma</span> once</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义类模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyVector</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 定义迭代器类型</span></span><br><span class="line">    <span class="keyword">using</span> iterator = T*;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 构造函数</span></span><br><span class="line">    <span class="built_in">MyVector</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyVector</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyVector</span>(<span class="keyword">const</span> MyVector&amp; v);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 赋值运算符</span></span><br><span class="line">    MyVector&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyVector&amp; v);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回迭代器的起始位置</span></span><br><span class="line">    <span class="function">iterator <span class="title">begin</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回迭代器的最后一个元素的下一个位置</span></span><br><span class="line">    <span class="function">iterator <span class="title">end</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 往容器末尾添加元素</span></span><br><span class="line">    <span class="function"><span class="keyword">bool</span> <span class="title">push_back</span><span class="params">(<span class="keyword">const</span> T&amp; t)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    T* m_data;          <span class="comment">// 底层数组</span></span><br><span class="line">    <span class="keyword">size_t</span> m_size;      <span class="comment">// 元素个数</span></span><br><span class="line">    <span class="keyword">size_t</span> m_capacity;  <span class="comment">// 最大容量</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 构造函数</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line">MyVector&lt;T&gt;::<span class="built_in">MyVector</span>() {</span><br><span class="line">    m_size = <span class="number">0</span>;</span><br><span class="line">    m_capacity = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 申请内存空间</span></span><br><span class="line">    m_data = <span class="keyword">new</span> T[m_capacity];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 析构函数</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line">MyVector&lt;T&gt;::~<span class="built_in">MyVector</span>() {</span><br><span class="line">    <span class="keyword">if</span> (m_data) {</span><br><span class="line">        <span class="keyword">delete</span>[] m_data;</span><br><span class="line">        m_data = <span class="literal">nullptr</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 拷贝构造函数</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line">MyVector&lt;T&gt;::<span class="built_in">MyVector</span>(<span class="keyword">const</span> MyVector&lt;T&gt;&amp; v) {</span><br><span class="line">    m_size = v.m_size;</span><br><span class="line">    m_capacity = v.m_capacity;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 深拷贝</span></span><br><span class="line">    m_data = <span class="keyword">new</span> T[m_capacity];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">size_t</span> i = <span class="number">0</span>; i &lt; m_size; ++i) {</span><br><span class="line">        m_data[i] = v.m_data[i];</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 赋值运算符</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line">MyVector&lt;T&gt;&amp; MyVector&lt;T&gt;::<span class="keyword">operator</span>=(<span class="keyword">const</span> MyVector&lt;T&gt;&amp; v) {</span><br><span class="line">    <span class="comment">// 防止自赋值</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span> == &amp;v) {</span><br><span class="line">        <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 申请新的内存空间</span></span><br><span class="line">    T* new_data = <span class="keyword">new</span> T[v.m_capacity];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">size_t</span> i = <span class="number">0</span>; i &lt; v.m_size; ++i) {</span><br><span class="line">        new_data[i] = v.m_data[i];</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放旧的内存空间</span></span><br><span class="line">    <span class="keyword">delete</span>[] m_data;</span><br><span class="line"></span><br><span class="line">    m_data = new_data;</span><br><span class="line">    m_size = v.m_size;</span><br><span class="line">    m_capacity = v.m_capacity;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 返回迭代器的起始位置</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">typename</span> MyVector&lt;T&gt;::iterator MyVector&lt;T&gt;::<span class="built_in">begin</span>() {</span><br><span class="line">    <span class="keyword">return</span> m_data;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 返回迭代器的最后一个元素的下一个位置</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">typename</span> MyVector&lt;T&gt;::iterator MyVector&lt;T&gt;::<span class="built_in">end</span>() {</span><br><span class="line">    <span class="keyword">return</span> m_data + m_size;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 往容器末尾添加元素</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">bool</span> MyVector&lt;T&gt;::<span class="built_in">push_back</span>(<span class="keyword">const</span> T&amp; t) {</span><br><span class="line">    <span class="keyword">if</span> (m_size == m_capacity) {</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    m_data[m_size] = t;</span><br><span class="line">    ++m_size;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li><code>main.hpp</code></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"MyVector.hpp"</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">show</span><span class="params">(MyVector&lt;<span class="keyword">int</span>&gt;&amp; v)</span> </span>{</span><br><span class="line">    <span class="keyword">for</span> (MyVector&lt;<span class="keyword">int</span>&gt;::iterator it = v.<span class="built_in">begin</span>(); it != v.<span class="built_in">end</span>(); ++it) {</span><br><span class="line">        cout &lt;&lt; *it &lt;&lt; <span class="string">" "</span>;</span><br><span class="line">    }</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    MyVector&lt;<span class="keyword">int</span>&gt; v1;</span><br><span class="line">    v1.<span class="built_in">push_back</span>(<span class="number">1</span>);</span><br><span class="line">    v1.<span class="built_in">push_back</span>(<span class="number">3</span>);</span><br><span class="line">    v1.<span class="built_in">push_back</span>(<span class="number">5</span>);</span><br><span class="line">    <span class="built_in">show</span>(v1);</span><br><span class="line"></span><br><span class="line">    MyVector&lt;<span class="keyword">int</span>&gt; v2 = v1;</span><br><span class="line">    <span class="built_in">show</span>(v2);</span><br><span class="line"></span><br><span class="line">    v2 = v1;</span><br><span class="line">    <span class="built_in">show</span>(v2);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">1 3 5 </span><br><span class="line">1 3 5 </span><br><span class="line">1 3 5 </span><br></pre></td></tr></tbody></table></figure><h5 id="非类型模板参数-1"><a href="#非类型模板参数-1" class="headerlink" title="非类型模板参数"></a>非类型模板参数</h5><div class="admonition note"><p class="admonition-title">关键点</p><p>在定义类模板时，因为 <code>T</code> 前边有一个 <code>typename</code> / <code>class</code>，这表示 <code>T</code> 代表一个类型，是一个类型参数。那么在模板参数列表里面，还可以定义非类型参数，这里的非类型参数代表的是一个值。既然非类型参数代表一个值，那么就不能用 <code>typename</code> / <code>class</code> 这种关键字来修饰这个值，而是必须要用具体的类型名称来指定非类型参数。比如，非类型参数 <code>S</code> 如果是个整型，就可以写成 <code>template&lt;typename T, int S&gt;</code>。当类模板实例化时，这种非类型参数的值通常需要由用户显式提供（一般情况下编译器不会自动推断），并且这些值都必须是常量表达式，因为类模板的实例化是在编译阶段完成的。另外，无论是函数模板还是类模板，非类型模板参数都可以指定默认值，比如 <code>template &lt;typename T, int A = 5&gt;</code>，但需要注意非类型模板参数的默认值必须从右向左连续定义。特别注意，浮点型不能作为非类型模板参数，比如 <code>float</code>、<code>double</code>。</p></div><ul><li><code>MyVector.hpp</code>，简单模拟实现 STL 的 vector 容器，不考虑扩容、移动语义等实现 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">pragma</span> once</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义类模板（使用类型模板参数和非类型模板参数）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">int</span> C = <span class="number">10</span>&gt;</span><br><span class="line">class MyVector {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 定义迭代器类型</span></span><br><span class="line">    <span class="keyword">using</span> iterator = T*;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 构造函数</span></span><br><span class="line">    <span class="built_in">MyVector</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">MyVector</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">MyVector</span>(<span class="keyword">const</span> MyVector&amp; v);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 赋值运算符</span></span><br><span class="line">    MyVector&amp; <span class="keyword">operator</span>=(<span class="keyword">const</span> MyVector&amp; v);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回迭代器的起始位置</span></span><br><span class="line">    <span class="function">iterator <span class="title">begin</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回迭代器的最后一个元素的下一个位置</span></span><br><span class="line">    <span class="function">iterator <span class="title">end</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 往容器末尾添加元素</span></span><br><span class="line">    <span class="function"><span class="keyword">bool</span> <span class="title">push_back</span><span class="params">(<span class="keyword">const</span> T&amp; t)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    T* m_data;          <span class="comment">// 底层数组</span></span><br><span class="line">    <span class="keyword">size_t</span> m_size;      <span class="comment">// 元素个数</span></span><br><span class="line">    <span class="keyword">size_t</span> m_capacity;  <span class="comment">// 最大容量</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 构造函数</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">int</span> C&gt;</span><br><span class="line">MyVector&lt;T, C&gt;::<span class="built_in">MyVector</span>() {</span><br><span class="line">    m_size = <span class="number">0</span>;</span><br><span class="line">    m_capacity = C &gt; <span class="number">0</span> ? C : <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 申请内存空间</span></span><br><span class="line">    m_data = <span class="keyword">new</span> T[m_capacity];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 析构函数</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">int</span> C&gt;</span><br><span class="line">MyVector&lt;T, C&gt;::~<span class="built_in">MyVector</span>() {</span><br><span class="line">    <span class="keyword">if</span> (m_data) {</span><br><span class="line">        <span class="keyword">delete</span>[] m_data;</span><br><span class="line">        m_data = <span class="literal">nullptr</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 拷贝构造函数</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">int</span> C&gt;</span><br><span class="line">MyVector&lt;T, C&gt;::<span class="built_in">MyVector</span>(<span class="keyword">const</span> MyVector&lt;T, C&gt;&amp; v) {</span><br><span class="line">    m_size = v.m_size;</span><br><span class="line">    m_capacity = v.m_capacity;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 深拷贝</span></span><br><span class="line">    m_data = <span class="keyword">new</span> T[m_capacity];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">size_t</span> i = <span class="number">0</span>; i &lt; m_size; ++i) {</span><br><span class="line">        m_data[i] = v.m_data[i];</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 赋值运算符</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">int</span> C&gt;</span><br><span class="line">MyVector&lt;T, C&gt;&amp; MyVector&lt;T, C&gt;::<span class="keyword">operator</span>=(<span class="keyword">const</span> MyVector&lt;T, C&gt;&amp; v) {</span><br><span class="line">    <span class="comment">// 防止自赋值</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span> == &amp;v) {</span><br><span class="line">        <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 申请新的内存空间</span></span><br><span class="line">    T* new_data = <span class="keyword">new</span> T[v.m_capacity];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">size_t</span> i = <span class="number">0</span>; i &lt; v.m_size; ++i) {</span><br><span class="line">        new_data[i] = v.m_data[i];</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放旧的内存空间</span></span><br><span class="line">    <span class="keyword">delete</span>[] m_data;</span><br><span class="line"></span><br><span class="line">    m_data = new_data;</span><br><span class="line">    m_size = v.m_size;</span><br><span class="line">    m_capacity = v.m_capacity;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 返回迭代器的起始位置</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">int</span> C&gt;</span><br><span class="line"><span class="keyword">typename</span> MyVector&lt;T, C&gt;::iterator MyVector&lt;T, C&gt;::<span class="built_in">begin</span>() {</span><br><span class="line">    <span class="keyword">return</span> m_data;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 返回迭代器的最后一个元素的下一个位置</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">int</span> C&gt;</span><br><span class="line"><span class="keyword">typename</span> MyVector&lt;T, C&gt;::iterator MyVector&lt;T, C&gt;::<span class="built_in">end</span>() {</span><br><span class="line">    <span class="keyword">return</span> m_data + m_size;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 往容器末尾添加元素</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">int</span> C&gt;</span><br><span class="line"><span class="keyword">bool</span> MyVector&lt;T, C&gt;::<span class="built_in">push_back</span>(<span class="keyword">const</span> T&amp; t) {</span><br><span class="line">    <span class="keyword">if</span> (m_size == m_capacity) {</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    m_data[m_size] = t;</span><br><span class="line">    ++m_size;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li><code>main.cpp</code></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"MyVector.hpp"</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">int</span> C&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">show</span><span class="params">(MyVector&lt;<span class="keyword">int</span>, C&gt;&amp; v)</span> </span>{</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">typename</span> MyVector&lt;<span class="keyword">int</span>, C&gt;::iterator it = v.<span class="built_in">begin</span>(); it != v.<span class="built_in">end</span>(); ++it) {</span><br><span class="line">        cout &lt;&lt; *it &lt;&lt; <span class="string">" "</span>;</span><br><span class="line">    }</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    MyVector&lt;<span class="keyword">int</span>, <span class="number">5</span>&gt; v1;</span><br><span class="line">    v1.<span class="built_in">push_back</span>(<span class="number">1</span>);</span><br><span class="line">    v1.<span class="built_in">push_back</span>(<span class="number">3</span>);</span><br><span class="line">    v1.<span class="built_in">push_back</span>(<span class="number">5</span>);</span><br><span class="line">    <span class="built_in">show</span>(v1);</span><br><span class="line"></span><br><span class="line">    MyVector&lt;<span class="keyword">int</span>, <span class="number">5</span>&gt; v2 = v1;</span><br><span class="line">    <span class="built_in">show</span>(v2);</span><br><span class="line"></span><br><span class="line">    v2 = v1;</span><br><span class="line">    <span class="built_in">show</span>(v2);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">1 3 5 </span><br><span class="line">1 3 5 </span><br><span class="line">1 3 5 </span><br></pre></td></tr></tbody></table></figure><h3 id="模板进阶"><a href="#模板进阶" class="headerlink" title="模板进阶"></a>模板进阶</h3><h4 id="typename-的使用场合"><a href="#typename-的使用场合" class="headerlink" title="typename 的使用场合"></a>typename 的使用场合</h4><p>在 C++ 中，typename 常见的三种使用场合：</p><ul><li><p>(1) 函数模板的定义：</p>  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这里可以将 typename 替换为 class</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function">T <span class="title">maxNum</span><span class="params">(T a, T b)</span> </span>{</span><br><span class="line">    ......</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>(2) 类模板的定义：</p>  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这里可以将 typename 替换为 class</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyVector</span> {</span></span><br><span class="line">    ......</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>(3) 类外访问类模板的类型成员：</p>  <figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyVector</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">using</span> iterator = T*;</span><br><span class="line"></span><br><span class="line">    <span class="function">iterator <span class="title">begin</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    ......</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="comment">// 这里访问类模板的类型成员 iterator 时，必须在最前面加上 typename，且不能替换成 class，否则编译器会误将 iterator 解析成类的静态成员</span></span><br><span class="line"><span class="keyword">typename</span> MyVector&lt;T&gt;::iterator MyVector&lt;T&gt;::<span class="built_in">begin</span>() {</span><br><span class="line">    ......</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h4 id="函数指针的使用方式"><a href="#函数指针的使用方式" class="headerlink" title="函数指针的使用方式"></a>函数指针的使用方式</h4><blockquote><p>函数指针作为其他函数的参数</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数指针类型（第一种写法，C 语义风格）</span></span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">int</span> <span class="params">(*FunType)</span><span class="params">(<span class="keyword">int</span>, <span class="keyword">int</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数指针类型（第二种写法，现代 C++ 的写法）</span></span><br><span class="line"><span class="comment">// using FunType = int(*)(int, int);</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sum</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>{</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 函数指针作为其他函数的参数</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">testFunc</span><span class="params">(<span class="keyword">int</span> i, <span class="keyword">int</span> j, FunType func)</span> </span>{</span><br><span class="line">    <span class="comment">// 通过调用函数指针调用函数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">func</span>(i, j);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> result = <span class="built_in">testFunc</span>(<span class="number">3</span>, <span class="number">5</span>, sum);</span><br><span class="line">    cout &lt;&lt; result &lt;&lt; endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">8</span><br></pre></td></tr></tbody></table></figure><h4 id="函数模板的趣味写法"><a href="#函数模板的趣味写法" class="headerlink" title="函数模板的趣味写法"></a>函数模板的趣味写法</h4><blockquote><p>在函数模板中，使用函数指针和函数对象</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数指针类型（第一种写法，C 语义风格）</span></span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">int</span> <span class="params">(*FunType)</span><span class="params">(<span class="keyword">int</span>, <span class="keyword">int</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数指针类型（第二种写法，现代 C++ 的写法）</span></span><br><span class="line"><span class="comment">// using FunType = int(*)(int, int);</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数模板（支持使用函数指针或函数对象作为其他函数的参数）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">typename</span> F&gt;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">testFunc</span><span class="params">(<span class="keyword">const</span> T &amp;i, <span class="keyword">const</span> T &amp;j, F func)</span> </span>{</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">func</span>(i, j);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义普通函数</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sum</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>{</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数对象（仿函数），重载了 operator()，可以像函数一样使用对象</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 构造函数</span></span><br><span class="line">    <span class="built_in">Test</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"Test()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 析构函数</span></span><br><span class="line">    ~<span class="built_in">Test</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"~Test()"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 拷贝构造函数</span></span><br><span class="line">    <span class="built_in">Test</span>(<span class="keyword">const</span> Test &amp;t) {</span><br><span class="line">        cout &lt;&lt; <span class="string">"Test(const Test&amp;)"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重载圆括号操作符</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">operator</span><span class="params">()</span><span class="params">(<span class="keyword">int</span> i, <span class="keyword">int</span> j)</span> <span class="keyword">const</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> i + j;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">test01</span><span class="params">()</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"------------ test01() ---------------"</span> &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 函数指针作为其他函数的参数</span></span><br><span class="line">    <span class="keyword">int</span> result = <span class="built_in">testFunc</span>(<span class="number">1</span>, <span class="number">3</span>, sum);</span><br><span class="line">    cout &lt;&lt; result &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">test02</span><span class="params">()</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"------------ test02() ---------------"</span> &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义函数对象（可调用对象）</span></span><br><span class="line">    Test obj;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 函数对象作为其他函数的参数（会额外调用 Test 类的拷贝构造函数）</span></span><br><span class="line">    <span class="keyword">int</span> result2 = <span class="built_in">testFunc</span>(<span class="number">5</span>, <span class="number">6</span>, obj);</span><br><span class="line">    cout &lt;&lt; result2 &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">test03</span><span class="params">()</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"------------ test03() ---------------"</span> &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 函数对象作为其他函数的参数（更高效的写法，直接使用临时对象，不会额外调用 Test 类的拷贝构造函数）</span></span><br><span class="line">    <span class="keyword">int</span> result3 = <span class="built_in">testFunc</span>(<span class="number">7</span>, <span class="number">8</span>, <span class="built_in">Test</span>());</span><br><span class="line">    cout &lt;&lt; result3 &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="built_in">test01</span>();</span><br><span class="line">    <span class="built_in">test02</span>();</span><br><span class="line">    <span class="built_in">test03</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">------------ test01() ---------------</span><br><span class="line">4</span><br><span class="line">------------ test02() ---------------</span><br><span class="line">Test()</span><br><span class="line">Test(const Test&amp;)</span><br><span class="line">~Test()</span><br><span class="line">11</span><br><span class="line">~Test()</span><br><span class="line">------------ test03() ---------------</span><br><span class="line">Test()</span><br><span class="line">~Test()</span><br><span class="line">15</span><br></pre></td></tr></tbody></table></figure><blockquote><p>函数指针与函数对象的简单介绍</p></blockquote><ul><li><p>函数指针（Function Pointer）</p><ul><li>定义：指向函数地址的指针<ul><li> C 语言的定义风格：<code>typedef int (*FunType)(int, int);</code></li><li>现代 C++ 的定义风格：<code>using FunType = int(*)(int, int);</code></li></ul></li><li>本质：存储函数入口地址，可用于间接调用函数</li><li>调用方式：通过指针调用，例如 <code>fp(a, b)</code> 或 <code>(*fp)(a, b)</code></li><li>特点：不具备状态、语法较复杂、灵活性较低但开销小，常用于 C 风格接口或回调函数</li></ul></li><li><p>函数对象（Function Object / 仿函数）</p><ul><li>定义：重载了 <code>operator()</code> 的类或结构体对象</li><li>本质：一个 “可调用的对象”（对象 + 函数行为）</li><li>调用方式：通过对象名加 <code>()</code> 调用，例如 <code>obj(a, b)</code></li><li>特点：可以携带状态（成员变量）、支持内联优化、可用于模板参数（STL 中广泛使用，比如 <code>std::less</code>、<code>std::greater</code>）</li></ul></li><li><p>函数指针与函数对象主要区别</p></li></ul><table><thead><tr><th>对比</th><th>函数指针</th><th>函数对象</th></tr></thead><tbody><tr><td>是否能存状态</td><td>❌</td><td>✅</td></tr><tr><td>是否可内联优化</td><td>一般不行</td><td>✅</td></tr><tr><td>灵活性</td><td>低</td><td>高</td></tr></tbody></table><h4 id="默认模板参数的使用"><a href="#默认模板参数的使用" class="headerlink" title="默认模板参数的使用"></a>默认模板参数的使用</h4><blockquote><p>函数模板使用默认模板参数</p></blockquote><ul><li>模板参数设置默认类型（<code>typename T = int</code>）</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数模板（使用模板参数默认值）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T = <span class="keyword">int</span>&gt;</span><br><span class="line">T <span class="built_in">add</span>(T a, T b) {</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="built_in">add</span>(<span class="number">1</span>, <span class="number">2</span>) &lt;&lt; endl;        <span class="comment">// 使用默认 T=int</span></span><br><span class="line">    cout &lt;&lt; add&lt;<span class="keyword">double</span>&gt;(<span class="number">1.5</span>, <span class="number">2.3</span>);    <span class="comment">// 指定 T=double</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>函数参数设置默认值（<code>T b = 10</code>）</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function">T <span class="title">add</span><span class="params">(T a, T b = <span class="number">10</span>)</span> </span>{</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="built_in">add</span>(<span class="number">5</span>, <span class="number">3</span>) &lt;&lt; endl;  <span class="comment">// 8</span></span><br><span class="line">    cout &lt;&lt; <span class="built_in">add</span>(<span class="number">5</span>) &lt;&lt; endl;     <span class="comment">// 15（b 使用默认值 10）</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>两者结合使用 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T = <span class="keyword">int</span>&gt;</span><br><span class="line">T <span class="built_in">add</span>(T a, T b = <span class="number">10</span>) {</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="built_in">add</span>(<span class="number">5</span>) &lt;&lt; endl;            <span class="comment">// T=int, b=10 → 15</span></span><br><span class="line">    cout &lt;&lt; add&lt;<span class="keyword">double</span>&gt;(<span class="number">5.5</span>) &lt;&lt; endl;  <span class="comment">// T=double, b=10 → 15.5</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><blockquote><p>类模板使用默认模板参数</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;exception&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义类模板（使用默认模板参数）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">size_t</span> size = <span class="number">10</span>&gt;</span><br><span class="line">class MyArray {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 重载 [] 操作运算符，返回非 const 对象</span></span><br><span class="line">    T&amp; <span class="keyword">operator</span>[](<span class="keyword">size_t</span> index) {</span><br><span class="line">        <span class="keyword">if</span> (index &lt; <span class="number">0</span> || index &gt; size - <span class="number">1</span>) {</span><br><span class="line">            <span class="keyword">throw</span> <span class="built_in">out_of_range</span>(<span class="string">"index out of range"</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> array[index];</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重载 [] 操作运算符，返回 const 对象</span></span><br><span class="line">    <span class="keyword">const</span> T&amp; <span class="keyword">operator</span>[](<span class="keyword">size_t</span> index) <span class="keyword">const</span> {</span><br><span class="line">        <span class="keyword">if</span> (index &lt; <span class="number">0</span> || index &gt; size - <span class="number">1</span>) {</span><br><span class="line">            <span class="keyword">throw</span> <span class="built_in">out_of_range</span>(<span class="string">"index out of range"</span>);</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> array[index];</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    T array[size];</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    MyArray&lt;<span class="keyword">int</span>&gt; array;     <span class="comment">// size = 10</span></span><br><span class="line">    array[<span class="number">0</span>] = <span class="number">10</span>;</span><br><span class="line">    array[<span class="number">1</span>] = <span class="number">20</span>;</span><br><span class="line">    cout &lt;&lt; array[<span class="number">0</span>] &lt;&lt; <span class="string">", "</span> &lt;&lt; array[<span class="number">1</span>] &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    MyArray&lt;<span class="keyword">int</span>, <span class="number">5</span>&gt; array2;     <span class="comment">// size = 5</span></span><br><span class="line">    array2[<span class="number">0</span>] = <span class="number">40</span>;</span><br><span class="line">    array2[<span class="number">1</span>] = <span class="number">50</span>;</span><br><span class="line">    cout &lt;&lt; array2[<span class="number">0</span>] &lt;&lt; <span class="string">", "</span> &lt;&lt; array2[<span class="number">1</span>] &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">10, 20</span><br><span class="line">40, 50</span><br></pre></td></tr></tbody></table></figure><h4 id="成员函数模板的使用"><a href="#成员函数模板的使用" class="headerlink" title="成员函数模板的使用"></a>成员函数模板的使用</h4><p>在 C++ 中，无论是普通类还是类模板，其成员函数（包括构造函数）都可以声明为函数模板（称为成员函数模板）。但由于虚函数（<code>virtual</code>）机制依赖于运行时多态，而函数模板是在编译期间实例化的，因此成员函数模板不能被声明为虚函数（<code>virtual</code>）。</p><div class="admonition warning"><p class="admonition-title">特别注意</p><p>对于类模板，其成员函数（包括普通成员函数和成员函数模板）通常采用 "按需实例化"（Lazy Instantiation）机制。只有当程序中实际使用（例如发生函数调用）某个成员函数时，编译器才会对该函数进行实例化。如果某个成员函数从未被使用，则不会被实例化。</p></div><h5 id="普通类的成员函数模板"><a href="#普通类的成员函数模板" class="headerlink" title="普通类的成员函数模板"></a>普通类的成员函数模板</h5><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 普通类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 成员函数模板</span></span><br><span class="line">    <span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line">    <span class="function">T <span class="title">sum</span><span class="params">(T v1, T v2)</span> </span>{</span><br><span class="line">        <span class="keyword">return</span> v1 + v2;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    Test obj;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 函数模板支持自动推断模板参数类型</span></span><br><span class="line">    <span class="keyword">int</span> result = obj.<span class="built_in">sum</span>(<span class="number">3</span>, <span class="number">5</span>);</span><br><span class="line"></span><br><span class="line">    cout &lt;&lt; result &lt;&lt; endl;     <span class="comment">// 输出 8</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="类模板的成员函数模板"><a href="#类模板的成员函数模板" class="headerlink" title="类模板的成员函数模板"></a>类模板的成员函数模板</h5><blockquote><p>第一种写法（类的声明与定义写在一起）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> A&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 成员函数模板</span></span><br><span class="line">    <span class="keyword">template</span> &lt;<span class="keyword">typename</span> B&gt;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(A v1, B v2)</span> </span>{</span><br><span class="line">        cout &lt;&lt; v1 &lt;&lt; <span class="string">", "</span> &lt;&lt; v2 &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 类模板不支持自动推断模板参数类型</span></span><br><span class="line">    Test&lt;<span class="keyword">int</span>&gt; obj;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 函数模板支持自动推断模板参数类型</span></span><br><span class="line">    obj.<span class="built_in">func</span>(<span class="number">3</span>, <span class="number">5.3</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><blockquote><p>第二种写法（类的声明与定义分开写）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> A&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 成员函数模板</span></span><br><span class="line">    <span class="keyword">template</span> &lt;<span class="keyword">typename</span> B&gt;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(A v1, B v2)</span></span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> A&gt;</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> B&gt;</span><br><span class="line"><span class="keyword">void</span> Test&lt;A&gt;::<span class="built_in">func</span>(A v1, B v2) {</span><br><span class="line">    cout &lt;&lt; v1 &lt;&lt; <span class="string">", "</span> &lt;&lt; v2 &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 类模板不支持自动推断模板参数类型</span></span><br><span class="line">    Test&lt;<span class="keyword">int</span>&gt; obj;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 函数模板支持自动推断模板参数类型</span></span><br><span class="line">    obj.<span class="built_in">func</span>(<span class="number">3</span>, <span class="number">5.3</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="模板显式实例化的使用"><a href="#模板显式实例化的使用" class="headerlink" title="模板显式实例化的使用"></a>模板显式实例化的使用</h4><p>为了避免在多个 <code>.cpp</code> 文件中重复实例化同一个模板（包括类模板和函数模板），C++ 提供了 “显式实例化”（Explicit Instantiation）机制。通过在某一个 <code>.cpp</code> 源文件中进行显式实例化，可以将模板实例化集中到单一个翻译单元，从而避免重复生成代码并减少编译和链接开销。实际上，编译器和链接器通常会对重复的模板实例进行合并（如 <code>COMDAT</code> 机制），但显式实例化仍然可以用于控制模板实例化的位置，并在一定程度上减少编译时间和代码膨胀。</p><div class="admonition warning"><p class="admonition-title">特别注意</p><ul><li>显式实例化（Explicit Instantiation）既适用于类模板，也适用于函数模板，其本质是为指定类型生成对应的模板实例代码。</li><li>函数模板的显式实例化：其本质是在某个 <code>.cpp</code> 源文件中为指定类型（如 <code>int</code>）生成对应的函数实现，其他翻译单元通过 <code>extern template</code> 声明避免函数模板重复实例化。</li><li>类模板的显式实例化：其本质是在某个 <code>.cpp</code> 源文件中为某个具体类型（如 <code>MyClass&lt;int&gt;</code>）生成完整的实例代码（包括类结构及其所有已定义的成员函数实现），其他翻译单元通过 <code>extern template</code> 声明避免类模板重复实例化。</li></ul></div><h5 id="类模板的显式实例化"><a href="#类模板的显式实例化" class="headerlink" title="类模板的显式实例化"></a>类模板的显式实例化</h5><ul><li><code>MyClass.h</code></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> MYCLASS_H</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MYCLASS_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类模板声明</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(T value)</span></span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 声明：显式实例化，可以写在多个地方（告诉编译器，MyClass&lt;int&gt; 这个实例已经在别处生成，当前翻译单元不要再实例化）</span></span><br><span class="line"><span class="keyword">extern</span> <span class="keyword">template</span> <span class="class"><span class="keyword">class</span> <span class="title">MyClass</span>&lt;</span><span class="keyword">int</span>&gt;;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br></pre></td></tr></tbody></table></figure><ul><li><code>MyClass.cpp</code></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"MyClass.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 类模板声明定义</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">void</span> MyClass&lt;T&gt;::<span class="built_in">print</span>(T value) {</span><br><span class="line">    cout &lt;&lt; <span class="string">"value: "</span> &lt;&lt; value &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义：显式实例化，只写在一个地方（告诉编译器，在当前 .cpp 源文件中，为 MyClass&lt;int&gt; 这个具体类型生成完整的实例代码）</span></span><br><span class="line"><span class="keyword">template</span> <span class="class"><span class="keyword">class</span> <span class="title">MyClass</span>&lt;</span><span class="keyword">int</span>&gt;;</span><br></pre></td></tr></tbody></table></figure><ul><li><code>main.cpp</code></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"MyClass.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    MyClass&lt;<span class="keyword">int</span>&gt; obj;</span><br><span class="line">    obj.<span class="built_in">print</span>(<span class="number">42</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="函数模板的显式实例化"><a href="#函数模板的显式实例化" class="headerlink" title="函数模板的显式实例化"></a>函数模板的显式实例化</h5><ul><li><code>func.h</code></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> FUNC_H</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> FUNC_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 函数模板声明</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">printValue</span><span class="params">(T value)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 声明：显式实例化，可以写在多个地方（告诉编译器，printValue&lt;int&gt;(int) 这个实例已经在别处生成，当前翻译单元不要再实例化）</span></span><br><span class="line"><span class="keyword">extern</span> <span class="keyword">template</span> <span class="keyword">void</span> printValue&lt;<span class="keyword">int</span>&gt;(<span class="keyword">int</span>);</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br></pre></td></tr></tbody></table></figure><ul><li><code>func.cpp</code></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"func.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 函数模板定义</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">printValue</span><span class="params">(T value)</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"value: "</span> &lt;&lt; value &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义：显式实例化，只写在一个地方（告诉编译器，在当前 .cpp 源文件中，为 printValue&lt;int&gt;(int) 这个具体类型生成完整的实例代码）</span></span><br><span class="line"><span class="keyword">template</span> <span class="keyword">void</span> printValue&lt;<span class="keyword">int</span>&gt;(<span class="keyword">int</span>);</span><br></pre></td></tr></tbody></table></figure><ul><li><code>main.cpp</code></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"func.h"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="built_in">printValue</span>(<span class="number">42</span>);  <span class="comment">// 使用 printValue&lt;int&gt;</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="使用-using-定义别名模板"><a href="#使用-using-定义别名模板" class="headerlink" title="使用 using 定义别名模板"></a>使用 using 定义别名模板</h4><p>在 C++ 中，使用 <code>using</code> 定义别名模板（也叫模板别名）是一种比 <code>typedef</code> 更强大且语法更清晰的特性。其核心形式为 <code>template&lt;typename ...&gt; using 别名 = 原类型;</code>，它允许为一个依赖于模板参数的类型表达式创建一个新的名称。与只能定义具体类型别名的 <code>typedef</code> 不同，<code>using</code> 可以直接定义别名模板（Alias Template），从而简化复杂的模板类型书写。例如，<code>template&lt;typename T&gt; using Vec = std::vector&lt;T&gt;;</code> 为 <code>std::vector</code> 创建了别名，之后 <code>Vec&lt;int&gt;</code> 就等价于 <code>std::vector&lt;int&gt;</code>。这一特性极大地提升了泛型编程中类型代码的可读性和可维护性。</p><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在 C++ 中，<code>using</code> 除了可以用于定义类型别名，还可以定义别名模板，它包含了 <code>typedef</code> 的所有功能。</p></div><h5 id="定义类型别名"><a href="#定义类型别名" class="headerlink" title="定义类型别名"></a>定义类型别名</h5><ul><li>使用 <code>typedef</code> 定义类型别名的写法（C 语言风格）</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;map&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 typedef 定义类型别名</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">typedef</span> <span class="keyword">unsigned</span> <span class="keyword">int</span> <span class="keyword">uint_t</span>;</span><br><span class="line">    <span class="keyword">uint_t</span> a;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">typedef</span> map&lt;string, <span class="keyword">int</span>&gt; map_s_i;</span><br><span class="line">    map_s_i m;</span><br><span class="line">    m.<span class="built_in">insert</span>({<span class="string">"first"</span>, <span class="number">1</span>});</span><br><span class="line">    m.<span class="built_in">insert</span>({<span class="string">"second"</span>, <span class="number">2</span>});</span><br><span class="line"></span><br><span class="line">    <span class="keyword">typedef</span> map&lt;string, string&gt; map_s_s;</span><br><span class="line">    map_s_s m2;</span><br><span class="line">    m2.<span class="built_in">insert</span>({<span class="string">"first"</span>, <span class="string">"one"</span>});</span><br><span class="line">    m2.<span class="built_in">insert</span>({<span class="string">"second"</span>, <span class="string">"two"</span>});</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>使用 <code>using</code> 定义类型别名的写法（现代 C++ 风格）</li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;map&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 typedef 定义类型别名</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">using</span> <span class="keyword">uint_t</span> = <span class="keyword">unsigned</span> <span class="keyword">int</span>;</span><br><span class="line">    <span class="keyword">uint_t</span> a;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">using</span> map_s_i = map&lt;string, <span class="keyword">int</span>&gt;;</span><br><span class="line">    map_s_i m;</span><br><span class="line">    m.<span class="built_in">insert</span>({<span class="string">"first"</span>, <span class="number">1</span>});</span><br><span class="line">    m.<span class="built_in">insert</span>({<span class="string">"second"</span>, <span class="number">2</span>});</span><br><span class="line"></span><br><span class="line">    <span class="keyword">using</span> map_s_s = map&lt;string, string&gt;;</span><br><span class="line">    map_s_s m2;</span><br><span class="line">    m2.<span class="built_in">insert</span>({<span class="string">"first"</span>, <span class="string">"one"</span>});</span><br><span class="line">    m2.<span class="built_in">insert</span>({<span class="string">"second"</span>, <span class="string">"two"</span>});</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="定义别名模板"><a href="#定义别名模板" class="headerlink" title="定义别名模板"></a>定义别名模板</h5><blockquote><p>在 C++ 中，使用 using 定义别名模板</p></blockquote><p>这里假设需要定义一个 <code>map</code> 类型，其中 <code>key</code> 的类型固定不变，但 <code>value</code> 的类型支持自定义，实现方式有两种：</p><ul><li>C++ 98 的写法 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;map&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// C++ 98 的写法，定义一个 map 类型，key 的类型固定不变，但 value 的类型可以自定义</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> st&gt;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">map_s</span> {</span></span><br><span class="line">    <span class="keyword">typedef</span> map&lt;string, st&gt; type;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    map_s&lt;<span class="keyword">int</span>&gt;::type m;</span><br><span class="line">    m.<span class="built_in">insert</span>({<span class="string">"first"</span>, <span class="number">1</span>});</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>C++ 11 的写法 </li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;map&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// C++ 11 的写法（使用别名模板），定义一个 map 类型，key 的类型固定不变，但 value 的类型可以自定义</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">str_map_t</span> = std::map&lt;string, T&gt;;  <span class="comment">// str_map_t 是类型别名</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">str_map_t</span>&lt;<span class="keyword">int</span>&gt; m;</span><br><span class="line">    m.<span class="built_in">insert</span>({<span class="string">"first"</span>, <span class="number">1</span>});</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><blockquote><p>在 C++ 中，使用 using 定义别名模板 + 函数指针</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义别名模板 + 函数指针</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">using</span> funcType = <span class="built_in">T</span> (*)(T, T);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sum</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>{</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 定义函数指针</span></span><br><span class="line">    funcType&lt;<span class="keyword">int</span>&gt; func;  <span class="comment">// funcType 是一个函数指针类型，func 是函数指针变量</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将函数地址赋值给函数指针变量</span></span><br><span class="line">    func = sum;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 调用函数指针</span></span><br><span class="line">    <span class="keyword">int</span> result = <span class="built_in">func</span>(<span class="number">1</span>, <span class="number">3</span>);</span><br><span class="line">    cout &lt;&lt; result &lt;&lt; endl;   <span class="comment">// 输出 4</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="显式指定模板实参"><a href="#显式指定模板实参" class="headerlink" title="显式指定模板实参"></a>显式指定模板实参</h4><p>在 C++ 中，” 显式指定模板实参” 是指在调用函数模板或实例化类模板时，由程序员在模板名后紧跟的尖括号内明确写出模板参数所对应的具体类型或值，而不是依赖编译器进行模板实参推导。这一语法通常用于解决编译器无法自动推导实参的场景，例如函数模板的返回类型与参数列表无关，或者存在多个模板参数但仅有部分能推导时，也可以显式指定部分实参，其余由编译器补全。通过显式指定，程序员可以精确控制模板实例化的行为，消除因类型推导可能产生的歧义。</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T1, <span class="keyword">typename</span> T2, <span class="keyword">typename</span> T3&gt;</span><br><span class="line"><span class="function">T1 <span class="title">sum</span><span class="params">(T2 i, T3 j)</span> </span>{</span><br><span class="line">    T1 result = i + j;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 显式指定模板实参，否则计算结果会出错（由于模板实参类型自动推断，导致整数溢出）</span></span><br><span class="line">    <span class="keyword">auto</span> result = sum&lt;<span class="keyword">double</span>, <span class="keyword">double</span>, <span class="keyword">double</span>&gt;(<span class="number">2000000000</span>, <span class="number">2000000000</span>);</span><br><span class="line">    cout &lt;&lt; result &lt;&lt; endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">4e+09</span><br></pre></td></tr></tbody></table></figure><h3 id="模板高级"><a href="#模板高级" class="headerlink" title="模板高级"></a>模板高级</h3><h4 id="模板特例化"><a href="#模板特例化" class="headerlink" title="模板特例化"></a>模板特例化</h4><h5 id="类模板特例化"><a href="#类模板特例化" class="headerlink" title="类模板特例化"></a>类模板特例化</h5><p>C++ 的模板特例化（Template Specialization）是一种允许为特定类型或特定值提供模板自定义实现的机制，用于处理泛型模板无法满足特殊需求的场景。它主要分为全特化（Full Specialization）和偏特化（Partial Specialization）两种形式：全特化为所有模板参数指定具体类型或值，生成一个完全确定的独立版本；偏特化则仅针对部分参数进行限制（如将泛型指针特化为特定指针类型），从而生成更特殊的模板版本。通过模板特例化，程序员可以在保持泛型接口统一的同时，为某些特定类型优化算法效率或修复语义问题，是模板元编程中实现条件分支和类型萃取的核心手段之一。值得注意的是，<strong>类模板全特化后是一个具体类（不再接受模板参数），而类模板偏特化后本质上仍然是类模板（仍然需要实例化）</strong>。</p><div class="admonition warning"><p class="admonition-title">特别注意</p><p>在 C++ 中，<strong>类模板必须先存在泛化版本（至少是声明），才能为其定义任何特化版本（全特化或偏特化）；特化是建立在泛化基础之上的特殊实现，不能独立存在</strong>。泛化版本是指定义模板时最通用、最基础的版本，其模板参数保持抽象（如 <code>typename T</code>），不针对任何具体类型或值进行特化；它是所有特化版本的原始模板，为后续可能的特例化提供了基础框架。</p></div><h6 id="类模板全特化"><a href="#类模板全特化" class="headerlink" title="类模板全特化"></a>类模板全特化</h6><blockquote><p>常规全特化（特化类模板）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义类模板（泛化版本）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">typename</span> U&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">TestClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"TestClass() 泛化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(T t, U u)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"printf(T, U) 泛化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当 T 和 U 这两个模板参数都为 int 类型时，实现一个类模板的全特化版本</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestClass</span>&lt;</span><span class="keyword">int</span>, <span class="keyword">int</span>&gt; {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">TestClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"TestClass() 全特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(<span class="keyword">int</span> t, <span class="keyword">int</span> u)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"printf(int, int) 全特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当 T 和 U 这两个模板参数都为 double 类型时，实现一个类模板的全特化版本</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestClass</span>&lt;</span><span class="keyword">double</span>, <span class="keyword">double</span>&gt; {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">TestClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"TestClass() 全特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(<span class="keyword">double</span> t, <span class="keyword">double</span> u)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"printf(double, double) 全特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 使用泛化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">int</span>, <span class="keyword">double</span>&gt; t;   <span class="comment">// 这是泛化对象</span></span><br><span class="line">    t.<span class="built_in">print</span>(<span class="number">3</span>, <span class="number">5.61</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 &lt;int, int&gt; 全特化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">int</span>, <span class="keyword">int</span>&gt; t2;     <span class="comment">// 这是全特化对象</span></span><br><span class="line">    t2.<span class="built_in">print</span>(<span class="number">7</span>, <span class="number">9</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 &lt;double, double&gt; 全特化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">double</span>, <span class="keyword">double</span>&gt; t3;     <span class="comment">// 这是全特化对象</span></span><br><span class="line">    t3.<span class="built_in">print</span>(<span class="number">2.45</span>, <span class="number">6.7</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">TestClass() 泛化版本</span><br><span class="line">printf(T, U) 泛化版本</span><br><span class="line">TestClass() 全特化版本</span><br><span class="line">printf(int, int) 全特化版本</span><br><span class="line">TestClass() 全特化版本</span><br><span class="line">printf(double, double) 全特化版本</span><br></pre></td></tr></tbody></table></figure><blockquote><p>特化成员函数，而不是类模板</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义类模板（泛化版本）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">typename</span> U&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">TestClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"TestClass() 泛化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(T t, U u)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"printf(T, U) 泛化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当 T 和 U 这两个模板类型参数都为 int 类型时，实现成员函数的特例化</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt;</span><br><span class="line"><span class="keyword">void</span> TestClass&lt;<span class="keyword">int</span>, <span class="keyword">int</span>&gt;::<span class="built_in">print</span>(<span class="keyword">int</span> t, <span class="keyword">int</span> u) {</span><br><span class="line">    cout &lt;&lt; <span class="string">"printf(int, int) 全特化版本"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当 T 和 U 这两个模板类型参数都为 double 类型时，实现成员函数的特例化</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt;</span><br><span class="line"><span class="keyword">void</span> TestClass&lt;<span class="keyword">double</span>, <span class="keyword">double</span>&gt;::<span class="built_in">print</span>(<span class="keyword">double</span> t, <span class="keyword">double</span> u) {</span><br><span class="line">    cout &lt;&lt; <span class="string">"printf(double, double) 全特化版本"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 使用泛化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">int</span>, <span class="keyword">double</span>&gt; t;   <span class="comment">// 这是泛化对象</span></span><br><span class="line">    t.<span class="built_in">print</span>(<span class="number">3</span>, <span class="number">5.61</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 &lt;int, int&gt; 全特化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">int</span>, <span class="keyword">int</span>&gt; t2;     <span class="comment">// 这是泛化对象</span></span><br><span class="line">    t2.<span class="built_in">print</span>(<span class="number">7</span>, <span class="number">9</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 &lt;double, double&gt; 全特化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">double</span>, <span class="keyword">double</span>&gt; t3;   <span class="comment">// 这是泛化对象</span></span><br><span class="line">    t3.<span class="built_in">print</span>(<span class="number">2.45</span>, <span class="number">6.7</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">TestClass() 泛化版本</span><br><span class="line">printf(T, U) 泛化版本</span><br><span class="line">TestClass() 泛化版本</span><br><span class="line">printf(int, int) 全特化版本</span><br><span class="line">TestClass() 泛化版本</span><br><span class="line">printf(double, double) 全特化版本</span><br></pre></td></tr></tbody></table></figure><h6 id="类模板偏特化"><a href="#类模板偏特化" class="headerlink" title="类模板偏特化"></a>类模板偏特化</h6><div class="admonition warning"><p class="admonition-title">特别注意</p><p>C++ 的类模板偏特化（局部特化）允许程序员在不放弃所有模板参数的情况下，对模板参数的数量子集进行具体化，或对参数的类型 / 值模式施加更具体的约束（如指针、数组、常量等），从而生成更匹配特定使用场景的模板定义。它不同于全特化（所有参数固定），也不同于函数模板的重载机制。</p></div><blockquote><p>类模板从参数数量上进行偏特化</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义类模板（泛化版本）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">typename</span> U, <span class="keyword">typename</span> W&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">TestClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"TestClass() 泛化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(T t, U u, W w)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"printf(T, U, W) 泛化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类模板偏特化，从参数数量上进行偏特化（有两个模板参数绑定了具体类型，剩下一个模板参数没绑定具体类型）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> U&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestClass</span>&lt;</span><span class="keyword">int</span>, U, <span class="keyword">int</span>&gt; {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">TestClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"TestClass() 偏特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(<span class="keyword">int</span> t, U u, <span class="keyword">int</span>)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"printf(int, U, int) 偏特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类模板偏特化，从参数数量上进行偏特化（有两个模板参数绑定了具体类型，剩下一个模板参数没绑定具体类型）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> U&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestClass</span>&lt;</span><span class="keyword">double</span>, U, <span class="keyword">double</span>&gt; {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">TestClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"TestClass() 偏特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(<span class="keyword">double</span> t, U u, <span class="keyword">double</span>)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"printf(double, U, double) 偏特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 使用泛化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">int</span>, <span class="keyword">int</span>, <span class="keyword">double</span>&gt; t1;  <span class="comment">// 这是泛化对象</span></span><br><span class="line">    t1.<span class="built_in">print</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3.14</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 &lt;int, U, int&gt; 偏特化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">int</span>, <span class="keyword">double</span>, <span class="keyword">int</span>&gt; t2;  <span class="comment">// 这是偏特化对象</span></span><br><span class="line">    t2.<span class="built_in">print</span>(<span class="number">1</span>, <span class="number">2.55</span>, <span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 &lt;double, U, double&gt; 偏特化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">double</span>, <span class="keyword">int</span>, <span class="keyword">double</span>&gt; t3;  <span class="comment">// 这是偏特化对象</span></span><br><span class="line">    t3.<span class="built_in">print</span>(<span class="number">1.0</span>, <span class="number">2</span>, <span class="number">3.14</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">TestClass() 泛化版本</span><br><span class="line">printf(T, U, W) 泛化版本</span><br><span class="line">TestClass() 偏特化版本</span><br><span class="line">printf(int, U, int) 偏特化版本</span><br><span class="line">TestClass() 偏特化版本</span><br><span class="line">printf(double, U, double) 偏特化版本</span><br></pre></td></tr></tbody></table></figure><blockquote><p>类模板从参数范围上进行偏特化</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义类模板（泛化版本）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">TestClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"TestClass() 泛化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(T t)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"printf(T) 泛化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类模板偏特化，从参数范围上进行偏特化（const T 的偏特化版本）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestClass</span>&lt;</span><span class="keyword">const</span> T&gt; {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">TestClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"TestClass() 偏特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(T t)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"printf(const T) 偏特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类模板偏特化，从参数范围上进行偏特化（T * 的偏特化版本）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestClass</span>&lt;</span>T *&gt; {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">TestClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"TestClass() 偏特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(T *t)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"printf(T *) 偏特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类模板偏特化，从参数范围上进行偏特化（T &amp; 的偏特化版本）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestClass</span>&lt;</span>T &amp;&gt; {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">TestClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"TestClass() 偏特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(T &amp;t)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"printf(T &amp;) 偏特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类模板偏特化，从参数范围上进行偏特化（T &amp;&amp; 的偏特化版本）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestClass</span>&lt;</span>T &amp;&amp;&gt; {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">TestClass</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"TestClass() 偏特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(T &amp;&amp;t)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"printf(T &amp;&amp;) 偏特化版本"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">int</span> num1 = <span class="number">10</span>;</span><br><span class="line">    <span class="keyword">const</span> <span class="keyword">int</span> num2 = <span class="number">20</span>;</span><br><span class="line">    <span class="keyword">int</span> &amp;num3 = num1;  <span class="comment">// 左值引用</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用泛化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">int</span>&gt; t1;  <span class="comment">// 这是泛化对象</span></span><br><span class="line">    t1.<span class="built_in">print</span>(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 &lt;const T&gt; 偏特化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">const</span> <span class="keyword">int</span>&gt; t2;  <span class="comment">// 这是偏特化对象</span></span><br><span class="line">    t2.<span class="built_in">print</span>(num2);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 &lt;T *&gt; 偏特化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">int</span> *&gt; t3;  <span class="comment">// 这是偏特化对象</span></span><br><span class="line">    t3.<span class="built_in">print</span>(&amp;num1);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 &lt;T &amp;&gt; 偏特化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">int</span> &amp;&gt; t4;  <span class="comment">// 这是偏特化对象</span></span><br><span class="line">    t4.<span class="built_in">print</span>(num3);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 &lt;T &amp;&amp;&gt; 偏特化版本</span></span><br><span class="line">    TestClass&lt;<span class="keyword">int</span> &amp;&amp;&gt; t5;  <span class="comment">// 这是偏特化对象</span></span><br><span class="line">    t5.<span class="built_in">print</span>(<span class="built_in">move</span>(num3));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">TestClass() 泛化版本</span><br><span class="line">printf(T) 泛化版本</span><br><span class="line">TestClass() 偏特化版本</span><br><span class="line">printf(const T) 偏特化版本</span><br><span class="line">TestClass() 偏特化版本</span><br><span class="line">printf(T *) 偏特化版本</span><br><span class="line">TestClass() 偏特化版本</span><br><span class="line">printf(T &amp;) 偏特化版本</span><br><span class="line">TestClass() 偏特化版本</span><br><span class="line">printf(T &amp;&amp;) 偏特化版本</span><br></pre></td></tr></tbody></table></figure><div class="admonition note"><p class="admonition-title">类模板从参数范围上进行偏特化，这里的参数范围是指什么？</p><ul><li>偏特化允许对类模板参数施加更具体的模式或约束（即缩小模板参数的范围），而不是仅仅减少参数个数。常见形式包括：</li><li>指针类型：<code>template &lt;typename T&gt; struct A&lt;T*&gt;</code></li><li>引用类型：<code>template &lt;typename T&gt; struct A&lt;T&amp;&gt;</code></li><li>常量类型：<code>template &lt;typename T&gt; struct A&lt;const T&gt;</code></li><li>数组类型：<code>template &lt;typename T, int N&gt; struct A&lt;T[N]&gt;</code></li><li>模板的模板参数：<code>template &lt;typename T, template&lt;typename&gt; class C&gt; struct A&lt;C&lt;T&gt;&gt;</code></li><li>常量值限制：<code>template &lt;int N&gt; struct A&lt;N&gt;;</code> 再偏特化 <code>template &lt;&gt; struct A&lt;0&gt;</code></li></ul></div><h5 id="函数模板特例化"><a href="#函数模板特例化" class="headerlink" title="函数模板特例化"></a>函数模板特例化</h5><div class="admonition warning"><p class="admonition-title">特别注意</p><p>函数模板的全特化，本质上是为该模板的某个特定参数组合提供一个显式的、独立的定义；它并不是一个新模板，也不是一个普通函数，但它参与重载决议时与普通函数有本质区别。全特化不引入重载，而是替换模板实例化时生成的版本。值得一提的是，<strong>编译器优先调用普通函数；没有普通函数时，才考虑函数模板。在同一个函数模板中，优先调用全特化版本，最后才用模板自动生成的通用版本（泛化版本）</strong>。</p></div><table><thead><tr><th>特性</th><th>函数重载</th><th>函数模板全特化</th></tr></thead><tbody><tr><td>引入新函数</td><td>✅ 是</td><td>❌ 否，依附于泛化版本（主模板）</td></tr><tr><td>独立参与重载决议</td><td>✅ 是</td><td>❌ 否</td></tr><tr><td>参数类型可完全不同</td><td>✅ 是</td><td>❌ 必须匹配泛化版本（主模板）的模板参数模式</td></tr><tr><td>可被显式实例化引用</td><td>❌ 否</td><td>✅ 是</td></tr></tbody></table><h6 id="函数模板全特化"><a href="#函数模板全特化" class="headerlink" title="函数模板全特化"></a>函数模板全特化</h6><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数模板（泛化版本）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">typename</span> U&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(T t, U u)</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"func(T, U) 泛化版本"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数模板（全特化版本）</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">int</span> t, <span class="keyword">int</span> u)</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"func(int, int) 全特化版本"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数模板（全特化版本）</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(<span class="keyword">double</span> t, <span class="keyword">double</span> u)</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"func(double, double) 全特化版本"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="comment">// 使用泛化版本</span></span><br><span class="line">    <span class="built_in">func</span>(<span class="number">3</span>, <span class="number">5.6</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 &lt;int, int&gt; 全特化版本</span></span><br><span class="line">    <span class="built_in">func</span>(<span class="number">5</span>, <span class="number">8</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 &lt;double, double&gt; 全特化版本</span></span><br><span class="line">    <span class="built_in">func</span>(<span class="number">6.5</span>, <span class="number">3.5</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">func(T, U) 泛化版本</span><br><span class="line">func(int, int) 全特化版本</span><br><span class="line">func(double, double) 全特化版本</span><br></pre></td></tr></tbody></table></figure><h6 id="函数模板偏特化"><a href="#函数模板偏特化" class="headerlink" title="函数模板偏特化"></a>函数模板偏特化</h6><div class="admonition warning"><p class="admonition-title">特别注意</p><p><strong>函数模板没有偏特化的概念，只有类模板、变量模板（C++ 14 起）支持偏特化。函数模板的 "类似偏特化" 效果通常是通过函数重载来实现</strong>。</p></div><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义函数模板（泛化版本）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">typename</span> U&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">func</span><span class="params">(T t, U u)</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"func(T, U) 泛化版本"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 错误写法，编译不通过，因为函数模板不支持偏特化</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> U&gt;</span><br><span class="line"><span class="keyword">void</span> func&lt;<span class="keyword">int</span>, U&gt;(<span class="keyword">int</span> i, U u) {</span><br><span class="line">    cout &lt;&lt; <span class="string">"func(int, U) 偏特化版本"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h5 id="模板特例化使用建议"><a href="#模板特例化使用建议" class="headerlink" title="模板特例化使用建议"></a>模板特例化使用建议</h5><p>在 C++ 中，模板特化版本的书写位置建议：</p><ul><li>模板的声明与实现都放在该 <code>.h</code> 文件中，不分离到 <code>.cpp</code> 文件中</li><li>模板的泛化版本（主模板）与特化版本应放在<strong>同一个 <code>.h</code> 文件</strong>中</li><li><code>.h</code> 文件内的顺序：<strong>先写泛化版本，后写特化版本</strong></li></ul><h4 id="可变参模板"><a href="#可变参模板" class="headerlink" title="可变参模板"></a>可变参模板</h4><h5 id="可变参函数模板"><a href="#可变参函数模板" class="headerlink" title="可变参函数模板"></a>可变参函数模板</h5><h6 id="可变参函数模板的概述"><a href="#可变参函数模板的概述" class="headerlink" title="可变参函数模板的概述"></a>可变参函数模板的概述</h6><p>在 C++ 中，可变参函数模板允许函数模板接受任意数量、任意不同类型的模板参数，其核心特点如下：</p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义可变参函数模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span>... Args&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(Args... args)</span> </span>{</span><br><span class="line">    <span class="comment">// 获取模板参数的数量</span></span><br><span class="line">    cout &lt;&lt; <span class="keyword">sizeof</span>...(args) &lt;&lt; endl;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>语法：<ul><li>用 <code>template &lt;typename... Args&gt;</code> 或 <code>template &lt;class... Args&gt;</code> 声明参数包</li></ul></li><li>参数数量：<ul><li>参数包可容纳 0 到多个任意类型的参数</li><li>可以通过 <code>sizeof...</code> 在编译期获取参数包大小（即参数数量），如 <code>sizeof...(args)</code></li></ul></li><li>参数包展开：<ul><li>使用 <code>...</code> 展开参数包，如 <code>args...</code></li></ul></li><li>递归实例化：<ul><li>典型实现方式是通过递归 + 特化终止</li></ul></li><li>常见用途：<ul><li>完美转发、元组、绑定器等场景</li></ul></li><li>注意事项：<ul><li>对于 <code>template &lt;typename... Args&gt;</code><ul><li><code>Args</code>（可变参数类型）代表一包不同的类型（不是单一的类型）</li><li><code>Args</code> 不是表示某种类型，而是表示 0 到多种不同的类型</li></ul></li><li>对于 <code>void print(Args... args) {}</code><ul><li><code>args</code>（可变参数）代表一包形参</li><li>各个形参的类型可以各不相同</li></ul></li></ul></li></ul><h6 id="可变参函数模板的使用"><a href="#可变参函数模板的使用" class="headerlink" title="可变参函数模板的使用"></a>可变参函数模板的使用</h6><blockquote><p>可变参函数模板的使用案例一（获取可变参数的数量）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义可变参函数模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span>... Args&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print1</span><span class="params">(Args... args)</span> </span>{</span><br><span class="line">    <span class="comment">// 获取模板参数的数量（第一种写法）</span></span><br><span class="line">    cout &lt;&lt; <span class="keyword">sizeof</span>...(args) &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义可变参函数模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">typename</span>... Args&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print2</span><span class="params">(T first, Args... args)</span> </span>{</span><br><span class="line">    <span class="comment">// 获取模板参数的数量（第二种写法）</span></span><br><span class="line">    cout &lt;&lt; <span class="keyword">sizeof</span>...(Args) &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="built_in">print1</span>();</span><br><span class="line">    <span class="built_in">print1</span>(<span class="number">1</span>, <span class="number">33</span>, <span class="number">44.5</span>);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print2</span>(<span class="number">1.2</span>, <span class="string">"hello"</span>);</span><br><span class="line">    <span class="built_in">print2</span>(<span class="number">2.3</span>, <span class="string">"hello"</span>, <span class="number">5</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">0</span><br><span class="line">3</span><br><span class="line">1</span><br><span class="line">2</span><br></pre></td></tr></tbody></table></figure><blockquote><p>可变参函数模板的使用案例二（通过递归调用展开参数包，即获取参数包里的参数值）</p></blockquote><p><img data-src="../../../asset/2026/04/cplusplus-template-class-ext-2.png"></p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 递归终止函数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">()</span> </span>{</span><br><span class="line">    cout &lt;&lt; <span class="string">"递归终止"</span> &lt;&lt; endl;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义可变参函数模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">typename</span>... Args&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(T first, Args... args)</span> </span>{</span><br><span class="line">    cout &lt;&lt; first &lt;&lt; endl;</span><br><span class="line">    <span class="comment">// 递归调用，展开参数包</span></span><br><span class="line">    <span class="built_in">print</span>(args...);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="built_in">print</span>(<span class="number">2</span>, <span class="number">3.14</span>, <span class="string">"hello"</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">2</span><br><span class="line">3.14</span><br><span class="line">hello</span><br><span class="line">递归终止</span><br></pre></td></tr></tbody></table></figure><blockquote><p>可变参函数模板的使用案例三（通过数组初始化时的逗号表达式配合 <code>{ }</code> 来展开参数包，即获取参数包里的参数值）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义可变参函数模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span>... Args&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(Args... args)</span> </span>{</span><br><span class="line">    <span class="comment">// 使用数组初始化，利用逗号运算符依次执行并丢弃结果</span></span><br><span class="line">    <span class="keyword">using</span> expander = <span class="keyword">int</span>[];</span><br><span class="line">    (<span class="keyword">void</span>)expander{<span class="number">0</span>, (std::cout &lt;&lt; args &lt;&lt; std::endl, <span class="number">0</span>)...};</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="built_in">print</span>(<span class="number">2</span>, <span class="number">3.14</span>, <span class="string">"hello"</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">2</span><br><span class="line">3.14</span><br><span class="line">hello</span><br></pre></td></tr></tbody></table></figure><ul><li><code>(std::cout &lt;&lt; args &lt;&lt; std::endl, 0)</code> 输出后返回 0</li><li><code>expander{0, 返回值1, 返回值2...}</code> 保证参数包的展开顺序</li><li><code>(void)</code> 用于避免编译器警告</li></ul><blockquote><p>可变参函数模板的使用案例四（通过数组初始化时的逗号表达式 + 完美转发来展开参数包，即获取参数包里的参数值）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义可变参函数模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span>... Args&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(Args&amp;&amp;... args)</span> </span>{</span><br><span class="line">    <span class="keyword">using</span> expander = <span class="keyword">int</span>[];</span><br><span class="line">    <span class="comment">// 使用数组初始化 + 完美转发，适合需要转发参数的场景</span></span><br><span class="line">    (<span class="keyword">void</span>)expander{<span class="number">0</span>, (std::cout &lt;&lt; std::forward&lt;Args&gt;(args) &lt;&lt; std::endl, <span class="number">0</span>)...};</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="built_in">print</span>(<span class="number">2</span>, <span class="number">3.14</span>, <span class="string">"hello"</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">2</span><br><span class="line">3.14</span><br><span class="line">hello</span><br></pre></td></tr></tbody></table></figure><h5 id="可变参类模板"><a href="#可变参类模板" class="headerlink" title="可变参类模板"></a>可变参类模板</h5><h6 id="可变参类模板的概述"><a href="#可变参类模板的概述" class="headerlink" title="可变参类模板的概述"></a>可变参类模板的概述</h6><p>在 C++ 中，可变参类模板允许类模板接受任意数量、任意不同类型的模板参数。</p><figure class="highlight cpp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义可变参类模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span>... Types&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line">    <span class="comment">// 获取模板参数的数量</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">constexpr</span> std::<span class="keyword">size_t</span> size = <span class="keyword">sizeof</span>...(Types);</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><ul><li><p>语法：</p><ul><li>用 <code>template &lt;typename... Types&gt;</code> 或 <code>template &lt;class... Types&gt;</code> 声明参数包</li></ul></li><li><p>参数数量：</p><ul><li>参数包可容纳 0 到多个任意类型的参数</li><li>可以通过 <code>sizeof...</code> 在编译期获取参数包大小（即参数数量），如 <code>sizeof...(Types)</code></li></ul></li><li><p>参数包展开：</p><ul><li>使用 <code>...</code> 展开参数包，常用于继承或成员变量声明，如：<code>std::tuple&lt;Types...&gt;</code></li></ul></li><li><p>递归继承：</p><ul><li>典型实现方式是通过递归继承 + 特例化终止</li></ul></li><li><p>常见用途：</p><ul><li>元组：<code>std::tuple&lt;int, double, string&gt;</code></li><li>变体：<code>std::variant&lt;int, double, string&gt;</code></li><li>类型容器：存储或操作多个类型</li><li>继承展开：多重继承参数包中的每个类型</li></ul></li><li><p>注意事项：</p><ul><li>对于 <code>template &lt;typename... Types&gt;</code><ul><li><code>Types</code>（可变参数类型）代表一包不同的类型（不是单一的类型）</li><li><code>Types</code> 不是表示某种类型，而是表示 0 到多种不同的类型</li></ul></li><li>与函数模板区别：<ul><li>类模板的参数包通常放在模板参数列表末尾（函数模板无此限制）</li></ul></li></ul></li></ul><h6 id="可变参类模板的使用"><a href="#可变参类模板的使用" class="headerlink" title="可变参类模板的使用"></a>可变参类模板的使用</h6><blockquote><p>可变参类模板的使用案例一（通过递归继承方式展开参数包，即获取参数包里的参数值）</p></blockquote><p><img data-src="../../../asset/2026/04/cplusplus-template-class-ext-1.png"></p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义可变参类模板（主模板）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span>... Others&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">myclasst</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 特例化终止</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">myclasst</span>&lt;</span>&gt; {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 默认构造函数</span></span><br><span class="line">    <span class="built_in">myclasst</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"特例化终止, address = "</span> &lt;&lt; <span class="keyword">this</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用递归继承展开参数包</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> First, <span class="keyword">typename</span>... Others&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">myclasst</span>&lt;</span>First, Others...&gt; : <span class="keyword">private</span> myclasst&lt;Others...&gt; {  <span class="comment">// 继承关系</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 默认构造函数</span></span><br><span class="line">    <span class="built_in">myclasst</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"address = "</span> &lt;&lt; <span class="keyword">this</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">myclasst</span>(First first, Others... others) : <span class="built_in">m_first</span>(first), myclasst&lt;Others...&gt;(others...) {</span><br><span class="line">        cout &lt;&lt; <span class="string">"address = "</span> &lt;&lt; <span class="keyword">this</span> &lt;&lt; endl;</span><br><span class="line">        <span class="comment">// 获取模板参数的数量</span></span><br><span class="line">        cout &lt;&lt; <span class="string">"size = "</span> &lt;&lt; <span class="keyword">sizeof</span>...(others) &lt;&lt; endl;</span><br><span class="line">        <span class="comment">// 获取模板参数的值</span></span><br><span class="line">        cout &lt;&lt; <span class="string">"value = "</span> &lt;&lt; m_first &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    First m_first;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="function">myclasst&lt;<span class="keyword">int</span>, <span class="keyword">float</span>, <span class="keyword">double</span>&gt; <span class="title">myc</span><span class="params">(<span class="number">2</span>, <span class="number">1.2f</span>, <span class="number">3.45</span>)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">特例化终止, address = 0x7ffef2def920</span><br><span class="line">address = 0x7ffef2def920</span><br><span class="line">size = 0</span><br><span class="line">value = 3.45</span><br><span class="line">address = 0x7ffef2def920</span><br><span class="line">size = 1</span><br><span class="line">value = 1.2</span><br><span class="line">address = 0x7ffef2def920</span><br><span class="line">size = 2</span><br><span class="line">value = 2</span><br></pre></td></tr></tbody></table></figure><blockquote><p>可变参类模板的使用案例二（通过递归组合方式展开参数包，即获取参数包里的参数值）</p></blockquote><p><img data-src="../../../asset/2026/04/cplusplus-template-class-ext-3.png"></p><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 可变参模板续、模板模板参数</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * (a) 可变参类模板：通过递归组合方式展开参数包</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义可变参类模板（主模板）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span>... Others&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">myclasst</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 特例化终止</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">myclasst</span>&lt;</span>&gt; {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 默认构造函数</span></span><br><span class="line">    <span class="built_in">myclasst</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"特例化终止, address = "</span> &lt;&lt; <span class="keyword">this</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用递归继承展开参数包</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> First, <span class="keyword">typename</span>... Others&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">myclasst</span>&lt;</span>First, Others...&gt; {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 默认构造函数</span></span><br><span class="line">    <span class="built_in">myclasst</span>() {</span><br><span class="line">        cout &lt;&lt; <span class="string">"address = "</span> &lt;&lt; <span class="keyword">this</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 有参构造函数</span></span><br><span class="line">    <span class="built_in">myclasst</span>(First first, Others... others) : <span class="built_in">m_first</span>(first), <span class="built_in">m_obj</span>(others...) {</span><br><span class="line">        cout &lt;&lt; <span class="string">"address = "</span> &lt;&lt; <span class="keyword">this</span> &lt;&lt; endl;</span><br><span class="line">        <span class="comment">// 获取模板参数的数量</span></span><br><span class="line">        cout &lt;&lt; <span class="string">"size = "</span> &lt;&lt; <span class="keyword">sizeof</span>...(others) &lt;&lt; endl;</span><br><span class="line">        <span class="comment">// 获取模板参数的值</span></span><br><span class="line">        cout &lt;&lt; <span class="string">"value = "</span> &lt;&lt; m_first &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    First m_first;</span><br><span class="line">    myclasst&lt;Others...&gt; m_obj;  <span class="comment">// 组合关系</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="function">myclasst&lt;<span class="keyword">int</span>, <span class="keyword">float</span>, <span class="keyword">double</span>&gt; <span class="title">myc</span><span class="params">(<span class="number">2</span>, <span class="number">1.2f</span>, <span class="number">3.45</span>)</span></span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">特例化终止, address = 0x7fff0b10b308</span><br><span class="line">address = 0x7fff0b10b300</span><br><span class="line">size = 0</span><br><span class="line">value = 3.45</span><br><span class="line">address = 0x7fff0b10b2f8</span><br><span class="line">size = 1</span><br><span class="line">value = 1.2</span><br><span class="line">address = 0x7fff0b10b2f0</span><br><span class="line">size = 2</span><br><span class="line">value = 2</span><br></pre></td></tr></tbody></table></figure><blockquote><p>可变参类模板的使用案例三（通过 <code>std::tuple</code> 和递归调用展开参数包，即获取参数包里的参数值）</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;tuple&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义可变参类模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">int</span> curCount, <span class="keyword">int</span> argsTotal, <span class="keyword">typename</span>... T&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">myclasst</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">(<span class="keyword">const</span> tuple&lt;T...&gt;&amp; t)</span> </span>{</span><br><span class="line">        cout &lt;&lt; get&lt;curCount&gt;(t) &lt;&lt; endl;</span><br><span class="line">        <span class="comment">// 递归调用</span></span><br><span class="line">        myclasst&lt;curCount + <span class="number">1</span>, argsTotal, T...&gt;::<span class="built_in">print</span>(t);</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 特化终止，用于结束递归调用</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">int</span> curCount, <span class="keyword">typename</span>... T&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">myclasst</span>&lt;</span>curCount, curCount, T...&gt; {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">(<span class="keyword">const</span> tuple&lt;T...&gt;&amp; t)</span> </span>{</span><br><span class="line">        cout &lt;&lt; <span class="string">"特化终止"</span> &lt;&lt; endl;</span><br><span class="line">    }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义可变参函数模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span>... T&gt;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">print</span><span class="params">(<span class="keyword">const</span> tuple&lt;T...&gt;&amp; t)</span> </span>{</span><br><span class="line">    myclasst&lt;<span class="number">0</span>, <span class="keyword">sizeof</span>...(T), T...&gt;::<span class="built_in">print</span>(t);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    <span class="function">tuple&lt;<span class="keyword">int</span>, <span class="keyword">float</span>, <span class="keyword">double</span>&gt; <span class="title">t</span><span class="params">(<span class="number">2</span>, <span class="number">2.5f</span>, <span class="number">3.14</span>)</span></span>;</span><br><span class="line">    <span class="built_in">print</span>(t);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>程序运行输出的结果如下：</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">2</span><br><span class="line">2.5</span><br><span class="line">3.14</span><br><span class="line">特化终止</span><br></pre></td></tr></tbody></table></figure><h5 id="模板模板参数"><a href="#模板模板参数" class="headerlink" title="模板模板参数"></a>模板模板参数</h5><blockquote><p>模板模板参数的概述</p></blockquote><ul><li>在 <code>template &lt;typename T&gt;</code> 中，<code>T</code> 叫做模板参数，因为前面都有 <code>typename</code> 修饰，所以又称为类型模板参数</li><li>模板模板参数是指：模板的参数本身又是一个模板，也就是说 <code>T</code> 这模板参数又是一个模板<ul><li>第一种写法：<code>template &lt;template &lt;typename&gt; class Container&gt;</code></li><li>第二种写法：<code>template &lt;template &lt;typename&gt; typename Container&gt;</code></li></ul></li><li>模板模板参数的特点：<ul><li>用 <code>template &lt;typename&gt;</code> 取代 <code>typename</code> 或 <code>class</code></li><li>参数名（如 <code>Container</code>）本身是一个模板，需要进一步实例化</li><li>从 C++ 17 开始可以用 <code>typename</code> 替代 <code>class</code>，比如：<code>template &lt;template &lt;typename&gt; typename Container&gt;</code></li></ul></li></ul><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 类型模板参数</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;      <span class="comment">// T 是类型</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span> {</span>};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 模板模板参数</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">template</span> &lt;<span class="keyword">typename</span>&gt; <span class="class"><span class="keyword">class</span> <span class="title">Container</span>&gt;</span>  <span class="comment">// Container 是一个模板</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    Container&lt;<span class="keyword">int</span>&gt; c;      <span class="comment">// Container 实例化为具体类型</span></span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><blockquote><p>模板模板参数的使用</p></blockquote><figure class="highlight c++"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;list&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义类模板（使用了模板模板参数）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">template</span> &lt;<span class="keyword">typename</span>&gt; <span class="keyword">typename</span> Container&gt;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span> {</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">MyClass</span>() {</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i) {</span><br><span class="line">            <span class="comment">// 插入数据</span></span><br><span class="line">            m_c.<span class="built_in">push_back</span>(i);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    Container&lt;T&gt; m_c;  <span class="comment">// Container 作为一个类模板来使用，T 作为一个类型来使用</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过 using 定义别名模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">using</span> MyList = list&lt;T, allocator&lt;T&gt;&gt;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过 using 定义别名模板</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">using</span> MyVector = vector&lt;T, allocator&lt;T&gt;&gt;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">    MyClass&lt;<span class="keyword">int</span>, MyList&gt; m1;</span><br><span class="line">    MyClass&lt;<span class="keyword">int</span>, MyVector&gt; m2;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
    
    
    <summary type="html">本文主要介绍 C++ 从入门到精通的内容。</summary>
    
    
    
    <category term="hide" scheme="https://www.techgrow.cn/categories/hide/"/>
    
    
    <category term="C++" scheme="https://www.techgrow.cn/tags/C/"/>
    
  </entry>
  
</feed>
