<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Stack Overflow</title>
  
  
  <link href="https://cpp-memory-leaks.github.io/atom.xml" rel="self"/>
  
  <link href="https://cpp-memory-leaks.github.io/"/>
  <updated>2025-09-14T14:48:01.286Z</updated>
  <id>https://cpp-memory-leaks.github.io/</id>
  
  <author>
    <name>OQS</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>深入理解VisualStudio中CXX或Qt项目的编码一致性原则</title>
    <link href="https://cpp-memory-leaks.github.io/2025/09/13/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3VisualStudio%E4%B8%ADCXX%E6%88%96Qt%E9%A1%B9%E7%9B%AE%E7%9A%84%E7%BC%96%E7%A0%81%E4%B8%80%E8%87%B4%E6%80%A7%E5%8E%9F%E5%88%99/"/>
    <id>https://cpp-memory-leaks.github.io/2025/09/13/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3VisualStudio%E4%B8%ADCXX%E6%88%96Qt%E9%A1%B9%E7%9B%AE%E7%9A%84%E7%BC%96%E7%A0%81%E4%B8%80%E8%87%B4%E6%80%A7%E5%8E%9F%E5%88%99/</id>
    <published>2025-09-13T10:22:31.000Z</published>
    <updated>2025-09-14T14:48:01.286Z</updated>
    
    <content type="html"><![CDATA[<p>在Windows平台使用Visual Studio进行C++或Qt开发时，文本编码问题是一个常见但容易被忽视的技术细节。本文将深入探讨如何通过保持编码一致性来彻底解决乱码问题。</p><h2 id="编码问题的核心：三位一体的一致性"><a href="#编码问题的核心：三位一体的一致性" class="headerlink" title="编码问题的核心：三位一体的一致性"></a>编码问题的核心：三位一体的一致性</h2><p>要完全避免乱码，需要保证三个环节的编码完全统一：</p><ol><li><strong>源代码文件编码</strong> - 源文件(.cpp&#x2F;.h)本身的存储编码格式</li><li><strong>编译器解析编码</strong> - MSVC编译器读取源文件时使用的编码假设</li><li><strong>运行时环境编码</strong> - 程序执行时控制台或GUI环境的默认编码</li></ol><h2 id="具体配置方案"><a href="#具体配置方案" class="headerlink" title="具体配置方案"></a>具体配置方案</h2><h3 id="1-统一源代码编码"><a href="#1-统一源代码编码" class="headerlink" title="1. 统一源代码编码"></a>1. 统一源代码编码</h3><p>推荐将所有源文件保存为带BOM的UTF-8编码：</p><ul><li>在VS中可通过”文件→高级保存选项”设置</li><li>或使用文本编辑器批量转换现有文件</li></ul><h3 id="2-配置编译器编码选项"><a href="#2-配置编译器编码选项" class="headerlink" title="2. 配置编译器编码选项"></a>2. 配置编译器编码选项</h3><p>在项目属性中明确指定编码参数：</p><ul><li>添加编译选项 <code>/utf-8</code> 强制使用UTF-8编码（等价于&#x2F;source-charset:utf-8 &#x2F;execution-charset:utf-8 ）**&#x2F;source-charset:utf-8 （指定<strong>源代码文件的字符编码</strong>）&#x2F;execution-charset:utf-8（设置执行编码） **</li><li>或在”配置属性→C&#x2F;C++→命令行”中直接添加</li></ul><h3 id="字符集处理流程"><a href="#字符集处理流程" class="headerlink" title="字符集处理流程"></a>字符集处理流程</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">源代码文件 → [/source-charset指定编码] → 内部表示 → [/execution-charset指定编码] → 可执行文件</span><br></pre></td></tr></table></figure><h3 id="3-处理运行时编码问题"><a href="#3-处理运行时编码问题" class="headerlink" title="3. 处理运行时编码问题"></a>3. 处理运行时编码问题</h3><p>对于控制台程序：</p><figure class="highlight plaintext"><table><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">#include &lt;windows.h&gt;</span><br><span class="line">#include &lt;iostream&gt;</span><br><span class="line"></span><br><span class="line">int main() &#123;</span><br><span class="line">    SetConsoleOutputCP(65001); // 设置为UTF-8编码</span><br><span class="line">    std::cout &lt;&lt; &quot;中文输出测试&quot; &lt;&lt; std::endl;</span><br><span class="line">    return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对于Qt程序：</p><figure class="highlight plaintext"><table><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">// 在main函数中设置编码</span><br><span class="line">QTextCodec::setCodecForLocale(QTextCodec::codecForName(&quot;UTF-8&quot;));</span><br></pre></td></tr></table></figure><h2 id="乱码现象及底层原因"><a href="#乱码现象及底层原因" class="headerlink" title="乱码现象及底层原因"></a>乱码现象及底层原因</h2><p><strong>现象</strong></p><p>​编译通过、运行通过</p><img src="/2025/09/13/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3VisualStudio%E4%B8%ADCXX%E6%88%96Qt%E9%A1%B9%E7%9B%AE%E7%9A%84%E7%BC%96%E7%A0%81%E4%B8%80%E8%87%B4%E6%80%A7%E5%8E%9F%E5%88%99/1728100107435-8091262b-cd59-41da-9b3a-b46e2f4e6ad3.png" class="" title="img"><p><strong>根本原因：</strong></p><p>​vs显示无乱码-&gt;源码字符集正确</p><p>​编译通过-&gt;编译字符集正确</p><p>​执行有乱码-&gt;执行字符集不正常</p><p>​源码字符集与执行字符集不一致，GCC的源码字符集与执行字符集默认是UTF-8编码，这是因为现在的Linux系统大多使用UTF-8编码。就算调整了Linux系统语言后，只是区域发生了变化，字符编码依然是UTF-8。所以我们的程序在“简体中文”与“英语”下，均能正确的显示中文字符。 MinGW中的GCC也是这样的，源码字符集与执行字符集默认是UTF-8编码。但是简体中文的Windows的默认编码是GB2312，会将printf输出UTF-8字符串误认为是GB2312，造成乱码。</p><p><strong>解决方案：</strong></p><h5 id="源码字符集的设置"><a href="#源码字符集的设置" class="headerlink" title="源码字符集的设置"></a><strong>源码字符集的设置</strong></h5><p>​（1）将所有的源代码格式设置为UTF-8（no Bom），VS：扩展—&gt;搜索Force UTF-8（no Bom）—&gt;关闭重启</p><img src="/2025/09/13/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3VisualStudio%E4%B8%ADCXX%E6%88%96Qt%E9%A1%B9%E7%9B%AE%E7%9A%84%E7%BC%96%E7%A0%81%E4%B8%80%E8%87%B4%E6%80%A7%E5%8E%9F%E5%88%99/1728100107409-27171a8f-3c93-4219-b804-6e7ca4e3ec8c.png" class="" title="img"><p>​（2）添加：&#x2F;source-charset:utf-8</p><img src="/2025/09/13/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3VisualStudio%E4%B8%ADCXX%E6%88%96Qt%E9%A1%B9%E7%9B%AE%E7%9A%84%E7%BC%96%E7%A0%81%E4%B8%80%E8%87%B4%E6%80%A7%E5%8E%9F%E5%88%99/1728100107420-0a8e00a4-0bd9-491a-a66d-3b488a7cdcf8.png" class="" title="img"><h5 id="执行字符集的设置"><a href="#执行字符集的设置" class="headerlink" title="执行字符集的设置"></a><strong>执行字符集的设置</strong></h5><p>命令行添加：&#x2F;execution-charset:utf-8 </p><img src="/2025/09/13/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3VisualStudio%E4%B8%ADCXX%E6%88%96Qt%E9%A1%B9%E7%9B%AE%E7%9A%84%E7%BC%96%E7%A0%81%E4%B8%80%E8%87%B4%E6%80%A7%E5%8E%9F%E5%88%99/1728100107645-83d4d9e8-d248-4f38-bbc0-db408fa4c878.png" class="" title="img"><h3 id="由中文注释问题出现的报错"><a href="#由中文注释问题出现的报错" class="headerlink" title="由中文注释问题出现的报错"></a>由中文注释问题出现的报错</h3><p><strong>现象：</strong></p><p>​编译不通过，明显语法没有错误，编译出现大量的错误</p><img src="/2025/09/13/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3VisualStudio%E4%B8%ADCXX%E6%88%96Qt%E9%A1%B9%E7%9B%AE%E7%9A%84%E7%BC%96%E7%A0%81%E4%B8%80%E8%87%B4%E6%80%A7%E5%8E%9F%E5%88%99/1728100107638-eb10980f-e2ab-4f13-8484-cd3681f7831b.png" class="" title="img"><img src="/2025/09/13/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3VisualStudio%E4%B8%ADCXX%E6%88%96Qt%E9%A1%B9%E7%9B%AE%E7%9A%84%E7%BC%96%E7%A0%81%E4%B8%80%E8%87%B4%E6%80%A7%E5%8E%9F%E5%88%99/1730791122492-f9f58a34-0a03-40ef-ad1a-62d0ec921a61.png" class="" title="img"><p><strong>根本原因：</strong></p><ol><li>visual studio不支持utf-8编码</li></ol><p><strong>解决方案：</strong></p><p>安装编码插件</p><img src="/2025/09/13/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3VisualStudio%E4%B8%ADCXX%E6%88%96Qt%E9%A1%B9%E7%9B%AE%E7%9A%84%E7%BC%96%E7%A0%81%E4%B8%80%E8%87%B4%E6%80%A7%E5%8E%9F%E5%88%99/1730791192260-f301b289-6b34-4dac-9ec5-8e0d764c7d05.png" class="" title="img"><p>重启软件后每个文件保存后就能解决</p><p>还有错误：</p><p>在命令行追加&#x2F;source-charset:utf-8 &#x2F;execution-charset:utf-8 </p><img src="/2025/09/13/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3VisualStudio%E4%B8%ADCXX%E6%88%96Qt%E9%A1%B9%E7%9B%AE%E7%9A%84%E7%BC%96%E7%A0%81%E4%B8%80%E8%87%B4%E6%80%A7%E5%8E%9F%E5%88%99/1728100108466-01dc9e33-9a19-4745-a2a7-8346c09d081f.png" class="" title="img"><h2 id="项目级最佳实践"><a href="#项目级最佳实践" class="headerlink" title="项目级最佳实践"></a>项目级最佳实践</h2><ol><li><strong>统一团队开发环境</strong>：确保所有开发者使用相同的编码设置</li><li><strong>版本控制配置</strong>：在.gitattributes中添加 <code>*.cpp text=utf-8</code></li><li><strong>静态分析集成</strong>：使用Clang-Tidy检查编码相关问题</li><li><strong>持续集成验证</strong>：在CI流水线中添加编码检查步骤</li></ol>]]></content>
    
    
    <summary type="html">aaaaaaa</summary>
    
    
    
    <category term="C++" scheme="https://cpp-memory-leaks.github.io/categories/C/"/>
    
    
    <category term="C++" scheme="https://cpp-memory-leaks.github.io/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>给程序赋予“状态”思维：聊聊Qt状态机的奇妙世界</title>
    <link href="https://cpp-memory-leaks.github.io/2025/09/13/%E7%BB%99%E7%A8%8B%E5%BA%8F%E8%B5%8B%E4%BA%88%E2%80%9C%E7%8A%B6%E6%80%81%E2%80%9D%E6%80%9D%E7%BB%B4-%E8%81%8A%E8%81%8AQt%E7%8A%B6%E6%80%81%E6%9C%BA%E7%9A%84%E5%A5%87%E5%A6%99%E4%B8%96%E7%95%8C/"/>
    <id>https://cpp-memory-leaks.github.io/2025/09/13/%E7%BB%99%E7%A8%8B%E5%BA%8F%E8%B5%8B%E4%BA%88%E2%80%9C%E7%8A%B6%E6%80%81%E2%80%9D%E6%80%9D%E7%BB%B4-%E8%81%8A%E8%81%8AQt%E7%8A%B6%E6%80%81%E6%9C%BA%E7%9A%84%E5%A5%87%E5%A6%99%E4%B8%96%E7%95%8C/</id>
    <published>2025-09-13T05:31:47.000Z</published>
    <updated>2025-09-13T10:59:36.637Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Qt-高级状态机"><a href="#Qt-高级状态机" class="headerlink" title="Qt 高级状态机"></a>Qt 高级状态机</h1><p>你是否曾写过这样的代码？</p><figure class="highlight c++"><table><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="keyword">if</span> (isPlaying) &#123;</span><br><span class="line">    <span class="built_in">pause</span>();</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (isPaused) &#123;</span><br><span class="line">    <span class="built_in">resume</span>();</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (isStopped) &#123;</span><br><span class="line">    <span class="built_in">play</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// ... 各种if else嵌套，自己都快绕晕了</span></span><br></pre></td></tr></table></figure><p>当程序需要处理多种“模式”或“状态”时，传统的 <code>if-else</code> 就像一团乱麻，难以维护且容易出错。</p><p>有没有一种更清晰、更强大的方式来管理这些状态呢？<strong>答案就是：状态机（State Machine）</strong>。今天，我们就用做蛋糕的比喻，来轻松理解这个听起来很高大上的概念。</p><h4 id="一、什么是状态机？一个做蛋糕的比喻"><a href="#一、什么是状态机？一个做蛋糕的比喻" class="headerlink" title="一、什么是状态机？一个做蛋糕的比喻"></a>一、什么是状态机？一个做蛋糕的比喻</h4><p>想象一下你正在做一个蛋糕，整个过程会经历几个明确的<strong>状态</strong>：</p><ol><li><strong>准备材料状态</strong>：你面前放着面粉、鸡蛋、糖。</li><li><strong>搅拌状态</strong>：你把所有材料混合在一起搅拌。</li><li><strong>烘烤状态</strong>：你把面糊放进烤箱。</li><li><strong>装饰状态</strong>：蛋糕出炉后，你给它抹上奶油、放上水果。</li></ol><p><strong>现在，重点来了：</strong></p><ul><li><strong>你不能在“准备材料”状态时，就直接把面粉放进烤箱</strong>，这会导致失败（甚至灾难！）。</li><li><strong>你必须先完成“搅拌”，才能进入“烘烤”</strong>。这些状态之间的切换是有严格<strong>规则</strong>的。</li></ul><p>这个<strong>有明确状态，并且状态之间按规则切换的系统，就是状态机</strong>！</p><p>在程序中，状态机就是一个<strong>官方的“状态管理员”</strong>。它负责：</p><ol><li>定义所有可能的状态（比如：停止、播放、暂停）。</li><li>定义状态切换的规则（比如：只有“播放”状态才能切换到“暂停”）。</li><li>在进入或退出某个状态时，执行相应的操作（比如：进入“播放”状态时，开始播放音乐）。</li></ol><h4 id="二、Qt状态机：你的专属“状态管理员”"><a href="#二、Qt状态机：你的专属“状态管理员”" class="headerlink" title="二、Qt状态机：你的专属“状态管理员”"></a>二、Qt状态机：你的专属“状态管理员”</h4><p>Qt框架提供了一套非常易用的状态机工具（QStateMachine），帮你轻松实现上面的想法。它有几个核心角色：</p><ul><li><strong>状态（State）</strong>：就是程序的各种模式，比如<code>停止状态</code>、<code>播放状态</code>、<code>暂停状态</code>。每个状态都知道“我是谁”。</li><li><strong>转换（Transition）</strong>：就是状态切换的<strong>条件和规则</strong>。比如“当用户点击了播放按钮（条件），就从停止状态<strong>转换</strong>到播放状态”。</li><li><strong>状态机（State Machine）</strong>：就是<strong>总管理员</strong>，它管理所有状态和转换规则，确保整个流程井然有序。</li></ul><h4 id="三、状态机有什么用？为什么我们需要它？"><a href="#三、状态机有什么用？为什么我们需要它？" class="headerlink" title="三、状态机有什么用？为什么我们需要它？"></a>三、状态机有什么用？为什么我们需要它？</h4><ol><li><strong>代码变得超级清晰</strong><ul><li>把一团乱麻的 <code>if-else</code> 逻辑，变成了直观的“状态图”。你一眼就能看明白整个程序的工作流程，就像看一张地图一样。</li></ul></li><li><strong>杜绝非法操作</strong><ul><li>状态管理员（状态机）会严格执行规则。比如，用户在“停止”状态时按下“暂停”按钮，状态机会发现这个操作没有定义规则，就不会执行任何动作。这大大提高了程序的健壮性。</li></ul></li><li><strong>管理复杂逻辑</strong><ul><li>对于有十几个甚至几十个状态的复杂程序（比如游戏角色、网络协议），状态机几乎是唯一能保持代码清晰可维护的工具。</li></ul></li><li><strong>完美匹配用户界面（UI）</strong><ul><li>在不同状态下，UI的显示是不同的。比如：<ul><li><strong>播放状态</strong>：“播放”按钮变灰（不可用），“暂停”按钮亮起。</li><li><strong>暂停状态</strong>：“暂停”按钮变灰，“播放”按钮又可用。</li></ul></li><li>用状态机来实现这种UI联动非常简单和自然。</li></ul></li></ol><h4 id="四、状态机在生活中和程序中的应用场景"><a href="#四、状态机在生活中和程序中的应用场景" class="headerlink" title="四、状态机在生活中和程序中的应用场景"></a>四、状态机在生活中和程序中的应用场景</h4><ul><li><strong>红绿灯</strong>：永远是“绿 -&gt; 黄 -&gt; 红 -&gt; 绿”的状态循环，绝不会从绿直接变红。</li><li><strong>音乐播放器</strong>：这是我们最经典的例子。“停止”、“播放”、“暂停”三个状态。</li><li><strong>游戏开发</strong>：游戏角色有“待机”、“奔跑”、“跳跃”、“攻击”等状态。你不能在“跳跃”时直接发起“攻击”，可能必须落地（回到“待机”或“奔跑”状态）后才能攻击。</li><li><strong>用户登录</strong>：“未登录”、“登录中”、“登录成功”、“登录失败”状态。根据不同的状态，给用户显示不同的界面。</li></ul><h4 id="五、状态机在程序中的实际应用"><a href="#五、状态机在程序中的实际应用" class="headerlink" title="五、状态机在程序中的实际应用"></a>五、状态机在程序中的实际应用</h4><p>下面是一个极度简化的播放器状态机示例，演示了基本结构：</p><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QStateMachine&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QState&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QFinalState&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QSignalTransition&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 假设有以下按钮和播放器核心</span></span><br><span class="line">QPushButton *playButton;</span><br><span class="line">QPushButton *pauseButton;</span><br><span class="line">QPushButton *stopButton;</span><br><span class="line"><span class="comment">// MyPlayerCore 是一个假想的播放器核心类</span></span><br><span class="line">MyPlayerCore *player; </span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 创建状态机和状态</span></span><br><span class="line">QStateMachine *machine = <span class="keyword">new</span> QStateMachine;</span><br><span class="line"></span><br><span class="line">QState *stoppedState = <span class="keyword">new</span> <span class="built_in">QState</span>();      <span class="comment">// 停止状态</span></span><br><span class="line">QState *playingState = <span class="keyword">new</span> <span class="built_in">QState</span>();      <span class="comment">// 播放状态</span></span><br><span class="line">QState *pausedState = <span class="keyword">new</span> <span class="built_in">QState</span>();       <span class="comment">// 暂停状态</span></span><br><span class="line">QFinalState *finalState = <span class="keyword">new</span> <span class="built_in">QFinalState</span>(); <span class="comment">// 最终状态（用于退出）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 为状态添加进入/退出操作</span></span><br><span class="line">stoppedState-&gt;<span class="built_in">assignProperty</span>(playButton, <span class="string">&quot;enabled&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">stoppedState-&gt;<span class="built_in">assignProperty</span>(pauseButton, <span class="string">&quot;enabled&quot;</span>, <span class="literal">false</span>);</span><br><span class="line">stoppedState-&gt;<span class="built_in">assignProperty</span>(stopButton, <span class="string">&quot;enabled&quot;</span>, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line">playingState-&gt;<span class="built_in">assignProperty</span>(playButton, <span class="string">&quot;enabled&quot;</span>, <span class="literal">false</span>);</span><br><span class="line">playingState-&gt;<span class="built_in">assignProperty</span>(pauseButton, <span class="string">&quot;enabled&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">playingState-&gt;<span class="built_in">assignProperty</span>(stopButton, <span class="string">&quot;enabled&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">QObject::<span class="built_in">connect</span>(playingState, &amp;QState::entered, player, &amp;MyPlayerCore::play);</span><br><span class="line"></span><br><span class="line">pausedState-&gt;<span class="built_in">assignProperty</span>(playButton, <span class="string">&quot;enabled&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">pausedState-&gt;<span class="built_in">assignProperty</span>(pauseButton, <span class="string">&quot;enabled&quot;</span>, <span class="literal">false</span>);</span><br><span class="line">pausedState-&gt;<span class="built_in">assignProperty</span>(stopButton, <span class="string">&quot;enabled&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">QObject::<span class="built_in">connect</span>(pausedState, &amp;QState::entered, player, &amp;MyPlayerCore::pause);</span><br><span class="line"></span><br><span class="line">QObject::<span class="built_in">connect</span>(stoppedState, &amp;QState::entered, player, &amp;MyPlayerCore::stop);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 定义状态转换</span></span><br><span class="line"><span class="comment">// 从停止状态 -&gt; 播放状态：当按下播放按钮时</span></span><br><span class="line">stoppedState-&gt;<span class="built_in">addTransition</span>(playButton, &amp;QPushButton::clicked, playingState);</span><br><span class="line"><span class="comment">// 从播放状态 -&gt; 暂停状态：当按下暂停按钮时</span></span><br><span class="line">playingState-&gt;<span class="built_in">addTransition</span>(pauseButton, &amp;QPushButton::clicked, pausedState);</span><br><span class="line"><span class="comment">// 从播放状态 -&gt; 停止状态：当按下停止按钮时</span></span><br><span class="line">playingState-&gt;<span class="built_in">addTransition</span>(stopButton, &amp;QPushButton::clicked, stoppedState);</span><br><span class="line"><span class="comment">// 从暂停状态 -&gt; 播放状态：当按下播放按钮时</span></span><br><span class="line">pausedState-&gt;<span class="built_in">addTransition</span>(playButton, &amp;QPushButton::clicked, playingState);</span><br><span class="line"><span class="comment">// 从暂停状态 -&gt; 停止状态：当按下停止按钮时</span></span><br><span class="line">pausedState-&gt;<span class="built_in">addTransition</span>(stopButton, &amp;QPushButton::clicked, stoppedState);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 设置初始状态并启动状态机</span></span><br><span class="line">machine-&gt;<span class="built_in">addState</span>(stoppedState);</span><br><span class="line">machine-&gt;<span class="built_in">addState</span>(playingState);</span><br><span class="line">machine-&gt;<span class="built_in">addState</span>(pausedState);</span><br><span class="line">machine-&gt;<span class="built_in">addState</span>(finalState);</span><br><span class="line"></span><br><span class="line">machine-&gt;<span class="built_in">setInitialState</span>(stoppedState);</span><br><span class="line">machine-&gt;<span class="built_in">start</span>();</span><br></pre></td></tr></table></figure><img src="/2025/09/13/%E7%BB%99%E7%A8%8B%E5%BA%8F%E8%B5%8B%E4%BA%88%E2%80%9C%E7%8A%B6%E6%80%81%E2%80%9D%E6%80%9D%E7%BB%B4-%E8%81%8A%E8%81%8AQt%E7%8A%B6%E6%80%81%E6%9C%BA%E7%9A%84%E5%A5%87%E5%A6%99%E4%B8%96%E7%95%8C/Snipaste_2025-09-13_14-58-17.png" class="" title="Snipaste_2025-09-13_14-58-17"><p>在这个例子中，状态机清晰地管理了播放器的三个状态和它们之间的所有可能转换。按钮的可用性以及播放器的核心操作（play， pause， stop）都与状态紧密绑定，逻辑非常清晰。<strong>不再需要通过按钮的状态或文本进行冗杂的判断。</strong></p><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p><strong>状态机的核心思想就是：</strong></p><blockquote><p><strong>任何复杂的行为，都可以分解成一系列简单的“状态”，并通过清晰的“规则”在这些状态之间切换。</strong></p></blockquote><p>Qt状态机框架就是这个思想的完美实现工具。它让你的代码：</p><ul><li><strong>更容易理解</strong>（像看地图）</li><li><strong>更容易维护</strong>（修改状态规则很方便）</li><li><strong>更少出错</strong>（杜绝非法操作）</li></ul><p><strong>何时使用 Qt 状态机？</strong><br>当你的程序（或其一部分）可以自然地描述为“在不同模式之间切换，并且每个模式有特定行为”时，就强烈考虑使用它。尤其是在处理用户界面、协议、游戏逻辑或任何有复杂生命周期的对象时，状态机是一个非常有价值的工具。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;Qt-高级状态机&quot;&gt;&lt;a href=&quot;#Qt-高级状态机&quot; class=&quot;headerlink&quot; title=&quot;Qt 高级状态机&quot;&gt;&lt;/a&gt;Qt 高级状态机&lt;/h1&gt;&lt;p&gt;你是否曾写过这样的代码？&lt;/p&gt;
&lt;figure class=&quot;highlight c++&quot;</summary>
      
    
    
    
    <category term="Qt" scheme="https://cpp-memory-leaks.github.io/categories/Qt/"/>
    
    
    <category term="Qt" scheme="https://cpp-memory-leaks.github.io/tags/Qt/"/>
    
  </entry>
  
  <entry>
    <title>C++ REST SDK (cpprestsdk) HTTPS 通信</title>
    <link href="https://cpp-memory-leaks.github.io/2025/09/12/C-REST-SDK-cpprestsdk-HTTPS-%E9%80%9A%E4%BF%A1/"/>
    <id>https://cpp-memory-leaks.github.io/2025/09/12/C-REST-SDK-cpprestsdk-HTTPS-%E9%80%9A%E4%BF%A1/</id>
    <published>2025-09-12T15:30:32.000Z</published>
    <updated>2025-09-13T10:13:24.491Z</updated>
    
    <content type="html"><![CDATA[<h1 id="C-REST-SDK-cpprestsdk-HTTPS-通信"><a href="#C-REST-SDK-cpprestsdk-HTTPS-通信" class="headerlink" title="C++ REST SDK (cpprestsdk) HTTPS 通信"></a>C++ REST SDK (cpprestsdk) HTTPS 通信</h1><h2 id="SSL-TLS-证书验证全过程解析"><a href="#SSL-TLS-证书验证全过程解析" class="headerlink" title="SSL&#x2F;TLS 证书验证全过程解析"></a>SSL&#x2F;TLS 证书验证全过程解析</h2><p><strong>文档版本:</strong> 1.2 | <strong>最后更新日期:</strong> 2025-09-12 | <strong>作者:</strong> oqs</p><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>C++ REST SDK (cpprestsdk) 使用 SSL&#x2F;TLS 访问 HTTPS 站点的过程是一套复杂的交互协议，其核心目的是为了<strong>验证网站身份的真实性</strong>并<strong>加密通信数据</strong>，防止信息被窃听或篡改。整个过程建立在标准的 TLS 握手基础之上，cpprestsdk 作为客户端封装了其中的复杂性。</p><img src="/2025/09/12/C-REST-SDK-cpprestsdk-HTTPS-%E9%80%9A%E4%BF%A1/image-20250912232821208.png" class="" title="image-20250912232821208"><h2 id="完整交互过程"><a href="#完整交互过程" class="headerlink" title="完整交互过程"></a>完整交互过程</h2><p>一次完整的 HTTPS 请求包含四个主要阶段：</p><ol><li><strong>TCP 连接建立</strong></li><li><strong>TLS 握手与安全通道建立 (核心)</strong></li><li><strong>HTTP 应用数据交换</strong></li><li><strong>连接终止</strong></li></ol><h3 id="阶段一：TCP-连接建立"><a href="#阶段一：TCP-连接建立" class="headerlink" title="阶段一：TCP 连接建立"></a>阶段一：TCP 连接建立</h3><ol><li><p><strong>客户端初始化</strong><br>应用程序创建 <code>web::http::client::http_client</code> 对象，并提供目标 HTTPS URL（例如 <code>https://api.example.com</code>）。</p></li><li><p><strong>解析与连接</strong><br>cpprestsdk 库从 URL 中解析出主机名（<code>api.example.com</code>），并默认使用端口 443。底层网络库（基于 Asio）通过操作系统网络栈与服务器进行 <strong>TCP 三次握手</strong>。</p></li><li><p><strong>连接就绪</strong><br>可靠的传输层 TCP 连接建立成功，为后续的 TLS 握手准备好通道。</p></li></ol><h3 id="阶段二：TLS-握手与证书验证-核心"><a href="#阶段二：TLS-握手与证书验证-核心" class="headerlink" title="阶段二：TLS 握手与证书验证 (核心)"></a>阶段二：TLS 握手与证书验证 (核心)</h3><p>这是整个过程中最关键的阶段，其核心任务是身份认证和密钥协商。</p><h4 id="TLS-握手流程"><a href="#TLS-握手流程" class="headerlink" title="TLS 握手流程"></a>TLS 握手流程</h4><ol><li><p><strong>Client Hello</strong><br>cpprestsdk（客户端）向服务器发送一条 <code>ClientHello</code> 消息。<br>消息内容包含：客户端支持的 TLS 协议版本、支持的<strong>密码套件</strong>列表、一个客户端生成的随机数。</p></li><li><p><strong>Server Hello &amp; Certificate</strong><br>服务器回应一条 <code>ServerHello</code> 消息，其中包含：双方协商确定的协议版本和密码套件、一个服务器生成的随机数。<br><strong>服务器发送其数字证书链</strong>。这个链条通常包括：</p><ul><li>**服务器地址证书 (Leaf Certificate)**：包含域名 <code>api.example.com</code> 和公钥。</li><li>**一个或多个中间证书 (Intermediate Certificates)**：用于链接到根证书。</li></ul></li><li><p><strong>证书验证 (cpprestsdk 核心动作)</strong></p><ul><li><strong>接收证书</strong>：cpprestsdk 底层依赖的 SSL 库（如 OpenSSL, SChannel）接收到服务器发来的证书链。</li><li><strong>验证决策</strong>：验证行为由 <code>http_client_config</code> 的配置决定，分为两条路径：<ul><li><strong>路径 A - 自定义验证回调</strong>：如果通过 <code>set_certificate_callback()</code> 设置了自定义函数，则<strong>完全绕过系统默认验证</strong>，直接调用该函数。</li><li><strong>路径 B - 系统默认验证</strong>：如果未设置自定义回调，则使用系统 SSL 库的严格验证流程。</li></ul></li><li><strong>系统验证步骤</strong>：<ol><li><strong>构建信任链</strong>：尝试使用客户端系统预装的<strong>受信任根证书颁发机构 (Trusted Root CAs)</strong> 的证书库，构建一条从服务器证书到可信根的完整链条。</li><li><strong>签名检查</strong>：使用上级 CA 证书的公钥验证下级证书的数字签名是否有效。</li><li><strong>有效期检查</strong>：检查证书是否在 <code>notBefore</code> 和 <code>notAfter</code> 的有效期内。</li><li><strong>域名检查</strong>：检查证书的 <strong>Subject Alternative Name (SAN)</strong> 或 <strong>Common Name (CN)</strong> 字段是否与请求的主机名精确匹配。<strong>SAN 优先于 CN</strong>。</li><li>**吊销状态检查 (可选)**：通过 <strong>CRL（证书吊销列表）</strong> 或 <strong>OCSP（在线证书状态协议）</strong> 查询证书是否已被签发机构吊销。</li></ol></li><li><strong>验证结果</strong>：<ul><li><strong>成功</strong>：继续执行后续步骤。</li><li><strong>失败</strong>：SSL 库会抛出错误，cpprestsdk 将其捕获并转换为异常抛出给应用程序，握手终止。</li></ul></li></ul></li><li><p><strong>密钥交换</strong><br>客户端生成一个 <strong>预主密钥 (Premaster Secret)<strong>。<br>客户端使用步骤 2 中收到的</strong>服务器公钥</strong>（来自服务器证书）加密这个预主密钥，并通过 <code>ClientKeyExchange</code> 消息发送给服务器。<br>只有拥有对应私钥的服务器才能解密获得预主密钥。</p></li><li><p><strong>生成会话密钥与完成握手</strong><br>客户端和服务器使用之前交换的两个随机数和预主密钥，<strong>独立生成相同的会话密钥</strong>。这些对称密钥将用于后续通信的加密和完整性验证。<br>双方交换 <code>ChangeCipherSpec</code> 和 <code>Finished</code> 消息，确认握手成功。<br><strong>安全加密通道正式建立</strong>，之后所有通信都将使用会话密钥进行加密和解密。</p></li></ol><h3 id="阶段三：HTTP-应用数据交换"><a href="#阶段三：HTTP-应用数据交换" class="headerlink" title="阶段三：HTTP 应用数据交换"></a>阶段三：HTTP 应用数据交换</h3><table><thead><tr><th>步骤</th><th>描述</th></tr></thead><tbody><tr><td>1. 发送 HTTP 请求</td><td>应用程序调用 <code>client.request(web::http::methods::GET).get()</code>。cpprestsdk 在 TLS 安全通道之上，以明文形式构建标准的 HTTP 请求（方法、路径、头域）。该明文请求被传递给 TLS 层进行<strong>加密</strong>，然后通过 TCP 连接发送。</td></tr><tr><td>2. 接收与处理 HTTP 响应</td><td>服务器收到加密数据，在其 TLS 层<strong>解密</strong>得到原始 HTTP 请求并进行处理。服务器返回的 HTTP 响应（状态码、头域、Body）也会先被其 TLS 层<strong>加密</strong>后再发送。</td></tr><tr><td>3. 客户端处理响应</td><td>客户端的 TLS 层<strong>解密</strong>收到的数据，将明文的 HTTP 响应数据传递给上层的 cpprestsdk <code>http_client</code> 对象。最终，应用程序收到一个可读的 <code>http_response</code> 对象，可以从中提取状态码和响应体（如 JSON）。</td></tr></tbody></table><h3 id="阶段四：连接终止"><a href="#阶段四：连接终止" class="headerlink" title="阶段四：连接终止"></a>阶段四：连接终止</h3><table><thead><tr><th>步骤</th><th>描述</th></tr></thead><tbody><tr><td>1. 安全关闭</td><td>请求完成后，连接被关闭。TLS 层会首先交换加密的 <code>close_notify</code> 警报消息，通知对方安全会话结束。</td></tr><tr><td>2. 关闭 TCP 连接</td><td>安全通道关闭后，底层 TCP 连接通过 FIN 包进行四次挥手，最终完全终止。</td></tr></tbody></table><h2 id="CppRestSDK-中的关键代码与配置"><a href="#CppRestSDK-中的关键代码与配置" class="headerlink" title="CppRestSDK 中的关键代码与配置"></a>CppRestSDK 中的关键代码与配置</h2><h3 id="基本使用（推荐：使用系统信任库）"><a href="#基本使用（推荐：使用系统信任库）" class="headerlink" title="基本使用（推荐：使用系统信任库）"></a>基本使用（推荐：使用系统信任库）</h3><p>这是最常见、最安全的方式，代码非常简单。cpprestsdk 会自动使用操作系统的受信任根证书存储进行验证。</p><figure class="highlight cpp"><table><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="keyword">include</span> <span class="string">&lt;cpprest/http_client.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;cpprest/filestream.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> web::http::client;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">simple_https_request</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 系统CA存储被隐式使用。只需创建客户端并发送请求。</span></span><br><span class="line">    <span class="function">http_client <span class="title">client</span><span class="params">(U(<span class="string">&quot;https://www.example.com/api/data&quot;</span>))</span></span>; </span><br><span class="line"></span><br><span class="line">    client.<span class="built_in">request</span>(web::http::methods::GET)</span><br><span class="line">        .<span class="built_in">then</span>([](http_response response) &#123;</span><br><span class="line">            <span class="comment">// TLS握手、加密、解密都已自动完成</span></span><br><span class="line">            std::wcout &lt;&lt; <span class="string">L&quot;Status: &quot;</span> &lt;&lt; response.<span class="built_in">status_code</span>() &lt;&lt; std::endl;</span><br><span class="line">            <span class="keyword">return</span> response.<span class="built_in">extract_string</span>();</span><br><span class="line">        &#125;)</span><br><span class="line">        .<span class="built_in">then</span>([](std::string body) &#123;</span><br><span class="line">            std::cout &lt;&lt; <span class="string">&quot;Response: &quot;</span> &lt;&lt; body &lt;&lt; std::endl;</span><br><span class="line">        &#125;).<span class="built_in">wait</span>(); <span class="comment">// 使用 .get() 亦可</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="危险配置：禁用证书验证（仅用于测试！）"><a href="#危险配置：禁用证书验证（仅用于测试！）" class="headerlink" title="危险配置：禁用证书验证（仅用于测试！）"></a>危险配置：禁用证书验证（仅用于测试！）</h3><blockquote><p><strong>警告：</strong> 此配置会完全禁用所有证书检查，使连接极易受到中间人攻击。绝对不要在生产环境中使用。</p></blockquote><figure class="highlight c++"><table><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="keyword">include</span> <span class="string">&lt;cpprest/http_client.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">insecure_request</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    http_client_config config;</span><br><span class="line">    config.<span class="built_in">set_validate_certificates</span>(<span class="literal">false</span>); <span class="comment">// 关闭所有证书验证</span></span><br><span class="line"></span><br><span class="line">    <span class="function">http_client <span class="title">client</span><span class="params">(U(<span class="string">&quot;https://self-signed.badssl.com/&quot;</span>), config)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 此时会接受任何证书，包括自签名、过期或域名不匹配的证书。</span></span><br><span class="line">    client.<span class="built_in">request</span>(web::http::methods::GET)</span><br><span class="line">        .<span class="built_in">then</span>([](http_response response) &#123;</span><br><span class="line">            <span class="comment">// ... 处理响应 ...</span></span><br><span class="line">        &#125;).<span class="built_in">wait</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="高级配置：自定义证书验证回调"><a href="#高级配置：自定义证书验证回调" class="headerlink" title="高级配置：自定义证书验证回调"></a>高级配置：自定义证书验证回调</h3><p>提供灵活性，但需自行实现所有安全逻辑，通常用于证书钉钉（Pinning）或特殊测试。</p><figure class="highlight c++"><table><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="meta">#<span class="keyword">include</span> <span class="string">&lt;cpprest/http_client.h&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="type">bool</span> <span class="title">my_certificate_callback</span><span class="params">(web::http::client::native_handle_type handle, <span class="type">const</span> std::string&amp; host)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 参数:</span></span><br><span class="line">    <span class="comment">// - handle: 底层SSL库的上下文句柄（如OpenSSL的X509_STORE_CTX*）</span></span><br><span class="line">    <span class="comment">// - host: 正在连接的主机名</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 这是一个高级接口，需要直接操作底层SSL库的API（如OpenSSL）。</span></span><br><span class="line">    <span class="comment">// 示例逻辑（伪代码）:</span></span><br><span class="line">    <span class="comment">// 1. 从 `handle` 中获取服务器证书链。</span></span><br><span class="line">    <span class="comment">// 2. 提取叶子证书的公钥或指纹（如SHA-256）。</span></span><br><span class="line">    <span class="comment">// 3. 与代码中预埋的预期值进行比较（证书钉钉）。</span></span><br><span class="line">    <span class="comment">// 4. 如果匹配，返回 true；否则返回 false。</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注意：返回 true 将接受证书，返回 false 将拒绝连接并抛出异常。</span></span><br><span class="line">    <span class="comment">// 对于测试，可以简单返回 true 来接受所有证书（效果同set_validate_certificates(false)）。</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>; <span class="comment">// 谨慎使用！</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">custom_validation_request</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    http_client_config config;</span><br><span class="line">    <span class="comment">// 设置自定义回调函数。一旦设置，系统默认验证将被绕过。</span></span><br><span class="line">    config.<span class="built_in">set_certificate_callback</span>(my_certificate_callback);</span><br><span class="line"></span><br><span class="line">    <span class="function">http_client <span class="title">client</span><span class="params">(U(<span class="string">&quot;https://example.com&quot;</span>), config)</span></span>;</span><br><span class="line"></span><br><span class="line">    client.<span class="built_in">request</span>(web::http::methods::GET)</span><br><span class="line">        .<span class="built_in">then</span>([](http_response response) &#123;</span><br><span class="line">            <span class="comment">// ... 处理响应 ...</span></span><br><span class="line">        &#125;).<span class="built_in">wait</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="核心概念"><a href="#核心概念" class="headerlink" title="核心概念"></a>核心概念</h2><ul><li>SSL&#x2F;TLS 协议</li><li>非对称加密</li><li>对称加密</li><li>数字证书</li><li>证书颁发机构 (CA)</li><li>公钥基础设施 (PKI)</li><li>证书链验证</li><li>证书钉钉 (Pinning)</li></ul><h2 id="证书类型"><a href="#证书类型" class="headerlink" title="证书类型"></a>证书类型</h2><table><thead><tr><th align="left">类型</th><th align="left">作用</th></tr></thead><tbody><tr><td align="left">根证书</td><td align="left">信任的锚点，自签名证书，预装在系统中</td></tr><tr><td align="left">中间证书</td><td align="left">由根证书签发，用于签发终端实体证书</td></tr><tr><td align="left">终端实体证书</td><td align="left">服务器证书，包含公钥和域名信息</td></tr></tbody></table><h2 id="最佳实践"><a href="#最佳实践" class="headerlink" title="最佳实践"></a>最佳实践</h2><ul><li>始终启用证书验证</li><li>保持系统根证书库更新</li><li>在生产环境中绝不禁用验证</li><li>使用强密码套件</li><li>考虑证书钉钉以提高安全性</li><li>正确处理SSL&#x2F;TLS错误和异常</li></ul><h2 id="常见错误"><a href="#常见错误" class="headerlink" title="常见错误"></a>常见错误</h2><ul><li>证书链不完整</li><li>证书域名不匹配</li><li>证书已过期或未生效</li><li>根证书不受信任</li><li>证书已被吊销</li><li>SSL&#x2F;TLS 版本不匹配</li></ul><h2 id="总结与最佳实践"><a href="#总结与最佳实践" class="headerlink" title="总结与最佳实践"></a>总结与最佳实践</h2><table><thead><tr><th align="left">要点</th><th align="left">描述</th></tr></thead><tbody><tr><td align="left">信任锚</td><td align="left">cpprestsdk 的默认安全性依赖于操作系统的<strong>受信任根证书存储</strong>。请确保系统定期更新，以获取最新的受信任 CA 列表。</td></tr><tr><td align="left">验证时机</td><td align="left">证书验证发生在 TLS 握手阶段，在任何应用层 HTTP 数据发送之前。如果失败，连接会立即终止。</td></tr><tr><td align="left">错误处理</td><td align="left">务必使用 <code>try-catch</code> 块来捕获 <code>web::http::http_exception</code> 或其他可能异常，以妥善处理网络和证书验证错误。</td></tr><tr><td align="left">安全警告</td><td align="left"><strong><code>set_validate_certificates(false)</code> 极度危险</strong>，仅用于临时测试环境。在生产环境中，永远不要禁用证书验证。</td></tr><tr><td align="left">自定义验证</td><td align="left">如果使用 <code>set_certificate_callback</code>，意味着你承担了全部验证责任。必须正确、安全地实现逻辑（如正确的证书钉钉），否则会引入安全风险。</td></tr><tr><td align="left">抽象层</td><td align="left">cpprestsdk 有效地隐藏了底层 SSL 库（OpenSSL&#x2F;SChannel）的复杂性，提供了一个统一的 C++ API，同时暴露了关键配置选项以满足高级需求。</td></tr></tbody></table>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;C-REST-SDK-cpprestsdk-HTTPS-通信&quot;&gt;&lt;a href=&quot;#C-REST-SDK-cpprestsdk-HTTPS-通信&quot; class=&quot;headerlink&quot; title=&quot;C++ REST SDK (cpprestsdk) HTTPS </summary>
      
    
    
    
    <category term="C++" scheme="https://cpp-memory-leaks.github.io/categories/C/"/>
    
    
    <category term="C++ cpprestsdk openssl tls https" scheme="https://cpp-memory-leaks.github.io/tags/C-cpprestsdk-openssl-tls-https/"/>
    
  </entry>
  
  <entry>
    <title>RAII思想</title>
    <link href="https://cpp-memory-leaks.github.io/2025/09/08/RAII%E6%80%9D%E6%83%B3/"/>
    <id>https://cpp-memory-leaks.github.io/2025/09/08/RAII%E6%80%9D%E6%83%B3/</id>
    <published>2025-09-08T14:05:35.000Z</published>
    <updated>2025-09-13T11:01:16.643Z</updated>
    
    
    
    
    
  </entry>
  
  <entry>
    <title>C++ REST SDK (cpprestsdk)编译ASIO版本过程</title>
    <link href="https://cpp-memory-leaks.github.io/2025/08/21/CppRestSDK%E7%BC%96%E8%AF%91ASIO%E7%89%88%E6%9C%AC%E8%BF%87%E7%A8%8B/"/>
    <id>https://cpp-memory-leaks.github.io/2025/08/21/CppRestSDK%E7%BC%96%E8%AF%91ASIO%E7%89%88%E6%9C%AC%E8%BF%87%E7%A8%8B/</id>
    <published>2025-08-21T13:25:28.000Z</published>
    <updated>2025-09-13T03:45:28.489Z</updated>
    
    <content type="html"><![CDATA[<h1 id="C-REST-SDK-cpprestsdk-编译ASIO版本过程"><a href="#C-REST-SDK-cpprestsdk-编译ASIO版本过程" class="headerlink" title="C++ REST SDK (cpprestsdk)编译ASIO版本过程"></a>C++ REST SDK (cpprestsdk)编译ASIO版本过程</h1><h2 id="1-下载源码"><a href="#1-下载源码" class="headerlink" title="1. 下载源码"></a>1. 下载源码</h2><p>使用git命令下载cpprest代码：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> -b v2.10.12 https://github.com/microsoft/cpprestsdk.git</span><br></pre></td></tr></table></figure><h2 id="2-修改下载的源码"><a href="#2-修改下载的源码" class="headerlink" title="2. 修改下载的源码"></a>2. 修改下载的源码</h2><h3 id="2-1-修复CMakeLists文件Bug"><a href="#2-1-修复CMakeLists文件Bug" class="headerlink" title="2.1 修复CMakeLists文件Bug"></a>2.1 修复CMakeLists文件Bug</h3><p>cpprestsdk的2.10.12版本的CMakeLists是有bug的，asio异步所需要的threadpool.cpp文件只有不排除websocket库的情况下才会链接到项目。显然目前项目是不需要websocket的，所以得强制让threadpool链接到项目里去，否则报无法解析的外部符号。</p><p>需要根据GitHub上该开源库的pull request修改：</p><ul><li>修改 <code>Release/src/CmakeLists.txt</code> 文件</li><li>修改 <code>Release/src/pplx/threadpool.cpp</code> 文件</li></ul><p>参考链接：<a href="https://github.com/microsoft/cpprestsdk/pull/1466/files">https://github.com/microsoft/cpprestsdk/pull/1466/files</a></p><h3 id="2-2-修改警告设置"><a href="#2-2-修改警告设置" class="headerlink" title="2.2 修改警告设置"></a>2.2 修改警告设置</h3><p>Release下的CMakeLists文件的第18行，默认开启将警告视为报错，cmake不会报错，但make的时候会将所有的warning都会作为错误抛出，需要将ON改为OFF。</p><figure class="highlight cmake"><table><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"># 原代码</span></span><br><span class="line"><span class="keyword">set</span>(WERROR <span class="keyword">ON</span> CACHE BOOL <span class="string">&quot;Treat Warnings as Errors.&quot;</span>)</span><br><span class="line"><span class="comment"># 修改为</span></span><br><span class="line"><span class="keyword">set</span>(WERROR <span class="keyword">OFF</span> CACHE BOOL <span class="string">&quot;Treat Warnings as Errors.&quot;</span>)</span><br></pre></td></tr></table></figure><p>或者可以在cmake命令中将该宏设置为OFF：<code>-DWERROR=OFF</code></p><p>参考链接：<a href="https://github.com/microsoft/cpprestsdk/issues/1290">https://github.com/microsoft/cpprestsdk/issues/1290</a></p><h2 id="3-编写cmake命令"><a href="#3-编写cmake命令" class="headerlink" title="3. 编写cmake命令"></a>3. 编写cmake命令</h2><h3 id="3-1-Windows"><a href="#3-1-Windows" class="headerlink" title="3.1 Windows"></a>3.1 Windows</h3><figure class="highlight plaintext"><table><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">&quot;D:\software\CMake\bin\cmake.exe&quot; ^</span><br><span class="line"> -DCMAKE_BUILD_TYPE=Debug ^</span><br><span class="line"> -G&quot;Visual Studio 15 2017 Win64&quot; ^</span><br><span class="line"> -DOPENSSL_ROOT_DIR=&quot;D:/DevThirdpartyLib_2017/Openssl/openssl-1.1.1g/&quot; ^</span><br><span class="line"> -DOPENSSL_LIBRARIES=&quot;D:/DevThirdpartyLib_2017/Openssl/openssl-1.1.1g/lib64&quot; ^</span><br><span class="line"> -DOPENSSL_SSL_LIBRARY=&quot;D:/DevThirdpartyLib_2017/Openssl/openssl-1.1.1g/lib64/libssl.lib&quot; ^</span><br><span class="line"> -DOPENSSL_CRYPTO_LIBRARY=&quot;D:/DevThirdpartyLib_2017/Openssl/openssl-1.1.1g/lib64/libcrypto.lib&quot; ^</span><br><span class="line"> -DOPENSSL_INCLUDE_DIR=&quot;D:/DevThirdpartyLib_2017/Openssl/openssl-1.1.1g/include&quot; ^</span><br><span class="line"> -DZLIB_INCLUDE_DIR=&quot;D:/DevThirdpartyLib_2017/Zlib/zlib_1_2_11/include&quot; ^</span><br><span class="line"> -DZLIB_LIBRARY=&quot;D:/DevThirdpartyLib_2017/Zlib/zlib_1_2_11/lib64/zlibd1.lib&quot; ^</span><br><span class="line"> -DCPPREST_EXCLUDE_WEBSOCKETS:BOOL=ON ^</span><br><span class="line"> -DBoost_INCLUDE_DIRS=&quot;D:/DevThirdpartyLib_2017/boost/boost_1_68&quot; ^</span><br><span class="line"> -DBOOST_LIBRARYDIR=&quot;D:/DevThirdpartyLib_2017/boost/boost_1_68/lib/x64&quot; ^</span><br><span class="line"> -DBOOST_ROOT=&quot;D:/DevThirdpartyLib_2017/boost/boost_1_68&quot; ^</span><br><span class="line"> -DBoost_SYSTEM_LIBRARY_DEBUG=&quot;D:/DevThirdpartyLib_2017/boost/boost_1_68/lib/x64/libboost_system-vc141-mt-gd-x64-1_68.lib&quot; ^</span><br><span class="line"> -DBoost_DATE_TIME_LIBRARY_DEBUG=&quot;D:/DevThirdpartyLib_2017/boost/boost_1_68/lib/x64/libboost_date_time-vc141-mt-gd-x64-1_68.lib&quot; ^</span><br><span class="line"> -DBoost_REGEX_LIBRARY_DEBUG=&quot;D:/DevThirdpartyLib_2017/boost/boost_1_68/lib/x64/libboost_regex-vc141-mt-gd-x64-1_68.lib&quot; ^</span><br><span class="line"> -DCPPREST_HTTP_CLIENT_IMPL:STRING=&quot;asio&quot; ^</span><br><span class="line"> -DCPPREST_HTTP_LISTENER_IMPL:STRING=&quot;asio&quot; ^</span><br><span class="line"> -DBUILD_TESTS:BOOL=OFF ^</span><br><span class="line"> -DBUILD_SAMPLES:BOOL=OFF</span><br></pre></td></tr></table></figure><p><strong>参数说明：</strong></p><ul><li><code>D:\software\CMake\bin\cmake.exe</code> 替换为实际的cmake安装路径</li><li><code>-DCMAKE_BUILD_TYPE</code> 和 <code>Win64</code> 根据所需Debug还是Release替换</li><li>在Windows上指定boost的根目录路径没生效，可能是由于boost的lib命名过于复杂，所以直接指定了所需要的库</li><li><code>-DCPPREST_EXCLUDE_WEBSOCKETS:BOOL=ON</code> 这个宏是为了排除websocket模块</li><li><code>-DCPPREST_HTTP_CLIENT_IMPL:STRING=&quot;asio&quot; -DCPPREST_HTTP_LISTENER_IMPL:STRING=&quot;asio&quot;</code> 是为了让cmake将cpprest的请求和监听底层实现指定为使用boost.asio</li><li><code>-DBUILD_TESTS:BOOL=OFF -DBUILD_SAMPLES:BOOL=OFF</code> 不要生成不必要的sample项目解决方案</li></ul><blockquote><p><strong>注意：</strong> 生成的sln的配置可能还不完善，一些库的路径可能是不对的，有问题的还需要配置一下。</p></blockquote><h3 id="3-2-Linux"><a href="#3-2-Linux" class="headerlink" title="3.2 Linux"></a>3.2 Linux</h3><figure class="highlight bash"><table><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">cmake \</span><br><span class="line">  -DCMAKE_INSTALL_PREFIX=/root/oqs/cpprestsdk_build/cpprestsdk \</span><br><span class="line">  -DCMAKE_BUILD_TYPE=Release \</span><br><span class="line">  -DCPPREST_EXCLUDE_WEBSOCKETS=ON \</span><br><span class="line">  -DZLIB_INCLUDE_DIR=/root/oqs/cpprestsdk_build/zlib/include \</span><br><span class="line">  -DZLIB_LIBRARY=/root/oqs/cpprestsdk_build/zlib/lib/libz.so \</span><br><span class="line">  -DZLIB_ROOT_DIR=/root/oqs/cpprestsdk_build/zlib/ \</span><br><span class="line">  -DOPENSSL_ROOT_DIR=/root/oqs/cpprestsdk_build/openssl/ \</span><br><span class="line">  -DOPENSSL_INCLUDE_DIR=/root/oqs/cpprestsdk_build/openssl/include \</span><br><span class="line">  -DBOOST_LIBRARYDIR=/root/oqs/cpprestsdk_build/boost/lib \</span><br><span class="line">  -DBOOST_ROOT=/root/oqs/cpprestsdk_build/boost/include \</span><br><span class="line">  -DCPPREST_HTTP_CLIENT_IMPL:STRING=<span class="string">&quot;asio&quot;</span> \</span><br><span class="line">  -DCPPREST_HTTP_LISTENER_IMPL:STRING=<span class="string">&quot;asio&quot;</span> \</span><br><span class="line">  -DBUILD_TESTS:BOOL=OFF \</span><br><span class="line">  -DBUILD_SAMPLES:BOOL=OFF</span><br></pre></td></tr></table></figure><p>宏的作用跟Windows上的大差不差。</p><h2 id="4-编译"><a href="#4-编译" class="headerlink" title="4. 编译"></a>4. 编译</h2><h3 id="4-1-Windows"><a href="#4-1-Windows" class="headerlink" title="4.1 Windows"></a>4.1 Windows</h3><p>直接用Visual Studio</p><h3 id="4-2-Linux"><a href="#4-2-Linux" class="headerlink" title="4.2 Linux"></a>4.2 Linux</h3><p>使用make命令或者 <code>make -j8</code>（多线程编译）</p><h2 id="5-使用"><a href="#5-使用" class="headerlink" title="5. 使用"></a>5. 使用</h2><h3 id="5-1-Linux和Windows都需要修改的地方"><a href="#5-1-Linux和Windows都需要修改的地方" class="headerlink" title="5.1 Linux和Windows都需要修改的地方"></a>5.1 Linux和Windows都需要修改的地方</h3><p><strong>CPPRest配置：</strong></p><p>cpprest的ssl_context函数是根据这个宏决定是否打开的，在Windows上使用asio版本的cpprest，需要在项目上根据需要在代码或者预处理器上加上这两个宏：</p><figure class="highlight cpp"><table><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">CPPREST_FORCE_HTTP_CLIENT_ASIO</span><br><span class="line">CPPREST_FORCE_HTTP_LISTENER_ASIO</span><br></pre></td></tr></table></figure><p><strong>gSOAP配置：</strong></p><figure class="highlight cpp"><table><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">WITH_IPV6 </span><br><span class="line">WITH_NO_IPV6_V6ONLY</span><br></pre></td></tr></table></figure><h3 id="5-2-Windows特定配置"><a href="#5-2-Windows特定配置" class="headerlink" title="5.2 Windows特定配置"></a>5.2 Windows特定配置</h3><p>在Windows上使用boost，由于boost自身有文件引用了winsock.h头文件，且我们的项目中stdafx.h中可能已经通过某些宏或者直接添加了该头文件，导致编译过程会报：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WinSock.h has already been included</span><br></pre></td></tr></table></figure><p>解决方案：</p><ol><li>将stdafx.h中引入该头文件的代码注释掉</li><li>在项目方案加上该宏：<code>WIN32_LEAN_AND_MEAN</code></li></ol><p>参考链接：<a href="https://stackoverflow.com/questions/30731173/error-winsock-h-has-already-been-included-boost-windows-qt">https://stackoverflow.com/questions/30731173/error-winsock-h-has-already-been-included-boost-windows-qt</a></p><p><strong>IPv6地址格式：</strong> IPv6地址注意要带上中括号 <code>[]</code></p><h3 id="5-3-Linux特定配置"><a href="#5-3-Linux特定配置" class="headerlink" title="5.3 Linux特定配置"></a>5.3 Linux特定配置</h3><p><strong>IPv6地址格式：</strong> 在Linux上，IPv6地址是不需要添加中括号的，否则会解析失败。</p><p><strong>双栈支持配置：</strong></p><p>在Linux上使用PF_UNSPEC在getaddrinfo后会得到INET（IPv4），这不是期望的结果。期望的效果是得到PF_INET6+WITH_NO_IPV6_V6ONLY（也就是WITH_IPV6 WITH_NO_IPV6_V6ONLY）从而达到双栈的效果。</p><p>原因是PF_UNSPEC在Linux上通过getaddrinfo获取协议族过程中，找到协议后（无论是IPv4还是IPv6）就直接返回了。</p><p><strong>修改方法：</strong></p><p>在 <code>stdsoap.cpp</code> 的第5714行将：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hints.ai_family = PF_UNSPEC</span><br></pre></td></tr></table></figure><p>改为：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hints.ai_family = PF_INET6</span><br></pre></td></tr></table></figure><blockquote><p><strong>注意：</strong> 在Windows上同样修改也没发现同样的问题，所以应该可以不区分平台。</p></blockquote>]]></content>
    
    
    <summary type="html">编译cpprestsdk过程中遇到的问题及解决方法</summary>
    
    
    
    <category term="C++" scheme="https://cpp-memory-leaks.github.io/categories/C/"/>
    
    
    <category term="C++" scheme="https://cpp-memory-leaks.github.io/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>使用libheif库读取heic文件的缩略图</title>
    <link href="https://cpp-memory-leaks.github.io/2025/03/17/%E4%BD%BF%E7%94%A8libheif%E5%BA%93%E8%AF%BB%E5%8F%96heic%E6%96%87%E4%BB%B6%E7%9A%84%E7%BC%A9%E7%95%A5%E5%9B%BE/"/>
    <id>https://cpp-memory-leaks.github.io/2025/03/17/%E4%BD%BF%E7%94%A8libheif%E5%BA%93%E8%AF%BB%E5%8F%96heic%E6%96%87%E4%BB%B6%E7%9A%84%E7%BC%A9%E7%95%A5%E5%9B%BE/</id>
    <published>2025-03-17T12:18:50.000Z</published>
    <updated>2025-09-13T03:34:03.004Z</updated>
    
    <content type="html"><![CDATA[<h1 id="使用libheif库读取heic文件的缩略图"><a href="#使用libheif库读取heic文件的缩略图" class="headerlink" title="使用libheif库读取heic文件的缩略图"></a>使用libheif库读取heic文件的缩略图</h1><h2 id="1-先使用vcpkg安装libheif库"><a href="#1-先使用vcpkg安装libheif库" class="headerlink" title="1.先使用vcpkg安装libheif库"></a>1.先使用vcpkg安装libheif库</h2><p>在开始之前，我们需要安装 libheif 库。通过 vcpkg 工具可以方便地安装所需的依赖，根据实际需求选择适合的位数以及动态库或静态库版本。</p><p>安装命令示例：</p><figure class="highlight plaintext"><table><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">vcpkg install libheif:x86-windows-static</span><br><span class="line">vcpkg install libheif:x64-windows-static</span><br></pre></td></tr></table></figure><p>完成安装后，确保正确配置 vcpkg 环境，能够被 CMake 或其他构建工具找到。</p><h2 id="2-解码缩略图和解码原图的区别"><a href="#2-解码缩略图和解码原图的区别" class="headerlink" title="2.解码缩略图和解码原图的区别"></a>2.解码缩略图和解码原图的区别</h2><p>在处理 HEIC 格式文件时，缩略图解码和原图解码的使用场景有所不同。以下从资源占用和代码实现两方面进行比较。</p><p>1.解码原图</p><p>解码原图时，图像数据会被完全加载到内存中，适合用于查看高清图片或后续进行深度处理。但这也意味着需要更多的内存和 CPU 资源。</p><img src="/2025/03/17/%E4%BD%BF%E7%94%A8libheif%E5%BA%93%E8%AF%BB%E5%8F%96heic%E6%96%87%E4%BB%B6%E7%9A%84%E7%BC%A9%E7%95%A5%E5%9B%BE/%E8%A7%A3%E7%A0%81%E5%8E%9F%E5%9B%BE%E5%8D%A0%E7%94%A8%E7%9A%84cpu%E5%92%8C%E5%86%85%E5%AD%98.gif" class="" title="解码原图占用的cpu和内存"><p>2.解码缩略图</p><p>如果仅用于预览或快速浏览，可以选择解码缩略图。这种方法占用资源更少，加载速度更快，非常适合需要快速显示大量图片的场景。</p><img src="/2025/03/17/%E4%BD%BF%E7%94%A8libheif%E5%BA%93%E8%AF%BB%E5%8F%96heic%E6%96%87%E4%BB%B6%E7%9A%84%E7%BC%A9%E7%95%A5%E5%9B%BE/%E5%8A%A0%E8%BD%BD%E7%BC%A9%E7%95%A5%E5%9B%BE%E5%8D%A0%E7%94%A8%E7%9A%84cpu%E5%92%8C%E5%86%85%E5%AD%98.gif" class="" title="加载缩略图占用的cpu和内存"><p>解码原图（查看图片使用）</p><figure class="highlight c++"><table><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="function"><span class="type">void</span> <span class="title">heifToQPixmap</span><span class="params">(<span class="type">const</span> QString&amp; filePath, QPixmap&amp; pixmap)</span> </span>&#123;</span><br><span class="line">QString extension = <span class="built_in">QFileInfo</span>(filePath).<span class="built_in">suffix</span>().<span class="built_in">toLower</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (extension == <span class="string">&quot;jpg&quot;</span> || extension == <span class="string">&quot;jpeg&quot;</span>) &#123;</span><br><span class="line"><span class="keyword">if</span> (!pixmap.<span class="built_in">load</span>(filePath)) &#123;</span><br><span class="line"><span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">&quot;Failed to load JPG image.&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line">std::string filePathStd = filePath.<span class="built_in">toStdString</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">heif_context</span>* ctx = <span class="built_in">heif_context_alloc</span>();</span><br><span class="line"><span class="keyword">if</span> (!ctx) &#123;</span><br><span class="line"><span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">&quot;Failed to allocate HEIF context.&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">heif_error</span> err = <span class="built_in">heif_context_read_from_file</span>(ctx, filePathStd.<span class="built_in">c_str</span>(), <span class="literal">nullptr</span>);</span><br><span class="line"><span class="keyword">if</span> (err.code != heif_error_Ok) &#123;</span><br><span class="line"><span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line"><span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">&quot;Failed to read HEIF file: &quot;</span> + std::<span class="built_in">string</span>(err.message));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">heif_image_handle</span>* handle = <span class="literal">nullptr</span>;</span><br><span class="line">err = <span class="built_in">heif_context_get_primary_image_handle</span>(ctx, &amp;handle);</span><br><span class="line"><span class="keyword">if</span> (err.code != heif_error_Ok) &#123;</span><br><span class="line"><span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line"><span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">&quot;Failed to get primary image handle: &quot;</span> + std::<span class="built_in">string</span>(err.message));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">heif_image</span>* img = <span class="literal">nullptr</span>;</span><br><span class="line">err = <span class="built_in">heif_decode_image</span>(handle, &amp;img, heif_colorspace_RGB, heif_chroma_interleaved_RGBA, <span class="literal">nullptr</span>);</span><br><span class="line"><span class="keyword">if</span> (err.code != heif_error_Ok) &#123;</span><br><span class="line"><span class="built_in">heif_image_handle_release</span>(handle);</span><br><span class="line"><span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line"><span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">&quot;Failed to decode HEIF image: &quot;</span> + std::<span class="built_in">string</span>(err.message));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> width = <span class="built_in">heif_image_get_width</span>(img, heif_channel_interleaved);</span><br><span class="line"><span class="type">int</span> height = <span class="built_in">heif_image_get_height</span>(img, heif_channel_interleaved);</span><br><span class="line"><span class="type">const</span> <span class="type">uint8_t</span>* data = <span class="built_in">heif_image_get_plane_readonly</span>(img, heif_channel_interleaved, <span class="literal">nullptr</span>);</span><br><span class="line"></span><br><span class="line"><span class="function">QVector&lt;uchar&gt; <span class="title">buffer</span><span class="params">(width * height * <span class="number">4</span>)</span></span>;</span><br><span class="line"><span class="built_in">memcpy</span>(buffer.<span class="built_in">data</span>(), data, buffer.<span class="built_in">size</span>());</span><br><span class="line"></span><br><span class="line"><span class="function">QImage <span class="title">image</span><span class="params">(buffer.data(), width, height, QImage::Format_RGBA8888)</span></span>;</span><br><span class="line">pixmap = QPixmap::<span class="built_in">fromImage</span>(image);</span><br><span class="line"></span><br><span class="line"><span class="built_in">heif_image_release</span>(img);</span><br><span class="line"><span class="built_in">heif_image_handle_release</span>(handle);</span><br><span class="line"><span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>解码缩略图（供预览使用）</p><figure class="highlight c++"><table><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="function"><span class="type">bool</span> <span class="title">loadHeicThumbnail</span><span class="params">(<span class="type">const</span> QString&amp; filePath, QPixmap&amp; thumbnail)</span> </span>&#123;</span><br><span class="line">QString extension = <span class="built_in">QFileInfo</span>(filePath).<span class="built_in">suffix</span>().<span class="built_in">toLower</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (extension == <span class="string">&quot;jpg&quot;</span> || extension == <span class="string">&quot;jpeg&quot;</span>) &#123;</span><br><span class="line"><span class="function">QImageReader <span class="title">reader</span><span class="params">(filePath)</span></span>;</span><br><span class="line">reader.<span class="built_in">setAutoTransform</span>(<span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">QSize originalSize = reader.<span class="built_in">size</span>(); </span><br><span class="line"><span class="keyword">if</span> (originalSize.<span class="built_in">isEmpty</span>()) &#123;</span><br><span class="line"><span class="built_in">qDebug</span>() &lt;&lt; <span class="string">&quot;Failed to read image size:&quot;</span> &lt;&lt; reader.<span class="built_in">errorString</span>() &lt;&lt; filePath;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line">QSize scaledSize = originalSize.<span class="built_in">scaled</span>(<span class="built_in">QSize</span>(originalSize/<span class="number">20</span>), Qt::KeepAspectRatio);</span><br><span class="line"></span><br><span class="line">reader.<span class="built_in">setScaledSize</span>(scaledSize);</span><br><span class="line"></span><br><span class="line">QImage image = reader.<span class="built_in">read</span>();</span><br><span class="line"><span class="keyword">if</span> (image.<span class="built_in">isNull</span>()) &#123;</span><br><span class="line"><span class="built_in">qWarning</span>() &lt;&lt; <span class="string">&quot;Failed to load image:&quot;</span> &lt;&lt; reader.<span class="built_in">errorString</span>() &lt;&lt; filePath;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">thumbnail = QPixmap::<span class="built_in">fromImage</span>(image);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">heif_context* ctx = <span class="built_in">heif_context_alloc</span>();</span><br><span class="line">heif_error err = <span class="built_in">heif_context_read_from_file</span>(ctx, filePath.<span class="built_in">toStdString</span>().<span class="built_in">c_str</span>(), <span class="literal">nullptr</span>);</span><br><span class="line"><span class="keyword">if</span> (err.code != heif_error_Ok) &#123;</span><br><span class="line"><span class="built_in">qDebug</span>() &lt;&lt; <span class="string">&quot;Failed to read HEIF file: &quot;</span>;</span><br><span class="line"><span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">heif_image_handle* handle = <span class="literal">nullptr</span>;</span><br><span class="line">err = <span class="built_in">heif_context_get_primary_image_handle</span>(ctx, &amp;handle);</span><br><span class="line"><span class="keyword">if</span> (err.code != heif_error_Ok) &#123;</span><br><span class="line"><span class="built_in">qDebug</span>() &lt;&lt; <span class="string">&quot;Failed to get primary image handle: &quot;</span>;</span><br><span class="line"><span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">heif_image_handle* thumbnail_handle = <span class="literal">nullptr</span>;</span><br><span class="line"><span class="type">int</span> num_thumbnails = <span class="built_in">heif_image_handle_get_number_of_thumbnails</span>(handle);</span><br><span class="line"><span class="keyword">if</span> (num_thumbnails &gt; <span class="number">0</span>) &#123;</span><br><span class="line">heif_item_id thumbnail_id;</span><br><span class="line"><span class="built_in">heif_image_handle_get_list_of_thumbnail_IDs</span>(handle, &amp;thumbnail_id, <span class="number">1</span>);</span><br><span class="line">err = <span class="built_in">heif_image_handle_get_thumbnail</span>(handle, thumbnail_id, &amp;thumbnail_handle);</span><br><span class="line"><span class="keyword">if</span> (err.code != heif_error_Ok) &#123;</span><br><span class="line"><span class="built_in">qDebug</span>() &lt;&lt; <span class="string">&quot;Failed to get thumbnail handle: &quot;</span>;</span><br><span class="line"><span class="built_in">heif_image_handle_release</span>(handle);</span><br><span class="line"><span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解码缩略图</span></span><br><span class="line">heif_image* thumbnail_image = <span class="literal">nullptr</span>;</span><br><span class="line">err = <span class="built_in">heif_decode_image</span>(thumbnail_handle, &amp;thumbnail_image, heif_colorspace_RGB, heif_chroma_interleaved_RGB, <span class="literal">nullptr</span>);</span><br><span class="line"><span class="keyword">if</span> (err.code != heif_error_Ok) &#123;</span><br><span class="line"><span class="built_in">qDebug</span>() &lt;&lt; <span class="string">&quot;Failed to decode thumbnail: &quot;</span> &lt;&lt; err.message;</span><br><span class="line"><span class="built_in">heif_image_handle_release</span>(thumbnail_handle);</span><br><span class="line"><span class="built_in">heif_image_handle_release</span>(handle);</span><br><span class="line"><span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> width = <span class="built_in">heif_image_get_width</span>(thumbnail_image, heif_channel_interleaved);</span><br><span class="line"><span class="type">int</span> height = <span class="built_in">heif_image_get_height</span>(thumbnail_image, heif_channel_interleaved);</span><br><span class="line"><span class="type">int</span> stride;</span><br><span class="line"><span class="type">const</span> <span class="type">uint8_t</span>* data = <span class="built_in">heif_image_get_plane_readonly</span>(thumbnail_image, heif_channel_interleaved, &amp;stride);</span><br><span class="line"><span class="function">QImage <span class="title">image</span><span class="params">(data, width, height, stride, QImage::Format_RGB888)</span></span>; \</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (image.<span class="built_in">isNull</span>()) &#123;</span><br><span class="line"><span class="built_in">qDebug</span>() &lt;&lt; <span class="string">&quot;Failed to convert HEIF thumbnail to QImage.&quot;</span>;</span><br><span class="line"><span class="built_in">heif_image_release</span>(thumbnail_image);</span><br><span class="line"><span class="built_in">heif_image_handle_release</span>(thumbnail_handle);</span><br><span class="line"><span class="built_in">heif_image_handle_release</span>(handle);</span><br><span class="line"><span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">thumbnail = QPixmap::<span class="built_in">fromImage</span>(image.<span class="built_in">copy</span>());<span class="comment">//不使用copy data释放掉后内存访问冲突</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">heif_image_release</span>(thumbnail_image);</span><br><span class="line"><span class="built_in">heif_image_handle_release</span>(thumbnail_handle);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line"><span class="built_in">qDebug</span>() &lt;&lt; <span class="string">&quot;No thumbnails found in the HEIF file.&quot;</span>;</span><br><span class="line"><span class="built_in">heif_image_handle_release</span>(handle);</span><br><span class="line"><span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">heif_image_handle_release</span>(handle);</span><br><span class="line"><span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="3-测试代码"><a href="#3-测试代码" class="headerlink" title="3.测试代码"></a>3.测试代码</h2><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QApplication&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QLabel&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QImage&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QPixmap&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;libheif/heif.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">QImage <span class="title">heifToQImage</span><span class="params">(<span class="type">const</span> std::string&amp; filePath)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 初始化 libheif 句柄</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">heif_context</span>* ctx = <span class="built_in">heif_context_alloc</span>();</span><br><span class="line">    <span class="keyword">if</span> (!ctx) &#123;</span><br><span class="line">        <span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">&quot;Failed to allocate HEIF context.&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 读取 HEIF 文件</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">heif_error</span> err = <span class="built_in">heif_context_read_from_file</span>(ctx, filePath.<span class="built_in">c_str</span>(), <span class="literal">nullptr</span>);</span><br><span class="line">    <span class="keyword">if</span> (err.code != heif_error_Ok) &#123;</span><br><span class="line">        <span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line">        <span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">&quot;Failed to read HEIF file: &quot;</span> + std::<span class="built_in">string</span>(err.message));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取主图像句柄</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">heif_image_handle</span>* handle = <span class="literal">nullptr</span>;</span><br><span class="line">    err = <span class="built_in">heif_context_get_primary_image_handle</span>(ctx, &amp;handle);</span><br><span class="line">    <span class="keyword">if</span> (err.code != heif_error_Ok) &#123;</span><br><span class="line">        <span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line">        <span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">&quot;Failed to get primary image handle: &quot;</span> + std::<span class="built_in">string</span>(err.message));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 解码图像</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">heif_image</span>* img = <span class="literal">nullptr</span>;</span><br><span class="line">    err = <span class="built_in">heif_decode_image</span>(handle, &amp;img, heif_colorspace_RGB, heif_chroma_interleaved_RGBA, <span class="literal">nullptr</span>);</span><br><span class="line">    <span class="keyword">if</span> (err.code != heif_error_Ok) &#123;</span><br><span class="line">        <span class="built_in">heif_image_handle_release</span>(handle);</span><br><span class="line">        <span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line">        <span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">&quot;Failed to decode HEIF image: &quot;</span> + std::<span class="built_in">string</span>(err.message));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取图像数据</span></span><br><span class="line">    <span class="type">int</span> width = <span class="built_in">heif_image_get_width</span>(img, heif_channel_interleaved);</span><br><span class="line">    <span class="type">int</span> height = <span class="built_in">heif_image_get_height</span>(img, heif_channel_interleaved);</span><br><span class="line">    <span class="type">const</span> <span class="type">uint8_t</span>* data = <span class="built_in">heif_image_get_plane_readonly</span>(img, heif_channel_interleaved, <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="function">QVector&lt;uchar&gt; <span class="title">buffer</span><span class="params">(width * height * <span class="number">4</span>)</span></span>; <span class="comment">// RGBA8888，每像素4字节</span></span><br><span class="line">    <span class="built_in">memcpy</span>(buffer.<span class="built_in">data</span>(), data, buffer.<span class="built_in">size</span>());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 转换为 QImage</span></span><br><span class="line">    <span class="function">QImage <span class="title">qimage</span><span class="params">(buffer.data(), width, height, QImage::Format_RGBA8888)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放资源</span></span><br><span class="line">    <span class="built_in">heif_image_release</span>(img);</span><br><span class="line">    <span class="built_in">heif_image_handle_release</span>(handle);</span><br><span class="line">    <span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> qimage.<span class="built_in">copy</span>(); <span class="comment">// 确保 QImage 独立持有数据</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span>* argv[])</span> </span>&#123;</span><br><span class="line">    <span class="function">QApplication <span class="title">app</span><span class="params">(argc, argv)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        QImage image = <span class="built_in">heifToQImage</span>(<span class="string">&quot;C:/Users/lianx/Pictures/IMG_0001.HEIC&quot;</span>);</span><br><span class="line"></span><br><span class="line">        QLabel label;</span><br><span class="line">        label.<span class="built_in">setPixmap</span>(QPixmap::<span class="built_in">fromImage</span>(image));</span><br><span class="line">        label.<span class="built_in">setScaledContents</span>(<span class="literal">true</span>);</span><br><span class="line">        label.<span class="built_in">resize</span>(image.<span class="built_in">size</span>()/<span class="number">2</span>);</span><br><span class="line">        label.<span class="built_in">show</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> app.<span class="built_in">exec</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">catch</span> (<span class="type">const</span> std::exception&amp; e) &#123;</span><br><span class="line">        std::cerr &lt;&lt; <span class="string">&quot;Error: &quot;</span> &lt;&lt; e.<span class="built_in">what</span>() &lt;&lt; std::endl;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="4-效果如图"><a href="#4-效果如图" class="headerlink" title="4.效果如图"></a>4.效果如图</h2><p><img src="/2025/03/17/%E4%BD%BF%E7%94%A8libheif%E5%BA%93%E8%AF%BB%E5%8F%96heic%E6%96%87%E4%BB%B6%E7%9A%84%E7%BC%A9%E7%95%A5%E5%9B%BE/image-20241225235523540.png"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;使用libheif库读取heic文件的缩略图&quot;&gt;&lt;a href=&quot;#使用libheif库读取heic文件的缩略图&quot; class=&quot;headerlink&quot; title=&quot;使用libheif库读取heic文件的缩略图&quot;&gt;&lt;/a&gt;使用libheif库读取heic文件的缩</summary>
      
    
    
    
    <category term="C++" scheme="https://cpp-memory-leaks.github.io/categories/C/"/>
    
    
    <category term="C++ libheif" scheme="https://cpp-memory-leaks.github.io/tags/C-libheif/"/>
    
  </entry>
  
  <entry>
    <title>Qt中各种图像的格式转换以及与cv::Mat图像格式之间的转换</title>
    <link href="https://cpp-memory-leaks.github.io/2025/01/22/Qt%E4%B8%AD%E5%90%84%E7%A7%8D%E5%9B%BE%E5%83%8F%E7%9A%84%E6%A0%BC%E5%BC%8F%E8%BD%AC%E6%8D%A2%E4%BB%A5%E5%8F%8A%E4%B8%8Ecv-Mat%E5%9B%BE%E5%83%8F%E6%A0%BC%E5%BC%8F%E7%9A%84%E8%BD%AC%E6%8D%A2/"/>
    <id>https://cpp-memory-leaks.github.io/2025/01/22/Qt%E4%B8%AD%E5%90%84%E7%A7%8D%E5%9B%BE%E5%83%8F%E7%9A%84%E6%A0%BC%E5%BC%8F%E8%BD%AC%E6%8D%A2%E4%BB%A5%E5%8F%8A%E4%B8%8Ecv-Mat%E5%9B%BE%E5%83%8F%E6%A0%BC%E5%BC%8F%E7%9A%84%E8%BD%AC%E6%8D%A2/</id>
    <published>2025-01-21T16:33:34.000Z</published>
    <updated>2025-09-13T11:01:14.092Z</updated>
    
    <content type="html"><![CDATA[<p>在使用 Qt 与 OpenCV 联合开发时，常常需要在 <code>QImage</code> 和 <code>cv::Mat</code> 之间进行图像格式转换。两者的图像格式不同，因此理解 <strong><code>QImage::Format</code> 与 <code>cv::Mat</code> 类型的对应关系</strong> 非常关键。以下是常见格式的对应关系表：</p><h2 id="1-QImage-Format-与-cv-Mat-类型对照表："><a href="#1-QImage-Format-与-cv-Mat-类型对照表：" class="headerlink" title="1. QImage::Format 与 cv::Mat 类型对照表："></a>1. <code>QImage::Format</code> 与 <code>cv::Mat</code> 类型对照表：</h2><table><thead><tr><th align="left">QImage::Format</th><th align="left">描述</th><th align="left">对应的 <code>cv::Mat</code> 类型</th><th>通道顺序</th></tr></thead><tbody><tr><td align="left"><code>QImage::Format_RGB888</code></td><td align="left">24-bit RGB（每像素3字节）</td><td align="left"><code>CV_8UC3</code></td><td>RGB → OpenCV中是BGR</td></tr><tr><td align="left"><code>QImage::Format_BGR888</code></td><td align="left">24-bit BGR（Qt 5.14+）</td><td align="left"><code>CV_8UC3</code></td><td>BGR</td></tr><tr><td align="left"><code>QImage::Format_ARGB32</code></td><td align="left">32-bit ARGB（Alpha在高字节）</td><td align="left"><code>CV_8UC4</code></td><td>BGRA（OpenCV里是BGRA）</td></tr><tr><td align="left"><code>QImage::Format_ARGB32_Premultiplied</code></td><td align="left">预乘Alpha的32位ARGB</td><td align="left"><code>CV_8UC4</code></td><td>BGRA（需额外处理Alpha）</td></tr><tr><td align="left"><code>QImage::Format_RGB32</code></td><td align="left">32-bit RGB（Alpha总为255）</td><td align="left"><code>CV_8UC4</code></td><td>BGRA（Alpha恒为255）</td></tr><tr><td align="left"><code>QImage::Format_Grayscale8</code></td><td align="left">8-bit 灰度图（Qt 5.5+）</td><td align="left"><code>CV_8UC1</code></td><td>灰度</td></tr><tr><td align="left"><code>QImage::Format_Indexed8</code></td><td align="left">8-bit索引图（常见于灰度图）</td><td align="left"><code>CV_8UC1</code></td><td>灰度</td></tr></tbody></table><h2 id="2-OpenCV图像底层"><a href="#2-OpenCV图像底层" class="headerlink" title="2. OpenCV图像底层"></a>2. OpenCV图像底层</h2><p>OpenCV 中的 <code>cv::Mat</code> 图像类型（type）是一个 <strong>整数类型常量（int）</strong>，这个整数中编码了两个关键信息：</p><ol><li><strong>每个通道的数据类型（depth）</strong></li><li><strong>通道数（channels）</strong></li></ol><p><strong>本质上的编码方式：</strong></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">type = (depth &amp; <span class="number">0x7</span>) + ((channels - <span class="number">1</span>) &lt;&lt; <span class="number">3</span>);</span><br></pre></td></tr></table></figure><p> <strong>depth 和 channels 的取值</strong></p><table><thead><tr><th>数据类型</th><th>depth 宏常量值</th><th>二进制值</th><th>含义</th></tr></thead><tbody><tr><td><code>CV_8U</code></td><td>0</td><td>000</td><td>8-bit unsigned char</td></tr><tr><td><code>CV_8S</code></td><td>1</td><td>001</td><td>8-bit signed char</td></tr><tr><td><code>CV_16U</code></td><td>2</td><td>010</td><td>16-bit unsigned short</td></tr><tr><td><code>CV_16S</code></td><td>3</td><td>011</td><td>16-bit signed short</td></tr><tr><td><code>CV_32S</code></td><td>4</td><td>100</td><td>32-bit signed int</td></tr><tr><td><code>CV_32F</code></td><td>5</td><td>101</td><td>32-bit float</td></tr><tr><td><code>CV_64F</code></td><td>6</td><td>110</td><td>64-bit double</td></tr></tbody></table><p> <strong>通道数的编码方式：</strong></p><p>通道数编码在高位部分，每个通道是 <code>(channels - 1) &lt;&lt; 3</code>：</p><table><thead><tr><th>通道数（C）</th><th><code>(channels-1)&lt;&lt;3</code></th><th>最终编码中的通道部分</th></tr></thead><tbody><tr><td>1</td><td>0</td><td>00000000</td></tr><tr><td>2</td><td>8</td><td>00001000</td></tr><tr><td>3</td><td>16</td><td>00010000</td></tr><tr><td>4</td><td>24</td><td>00011000</td></tr></tbody></table><p><strong>举例：<code>CV_8UC3</code> 是怎么计算出来的？</strong></p><ul><li><code>CV_8U</code> 的值是 <code>0</code></li><li><code>C3</code> 表示通道数是 3 → <code>(3-1)&lt;&lt;3 = 16</code></li><li>所以 <code>CV_8UC3 = 0 + 16 = 16</code></li></ul><blockquote><p><code>cv::Mat::type()</code> 返回的值就是这个整数（16）。</p></blockquote><p>再举几个：</p><table><thead><tr><th>宏定义</th><th>值</th><th>分解</th></tr></thead><tbody><tr><td><code>CV_8UC1</code></td><td>0</td><td><code>CV_8U + ((1-1)&lt;&lt;3)</code></td></tr><tr><td><code>CV_8UC2</code></td><td>8</td><td><code>CV_8U + ((2-1)&lt;&lt;3)</code></td></tr><tr><td><code>CV_8UC3</code></td><td>16</td><td><code>CV_8U + ((3-1)&lt;&lt;3)</code></td></tr><tr><td><code>CV_8UC4</code></td><td>24</td><td><code>CV_8U + ((4-1)&lt;&lt;3)</code></td></tr><tr><td><code>CV_16UC1</code></td><td>2</td><td><code>CV_16U + ((1-1)&lt;&lt;3)</code></td></tr><tr><td><code>CV_16UC3</code></td><td>18</td><td><code>CV_16U + ((3-1)&lt;&lt;3)</code></td></tr><tr><td><code>CV_32FC1</code></td><td>5</td><td><code>CV_32F + ((1-1)&lt;&lt;3)</code></td></tr><tr><td><code>CV_32FC3</code></td><td>21</td><td><code>CV_32F + ((3-1)&lt;&lt;3)</code></td></tr></tbody></table><h3 id="2-1-OpenCV-图像类型解释"><a href="#2-1-OpenCV-图像类型解释" class="headerlink" title="2.1 OpenCV 图像类型解释"></a>2.1 OpenCV 图像类型解释</h3><figure class="highlight c++"><table><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">====================== OpenCV 图像类型解剖 ======================</span><br><span class="line"></span><br><span class="line">类型格式：   CV_&lt;bit-depth&gt;&#123;U|S|F&#125;C&lt;channels&gt;</span><br><span class="line">--------------------------------------------------------------</span><br><span class="line">- CV         : OpenCV 类型前缀</span><br><span class="line">- bit-depth  : 每个通道的位深（<span class="number">8</span>/<span class="number">16</span>/<span class="number">32</span>/<span class="number">64</span>）</span><br><span class="line">- U/S/F      : </span><br><span class="line">    U = <span class="type">unsigned</span>（无符号整数） → uchar/ushort</span><br><span class="line">    S = <span class="type">signed</span>（有符号整数）   → <span class="type">char</span>/<span class="type">short</span>/<span class="type">int</span></span><br><span class="line">    F = <span class="type">float</span>（浮点数）        → <span class="type">float</span>/<span class="type">double</span></span><br><span class="line">- C&lt;channels&gt;: 通道数（C1~C4）</span><br><span class="line"></span><br><span class="line">====================== 常见类型举例 ========================</span><br><span class="line"></span><br><span class="line">CV_8UC1  → <span class="number">8</span>位无符号灰度图       → uchar 灰度图</span><br><span class="line">CV_8UC3  → <span class="number">8</span>位无符号彩色图（BGR）→ uchar 彩色图（BGR）</span><br><span class="line">CV_8UC4  → <span class="number">8</span>位无符号<span class="number">4</span>通道（BGRA）→ uchar BGRA 图</span><br><span class="line"></span><br><span class="line">CV_16UC1 → <span class="number">16</span>位无符号灰度图      → ushort 深度图、医学图像</span><br><span class="line">CV_16SC1 → <span class="number">16</span>位有符号灰度图      → <span class="type">short</span> 常用于梯度图、滤波</span><br><span class="line">CV_32FC1 → <span class="number">32</span>位浮点灰度图        → <span class="type">float</span> 图像处理、光流</span><br><span class="line">CV_64FC4 → <span class="number">64</span>位浮点<span class="number">4</span>通道图        → <span class="type">double</span> 高频计算图</span><br><span class="line"></span><br><span class="line">==================== 底层编码机制 =======================</span><br><span class="line"></span><br><span class="line">OpenCV 图像类型实际是一个整数，编码方式如下：</span><br><span class="line"></span><br><span class="line">type = (depth &amp; <span class="number">0x7</span>) + ((channels - <span class="number">1</span>) &lt;&lt; <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line">depth 类型编码：</span><br><span class="line">    CV_8U  = <span class="number">0</span>   → <span class="type">unsigned</span> <span class="type">char</span></span><br><span class="line">    CV_8S  = <span class="number">1</span>   → <span class="type">char</span></span><br><span class="line">    CV_16U = <span class="number">2</span>   → <span class="type">unsigned</span> <span class="type">short</span></span><br><span class="line">    CV_16S = <span class="number">3</span>   → <span class="type">short</span></span><br><span class="line">    CV_32S = <span class="number">4</span>   → <span class="type">int</span></span><br><span class="line">    CV_32F = <span class="number">5</span>   → <span class="type">float</span></span><br><span class="line">    CV_64F = <span class="number">6</span>   → <span class="type">double</span></span><br><span class="line"></span><br><span class="line">通道数编码：(channels - <span class="number">1</span>) &lt;&lt; <span class="number">3</span></span><br><span class="line">    C1 = <span class="number">0</span> &lt;&lt; <span class="number">3</span> = <span class="number">0</span></span><br><span class="line">    C2 = <span class="number">1</span> &lt;&lt; <span class="number">3</span> = <span class="number">8</span></span><br><span class="line">    C3 = <span class="number">2</span> &lt;&lt; <span class="number">3</span> = <span class="number">16</span></span><br><span class="line">    C4 = <span class="number">3</span> &lt;&lt; <span class="number">3</span> = <span class="number">24</span></span><br><span class="line"></span><br><span class="line">例：CV_16UC3 = <span class="number">2</span>(depth) + (<span class="number">3</span><span class="number">-1</span>)&lt;&lt;<span class="number">3</span> = <span class="number">2</span> + <span class="number">16</span> = <span class="number">18</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="type">int</span> depth    = <span class="built_in">CV_MAT_DEPTH</span>(mat.<span class="built_in">type</span>());    <span class="comment">// 数据类型</span></span><br><span class="line"><span class="type">int</span> channels = <span class="built_in">CV_MAT_CN</span>(mat.<span class="built_in">type</span>());       <span class="comment">// 通道数</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="2-2-图像类型解剖图"><a href="#2-2-图像类型解剖图" class="headerlink" title="2.2 图像类型解剖图"></a>2.2 图像类型解剖图</h3><figure class="highlight c++"><table><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><br><span class="line">        │ CV_16UC3     │</span><br><span class="line">        └─────┬────────┘</span><br><span class="line">              │</span><br><span class="line">     ┌────────▼────────────┐</span><br><span class="line">     │ CV_&lt;Depth&gt;&lt;C&gt;&lt;N&gt;    │</span><br><span class="line">     │                    │</span><br><span class="line">     │ Depth: <span class="number">16U</span>         │ → <span class="type">unsigned</span> <span class="type">short</span>（<span class="number">16</span>位无符号）</span><br><span class="line">     │ C: C3              │ → 三通道（一般 BGR）</span><br><span class="line">     └────────────────────┘</span><br><span class="line"></span><br><span class="line">底层编码：</span><br><span class="line">    depth = CV_16U = <span class="number">2</span>（<span class="number">0000</span> <span class="number">0010</span>）</span><br><span class="line">    channels = <span class="number">3</span> → (<span class="number">3</span><span class="number">-1</span>)&lt;&lt;<span class="number">3</span> = <span class="number">16</span>（<span class="number">0001</span> <span class="number">0000</span>）</span><br><span class="line">    → 最终类型 = <span class="number">2</span> + <span class="number">16</span> = <span class="number">18</span></span><br><span class="line"></span><br><span class="line">    CV_16UC3 = <span class="number">18</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="3-图像格式转换"><a href="#3-图像格式转换" class="headerlink" title="3. 图像格式转换"></a>3. 图像格式转换</h2><h3 id="3-1cv-Mat-转-QImage"><a href="#3-1cv-Mat-转-QImage" class="headerlink" title="3.1cv::Mat 转 QImage"></a>3.1<code>cv::Mat</code> 转 <code>QImage</code></h3><figure class="highlight c++"><table><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="function">QImage <span class="title">Mat2QImage</span><span class="params">(<span class="type">const</span> cv::Mat&amp; mat)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">switch</span> (mat.<span class="built_in">type</span>()) &#123;</span><br><span class="line">    <span class="keyword">case</span> CV_8UC1: <span class="comment">// 灰度图</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">QImage</span>(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_Grayscale8).<span class="built_in">copy</span>();</span><br><span class="line">    <span class="keyword">case</span> CV_8UC3: <span class="comment">// 彩色图（BGR）</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">QImage</span>(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_RGB888).<span class="built_in">rgbSwapped</span>().<span class="built_in">copy</span>();</span><br><span class="line">    <span class="keyword">case</span> CV_8UC4: <span class="comment">// BGRA 图</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">QImage</span>(mat.data, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32).<span class="built_in">copy</span>();</span><br><span class="line">    <span class="keyword">default</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">QImage</span>(); <span class="comment">// 不支持的格式</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="3-2-QImage-转-cv-Mat"><a href="#3-2-QImage-转-cv-Mat" class="headerlink" title="3.2 QImage 转 cv::Mat"></a>3.2 <code>QImage</code> 转 <code>cv::Mat</code></h3><figure class="highlight c++"><table><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="function">cv::Mat <span class="title">QImage2Mat</span><span class="params">(<span class="type">const</span> QImage&amp; image)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">switch</span> (image.format()) &#123;</span><br><span class="line">    <span class="keyword">case</span> QImage::Format_RGB888:</span><br><span class="line">        <span class="keyword">return</span> cv::<span class="built_in">Mat</span>(image.<span class="built_in">height</span>(), image.<span class="built_in">width</span>(), CV_8UC3,</span><br><span class="line">                       <span class="built_in">const_cast</span>&lt;uchar*&gt;(image.<span class="built_in">bits</span>()), image.<span class="built_in">bytesPerLine</span>()).<span class="built_in">clone</span>();</span><br><span class="line">    <span class="keyword">case</span> QImage::Format_BGR888:</span><br><span class="line">        <span class="keyword">return</span> cv::<span class="built_in">Mat</span>(image.<span class="built_in">height</span>(), image.<span class="built_in">width</span>(), CV_8UC3,</span><br><span class="line">                       <span class="built_in">const_cast</span>&lt;uchar*&gt;(image.<span class="built_in">bits</span>()), image.<span class="built_in">bytesPerLine</span>()).<span class="built_in">clone</span>();</span><br><span class="line">    <span class="keyword">case</span> QImage::Format_ARGB32:</span><br><span class="line">    <span class="keyword">case</span> QImage::Format_RGB32:</span><br><span class="line">        <span class="keyword">return</span> cv::<span class="built_in">Mat</span>(image.<span class="built_in">height</span>(), image.<span class="built_in">width</span>(), CV_8UC4,</span><br><span class="line">                       <span class="built_in">const_cast</span>&lt;uchar*&gt;(image.<span class="built_in">bits</span>()), image.<span class="built_in">bytesPerLine</span>()).<span class="built_in">clone</span>();</span><br><span class="line">    <span class="keyword">case</span> QImage::Format_Grayscale8:</span><br><span class="line">    <span class="keyword">case</span> QImage::Format_Indexed8:</span><br><span class="line">        <span class="keyword">return</span> cv::<span class="built_in">Mat</span>(image.<span class="built_in">height</span>(), image.<span class="built_in">width</span>(), CV_8UC1,</span><br><span class="line">                       <span class="built_in">const_cast</span>&lt;uchar*&gt;(image.<span class="built_in">bits</span>()), image.<span class="built_in">bytesPerLine</span>()).<span class="built_in">clone</span>();</span><br><span class="line">    <span class="keyword">case</span> QImage::Format_Grayscale16: &#123;</span><br><span class="line">        <span class="keyword">return</span> cv::<span class="built_in">Mat</span>(image.<span class="built_in">height</span>(), image.<span class="built_in">width</span>(), CV_16UC1,</span><br><span class="line">                       <span class="built_in">const_cast</span>&lt;uchar*&gt;(image.<span class="built_in">bits</span>()), image.<span class="built_in">bytesPerLine</span>()).<span class="built_in">clone</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">default</span>:</span><br><span class="line">        <span class="keyword">return</span> cv::<span class="built_in">Mat</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="4-注意事项："><a href="#4-注意事项：" class="headerlink" title="4. 注意事项："></a>4. 注意事项：</h2><ul><li><code>QImage::Format_RGB888</code> 和 OpenCV 的 <code>CV_8UC3</code> 通道顺序不同（OpenCV默认是 BGR），所以常需要 <code>.rgbSwapped()</code>。</li><li><code>QImage::Format_RGB32</code> 实际也是 BGRA 格式，只不过 Alpha 通道为 255，OpenCV 也按 4 通道处理。</li><li><code>ARGB32_Premultiplied</code> 通常要特别处理 Alpha 通道（不常用）。</li><li>为了避免内存共享带来的问题，建议 <code>.copy()</code> 或 <code>.clone()</code>。</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在使用 Qt 与 OpenCV 联合开发时，常常需要在 &lt;code&gt;QImage&lt;/code&gt; 和 &lt;code&gt;cv::Mat&lt;/code&gt; 之间进行图像格式转换。两者的图像格式不同，因此理解 &lt;strong&gt;&lt;code&gt;QImage::Format&lt;/code&gt; 与 &lt;c</summary>
      
    
    
    
    <category term="Qt" scheme="https://cpp-memory-leaks.github.io/categories/Qt/"/>
    
    
    <category term="Qt" scheme="https://cpp-memory-leaks.github.io/tags/Qt/"/>
    
  </entry>
  
  <entry>
    <title>Qt使用libheif库显示苹果的heic图片</title>
    <link href="https://cpp-memory-leaks.github.io/2024/12/25/Qt%E4%BD%BF%E7%94%A8libheif%E5%BA%93%E6%98%BE%E7%A4%BA%E8%8B%B9%E6%9E%9C%E7%9A%84heic%E5%9B%BE%E7%89%87/"/>
    <id>https://cpp-memory-leaks.github.io/2024/12/25/Qt%E4%BD%BF%E7%94%A8libheif%E5%BA%93%E6%98%BE%E7%A4%BA%E8%8B%B9%E6%9E%9C%E7%9A%84heic%E5%9B%BE%E7%89%87/</id>
    <published>2024-12-25T12:17:31.000Z</published>
    <updated>2025-09-13T11:00:51.207Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Qt使用libheif库显示苹果的heic图片"><a href="#Qt使用libheif库显示苹果的heic图片" class="headerlink" title="Qt使用libheif库显示苹果的heic图片"></a>Qt使用libheif库显示苹果的heic图片</h1><h2 id="1-先使用vcpkg安装libheif库"><a href="#1-先使用vcpkg安装libheif库" class="headerlink" title="1.先使用vcpkg安装libheif库"></a>1.先使用vcpkg安装libheif库</h2><p>自己根据需要选择位数以及动态库还是静态库</p><figure class="highlight plaintext"><table><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">vcpkg install libheif:x86-windows-static</span><br><span class="line">vcpkg install libheif:x64-windows-static</span><br></pre></td></tr></table></figure><h2 id="2-直接上代码"><a href="#2-直接上代码" class="headerlink" title="2.直接上代码"></a>2.直接上代码</h2><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QApplication&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QLabel&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QImage&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QPixmap&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;libheif/heif.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">QImage <span class="title">heifToQImage</span><span class="params">(<span class="type">const</span> std::string&amp; filePath)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 初始化 libheif 句柄</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">heif_context</span>* ctx = <span class="built_in">heif_context_alloc</span>();</span><br><span class="line">    <span class="keyword">if</span> (!ctx) &#123;</span><br><span class="line">        <span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">&quot;Failed to allocate HEIF context.&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 读取 HEIF 文件</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">heif_error</span> err = <span class="built_in">heif_context_read_from_file</span>(ctx, filePath.<span class="built_in">c_str</span>(), <span class="literal">nullptr</span>);</span><br><span class="line">    <span class="keyword">if</span> (err.code != heif_error_Ok) &#123;</span><br><span class="line">        <span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line">        <span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">&quot;Failed to read HEIF file: &quot;</span> + std::<span class="built_in">string</span>(err.message));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取主图像句柄</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">heif_image_handle</span>* handle = <span class="literal">nullptr</span>;</span><br><span class="line">    err = <span class="built_in">heif_context_get_primary_image_handle</span>(ctx, &amp;handle);</span><br><span class="line">    <span class="keyword">if</span> (err.code != heif_error_Ok) &#123;</span><br><span class="line">        <span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line">        <span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">&quot;Failed to get primary image handle: &quot;</span> + std::<span class="built_in">string</span>(err.message));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 解码图像</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">heif_image</span>* img = <span class="literal">nullptr</span>;</span><br><span class="line">    err = <span class="built_in">heif_decode_image</span>(handle, &amp;img, heif_colorspace_RGB, heif_chroma_interleaved_RGBA, <span class="literal">nullptr</span>);</span><br><span class="line">    <span class="keyword">if</span> (err.code != heif_error_Ok) &#123;</span><br><span class="line">        <span class="built_in">heif_image_handle_release</span>(handle);</span><br><span class="line">        <span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line">        <span class="keyword">throw</span> std::<span class="built_in">runtime_error</span>(<span class="string">&quot;Failed to decode HEIF image: &quot;</span> + std::<span class="built_in">string</span>(err.message));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取图像数据</span></span><br><span class="line">    <span class="type">int</span> width = <span class="built_in">heif_image_get_width</span>(img, heif_channel_interleaved);</span><br><span class="line">    <span class="type">int</span> height = <span class="built_in">heif_image_get_height</span>(img, heif_channel_interleaved);</span><br><span class="line">    <span class="type">const</span> <span class="type">uint8_t</span>* data = <span class="built_in">heif_image_get_plane_readonly</span>(img, heif_channel_interleaved, <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="function">QVector&lt;uchar&gt; <span class="title">buffer</span><span class="params">(width * height * <span class="number">4</span>)</span></span>; <span class="comment">// RGBA8888，每像素4字节</span></span><br><span class="line">    <span class="built_in">memcpy</span>(buffer.<span class="built_in">data</span>(), data, buffer.<span class="built_in">size</span>());</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 转换为 QImage</span></span><br><span class="line">    <span class="function">QImage <span class="title">qimage</span><span class="params">(buffer.data(), width, height, QImage::Format_RGBA8888)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 释放资源</span></span><br><span class="line">    <span class="built_in">heif_image_release</span>(img);</span><br><span class="line">    <span class="built_in">heif_image_handle_release</span>(handle);</span><br><span class="line">    <span class="built_in">heif_context_free</span>(ctx);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> qimage.<span class="built_in">copy</span>(); <span class="comment">// 确保 QImage 独立持有数据</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span>* argv[])</span> </span>&#123;</span><br><span class="line">    <span class="function">QApplication <span class="title">app</span><span class="params">(argc, argv)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        QImage image = <span class="built_in">heifToQImage</span>(<span class="string">&quot;C:/Users/lianx/Pictures/IMG_0001.HEIC&quot;</span>);</span><br><span class="line"></span><br><span class="line">        QLabel label;</span><br><span class="line">        label.<span class="built_in">setPixmap</span>(QPixmap::<span class="built_in">fromImage</span>(image));</span><br><span class="line">        label.<span class="built_in">setScaledContents</span>(<span class="literal">true</span>);</span><br><span class="line">        label.<span class="built_in">resize</span>(image.<span class="built_in">size</span>()/<span class="number">2</span>);</span><br><span class="line">        label.<span class="built_in">show</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> app.<span class="built_in">exec</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">catch</span> (<span class="type">const</span> std::exception&amp; e) &#123;</span><br><span class="line">        std::cerr &lt;&lt; <span class="string">&quot;Error: &quot;</span> &lt;&lt; e.<span class="built_in">what</span>() &lt;&lt; std::endl;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="3-效果如图"><a href="#3-效果如图" class="headerlink" title="3.效果如图"></a>3.效果如图</h2><img src="/2024/12/25/Qt%E4%BD%BF%E7%94%A8libheif%E5%BA%93%E6%98%BE%E7%A4%BA%E8%8B%B9%E6%9E%9C%E7%9A%84heic%E5%9B%BE%E7%89%87/image-20241225235523540.png" class="" title="image-20241225235523540">]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;Qt使用libheif库显示苹果的heic图片&quot;&gt;&lt;a href=&quot;#Qt使用libheif库显示苹果的heic图片&quot; class=&quot;headerlink&quot; title=&quot;Qt使用libheif库显示苹果的heic图片&quot;&gt;&lt;/a&gt;Qt使用libheif库显示苹果的</summary>
      
    
    
    
    <category term="Qt" scheme="https://cpp-memory-leaks.github.io/categories/Qt/"/>
    
    
    <category term="Qt" scheme="https://cpp-memory-leaks.github.io/tags/Qt/"/>
    
  </entry>
  
  <entry>
    <title>QCheckBox的三态复选框和反选</title>
    <link href="https://cpp-memory-leaks.github.io/2024/12/25/QCheckBox%E7%9A%84%E4%B8%89%E6%80%81%E5%A4%8D%E9%80%89%E6%A1%86%E5%92%8C%E5%8F%8D%E9%80%89/"/>
    <id>https://cpp-memory-leaks.github.io/2024/12/25/QCheckBox%E7%9A%84%E4%B8%89%E6%80%81%E5%A4%8D%E9%80%89%E6%A1%86%E5%92%8C%E5%8F%8D%E9%80%89/</id>
    <published>2024-12-25T11:10:56.000Z</published>
    <updated>2025-09-13T11:00:18.384Z</updated>
    
    <content type="html"><![CDATA[<h1 id="QCheckBox的三态复选框以及反选"><a href="#QCheckBox的三态复选框以及反选" class="headerlink" title="QCheckBox的三态复选框以及反选"></a>QCheckBox的三态复选框以及反选</h1><p>在使用这个控件的时候，需求有可能是：<strong>点击复选框实线反选，并且选中项目的时候需要显示未选中、部分选中和全选</strong>。在这个需求当中，首先想到的是直接启用三态复选框，但是点击的时候就变成了可以点三次，这就和反选冲突了。</p><h2 id="1-QCheckBox的三种状态："><a href="#1-QCheckBox的三种状态：" class="headerlink" title="1.QCheckBox的三种状态："></a>1.QCheckBox的三种状态：</h2><figure class="highlight c++"><table><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">enum</span> <span class="title class_">CheckState</span> &#123;</span><br><span class="line">    Unchecked,</span><br><span class="line">    PartiallyChecked,</span><br><span class="line">    Checked</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="2-如何启用三态复选框"><a href="#2-如何启用三态复选框" class="headerlink" title="2.如何启用三态复选框"></a>2.如何启用三态复选框</h2><p>使用setTristate(true)即可开启，使用void QCheckBox::setCheckState(<a href="../qtcore/qt.html#CheckState-enum">Qt::CheckState</a> <em>state</em>)参数为Qt::PartiallyChecked也会启用三态</p><img src="/2024/12/25/QCheckBox%E7%9A%84%E4%B8%89%E6%80%81%E5%A4%8D%E9%80%89%E6%A1%86%E5%92%8C%E5%8F%8D%E9%80%89/image-20241225165142718.png" class="" title="image-20241225165142718"><h2 id="3-如何实现需求"><a href="#3-如何实现需求" class="headerlink" title="3.如何实现需求"></a>3.如何实现需求</h2><p>显示选中状态：选择项目的时候根据选中情况设置QCheckBox的状态（如在QListView中可以通过QItemSelectionModel::selectionChanged信号实时统计）</p><figure class="highlight c++"><table><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="keyword">if</span> (selectedItems == <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">ui-&gt;checkBox_select-&gt;<span class="built_in">setCheckState</span>(Qt::Unchecked);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (selectedItems &lt; totalItems)</span><br><span class="line">&#123;</span><br><span class="line">ui-&gt;checkBox_select-&gt;<span class="built_in">setCheckState</span>(Qt::PartiallyChecked);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">ui-&gt;checkBox_select-&gt;<span class="built_in">setCheckState</span>(Qt::Checked);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>反选：点击checkBox后关掉三态，当你再次触发selectionChanged时三态复选框重新启用，即可实现实时选中及反选功能</p><figure class="highlight c++"><table><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="built_in">connect</span>(ui-&gt;checkBox_select, &amp;QCheckBox::clicked, <span class="keyword">this</span>, &amp;MainWindow::selectAll);</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PhoneToPCPage::selectAll</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">ui-&gt;checkBox_select-&gt;<span class="built_in">setTristate</span>(<span class="literal">false</span>);</span><br><span class="line">QListView* listView = <span class="built_in">getListView</span>();</span><br><span class="line"><span class="keyword">if</span> (listView == <span class="literal">nullptr</span>)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line">QItemSelectionModel* selectionModel = listView-&gt;<span class="built_in">selectionModel</span>();</span><br><span class="line">QAbstractItemModel* model = listView-&gt;<span class="built_in">model</span>();</span><br><span class="line"><span class="type">bool</span> selectAll = ui-&gt;checkBox_select-&gt;<span class="built_in">checkState</span>() == Qt::Checked;</span><br><span class="line"><span class="keyword">if</span> (selectAll) &#123;</span><br><span class="line">QItemSelection selection;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> row = <span class="number">0</span>; row &lt; model-&gt;<span class="built_in">rowCount</span>(); ++row) &#123;</span><br><span class="line">QModelIndex index = model-&gt;<span class="built_in">index</span>(row, <span class="number">0</span>);</span><br><span class="line">selection.<span class="built_in">select</span>(index, index);</span><br><span class="line">&#125;</span><br><span class="line">selectionModel-&gt;<span class="built_in">select</span>(selection, QItemSelectionModel::Select);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">selectionModel-&gt;<span class="built_in">clearSelection</span>();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="3-效果图"><a href="#3-效果图" class="headerlink" title="3.效果图"></a>3.效果图</h2><img src="/2024/12/25/QCheckBox%E7%9A%84%E4%B8%89%E6%80%81%E5%A4%8D%E9%80%89%E6%A1%86%E5%92%8C%E5%8F%8D%E9%80%89/image-20241225170603608.png" class="" title="image-20241225170603608"><img src="/2024/12/25/QCheckBox%E7%9A%84%E4%B8%89%E6%80%81%E5%A4%8D%E9%80%89%E6%A1%86%E5%92%8C%E5%8F%8D%E9%80%89/image-20241225170618346.png" class="" title="image-20241225170618346"><img src="/2024/12/25/QCheckBox%E7%9A%84%E4%B8%89%E6%80%81%E5%A4%8D%E9%80%89%E6%A1%86%E5%92%8C%E5%8F%8D%E9%80%89/image-20241225170629562.png" class="" title="image-20241225170629562">]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;QCheckBox的三态复选框以及反选&quot;&gt;&lt;a href=&quot;#QCheckBox的三态复选框以及反选&quot; class=&quot;headerlink&quot; title=&quot;QCheckBox的三态复选框以及反选&quot;&gt;&lt;/a&gt;QCheckBox的三态复选框以及反选&lt;/h1&gt;&lt;p&gt;在使</summary>
      
    
    
    
    <category term="Qt" scheme="https://cpp-memory-leaks.github.io/categories/Qt/"/>
    
    
    <category term="Qt" scheme="https://cpp-memory-leaks.github.io/tags/Qt/"/>
    
  </entry>
  
  <entry>
    <title>Qt高性能列表控件之QListView的使用及优点</title>
    <link href="https://cpp-memory-leaks.github.io/2024/12/17/Qt%E9%AB%98%E6%80%A7%E8%83%BD%E5%88%97%E8%A1%A8%E6%8E%A7%E4%BB%B6%E4%B9%8BQListView%E7%9A%84%E4%BD%BF%E7%94%A8%E5%8F%8A%E4%BC%98%E7%82%B9/"/>
    <id>https://cpp-memory-leaks.github.io/2024/12/17/Qt%E9%AB%98%E6%80%A7%E8%83%BD%E5%88%97%E8%A1%A8%E6%8E%A7%E4%BB%B6%E4%B9%8BQListView%E7%9A%84%E4%BD%BF%E7%94%A8%E5%8F%8A%E4%BC%98%E7%82%B9/</id>
    <published>2024-12-17T07:16:36.000Z</published>
    <updated>2025-09-13T11:00:26.193Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Qt高性能列表控件之QListView的使用及优点"><a href="#Qt高性能列表控件之QListView的使用及优点" class="headerlink" title="Qt高性能列表控件之QListView的使用及优点"></a>Qt高性能列表控件之QListView的使用及优点</h1><h2 id="1-高性能列表如何能实线高性能？"><a href="#1-高性能列表如何能实线高性能？" class="headerlink" title="1.高性能列表如何能实线高性能？"></a>1.高性能列表如何能实线高性能？</h2><p>高性能列表无非就解决两个痛点：</p><p><strong>数据存放在哪里</strong></p><p><strong>数据如何展示</strong></p><p>为何QListWidget就不能高性能呢？<code>QListWidget</code> 是一个方便的控件，它内部管理了一个项目列表，并提供了一些简单的接口来添加、删除和修改这些项目。但<strong>没有对数据存储和数据展示进行过多的优化</strong>，这种方式适合于简单的应用场景，其中列表的大小不会很大，因为每个项目都会被存储为一个 <code>QListWidgetItem</code> 对象。</p><p>在QListView体系里，<strong>QAbstractListModel</strong>解决的是“<strong>数据存哪</strong>”，解决的是第一个问题，而<strong>QAbstractItemDelegate</strong>解决的是数据“<strong>如何展示</strong>”，解决的是第二个问题。</p><h2 id="2-QListView和QAbstractListModel解决数据存哪"><a href="#2-QListView和QAbstractListModel解决数据存哪" class="headerlink" title="2.QListView和QAbstractListModel解决数据存哪"></a>2.QListView和QAbstractListModel解决数据存哪</h2><p>这里就不从QAbstractListModel派生写自定义的类了，直接使用Qt从QAbstractListModel派生的类QStandardItemModel</p><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//初始化QListView</span></span><br><span class="line">QListView* photoListViewphotoListView = <span class="keyword">new</span> <span class="built_in">QListView</span>(<span class="keyword">this</span>);</span><br><span class="line">photoListView-&gt;<span class="built_in">setViewMode</span>(QListView::IconMode);</span><br><span class="line">photoListView-&gt;<span class="built_in">setResizeMode</span>(QListView::Adjust);</span><br><span class="line">photoListView-&gt;<span class="built_in">setIconSize</span>(<span class="built_in">QSize</span>(<span class="number">150</span>, <span class="number">150</span>));</span><br><span class="line">photoListView-&gt;<span class="built_in">setSpacing</span>(<span class="number">10</span>);</span><br><span class="line">photoListView-&gt;<span class="built_in">setSelectionMode</span>(QAbstractItemView::ExtendedSelection);</span><br><span class="line">photoListView-&gt;<span class="built_in">setUniformItemSizes</span>(<span class="literal">true</span>);</span><br><span class="line">photoListView-&gt;<span class="built_in">setVerticalScrollBarPolicy</span>(Qt::ScrollBarAlwaysOff);</span><br><span class="line">photoListView-&gt;<span class="built_in">setAttribute</span>(Qt::WA_Hover); <span class="comment">// 启用 Hover 事件</span></span><br><span class="line">photoListView-&gt;<span class="built_in">viewport</span>()-&gt;<span class="built_in">setAttribute</span>(Qt::WA_Hover);</span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化QStandardItemModel</span></span><br><span class="line">QStandardItemModel* m_photoModel = <span class="keyword">new</span> <span class="built_in">QStandardItemModel</span>(<span class="keyword">this</span>);</span><br><span class="line">ui-&gt;listView_Photos-&gt;<span class="built_in">setItemDelegate</span>(<span class="keyword">new</span> <span class="built_in">PhotoDelegate</span>(ui-&gt;listView_Photos));<span class="comment">//这是解决数据如何展示的</span></span><br><span class="line">ui-&gt;listView_Photos-&gt;<span class="built_in">model</span>()-&gt;<span class="built_in">removeRows</span>(<span class="number">0</span>, ui-&gt;listView_Photos-&gt;<span class="built_in">model</span>()-&gt;<span class="built_in">rowCount</span>(<span class="built_in">QModelIndex</span>()));</span><br><span class="line"><span class="comment">//模拟数据</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span> photoInfo : m_vecPhoto) &#123;</span><br><span class="line">QStandardItem* item = <span class="keyword">new</span> QStandardItem;</span><br><span class="line">item-&gt;<span class="built_in">setEditable</span>(<span class="literal">false</span>);</span><br><span class="line">item-&gt;<span class="built_in">setData</span>(<span class="string">&quot;:/images/Demo.png&quot;</span>, Qt::UserRole);</span><br><span class="line"><span class="comment">//item-&gt;setData(photoInfo.duration,Qt::UserRole);</span></span><br><span class="line">item-&gt;<span class="built_in">setData</span>(photoInfo.fileName, Qt::UserRole + <span class="number">1</span>);</span><br><span class="line">item-&gt;<span class="built_in">setData</span>(photoInfo.width + <span class="string">&quot;x&quot;</span> + photoInfo.height, Qt::UserRole + <span class="number">2</span>);</span><br><span class="line">item-&gt;<span class="built_in">setData</span>(photoInfo.dateCreated, Qt::UserRole + <span class="number">3</span>);</span><br><span class="line">item-&gt;<span class="built_in">setData</span>(photoInfo.fileSize, Qt::UserRole + <span class="number">4</span>);</span><br><span class="line">item-&gt;<span class="built_in">setData</span>(photoInfo.dir, Qt::UserRole + <span class="number">5</span>);</span><br><span class="line"><span class="comment">//将数据存放到QStandardItemModel里面</span></span><br><span class="line">m_photoModel-&gt;<span class="built_in">appendRow</span>(item);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="3-QListView和QAbstractItemDelegate解决数据如何展示"><a href="#3-QListView和QAbstractItemDelegate解决数据如何展示" class="headerlink" title="3.QListView和QAbstractItemDelegate解决数据如何展示"></a>3.QListView和QAbstractItemDelegate解决数据如何展示</h2><p>QStyledItemDelegate是从QAbstractItemDelegate派生的类，<strong>QListView的View模型采用的是paint函数来呈现，paint的形式用起来更复杂，但性能天花板更高</strong>。这就是QListView的性能如此高的原因。</p><p>包括了IconMode以及ListMode的显示</p><figure class="highlight c++"><table><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><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">PhotoDelegate</span> : <span class="keyword">public</span> QStyledItemDelegate &#123;</span><br><span class="line">Q_OBJECT</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">ViewMode</span> &#123; ListMode, GridMode &#125;;</span><br><span class="line"><span class="function"><span class="keyword">explicit</span> <span class="title">PhotoDelegate</span><span class="params">(QListView* parent = <span class="literal">nullptr</span>)</span></span></span><br><span class="line"><span class="function">: QStyledItemDelegate(parent) &#123;</span></span><br><span class="line">listView = parent;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">paint</span><span class="params">(QPainter* painter, <span class="type">const</span> QStyleOptionViewItem&amp; option, <span class="type">const</span> QModelIndex&amp; index)</span> <span class="type">const</span> <span class="keyword">override</span> </span>&#123;</span><br><span class="line">painter-&gt;<span class="built_in">save</span>();</span><br><span class="line">painter-&gt;<span class="built_in">setRenderHint</span>(QPainter::Antialiasing);</span><br><span class="line">QListView::ViewMode viewMode = listView-&gt;<span class="built_in">viewMode</span>();</span><br><span class="line"><span class="function">QPixmap <span class="title">pixmap</span><span class="params">(index.data(Qt::UserRole).toString())</span></span>;</span><br><span class="line"><span class="comment">//图片信息</span></span><br><span class="line">QString fileName = index.<span class="built_in">data</span>(Qt::UserRole + <span class="number">1</span>).<span class="built_in">toString</span>();</span><br><span class="line">QString fileReso = index.<span class="built_in">data</span>(Qt::UserRole + <span class="number">2</span>).<span class="built_in">toString</span>();</span><br><span class="line">QString fileDate = index.<span class="built_in">data</span>(Qt::UserRole + <span class="number">3</span>).<span class="built_in">toString</span>();</span><br><span class="line">QString fileSize = index.<span class="built_in">data</span>(Qt::UserRole + <span class="number">4</span>).<span class="built_in">toString</span>();</span><br><span class="line"></span><br><span class="line">QRect iconRect = <span class="built_in">QRect</span>(option.rect.<span class="built_in">left</span>() + <span class="number">5</span>, option.rect.<span class="built_in">top</span>() + <span class="number">5</span>, <span class="number">50</span>, <span class="number">50</span>); <span class="comment">// 图标区域</span></span><br><span class="line">QRect nameRect = <span class="built_in">QRect</span>(iconRect.<span class="built_in">right</span>() - <span class="number">50</span>, option.rect.<span class="built_in">top</span>() + <span class="number">100</span>, option.rect.<span class="built_in">width</span>(), <span class="number">20</span>); <span class="comment">// 文件名</span></span><br><span class="line">QRect dateRect = <span class="built_in">QRect</span>(iconRect.<span class="built_in">right</span>() - <span class="number">50</span>, option.rect.<span class="built_in">top</span>() + <span class="number">120</span>, option.rect.<span class="built_in">width</span>(), <span class="number">20</span>); <span class="comment">// 日期</span></span><br><span class="line">QRect sizeRect = <span class="built_in">QRect</span>(iconRect.<span class="built_in">right</span>() - <span class="number">50</span>, option.rect.<span class="built_in">top</span>() + <span class="number">140</span>, option.rect.<span class="built_in">width</span>(), <span class="number">20</span>); <span class="comment">// 大小</span></span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> isSelected = option.state &amp; QStyle::State_Selected;</span><br><span class="line"><span class="type">bool</span> isHovered = option.state &amp; QStyle::State_MouseOver;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (viewMode == QListView::ViewMode::IconMode)</span><br><span class="line">&#123;</span><br><span class="line">QString filePath = index.<span class="built_in">data</span>(Qt::UserRole).<span class="built_in">value</span>&lt;QString&gt;();</span><br><span class="line">QRect imageRect = option.rect;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置图片为正方形，填充区域</span></span><br><span class="line"><span class="type">int</span> size = <span class="built_in">qMin</span>(imageRect.<span class="built_in">width</span>(), imageRect.<span class="built_in">height</span>());</span><br><span class="line"><span class="function">QRect <span class="title">squareRect</span><span class="params">(imageRect.topLeft(), QSize(size, size))</span></span>;</span><br><span class="line">squareRect.<span class="built_in">moveCenter</span>(imageRect.<span class="built_in">center</span>());</span><br><span class="line"></span><br><span class="line"><span class="comment">// 抗锯齿</span></span><br><span class="line">painter-&gt;<span class="built_in">setRenderHint</span>(QPainter::Antialiasing);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 裁剪，绘图</span></span><br><span class="line">QPainterPath clipPath;</span><br><span class="line">clipPath.<span class="built_in">addRoundedRect</span>(squareRect, <span class="number">10</span>, <span class="number">10</span>);</span><br><span class="line">painter-&gt;<span class="built_in">setClipPath</span>(clipPath);</span><br><span class="line">painter-&gt;<span class="built_in">drawPixmap</span>(squareRect, pixmap, pixmap.<span class="built_in">rect</span>());</span><br><span class="line"><span class="keyword">if</span> (!isSelected)</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">// 图标</span></span><br><span class="line"><span class="function">QPixmap <span class="title">checkIcon</span><span class="params">(<span class="string">&quot;:/images/unselect.png&quot;</span>)</span></span>;</span><br><span class="line"><span class="function">QRect <span class="title">checkRect</span><span class="params">(squareRect.topRight() - QPoint(SELECT_ICON_SIZE + <span class="number">5</span>, <span class="number">-5</span>),</span></span></span><br><span class="line"><span class="params"><span class="function">QSize(SELECT_ICON_SIZE, SELECT_ICON_SIZE))</span></span>;</span><br><span class="line">painter-&gt;<span class="built_in">drawPixmap</span>(checkRect, checkIcon);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绘制选中时的边框和勾选图标</span></span><br><span class="line"><span class="keyword">if</span> (isSelected) &#123;</span><br><span class="line"><span class="comment">// 蓝框</span></span><br><span class="line">painter-&gt;<span class="built_in">setPen</span>(<span class="built_in">QPen</span>(<span class="built_in">QColor</span>(<span class="number">41</span>, <span class="number">95</span>, <span class="number">204</span>), <span class="number">3</span>));</span><br><span class="line">painter-&gt;<span class="built_in">drawRoundedRect</span>(squareRect, <span class="number">10</span>, <span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 图标</span></span><br><span class="line"><span class="function">QPixmap <span class="title">checkIcon</span><span class="params">(<span class="string">&quot;:/images/selected.png&quot;</span>)</span></span>;</span><br><span class="line"><span class="function">QRect <span class="title">checkRect</span><span class="params">(squareRect.topRight() - QPoint(SELECT_ICON_SIZE + <span class="number">5</span>, <span class="number">-5</span>), QSize(SELECT_ICON_SIZE, SELECT_ICON_SIZE))</span></span>;</span><br><span class="line">painter-&gt;<span class="built_in">drawPixmap</span>(checkRect, checkIcon);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (isHovered)</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">// 蓝框</span></span><br><span class="line">painter-&gt;<span class="built_in">setPen</span>(<span class="built_in">QPen</span>(<span class="built_in">QColor</span>(<span class="number">41</span>, <span class="number">95</span>, <span class="number">204</span>), <span class="number">3</span>));</span><br><span class="line">painter-&gt;<span class="built_in">drawRoundedRect</span>(squareRect, <span class="number">10</span>, <span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">painter-&gt;<span class="built_in">setPen</span>(Qt::black);</span><br><span class="line">painter-&gt;<span class="built_in">drawText</span>(nameRect, Qt::AlignLeft | Qt::AlignVCenter, fileName);</span><br><span class="line">painter-&gt;<span class="built_in">drawText</span>(dateRect, Qt::AlignLeft | Qt::AlignVCenter, fileDate);</span><br><span class="line">painter-&gt;<span class="built_in">drawText</span>(sizeRect, Qt::AlignLeft | Qt::AlignVCenter, fileSize);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (viewMode == QListView::ViewMode::ListMode)</span><br><span class="line">&#123;</span><br><span class="line"><span class="comment">//listView-&gt;setSpacing(5);</span></span><br><span class="line"><span class="keyword">if</span> (isHovered)</span><br><span class="line">&#123;</span><br><span class="line">QRect <span class="built_in">fileRect</span>(option.rect.<span class="built_in">topLeft</span>(), option.rect.<span class="built_in">size</span>());</span><br><span class="line"><span class="type">int</span> borderRadius = <span class="number">10</span>; <span class="comment">// 圆角的半径</span></span><br><span class="line"></span><br><span class="line">painter-&gt;<span class="built_in">setPen</span>(Qt::NoPen); <span class="comment">// 去掉边框</span></span><br><span class="line">painter-&gt;<span class="built_in">setBrush</span>(<span class="built_in">QColor</span>(<span class="number">235</span>, <span class="number">241</span>, <span class="number">255</span>)); <span class="comment">// 设置填充颜色</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 圆角背景</span></span><br><span class="line">painter-&gt;<span class="built_in">drawRoundedRect</span>(fileRect, borderRadius, borderRadius);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 选中状态边框</span></span><br><span class="line"><span class="keyword">if</span> (isSelected) &#123;</span><br><span class="line"><span class="function">QRect <span class="title">fileRect</span><span class="params">(option.rect.topLeft(), option.rect.size())</span></span>;</span><br><span class="line"><span class="type">int</span> borderRadius = <span class="number">10</span>; <span class="comment">// 圆角的半径</span></span><br><span class="line">painter-&gt;<span class="built_in">setPen</span>(Qt::NoPen);</span><br><span class="line"><span class="comment">// 设置背景颜色</span></span><br><span class="line">painter-&gt;<span class="built_in">setBrush</span>(<span class="built_in">QColor</span>(<span class="number">235</span>, <span class="number">241</span>, <span class="number">255</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绘制带圆角的背景</span></span><br><span class="line">painter-&gt;<span class="built_in">drawRoundedRect</span>(fileRect, borderRadius, borderRadius);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绘制选中图标</span></span><br><span class="line">QRect checkIconRect = <span class="built_in">QRect</span>(option.rect.<span class="built_in">left</span>() + <span class="number">15</span>, option.rect.<span class="built_in">top</span>() + <span class="number">20</span>, SELECT_ICON_SIZE, SELECT_ICON_SIZE);</span><br><span class="line"><span class="function">QPixmap <span class="title">checkIcon</span><span class="params">(<span class="string">&quot;:/images/selected.png&quot;</span>)</span></span>;</span><br><span class="line">painter-&gt;<span class="built_in">drawPixmap</span>(checkIconRect, checkIcon);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line">QRect checkIconRect = <span class="built_in">QRect</span>(option.rect.<span class="built_in">left</span>() + <span class="number">15</span>, option.rect.<span class="built_in">top</span>() + <span class="number">20</span>, SELECT_ICON_SIZE, SELECT_ICON_SIZE);</span><br><span class="line"><span class="function">QPixmap <span class="title">checkIcon</span><span class="params">(<span class="string">&quot;:/images/unselect.png&quot;</span>)</span></span>;</span><br><span class="line">painter-&gt;<span class="built_in">drawPixmap</span>(checkIconRect, checkIcon);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绘制区域</span></span><br><span class="line">QRect iconRect = <span class="built_in">QRect</span>(option.rect.<span class="built_in">left</span>() + <span class="number">45</span>, option.rect.<span class="built_in">top</span>() + <span class="number">7</span>, <span class="number">44</span>, <span class="number">44</span>); <span class="comment">// 图标区域</span></span><br><span class="line">QRect nameRect = <span class="built_in">QRect</span>(iconRect.<span class="built_in">right</span>() + <span class="number">40</span>, option.rect.<span class="built_in">top</span>() + <span class="number">20</span>, <span class="number">200</span>, <span class="number">20</span>); <span class="comment">// 文件名</span></span><br><span class="line">QRect resoRect = <span class="built_in">QRect</span>(nameRect.<span class="built_in">right</span>() + <span class="number">40</span>, option.rect.<span class="built_in">top</span>() + <span class="number">20</span>, <span class="number">100</span>, <span class="number">20</span>); <span class="comment">// 分辨率</span></span><br><span class="line">QRect dateRect = <span class="built_in">QRect</span>(resoRect.<span class="built_in">right</span>() + <span class="number">40</span>, option.rect.<span class="built_in">top</span>() + <span class="number">20</span>, <span class="number">200</span>, <span class="number">20</span>); <span class="comment">// 日期</span></span><br><span class="line">QRect sizeRect = <span class="built_in">QRect</span>(dateRect.<span class="built_in">right</span>() + <span class="number">40</span>, option.rect.<span class="built_in">top</span>() + <span class="number">20</span>, <span class="number">100</span>, <span class="number">20</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="type">int</span> borderRadius = <span class="number">10</span>;</span><br><span class="line">QPainterPath path;</span><br><span class="line">path.<span class="built_in">addRoundedRect</span>(iconRect, borderRadius, borderRadius);</span><br><span class="line"></span><br><span class="line">painter-&gt;<span class="built_in">save</span>();</span><br><span class="line"><span class="comment">// 抗锯齿</span></span><br><span class="line">painter-&gt;<span class="built_in">setRenderHint</span>(QPainter::Antialiasing, <span class="literal">true</span>);</span><br><span class="line"><span class="comment">// 裁剪区域</span></span><br><span class="line">painter-&gt;<span class="built_in">setClipPath</span>(path);</span><br><span class="line">painter-&gt;<span class="built_in">drawPixmap</span>(iconRect, pixmap);</span><br><span class="line">painter-&gt;<span class="built_in">restore</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绘制文件名、日期和大小</span></span><br><span class="line">painter-&gt;<span class="built_in">setPen</span>(Qt::black);</span><br><span class="line">painter-&gt;<span class="built_in">drawText</span>(nameRect, Qt::AlignLeft | Qt::AlignVCenter, fileName);</span><br><span class="line">painter-&gt;<span class="built_in">setPen</span>(Qt::gray);</span><br><span class="line">painter-&gt;<span class="built_in">drawText</span>(resoRect, Qt::AlignLeft | Qt::AlignVCenter, fileReso);</span><br><span class="line">painter-&gt;<span class="built_in">drawText</span>(dateRect, Qt::AlignLeft | Qt::AlignVCenter, fileDate);</span><br><span class="line"></span><br><span class="line">painter-&gt;<span class="built_in">drawText</span>(sizeRect, Qt::AlignLeft | Qt::AlignVCenter, fileSize);</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">painter-&gt;<span class="built_in">restore</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function">QSize <span class="title">sizeHint</span><span class="params">(<span class="type">const</span> QStyleOptionViewItem&amp; option, <span class="type">const</span> QModelIndex&amp; index)</span> <span class="type">const</span> <span class="keyword">override</span> </span>&#123;</span><br><span class="line"><span class="built_in">Q_UNUSED</span>(index);</span><br><span class="line"><span class="comment">//固定大小</span></span><br><span class="line"><span class="keyword">if</span> (listView-&gt;<span class="built_in">viewMode</span>() == QListView::ViewMode::IconMode) &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="built_in">QSize</span>(<span class="number">170</span>, <span class="number">170</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">return</span>  <span class="built_in">QSize</span>(<span class="number">50</span>, <span class="number">60</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">editorEvent</span><span class="params">(QEvent* event, QAbstractItemModel* model, <span class="type">const</span> QStyleOptionViewItem&amp; option,</span></span></span><br><span class="line"><span class="params"><span class="function"><span class="type">const</span> QModelIndex&amp; index)</span> <span class="keyword">override</span> </span>&#123;</span><br><span class="line"><span class="keyword">if</span> (event-&gt;<span class="built_in">type</span>() == QEvent::MouseButtonPress) &#123;</span><br><span class="line">QMouseEvent* mouseEvent = <span class="built_in">static_cast</span>&lt;QMouseEvent*&gt;(event);</span><br><span class="line">QListView::ViewMode viewMode = listView-&gt;<span class="built_in">viewMode</span>();</span><br><span class="line"></span><br><span class="line">QRect checkRect;</span><br><span class="line"><span class="keyword">if</span> (viewMode == QListView::ViewMode::IconMode) &#123;</span><br><span class="line">checkRect = <span class="built_in">QRect</span>(option.rect.<span class="built_in">topRight</span>() - <span class="built_in">QPoint</span>(SELECT_ICON_SIZE + <span class="number">5</span>, <span class="number">-5</span>),</span><br><span class="line"><span class="built_in">QSize</span>(SELECT_ICON_SIZE, SELECT_ICON_SIZE));</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">checkRect = <span class="built_in">QRect</span>(option.rect.<span class="built_in">left</span>() + <span class="number">15</span>, option.rect.<span class="built_in">top</span>() + <span class="number">20</span>, SELECT_ICON_SIZE, SELECT_ICON_SIZE);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (checkRect.<span class="built_in">contains</span>(mouseEvent-&gt;<span class="built_in">pos</span>())) &#123;</span><br><span class="line"><span class="type">bool</span> isSelected = index.<span class="built_in">data</span>(Qt::UserRole + <span class="number">15</span>).<span class="built_in">toBool</span>();</span><br><span class="line">model-&gt;<span class="built_in">setData</span>(index, !isSelected, Qt::UserRole + <span class="number">15</span>);</span><br><span class="line">QItemSelectionModel* selectionModel = listView-&gt;<span class="built_in">selectionModel</span>();</span><br><span class="line"><span class="keyword">if</span> (!isSelected) &#123;</span><br><span class="line">selectionModel-&gt;<span class="built_in">select</span>(index, QItemSelectionModel::Select);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">selectionModel-&gt;<span class="built_in">select</span>(index, QItemSelectionModel::Deselect);</span><br><span class="line">&#125;</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">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> QStyledItemDelegate::<span class="built_in">editorEvent</span>(event, model, option, index);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">QListView* listView;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="4-效果图"><a href="#4-效果图" class="headerlink" title="4.效果图"></a>4.效果图</h2><img src="/2024/12/17/Qt%E9%AB%98%E6%80%A7%E8%83%BD%E5%88%97%E8%A1%A8%E6%8E%A7%E4%BB%B6%E4%B9%8BQListView%E7%9A%84%E4%BD%BF%E7%94%A8%E5%8F%8A%E4%BC%98%E7%82%B9/image-20241225153907663.png" class="" title="image-20241225153907663"><img src="/2024/12/17/Qt%E9%AB%98%E6%80%A7%E8%83%BD%E5%88%97%E8%A1%A8%E6%8E%A7%E4%BB%B6%E4%B9%8BQListView%E7%9A%84%E4%BD%BF%E7%94%A8%E5%8F%8A%E4%BC%98%E7%82%B9/image-20241225153943414.png" class="" title="image-20241225153943414"><img src="/2024/12/17/Qt%E9%AB%98%E6%80%A7%E8%83%BD%E5%88%97%E8%A1%A8%E6%8E%A7%E4%BB%B6%E4%B9%8BQListView%E7%9A%84%E4%BD%BF%E7%94%A8%E5%8F%8A%E4%BC%98%E7%82%B9/image-20241225154833298.png" class="" title="image-20241225154833298"><img src="/2024/12/17/Qt%E9%AB%98%E6%80%A7%E8%83%BD%E5%88%97%E8%A1%A8%E6%8E%A7%E4%BB%B6%E4%B9%8BQListView%E7%9A%84%E4%BD%BF%E7%94%A8%E5%8F%8A%E4%BC%98%E7%82%B9/image-20241225154855709.png" class="" title="image-20241225154855709">]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;Qt高性能列表控件之QListView的使用及优点&quot;&gt;&lt;a href=&quot;#Qt高性能列表控件之QListView的使用及优点&quot; class=&quot;headerlink&quot; title=&quot;Qt高性能列表控件之QListView的使用及优点&quot;&gt;&lt;/a&gt;Qt高性能列表控件之Q</summary>
      
    
    
    
    <category term="Qt" scheme="https://cpp-memory-leaks.github.io/categories/Qt/"/>
    
    
    <category term="Qt" scheme="https://cpp-memory-leaks.github.io/tags/Qt/"/>
    
  </entry>
  
  <entry>
    <title>自定义摇杆</title>
    <link href="https://cpp-memory-leaks.github.io/2024/11/07/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%91%87%E6%9D%86/"/>
    <id>https://cpp-memory-leaks.github.io/2024/11/07/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%91%87%E6%9D%86/</id>
    <published>2024-11-07T02:20:12.000Z</published>
    <updated>2025-09-13T10:59:59.356Z</updated>
    
    <content type="html"><![CDATA[<h1 id="自定义摇杆"><a href="#自定义摇杆" class="headerlink" title="自定义摇杆"></a>自定义摇杆</h1><p>今天做项目的时候，接到一个需求，需要做一个摇杆去控制物体的移动，功能还是挺复杂的</p><h2 id="1-功能："><a href="#1-功能：" class="headerlink" title="1.功能："></a>1.功能：</h2><ol><li>点击内圈发送一次对应的方向的信号</li><li>松开鼠标的时候如果在内圈就发送结束信号（第一第二点总结起来就是按下跑，松开停）</li><li>松开的时候如果鼠标位置在外圈，就不发送结束信号，一直跑</li><li>点击结束按钮或者内圈范围都会发送信号</li></ol><h3 id="直接上图"><a href="#直接上图" class="headerlink" title="直接上图"></a>直接上图</h3><p>原始状态：</p><img src="/2024/11/07/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%91%87%E6%9D%86/image-20241225143453202.png" class="" title="image-20241225143453202"><p>持续行走状态：</p><img src="/2024/11/07/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%91%87%E6%9D%86/image-20241225143636849.png" class="" title="image-20241225143636849"><h2 id="2-代码"><a href="#2-代码" class="headerlink" title="2.代码"></a>2.代码</h2><p>DirectionalControl.h头文件</p><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> DIRECTIONALCONTROL_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DIRECTIONALCONTROL_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QWidget&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QMouseEvent&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QPainter&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QPushButton&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QCheckBox&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QPointF&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QDebug&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DirectionalControl</span> : <span class="keyword">public</span> QWidget &#123;</span><br><span class="line">Q_OBJECT</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">DirectionButton</span></span><br><span class="line">&#123;</span><br><span class="line">UpButton,</span><br><span class="line">DownButton,</span><br><span class="line">LeftButton,</span><br><span class="line">RightButton,</span><br><span class="line">StopButton</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="keyword">explicit</span> <span class="title">DirectionalControl</span><span class="params">(QWidget* parent = <span class="literal">nullptr</span>)</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span>  <span class="title">resetDragPntPos</span><span class="params">()</span></span>;</span><br><span class="line"><span class="keyword">public</span> slots:</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">sendDirectionChanged</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">signals:</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">directionChanged</span><span class="params">(<span class="type">double</span> angle)</span></span>; <span class="comment">// 信号：方向改变</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">startMove</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">stopMove</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">handleClick</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">paintEvent</span><span class="params">(QPaintEvent* event)</span> <span class="keyword">override</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">mousePressEvent</span><span class="params">(QMouseEvent* event)</span> <span class="keyword">override</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">mouseMoveEvent</span><span class="params">(QMouseEvent* event)</span> <span class="keyword">override</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">mouseReleaseEvent</span><span class="params">(QMouseEvent* event)</span> <span class="keyword">override</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">updateDragPntDirection</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">keyReleaseEvent</span><span class="params">(QKeyEvent* event)</span> <span class="keyword">override</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">keyPressEvent</span><span class="params">(QKeyEvent* event)</span><span class="keyword">override</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> outerRadius = <span class="number">80</span>; <span class="comment">// 外圈半径</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> innerRadius = <span class="number">45</span>;  <span class="comment">// 内圈半径</span></span><br><span class="line">QPointF center;              <span class="comment">// 中心点</span></span><br><span class="line">QPointF dragPoint;           <span class="comment">// 当前拖动点</span></span><br><span class="line"><span class="type">bool</span> isDragging = <span class="literal">false</span>;     <span class="comment">// 是否正在拖动</span></span><br><span class="line"><span class="type">double</span> m_angle;</span><br><span class="line"><span class="type">bool</span> m_moving = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">QPushButton* stopButton;    <span class="comment">//按钮</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">QTimer* m_timer;            <span class="comment">//定时器，控制angleChange()发送频率</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isInsideOuterCircle</span><span class="params">(<span class="type">const</span> QPointF&amp; point)</span></span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isInsideInnerCircle</span><span class="params">(<span class="type">const</span> QPointF&amp; point)</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">calculateDirection</span><span class="params">()</span></span>; <span class="comment">// 计算当前方向</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setupButtons</span><span class="params">()</span></span>;       <span class="comment">// 初始化按钮布局</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">// DIRECTIONALCONTROL_H</span></span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>DirectionalControl.cpp源代码</p><figure class="highlight c++"><table><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><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;DirectionalControl.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;corecrt_math_defines.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QtMath&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;QTimer&gt;</span></span></span><br><span class="line"></span><br><span class="line">DirectionalControl::<span class="built_in">DirectionalControl</span>(QWidget* parent)</span><br><span class="line">: <span class="built_in">QWidget</span>(parent), <span class="built_in">center</span>(<span class="number">100</span>, <span class="number">100</span>) &#123;</span><br><span class="line"></span><br><span class="line"><span class="built_in">setFixedSize</span>(<span class="number">200</span>, <span class="number">200</span>);</span><br><span class="line"><span class="built_in">setWindowFlags</span>(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);</span><br><span class="line"><span class="built_in">setAttribute</span>(Qt::WA_TranslucentBackground);</span><br><span class="line"><span class="built_in">setFocusPolicy</span>(Qt::StrongFocus);</span><br><span class="line"><span class="comment">//this-&gt;setCursor(Qt::CursorShape::PointingHandCursor);</span></span><br><span class="line">m_timer = <span class="keyword">new</span> <span class="built_in">QTimer</span>(<span class="keyword">this</span>);</span><br><span class="line">dragPoint.<span class="built_in">setX</span>(center.<span class="built_in">x</span>());</span><br><span class="line">dragPoint.<span class="built_in">setY</span>(center.<span class="built_in">y</span>());</span><br><span class="line"></span><br><span class="line">QSize size = <span class="keyword">this</span>-&gt;<span class="built_in">size</span>();</span><br><span class="line"><span class="built_in">setupButtons</span>();</span><br><span class="line"></span><br><span class="line"><span class="function">QFile <span class="title">file</span><span class="params">(<span class="string">&quot;:/qss/resources/qss/DirectionControl.qss&quot;</span>)</span></span>;</span><br><span class="line">QByteArray style;</span><br><span class="line"><span class="keyword">if</span> (file.<span class="built_in">open</span>(QIODevice::ReadOnly))</span><br><span class="line">&#123;</span><br><span class="line">style += file.<span class="built_in">readAll</span>();</span><br><span class="line">file.<span class="built_in">close</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">setStyleSheet</span>(style);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">DirectionalControl::resetDragPntPos</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">dragPoint.<span class="built_in">setX</span>(center.<span class="built_in">x</span>());</span><br><span class="line">dragPoint.<span class="built_in">setY</span>(center.<span class="built_in">y</span>());</span><br><span class="line"><span class="built_in">update</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">DirectionalControl::sendDirectionChanged</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="function">emit <span class="title">directionChanged</span><span class="params">(m_angle)</span></span>;</span><br><span class="line">&#125;</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="type">void</span> <span class="title">DirectionalControl::setupButtons</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="comment">// 中心按钮：开始/暂停</span></span><br><span class="line">stopButton = <span class="keyword">new</span> <span class="built_in">QPushButton</span>(<span class="keyword">this</span>);</span><br><span class="line">stopButton-&gt;<span class="built_in">move</span>(<span class="number">90</span>, <span class="number">90</span>);</span><br><span class="line">stopButton-&gt;<span class="built_in">setObjectName</span>(<span class="string">&quot;stopButton&quot;</span>);</span><br><span class="line"><span class="built_in">connect</span>(stopButton, &amp;QPushButton::clicked, [<span class="keyword">this</span>]() &#123;</span><br><span class="line">emit <span class="built_in">stopMove</span>();</span><br><span class="line">m_moving = <span class="literal">false</span>;</span><br><span class="line">dragPoint.<span class="built_in">setX</span>(center.<span class="built_in">x</span>());</span><br><span class="line">dragPoint.<span class="built_in">setY</span>(center.<span class="built_in">y</span>());</span><br><span class="line">stopButton-&gt;<span class="built_in">hide</span>();</span><br><span class="line"><span class="built_in">update</span>();</span><br><span class="line">&#125;);</span><br><span class="line">stopButton-&gt;<span class="built_in">hide</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">DirectionalControl::paintEvent</span><span class="params">(QPaintEvent* event)</span> </span>&#123;</span><br><span class="line"><span class="built_in">Q_UNUSED</span>(event);</span><br><span class="line"></span><br><span class="line"><span class="function">QPainter <span class="title">painter</span><span class="params">(<span class="keyword">this</span>)</span></span>;</span><br><span class="line"><span class="comment">// 抗锯齿</span></span><br><span class="line">painter.<span class="built_in">setRenderHint</span>(QPainter::Antialiasing, <span class="literal">true</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">painter.<span class="built_in">setPen</span>(Qt::NoPen);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">const</span> QColor <span class="title">outerCircleColor</span><span class="params">(<span class="number">200</span>, <span class="number">220</span>, <span class="number">255</span>, <span class="number">200</span>)</span></span>; <span class="comment">// 外圈颜色</span></span><br><span class="line"><span class="function"><span class="type">const</span> QColor <span class="title">innerCircleColor</span><span class="params">(<span class="number">249</span>, <span class="number">249</span>, <span class="number">249</span>, <span class="number">150</span>)</span></span>; <span class="comment">// 内圈颜色</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> centerDotRadius = <span class="number">3</span>; <span class="comment">// 中心点半径</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> dragRadius = <span class="number">15</span>;     <span class="comment">// 拖动点半径</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 绘制外圈</span></span><br><span class="line">painter.<span class="built_in">setBrush</span>(outerCircleColor);</span><br><span class="line">painter.<span class="built_in">drawEllipse</span>(center, outerRadius, outerRadius);</span><br><span class="line"></span><br><span class="line"><span class="comment">//绘制内内圈</span></span><br><span class="line">painter.<span class="built_in">setPen</span>(<span class="built_in">QPen</span>(<span class="built_in">QColor</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">150</span>), <span class="number">3</span>));<span class="comment">//外边框</span></span><br><span class="line">painter.<span class="built_in">setBrush</span>(innerCircleColor);</span><br><span class="line">painter.<span class="built_in">drawEllipse</span>(center, dragRadius, dragRadius);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绘制内圈</span></span><br><span class="line">painter.<span class="built_in">setPen</span>(<span class="built_in">QPen</span>(<span class="built_in">QColor</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>, <span class="number">150</span>), <span class="number">3</span>));<span class="comment">//外边框</span></span><br><span class="line">painter.<span class="built_in">setBrush</span>(innerCircleColor);</span><br><span class="line">painter.<span class="built_in">drawEllipse</span>(center, innerRadius, innerRadius);</span><br><span class="line"><span class="comment">// 确保 dragPoint 不进入内圈且不超出外圈</span></span><br><span class="line"><span class="type">double</span> dx = dragPoint.<span class="built_in">x</span>() - center.<span class="built_in">x</span>();</span><br><span class="line"><span class="type">double</span> dy = dragPoint.<span class="built_in">y</span>() - center.<span class="built_in">y</span>();</span><br><span class="line"><span class="type">double</span> distance = std::<span class="built_in">sqrt</span>(dx * dx + dy * dy);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果 dragPoint 超出了外圈，修正到外圈边缘</span></span><br><span class="line"><span class="keyword">if</span> (distance &gt; outerRadius) &#123;</span><br><span class="line"><span class="type">double</span> scale = outerRadius / distance;</span><br><span class="line">dragPoint.<span class="built_in">setX</span>(center.<span class="built_in">x</span>() + dx * scale);</span><br><span class="line">dragPoint.<span class="built_in">setY</span>(center.<span class="built_in">y</span>() + dy * scale);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">painter.<span class="built_in">setPen</span>(<span class="built_in">QPen</span>(<span class="built_in">QColor</span>(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>)));</span><br><span class="line"><span class="function">QFont <span class="title">font</span><span class="params">(<span class="string">&quot;Arial&quot;</span>, <span class="number">20</span>, QFont::Bold)</span></span>;</span><br><span class="line"><span class="comment">// 设置画笔的字体</span></span><br><span class="line">painter.<span class="built_in">setFont</span>(font);</span><br><span class="line"></span><br><span class="line">painter.<span class="built_in">drawText</span>(<span class="number">87</span>, <span class="number">50</span>, <span class="string">&quot;W&quot;</span>);</span><br><span class="line">painter.<span class="built_in">drawText</span>(<span class="number">28</span>, <span class="number">110</span>, <span class="string">&quot;A&quot;</span>);</span><br><span class="line">painter.<span class="built_in">drawText</span>(<span class="number">90</span>, <span class="number">172</span>, <span class="string">&quot;S&quot;</span>);</span><br><span class="line">painter.<span class="built_in">drawText</span>(<span class="number">152</span>, <span class="number">110</span>, <span class="string">&quot;D&quot;</span>);</span><br><span class="line"><span class="comment">// 设置画刷颜色为白色并绘制拖动点</span></span><br><span class="line">painter.<span class="built_in">setPen</span>(Qt::NoPen);</span><br><span class="line">painter.<span class="built_in">setBrush</span>(Qt::white);</span><br><span class="line">painter.<span class="built_in">drawEllipse</span>(dragPoint, dragRadius, dragRadius);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在拖动点的中心绘制中心点</span></span><br><span class="line">painter.<span class="built_in">setPen</span>(<span class="built_in">QPen</span>(<span class="built_in">QColor</span>(<span class="number">0</span>, <span class="number">117</span>, <span class="number">255</span>), <span class="number">5</span>));</span><br><span class="line">painter.<span class="built_in">setBrush</span>(<span class="built_in">QColor</span>(<span class="number">0</span>, <span class="number">117</span>, <span class="number">255</span>));</span><br><span class="line">painter.<span class="built_in">drawEllipse</span>(dragPoint, centerDotRadius, centerDotRadius);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">DirectionalControl::mousePressEvent</span><span class="params">(QMouseEvent* event)</span> </span>&#123;</span><br><span class="line">QPointF pos = event-&gt;<span class="built_in">pos</span>();</span><br><span class="line"><span class="comment">//if (isInsideOuterCircle(pos) &amp;&amp; isInsideInnerCircle(pos)) &#123;</span></span><br><span class="line">isDragging = <span class="literal">true</span>;</span><br><span class="line">dragPoint = pos;</span><br><span class="line"><span class="function">emit <span class="title">startMove</span><span class="params">()</span></span>;</span><br><span class="line">m_moving = <span class="literal">true</span>;</span><br><span class="line"><span class="built_in">calculateDirection</span>();</span><br><span class="line"><span class="built_in">sendDirectionChanged</span>();</span><br><span class="line"><span class="comment">// 限制拖动点到外圈的范围</span></span><br><span class="line"><span class="type">double</span> dx = pos.<span class="built_in">x</span>() - center.<span class="built_in">x</span>();</span><br><span class="line"><span class="type">double</span> dy = pos.<span class="built_in">y</span>() - center.<span class="built_in">y</span>();</span><br><span class="line"><span class="type">double</span> distance = std::<span class="built_in">sqrt</span>(dx * dx + dy * dy);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (distance &lt; innerRadius) &#123;</span><br><span class="line"><span class="function">emit <span class="title">handleClick</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line">m_timer-&gt;<span class="built_in">start</span>(<span class="number">1000</span>);</span><br><span class="line"><span class="built_in">connect</span>(m_timer, &amp;QTimer::timeout, <span class="keyword">this</span>, &amp;DirectionalControl::sendDirectionChanged, Qt::<span class="built_in">ConnectionType</span>(Qt::AutoConnection | Qt::UniqueConnection));</span><br><span class="line"><span class="built_in">update</span>();</span><br><span class="line"><span class="comment">//&#125;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">DirectionalControl::mouseMoveEvent</span><span class="params">(QMouseEvent* event)</span> </span>&#123;</span><br><span class="line"><span class="keyword">if</span> (isDragging) &#123;</span><br><span class="line">QPointF pos = event-&gt;<span class="built_in">pos</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 限制拖动点到外圈的范围</span></span><br><span class="line"><span class="type">double</span> dx = pos.<span class="built_in">x</span>() - center.<span class="built_in">x</span>();</span><br><span class="line"><span class="type">double</span> dy = pos.<span class="built_in">y</span>() - center.<span class="built_in">y</span>();</span><br><span class="line"><span class="type">double</span> distance = std::<span class="built_in">sqrt</span>(dx * dx + dy * dy);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (distance &gt; outerRadius) &#123;</span><br><span class="line"><span class="comment">// 将拖动点限制在外圈的边界</span></span><br><span class="line"><span class="type">double</span> scale = outerRadius / distance;</span><br><span class="line">dragPoint.<span class="built_in">setX</span>(center.<span class="built_in">x</span>() + dx * scale);</span><br><span class="line">dragPoint.<span class="built_in">setY</span>(center.<span class="built_in">y</span>() + dy * scale);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*else if (distance &gt; innerRadius)</span></span><br><span class="line"><span class="comment">&#123;</span></span><br><span class="line"><span class="comment">dragPoint.setX(center.x());</span></span><br><span class="line"><span class="comment">dragPoint.setY(center.y());</span></span><br><span class="line"><span class="comment">&#125;*/</span></span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">dragPoint = pos;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">calculateDirection</span>();</span><br><span class="line"><span class="built_in">update</span>();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">DirectionalControl::mouseReleaseEvent</span><span class="params">(QMouseEvent* event)</span> </span>&#123;</span><br><span class="line"><span class="built_in">Q_UNUSED</span>(event);</span><br><span class="line">isDragging = <span class="literal">false</span>;</span><br><span class="line"><span class="comment">// TODO 点击内圈跑一步，点击外圈持续跑</span></span><br><span class="line"><span class="comment">// 释放时恢复到中心</span></span><br><span class="line"><span class="comment">// dragPoint = center;</span></span><br><span class="line"></span><br><span class="line">QPointF pos = event-&gt;<span class="built_in">pos</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 限制拖动点到外圈的范围</span></span><br><span class="line"><span class="type">double</span> dx = pos.<span class="built_in">x</span>() - center.<span class="built_in">x</span>();</span><br><span class="line"><span class="type">double</span> dy = pos.<span class="built_in">y</span>() - center.<span class="built_in">y</span>();</span><br><span class="line"><span class="type">double</span> distance = std::<span class="built_in">sqrt</span>(dx * dx + dy * dy);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (distance &gt; innerRadius) &#123;</span><br><span class="line"><span class="comment">// 将拖动点限制在外圈的边界</span></span><br><span class="line"><span class="type">double</span> distanceFromCenter = (outerRadius + innerRadius) / <span class="number">2</span>;</span><br><span class="line">dragPoint.<span class="built_in">setX</span>(center.<span class="built_in">x</span>() +</span><br><span class="line">distanceFromCenter * std::<span class="built_in">cos</span>(<span class="built_in">qDegreesToRadians</span>(m_angle)));</span><br><span class="line">dragPoint.<span class="built_in">setY</span>(center.<span class="built_in">y</span>() -</span><br><span class="line">distanceFromCenter * std::<span class="built_in">sin</span>(<span class="built_in">qDegreesToRadians</span>(m_angle)));</span><br><span class="line">stopButton-&gt;<span class="built_in">show</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (distance &lt; innerRadius)<span class="comment">//拖动点释放前在内圈，就停止</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">calculateDirection</span>();</span><br><span class="line">dragPoint.<span class="built_in">setX</span>(center.<span class="built_in">x</span>());</span><br><span class="line">dragPoint.<span class="built_in">setY</span>(center.<span class="built_in">y</span>());</span><br><span class="line">stopButton-&gt;<span class="built_in">hide</span>();</span><br><span class="line"><span class="function">emit <span class="title">stopMove</span><span class="params">()</span></span>;</span><br><span class="line">m_moving = <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*else &#123;</span></span><br><span class="line"><span class="comment">dragPoint = pos;</span></span><br><span class="line"><span class="comment">&#125;*/</span></span><br><span class="line"><span class="comment">/*double distanceFromCenter = (outerRadius + innerRadius) / 2;</span></span><br><span class="line"><span class="comment">dragPoint.setX(center.x() +</span></span><br><span class="line"><span class="comment">distanceFromCenter * std::cos(qDegreesToRadians(m_angle)));</span></span><br><span class="line"><span class="comment">dragPoint.setY(center.y() -</span></span><br><span class="line"><span class="comment">distanceFromCenter * std::sin(qDegreesToRadians(m_angle)));*/</span></span><br><span class="line">m_timer-&gt;<span class="built_in">stop</span>();</span><br><span class="line"><span class="built_in">update</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">DirectionalControl::updateDragPntDirection</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="type">double</span> distanceFromCenter = (outerRadius + innerRadius) / <span class="number">2</span>;</span><br><span class="line">dragPoint.<span class="built_in">setX</span>(center.<span class="built_in">x</span>() + distanceFromCenter * std::<span class="built_in">cos</span>(<span class="built_in">qDegreesToRadians</span>(m_angle)));</span><br><span class="line">dragPoint.<span class="built_in">setY</span>(center.<span class="built_in">y</span>() - distanceFromCenter * std::<span class="built_in">sin</span>(<span class="built_in">qDegreesToRadians</span>(m_angle)));</span><br><span class="line"><span class="built_in">update</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">DirectionalControl::keyPressEvent</span><span class="params">(QKeyEvent* event)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">QWidget::<span class="built_in">keyPressEvent</span>(event);</span><br><span class="line"></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">* bool QKeyEvent::isAutoRepeat() const</span></span><br><span class="line"><span class="comment">* Returns true if this event comes from an auto-repeating key; </span></span><br><span class="line"><span class="comment">* returns false if it comes from an initial key press.</span></span><br><span class="line"><span class="comment">* Note that if the event is a multiple-key compressed event that is partly due to auto-repeat,</span></span><br><span class="line"><span class="comment">* this function could return either true or false indeterminately.</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="keyword">if</span> (event-&gt;<span class="built_in">isAutoRepeat</span>()) &#123;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">switch</span> (event-&gt;<span class="built_in">key</span>()) &#123;</span><br><span class="line"><span class="keyword">case</span> Qt::Key_W:</span><br><span class="line">m_angle = <span class="number">90</span>;</span><br><span class="line"><span class="built_in">sendDirectionChanged</span>();</span><br><span class="line"><span class="function">emit <span class="title">handleClick</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">emit <span class="title">startMove</span><span class="params">()</span></span>;</span><br><span class="line">stopButton-&gt;<span class="built_in">show</span>();</span><br><span class="line">m_moving = <span class="literal">true</span>;</span><br><span class="line"><span class="built_in">updateDragPntDirection</span>();</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> Qt::Key_A:</span><br><span class="line">m_angle = <span class="number">180</span>;</span><br><span class="line"><span class="built_in">sendDirectionChanged</span>();</span><br><span class="line"><span class="function">emit <span class="title">handleClick</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">emit <span class="title">startMove</span><span class="params">()</span></span>;</span><br><span class="line">stopButton-&gt;<span class="built_in">show</span>();</span><br><span class="line">m_moving = <span class="literal">true</span>;</span><br><span class="line"><span class="built_in">updateDragPntDirection</span>();</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> Qt::Key_S:</span><br><span class="line">m_angle = <span class="number">270</span>;</span><br><span class="line"><span class="built_in">sendDirectionChanged</span>();</span><br><span class="line"><span class="function">emit <span class="title">handleClick</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">emit <span class="title">startMove</span><span class="params">()</span></span>;</span><br><span class="line">stopButton-&gt;<span class="built_in">show</span>();</span><br><span class="line">m_moving = <span class="literal">true</span>;</span><br><span class="line"><span class="built_in">updateDragPntDirection</span>();</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> Qt::Key_D:</span><br><span class="line">m_angle = <span class="number">0</span>;</span><br><span class="line"><span class="built_in">sendDirectionChanged</span>();</span><br><span class="line"><span class="function">emit <span class="title">handleClick</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function">emit <span class="title">startMove</span><span class="params">()</span></span>;</span><br><span class="line">stopButton-&gt;<span class="built_in">show</span>();</span><br><span class="line">m_moving = <span class="literal">true</span>;</span><br><span class="line"><span class="built_in">updateDragPntDirection</span>();</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">default</span>:</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 标记事件为已处理</span></span><br><span class="line">event-&gt;<span class="built_in">accept</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">DirectionalControl::keyReleaseEvent</span><span class="params">(QKeyEvent* event)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">QWidget::<span class="built_in">keyReleaseEvent</span>(event);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 只处理非自动重复的松键事件</span></span><br><span class="line"><span class="keyword">if</span> (event-&gt;<span class="built_in">isAutoRepeat</span>()) &#123;</span><br><span class="line"><span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">switch</span> (event-&gt;<span class="built_in">key</span>()) &#123;</span><br><span class="line"><span class="keyword">case</span> Qt::Key_W:</span><br><span class="line"><span class="function">emit <span class="title">stopMove</span><span class="params">()</span></span>;</span><br><span class="line">m_moving = <span class="literal">false</span>;</span><br><span class="line"><span class="built_in">resetDragPntPos</span>();</span><br><span class="line">stopButton-&gt;<span class="built_in">hide</span>();</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> Qt::Key_A:</span><br><span class="line"><span class="function">emit <span class="title">stopMove</span><span class="params">()</span></span>;</span><br><span class="line">m_moving = <span class="literal">false</span>;</span><br><span class="line"><span class="built_in">resetDragPntPos</span>();</span><br><span class="line">stopButton-&gt;<span class="built_in">hide</span>();</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> Qt::Key_S:</span><br><span class="line"><span class="function">emit <span class="title">stopMove</span><span class="params">()</span></span>;</span><br><span class="line">m_moving = <span class="literal">false</span>;</span><br><span class="line"><span class="built_in">resetDragPntPos</span>();</span><br><span class="line">stopButton-&gt;<span class="built_in">hide</span>();</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> Qt::Key_D:</span><br><span class="line"><span class="function">emit <span class="title">stopMove</span><span class="params">()</span></span>;</span><br><span class="line">m_moving = <span class="literal">false</span>;</span><br><span class="line"><span class="built_in">resetDragPntPos</span>();</span><br><span class="line">stopButton-&gt;<span class="built_in">hide</span>();</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">case</span> Qt::Key_Space:</span><br><span class="line"><span class="function">emit <span class="title">stopMove</span><span class="params">()</span></span>;</span><br><span class="line">m_moving = <span class="literal">false</span>;</span><br><span class="line"><span class="built_in">resetDragPntPos</span>();</span><br><span class="line">stopButton-&gt;<span class="built_in">hide</span>();</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"><span class="keyword">default</span>:</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 标记事件为已处理</span></span><br><span class="line">event-&gt;<span class="built_in">accept</span>();</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">DirectionalControl::isInsideOuterCircle</span><span class="params">(<span class="type">const</span> QPointF&amp; point)</span> </span>&#123;</span><br><span class="line"><span class="type">double</span> dx = point.<span class="built_in">x</span>() - center.<span class="built_in">x</span>();</span><br><span class="line"><span class="type">double</span> dy = point.<span class="built_in">y</span>() - center.<span class="built_in">y</span>();</span><br><span class="line"><span class="keyword">return</span> std::<span class="built_in">sqrt</span>(dx * dx + dy * dy) &lt;= outerRadius;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">DirectionalControl::isInsideInnerCircle</span><span class="params">(<span class="type">const</span> QPointF&amp; point)</span> </span>&#123;</span><br><span class="line"><span class="type">double</span> dx = point.<span class="built_in">x</span>() - center.<span class="built_in">x</span>();</span><br><span class="line"><span class="type">double</span> dy = point.<span class="built_in">y</span>() - center.<span class="built_in">y</span>();</span><br><span class="line"><span class="keyword">return</span> std::<span class="built_in">sqrt</span>(dx * dx + dy * dy) &lt;= innerRadius;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">DirectionalControl::calculateDirection</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="type">double</span> dx = dragPoint.<span class="built_in">x</span>() - center.<span class="built_in">x</span>();</span><br><span class="line"><span class="comment">// Y 轴正方向向下，所以取反</span></span><br><span class="line"><span class="type">double</span> dy = center.<span class="built_in">y</span>() - dragPoint.<span class="built_in">y</span>();</span><br><span class="line"><span class="type">double</span> angle = std::<span class="built_in">atan2</span>(dy, dx) * (<span class="number">180.0</span> / M_PI);</span><br><span class="line"><span class="comment">// 保证角度为 0-360 度</span></span><br><span class="line"><span class="keyword">if</span> (angle &lt; <span class="number">0</span>) &#123;</span><br><span class="line">angle += <span class="number">360.0</span>;</span><br><span class="line">&#125;</span><br><span class="line">m_angle = angle;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;自定义摇杆&quot;&gt;&lt;a href=&quot;#自定义摇杆&quot; class=&quot;headerlink&quot; title=&quot;自定义摇杆&quot;&gt;&lt;/a&gt;自定义摇杆&lt;/h1&gt;&lt;p&gt;今天做项目的时候，接到一个需求，需要做一个摇杆去控制物体的移动，功能还是挺复杂的&lt;/p&gt;
&lt;h2 id=&quot;1-功能：</summary>
      
    
    
    
    <category term="Qt" scheme="https://cpp-memory-leaks.github.io/categories/Qt/"/>
    
    
    <category term="Qt 自定义控件" scheme="https://cpp-memory-leaks.github.io/tags/Qt-%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A7%E4%BB%B6/"/>
    
  </entry>
  
  <entry>
    <title>QT详解</title>
    <link href="https://cpp-memory-leaks.github.io/2024/09/13/QT%E8%AF%A6%E8%A7%A3/"/>
    <id>https://cpp-memory-leaks.github.io/2024/09/13/QT%E8%AF%A6%E8%A7%A3/</id>
    <published>2024-09-13T10:08:29.000Z</published>
    <updated>2025-09-13T11:01:08.418Z</updated>
    
    <content type="html"><![CDATA[<h1 id="QT详解"><a href="#QT详解" class="headerlink" title="QT详解"></a>QT详解</h1><ol><li><strong>Qt 可以做什么？</strong></li></ol><p>Qt 虽然经常被当做一个 GUI 库，用来开发图形界面应用程序，但这并不是 Qt 的全部；Qt 除了可以绘制漂亮的界面（包括控件、布局、交互），还包含很多其它功能，比如多线程、访问数据库、图像处理、音频视频处理、网络通信、文件操作等，这些 Qt 都已经内置了。       总起来说，Qt 主要用于桌面程序开发和嵌入式开发。</p><ol start="2"><li><strong>Qt 来开发 Windows 桌面程序有以下优点：</strong></li></ol><ul><li><p>简单易学：Qt 封装的很好，几行代码就可以开发出一个简单的客户端，不需要了解 Windows API。</p></li><li><p>资料丰富：资料丰富能够成倍降低学习成本，否则你只能去看源码，关于 DirectUI、Htmlayout、aardio 的资料就很少。</p></li><li><p>漂亮的界面：Qt 很容易做出漂亮的界面和炫酷的动画，而 MFC、WTL、wxWidgets 比较麻烦。</p></li><li><p>独立安装：Qt 程序最终会编译为本地代码，不需要其他库的支撑，而 Java 要安装虚拟机，C# 要安装 .NET Framework。</p></li><li><p>跨平台：如果你的程序需要运行在多个平台下，同时又希望降低开发成本，Qt 几乎是必备的。</p></li></ul><ol start="3"><li>学习QT的基础</li></ol><p>C++依旧是 Qt 的主要编程语言，Qt 5 也并没有忽略它，Qt 5 添加了很多新的 C++ API，而且会持续更新。引入 QML 只是 Qt 5 提供的另外一种选择，并不是让它成为唯一的选择。C++ 是 Qt 的基础，无论如何都要掌握总的来说，C++ 对于 Qt 是不可或缺的，而 QML 只是一个加分项。</p><ol start="4"><li>认识一下Qt用到的开发工具</li></ol><p><a href="http://c.biancheng.net/qt/">Qt</a>不是凭空产生的，它是基于现有工具链打造而成的，它所使用的编译器、链接器、调试器等都不是自己的，Qt 官方只是开发了上层工具。下面我们分几个部分讲解 Qt 使用到的工具链。</p><h2 id="GNU-工具集-在-GNU-工具集里面，开发时常见到的几个罗列如下（这些工具通常位于-Linux-或-Unix-系统里的-usr-bin-目录）："><a href="#GNU-工具集-在-GNU-工具集里面，开发时常见到的几个罗列如下（这些工具通常位于-Linux-或-Unix-系统里的-usr-bin-目录）：" class="headerlink" title="GNU 工具集 在 GNU 工具集里面，开发时常见到的几个罗列如下（这些工具通常位于 Linux 或 Unix 系统里的 &#x2F;usr&#x2F;bin&#x2F; 目录）："></a>GNU 工具集 在 GNU 工具集里面，开发时常见到的几个罗列如下（这些工具通常位于 Linux 或 Unix 系统里的 &#x2F;usr&#x2F;bin&#x2F; 目录）：</h2><table><thead><tr><th>工具</th><th>说明</th></tr></thead><tbody><tr><td>gcc</td><td>GNU C 语言编译器。</td></tr><tr><td>g++</td><td>GNU <a href="http://c.biancheng.net/cplus/">C++</a> 语言编译器。</td></tr><tr><td>ld</td><td>GNU 链接器，将目标文件和库文件链接起来，创建可执行程序和动态链接库。</td></tr><tr><td>ar</td><td>生成静态库 .a ，可以编辑和管理静态链接库。</td></tr><tr><td>make</td><td>生成器，可以根据 makefile 文件自动编译链接生成可执行程序或库文件。</td></tr><tr><td>gdb</td><td>调试器，用于调试可执行程序。</td></tr><tr><td>ldd</td><td>查看可执行文件依赖的共享库（扩展名 .so，也叫动态链接库）。</td></tr></tbody></table><h2 id="MinGW"><a href="#MinGW" class="headerlink" title="MinGW"></a>MinGW</h2><p>原本 GNU 工具只在 Linux&#x2F;Unix 系统里才有，随着 Windows 系统的广泛使用， 为了在 Windows 系统里可以使用 GNU 工具，诞生了 MinGW（Minimalist GNU for Windows） 项目，利用 MinGW 就可以生成 Windows 里面的 exe 程序和 dll 链接库。 需要注意的是，MinGW 与 Linux&#x2F;Unix 系统里 GNU 工具集的有些区别：</p><ul><li><p>MinGW 里面工具带有扩展名 .exe， Linux&#x2F;Unix 系统里工具通常都是没有扩展名的。</p></li><li><p>MinGW 里面的生成器文件名为 mingw32-make.exe，Linux&#x2F;Unix 系统里就叫 make。</p></li><li><p>MinGW 在链接时是链接到 *.a 库引用文件，生成的可执行程序运行时依赖 *.dll，而 Linux&#x2F;Unix 系统里链接时和运行时都是使用 *.so 。</p></li></ul><p>另外 MinGW 里也没有 ldd 工具，因为 Windows 不使用 .so 共享库文件。如果要查看 Windows 里可执行文件的依赖库，需要使用微软自家的 Dependency Walker 工具。Windows 里面动态库扩展名为 .dll，MinGW 可以通过 dlltool 来生成用于创建和使用动态链接库需要的文件，如 .def 和 .lib。 MinGW 原本是用于生成 32 位程序的，随着 64 位系统流行起来， 从 MinGW 分离出来了 MinGW-w64 项目，该项目同时支持生成 64 位和 32 位程序。Qt 的 MinGW 版本库就是使用 MinGW-w64 项目里面的工具集生成的。</p><h4 id="MSYS（Minimal-SYStem）"><a href="#MSYS（Minimal-SYStem）" class="headerlink" title="MSYS（Minimal SYStem）"></a>MSYS（Minimal SYStem）</h4><p>另外提一下，由于 MinGW 本身主要就是编译链接等工具和头文件、库文件，并不包含系统管理、文件操作之类的 Shell 环境， 这对希望用类 Unix 命令的开发者来说还是不够用的。 所以 MinGW 官方又推出了 MSYS（Minimal SYStem），相当于是一个部署在 Windows 系统里面的小型 Unix 系统环境， 移植了很多 Unix&#x2F;Linux 命令行工具和配置文件等等，是对 MinGW 的扩展。         MSYS 对于熟悉 Unix&#x2F;Linux 系统环境或者要尝试学习 Unix&#x2F;Linux 系统的人都是一种便利。MSYS 和 MinGW 的安装升级都是通过其官方的 mingw-get 工具实现，二者是统一下载安装管理的。         对于 MinGW-w64 项目，它对应的小型系统环境叫 MSYS2（Minimal SYStem 2），MSYS2 是 MSYS 的衍生版，不仅支持 64 位系统和 32 位系统，还有自己的独特的软件包管理工具，它从 Arch Linux 系统里移植了 pacman 软件管理工具，所以装了 MSYS2 之后，可以直接通过 pacman 来下载安装软件，而且可以自动解决依赖关系、方便系统升级等。装了 MSYS2 之后，不需要自己去下载 MinGW-w64，可以直接用 pacman 命令安装编译链接工具和 git 工具等。         MinGW 项目主页（含 MSYS）： <a href="http://www.mingw.org/">http://www.mingw.org/</a></p><p>MinGW-w64 项目主页： <a href="https://sourceforge.net/projects/mingw-w64/">https://sourceforge.net/projects/mingw-w64/</a></p><p>MSYS2 项目主页： <a href="https://sourceforge.net/projects/msys2/">https://sourceforge.net/projects/msys2/</a></p><h2 id="CMake"><a href="#CMake" class="headerlink" title="CMake"></a>CMake</h2><p>CMake（Cross platform Make）是一个开源的跨平台自动化构建工具， 可以跨平台地生成各式各样的 makefile 或者 project 文件， 支持利用各种编译工具生成可执行程序或链接库。         CMake 自己不编译程序， 它相当于用自己的构建脚本 CMakeLists.txt，叫各种编译工具集去生成可执行程序或链接库。         一般用于编译程序的 makefile 文件比较复杂，自己去编写比较麻烦， 而利用 CMake ，就可以编写相对简单的 CMakeLists.txt ，由 CMake 根据 CMakeLists.txt 自动生成 makefile，然后就可以用 make 生成可执行程序或链接库。         本教程里面是使用 Qt 官方的 qmake 工具生成 makefile 文件，没有用 CMake。这里之所以提 CMake，是因为整个 KDE 桌面环境的茫茫多程序都是用 CMake 脚本构建的，另外跨平台的程序&#x2F;库如 Boost C++ Libraries、<a href="http://c.biancheng.net/opencv/">OpenCV</a>、LLVM、Clang 等也都是用 CMake 脚本构建的。以后如果接触到这些东西，是需要了解 CMake 的。         CMake 项目主页：<a href="https://cmake.org/">https://cmake.org/</a></p><p>KDE 项目主页：<a href="https://www.kde.org/">https://www.kde.org/</a></p><h2 id="Qt-工具集"><a href="#Qt-工具集" class="headerlink" title="Qt 工具集"></a>Qt 工具集</h2><p>Qt 官方的开发环境安装包里有自己专门的开发工具，之前用过 qmake 命令。qmake 是 Qt 开发最核心的工具，既可以生成 Qt 项目文件 .pro ，也可以自动生成项目的 Makefile 文件。         这里将常用的 Qt 开发工具列表如下：</p><table><thead><tr><th>工具</th><th>说明</th></tr></thead><tbody><tr><td>qmake</td><td>核心的项目构建工具，可以生成跨平台的 .pro 项目文件，并能依据不同操作系统和编译工具生成相应的 Makefile，用于构建可执行程序或链接库。</td></tr><tr><td>uic</td><td>User Interface Compiler，用户界面编译器，Qt 使用 XML 语法格式的 .ui 文件定义用户界面，uic 根据 .ui 文件生成用于创建用户界面的 C++ 代码头文件，比如 ui_*****.h 。</td></tr><tr><td>moc</td><td>Meta-Object Compiler，元对象编译器，moc 处理 C++ 头文件的类定义里面的 Q_OBJECT 宏，它会生成源代码文件，比如 moc_*****.cpp ，其中包含相应类的元对象代码，元对象代码主要用于实现 Qt 信号&#x2F;槽机制、运行时类型定义、动态属性系统。</td></tr><tr><td>rcc</td><td>Resource Compiler，资源文件编译器，负责在项目构建过程中编译 .qrc 资源文件，将资源嵌入到最终的 Qt 程序里。</td></tr><tr><td>qtcreator</td><td>集成开发环境，包含项目生成管理、代码编辑、图形界面可视化编辑、 编译生成、程序调试、上下文帮助、版本控制系统集成等众多功能， 还支持手机和嵌入式设备的程序生成部署。</td></tr><tr><td>assistant</td><td>Qt 助手，帮助文档浏览查询工具，Qt 库所有模块和开发工具的帮助文档、示例代码等都可以检索到，是 Qt 开发必备神器，也可用于自学 Qt。</td></tr><tr><td>designer</td><td>Qt 设计师，专门用于可视化编辑图形用户界面（所见即所得），生成 .ui 文件用于 Qt 项目。</td></tr><tr><td>linguist</td><td>Qt 语言家，代码里用 tr() 宏包裹的就是可翻译的字符串，开发人员可用 lupdate 命令生成项目的待翻译字符串文件 .ts，用 linguist 翻译多国语言 .ts ，翻译完成后用 lrelease 命令生成 .qm 文件，然后就可用于多国语言界面显示。</td></tr><tr><td>qmlscene</td><td>在 Qt 4.x 里是用 qmlviewer 进行 QML 程序的原型设计和测试，Qt 5 用 qmlscene 取代了旧的 qmlviewer。新的 qmlscene 另外还支持 Qt 5 中的新特性 scenegraph 。</td></tr></tbody></table><ol start="5"><li>Qt编程涉及的术语和名词</li></ol><p>&lt; <a href="http://c.biancheng.net/view/3868.html">Qt用到的开发工具</a><a href="http://c.biancheng.net/view/1804.html">Qt Creator的初步使用</a></p><p>本节我们来介绍一下使用 <a href="http://c.biancheng.net/qt/">Qt</a>编程过程中常用的术语和名字，它们不一定专属于 Qt，在其它的 C&#x2F;<a href="http://c.biancheng.net/cplus/">C++</a>开发过程中也会使用到。</p><h2 id="Project"><a href="#Project" class="headerlink" title="Project"></a>Project</h2><p>Project 的中文翻译是“项目”或者“工程”，这里的项目是指为实现某个相对独立功能的程序代码合集，这些代码不单单是放在一块，而是有相互之间的关联性，并且有专门负责管理该项目的项目文件，比如：</p><ul><li><p>Qt 使用 .pro 文件管理项目；</p></li><li><p>VC++ 则使用 .vcproj 作为项目文件。</p></li></ul><p>集成开发环境通常都是依据项目文件（.pro&#x2F;.vcproj）管理和构建项目。</p><h2 id="Makefile"><a href="#Makefile" class="headerlink" title="Makefile"></a>Makefile</h2><p>即生成脚本，虽然可以直接调用编译器如 g++ 编译程序，但是如果项目里的代码文件变多了，哪些代码文件更新了需要重新编译，哪些代码没有改不需要重新编译等等，靠程序员自己记忆去处理是比较麻烦的事，还有哪些代码需要预处理或是链接哪些库文件， 这些都是繁杂的过程。为了规范程序的编译生成过程，产生了规范化的生成脚本，就是 Makefile，生成器 make 可以依据规范的 Makefile 自动生成目标程序或库文件。 简单的说，就是定义好 Makefile ，让程序员只需要去关注如何编写代码，而生成程序过程中的脏活累活都交给 make 程序。 现在 Makefile 通常都有工具自动生成，如 qmake 工具， 这样就大量减轻了程序员的负担。</p><h2 id="Debug-和-Release"><a href="#Debug-和-Release" class="headerlink" title="Debug 和 Release"></a>Debug 和 Release</h2><p>Debug 即调试，Release 即发行。代码编写之后，生成的目标程序或库文件通常不会绝对正确，或多或少有些毛病（bug）， 因此需要进行纠错调试（Debug）。调试过程中需要源代码和二进制目标程序之间一一对应的关系， 这样才能定位到错误代码，所以 Debug 版本的程序是臃肿而不进行优化的。 与之相对的是 Release 发行版，在纠正了发觉到的错误后，需要发布程序用于实际用途，实际应用时强调运行效率高，减少冗余代码，因此会对二进制程序进行大量优化，提升性能。这样发布的二进制目标程序就是 Release 版。 Debug 版本和 Release 版本使用的库文件不一样：</p><ul><li><p>Debug 版本程序通常链接的也是 Debug 版本的库文件，比如 libQt5Guid.a&#x2F;Qt5Guid.dll，库文件的简短名（不含扩展名）都是以 d 结尾的，Debug 库通常都比较大 。</p></li><li><p>Release 版本程序链接的通常就是 Release 版本的库文件，Release 版本库文件名字比 Debug 版本库文件少一个字母 d ，如 libQt5Gui.a&#x2F;Qt5Gui.dll，而且 Release 版本库一般都比 Debug 版本小很多，运行效率也高很多。</p></li></ul><h2 id="C-11-标准"><a href="#C-11-标准" class="headerlink" title="C++11 标准"></a>C++11 标准</h2><p>时代在变化，C++ 标准也在前进。C++ 正式公布标准有 C++98、C++03、C++11。最新的 C++11 标准是2011年8月12日公布的，在公布之前该标准原名为 C++0x 。这是一次较大的修订和扩充，建议读者专门学一下。 Qt 从 4.8 版本就开始用 C++11 新特性了。编译器里面开始支持 C++11 的版本是 MSVC 2010、<a href="http://c.biancheng.net/gcc/">GCC</a>4.5、Clang 3.1，这之后版本的编译器都在逐步完善对 C++11 的支持，现在新版本编译器对新标准的支持都比较全面了。 Qt 官方在编译 Qt5 库的时候都是开启 C++11 特性的，如果我们要在自己项目代码启用新标准，需要在 .pro 文件里面添加一行：</p><p>CONFIG +&#x3D; c++11</p><p>如果是 Qt4 版本则是添加：</p><p>gcc:CXXFLAGS +&#x3D; -std&#x3D;c++0x</p><p>MSVC 编译器默认开启 C++11 特性，GCC（g++命令）则需要自己添加选项 -std&#x3D;c++0x ，上面 CXXFLAGS 就是为 GCC 编译器（g++命令）添加 -std&#x3D;c++0x 选项。</p><h2 id="Dynamic-Link-和-Static-Link"><a href="#Dynamic-Link-和-Static-Link" class="headerlink" title="Dynamic Link 和 Static Link"></a>Dynamic Link 和 Static Link</h2><p>Dynamic Link 即动态链接，Static Link 即静态链接。</p><h4 id="动态链接库"><a href="#动态链接库" class="headerlink" title="动态链接库"></a>动态链接库</h4><p>目标程序通常都不是独立个体，生成程序时都需要链接其他的库，要用到其他库的代码。对于多个程序同时运行而言，内存中就可能有同一个库的多个副本，占用了太多内存而干的活差不多。 为了优化内存运用效率，引入了动态链接库（Dynamic Link Library），或叫共享库（Shared Object）。使用动态链接库时，内存中只需要一份该库文件，其他程序要使用该库文件时，只要链接过来就行了。由于动态库文件外置，链接到动态库的目标程序相对比较小，因为剥离了大量库代码，而只需要一些链接指针。 使用动态库，也意味着程序需要链接到如 *.dll 或 *.so 文件，得提前装好动态库文件，然后目标程序才能正常运行。</p><h4 id="静态链接库"><a href="#静态链接库" class="headerlink" title="静态链接库"></a>静态链接库</h4><p>静态库就是将链接库的代码和自己编写的代码都编译链接到一块，链接到静态库的程序通常比较大，但好处是运行时依赖的库文件很少，因为目标程序自己内部集成了很多库代码。</p><h4 id="库文件后缀"><a href="#库文件后缀" class="headerlink" title="库文件后缀"></a>库文件后缀</h4><p>Linux&#x2F;Unix 系统里静态库扩展名一般是 .a，动态库扩展名一般是 .so 。Windows 系统里 VC 编译器用的静态库扩展名一般是 .lib，动态库扩展名一般是 .dll 。 MinGW 比较特殊，是将 GNU 工具集和链接库从 Linux&#x2F;Unix 系统移植到 Windows 里， 有意思的情况就出现了，MinGW 使用的静态库扩展名为 .a ，而其动态库扩展名则为 .dll， .a 仅在生成目标程序过程中使用，.dll 则是在目标程序运行时使用。</p><h2 id="Explicit-Linking-和-Implicit-Linking"><a href="#Explicit-Linking-和-Implicit-Linking" class="headerlink" title="Explicit Linking 和 Implicit Linking"></a>Explicit Linking 和 Implicit Linking</h2><p>Explicit Linking 即显式链接，Implicit Linking 即隐式链接，这两种都是动态链接库的使用方式。 动态链接库通常都有其导出函数列表， 告知其他可执行程序可以使用它的哪些函数。可执行程序使用这些导出函数有两种方式：一是在运行时使用主动加载动态库的函数，Linux 里比如用 dlopen 函数打开并加载动态库，Windows 里一般用 LoadLibrary 打开并加载动态库，只有当程序代码执行到这些函数时，其参数里的动态库才会被加载，这就是显式链接。显式链接方式是在运行时加载动态库，其程序启动时并不检查这些动态库是否存在。 隐式链接是最为常见的，所有的编译环境默认都是采用隐式链接的方式使用动态库。隐式链接会在链接生成可执行程序时就确立依赖关系，在该程序启动时，操作系统自动会检查它依赖的动态库，并一一加载到该程序的内存空间，程序员就不需要操心什么时候加载动态库了。比如 VC 编译环境，链接时使用动态库对应的 .lib 文件（包含动态库的导出函数声明，但没有实际功能代码），在 .exe 程序运行前系统会检查依赖的 .dll，如果找不到某个动态库就会出现类似下图对话框：</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/8106cd02-3b24-487b-9e1c-78c1c7a4bc65.gif" alt="image"></p><p>MinGW 是将动态库的导出函数声明放在了 .a 文件里，程序运行依赖的动态库也是 .dll 。 请注意，VC 链接器使用的 .lib 文件分两类，一种是完整的静态库，体积比较大，另一种是动态库的导出声明，体积比较小。MinGW 链接器使用的 .a 文件也是类似的，Qt 官方库都是按照动态库发布的，静态库只有自己编译才会有。</p><ol start="6"><li>Qt Creator的初步使用</li></ol><p>启动 <a href="http://c.biancheng.net/qt/">Qt</a>Creator，出现如图 1 所示的主窗口：</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/4044f0d5-dff3-48ef-8c13-1e76e2570fb6.gif" alt="image">图 1 Qt Creator主窗口</p><p>Qt Creator 的界面很简洁。上方是主菜单栏，左侧是主工具栏，窗口的中间部分是工作区。根据设计内容不同，工作区会显示不同的内容。 图 1 是在左侧主工具栏单击“Welcome（欢迎）”按钮后显示实例的界面。这时工作区的左侧有 “Projects”、“Examples（示例）”、“Tutorials（教程）”、“Get Started Now”几个按钮，单击后会在主工作区显示相应的内容：</p><ul><li><p>单击“Projects”按钮后，工作区显示新建项目按钮和最近打开项目的列表。</p></li><li><p>单击“Examples（示例）”按钮后，工作区显示 Qt 自带的大量实例，选择某个实例就可以在 Qt Creator 中打开该项目源程序。</p></li><li><p>单击“Tutorials（教程）”按钮后，工作区显示各种视频教程，查看视频教程需要联网并使用浏览器打开。</p></li><li><p>单击“Get Started Now”按钮，工作区显示“Qt Creator Manual”帮助主题内容。</p></li></ul><p>主窗口左侧是主工具栏，主工具栏提供了项目文件编辑、窗体设计、程序调试、项目设置等各种功能按钮。</p><h2 id="Qt-Creator-的设置"><a href="#Qt-Creator-的设置" class="headerlink" title="Qt Creator 的设置"></a>Qt Creator 的设置</h2><p>对 Qt Creator 可以进行一些设置，如刚安装好的 Qt Creator 界面语言可能是中文，也可以选择将 Qt Creator 的界面语言设置为英文。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/284a1bf7-28ee-4c63-aba1-0e270dba4874.gif" alt="image">图 2 Options 的构建和运行设置页面</p><p>单击 Qt Creator 菜单栏的 Tools→Options 菜单项会打开选项设置对话框（如图 2 所示）。对话框的左侧是可设置的内容分组，单击后右侧出现具体的设置界面。常用的设置包括以下几点：</p><ol><li><p>Environment（环境） 设置：在 Interface 页面可以设置语言和主题，本教程全部以中文界面的 Qt Creator 进行讲解，所以语言选择为 Chinese（China）；为了使界面抓图更清晰，设置主题为 Flat Light。更改语言和主题后需要重新启动 Qt Creator 才会生效。</p></li><li><p>Text Editor（文本编辑器）设置：在此界面可以设置文本编辑器的字体，设置各种类型文字的字体颜色，如关键字、数字、字符串、注释等字体颜色，也可以选择不同的配色主题。编辑器缺省字体的大小为 9，可以修改得大一些。</p></li><li><p>Build &amp; Run（构建和运行）设置：图 2 显示的是 Build &amp; Run 的设置界面，它有以下几个页面。</p><ul><li><p>Kits（构建套件）页面显示 Qt Creator 可用的编译工具。</p></li><li><p>Qt Versions 页面显示安装的 Qt 版本。</p></li><li><p>Compliers（编译器）页面显示系统里可用的 C 和 <a href="http://c.biancheng.net/cplus/">C++</a>编译器，由于安装了 MinGW 和 Visual Studio 2015，Qt Creator 会自动检测出这些编译器。</p></li><li><p>Debuggers 页面显示 Qt Creator 自动检测到的调试器，有 GNU gdb for MinGW 调试器和 Windows 的 CDB 调试器。</p></li></ul></li><li><p>编写第一个Qt程序</p></li></ol><p>学习一种编程语言或编程环境，通常会先编写一个“Hello World”程序。我们也用 <a href="http://c.biancheng.net/qt/">Qt</a>Creator 编写一个“Hello World”程序，以初步了解 Qt Creator 设计应用程序的基本过程，对使用 Qt Creator 编写 Qt <a href="http://c.biancheng.net/cplus/">C++</a>应用程序建立初步的了解。</p><h2 id="新建一个项目"><a href="#新建一个项目" class="headerlink" title="新建一个项目"></a>新建一个项目</h2><p>单击 Qt Creator 的菜单项文件-&gt;新建文件或项目，出现如图 1 所示的对话框。在这个对话框里选择需要创建的项目或文件的模板。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/66c676c4-5847-4ce1-9c35-2a8da18b96c4.gif" alt="image">图 1 新建文件或项目对话框</p><p>Qt Creator 可以创建多种项目，在最左侧的列表框中单击“Application”，中间的列表框中列出了可以创建的应用程序的模板，各类应用程序如下：</p><ul><li><p>Qt Widgets Application，支持桌面平台的有图形用户界面（Graphic User Interface，GUI） 界面的应用程序。GUI 的设计完全基于 C++ 语言，采用 Qt 提供的一套 C++ 类库。</p></li><li><p>Qt Console Application，控制台应用程序，无 GUI 界面，一般用于学习 C&#x2F;C++ 语言，只需要简单的输入输出操作时可创建此类项目。</p></li><li><p>Qt Quick Application，创建可部署的 Qt Quick 2 应用程序。Qt Quick 是 Qt 支持的一套 GUI 开发架构，其界面设计采用 QML 语言，程序架构采用 C++ 语言。利用 Qt Quick 可以设计非常炫的用户界面，一般用于移动设备或嵌入式设备上无边框的应用程序的设计。</p></li><li><p>Qt Quick Controls 2 Application，创建基于 Qt Quick Controls 2 组件的可部署的 Qt Quick 2 应用程序。Qt Quick Controls 2 组件只有 Qt 5.7 及以后版本才有。</p></li><li><p>Qt Canvas 3D Application，创建 Qt Canvas 3D QML 项目，也是基于 QML 语言的界面设计，支持 3D 画布。</p></li></ul><p>在图 1 显示的对话框中选择项目类型为 Qt Widgets Application 后，单击“Choose…”按钮，出现如图 2 所示的新建项目向导：</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/6d9a532e-09ef-4461-ab16-fabd6c6a1b09.gif" alt="image">图 2 新建项目向导第 1 步：项目名称和项目存储位置设置</p><p>在图 2 中，选择一个目录，如“E:\QtDemo”，再设置项目名称为 Demo， 这样新建项目后，会在“E:\QtDemo”目录下新建一个目录，项目所有文件保 存在目录“E:\QtDemo\Demo\”下。 在图 2 中设置好项目名称和保存路径后，单击“Next”按钮，出现如图 3 所示的选择编译工具的界面：</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/41fd2068-80ce-4596-b6d6-4e6077e48c83.gif" alt="image">图 3 新建项目向导第 2 步：选择编译工具</p><p>可以将这几个编译工具都选中，在编译项目时再选择一个作为当前使用的编译工具，这样可以编译生成不同版本的可执行程序。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/b33a5462-3fe8-40df-9876-a0bcc80625c6.gif" alt="image">图 4 新建项目想到第 3 步：选择界面基类</p><p>在图 3 显示的界面中单击“Next”按钮，出现如图 4 所示的界面。在此界面中选择需要创建界面的基类（base class）。有 3 种基类可以选择：</p><ol><li><p>QMainWindow 是主窗口类，主窗口具有主菜单栏、工具栏和状态栏，类似于一般的应用程序的主窗口；</p></li><li><p>QWidget 是所有具有可视界面类的基类，选择 QWidget 创建的界面对各种界面组件都可以 支持；</p></li><li><p>QDialog 是对话框类，可建立一个基于对话框的界面；</p></li></ol><p>在此选择 QMainWindow 作为基类，自动更改的各个文件名不用手动去修改。勾选“创建界面”复选框。这个选项如果勾选，就会由 Qt Creator 创建用户界面文件，否则，需要自己编程手工创建界面。初始学习，为了了解 Qt Creator 的设计功能，勾选此选项。 然后单击“Next”按钮，出现一个页面，总结了需要创建的文件和文件保存目录，单击“完成”按钮就可以完成项目的创建。</p><h2 id="项目的文件组成和管理"><a href="#项目的文件组成和管理" class="headerlink" title="项目的文件组成和管理"></a>项目的文件组成和管理</h2><p>完成了以上新建项目的步骤后，在 Qt Creator 的左侧工具栏中单击“编辑”按钮，可显示如图 5 所示的窗口。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/686eba8f-4737-4b95-b120-083eadce8bd6.gif" alt="image">图 5 项目管理与文件编辑界面</p><p>窗口左侧有上下两个子窗口，上方的目录树显示了项目内文件的组织结构，显示当 前项目为 Demo。项目的名称构成目录树的一个根节点，Qt Creator 可以打开多个项目，但是只有一个活动项目，活动项目的项目名称节点用粗体字体表示。 在项目名称节点下面，分组管理着项目内的各种源文件，几个文件及分组分别为以下几项：</p><ul><li><p>Demo.pro 是项目管理文件，包括一些对项目的设置项。</p></li><li><p>Headers 分组，该节点下是项目内的所有头文件（.h），图 5 中所示项目有一个头文件 mainwindow.h，是主窗口类的头文件。</p></li><li><p>Sources 分组：该节点下是项目内的所有 C++源文件（.cpp），图 5 中所示项目有两个 C++ 源文件，mainwindow.cpp 是主窗口类的实现文件，与 mainwindow.h 文件对应。main.cpp 是主函数文件，也是应用程序的入口。</p></li><li><p>Forms 分组：该节点下是项目内的所有界面文件（.ui）。图 5 中所示项目有一个界面文件mainwindow.ui，是主窗口的界面文件。界面文件是文本文件，使用 XML 语言描述界面的组成。</p></li></ul><p>左侧上下两个子窗口的显示内容可以通过其上方的一个下拉列表框进行选择，可以选择的显示内容包括项目、打开文档、书签、文件系统、类视图、大纲等。在图 5 中，上方的子窗口显示了项目的文件目录树，下方显示打开的文件列表。可以在下方选择显示类视图，这样下方则显示项目内所有的类的结构，便于程序浏览和快速切换到需要的代码位置。 双击文件目录树中的文件mainwindow.ui，出现如图 6 所示的窗体设计界面：</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/18aa8643-fd4e-4934-82c9-b55f2f1ddbe1.gif" alt="image">图 6 集成在 Qt Creator 中UI设计器</p><p>这个界面实际上是 Qt Creator 中集成的 Qt Designer。窗口左侧是分组的组件面板，中间是设计的窗体。在组件面板的 Display Widgets 分组里，将一个Label组件拖放到设计的窗体上面。双击刚刚放置的 Label 组件，可以编辑其文字内容，将文字内容更改为“Hello, World!”。还可以在窗口右下方的属性编辑器里编辑标签的 Font 属性，Point Size（点大小）更改为 12，勾选粗体。</p><h2 id="项目的编译、调试与运行"><a href="#项目的编译、调试与运行" class="headerlink" title="项目的编译、调试与运行"></a>项目的编译、调试与运行</h2><p>单击主窗口左侧工具栏上的“项目”按钮，出现如图 7 所示的项目编译设置界面。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/bf563ae6-1b67-4927-abb9-f1d555278878.gif" alt="image">图 7 项目编译器选择和设置界面</p><p>界面左侧一栏的“Build &amp; Run”下面显示了本项目中可用的编译器工具，要使用哪一个编译器用于项目编译，单击其名称即可，选择的编译器名称会用粗体字表示。这里选择使用 MinGW 32bit 编译器。 每个编译器又有 Build 和 Run 两个设置界面。在 Build 设置界面上，有一个“Shadow build” 复选框。如果勾选此项，编译后将在项目的同级目录下建立一个编译后的文件目录，目录名称包含编译器信息，这种方式一般用于使用不同编译器创建不同版本的可执行文件。如果不勾选此项，编译后将在项目的目录下建立“Debug”和“Release”子目录用于存放编译后的文件。 在设计完 mainwindow.ui 文件，并设置好编译工具之后，就可以对项目进行编译、调试或运行。主窗口左侧工具栏下方有 4 个按钮，其功能见表 1。</p><table><thead><tr><th>图标</th><th>作用</th><th>快捷键</th></tr></thead><tbody><tr><td><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/33357e0a-b44f-4623-8db1-e2429c88067a.gif" alt="image"></td><td>弹出菜单选择编译工具和编译模式，如 Debug或 Release模式</td><td></td></tr><tr><td><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/488ad3ff-0509-489d-ba4c-02b2e0606f8f.gif" alt="image"></td><td>直接运行程序，如果修改后未编译，会先进行编译。即使在程序中设置了断点，此方式运行的程序也无法调试。</td><td>Ctrl+R</td></tr><tr><td><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/98f9ef25-2d18-4292-b376-624de70f404d.gif" alt="image"></td><td>项目需要以Debug模式编译，点此按钮开始调试运行，可以在程序中设置断点。若是以 Release模式编译，点此按钮也无法进行调试。</td><td>F5</td></tr><tr><td><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/fa4430d5-2d0f-48e9-885f-0fc39d65ecc9.gif" alt="image"></td><td>编译当前项目</td><td>Ctrl+B</td></tr></tbody></table><p>首先对项目进行编译，没有错误后，再运行程序。程序运行的界面如图 8 所示。这就是一个标准的桌面应用程序，我们采用可视化的方式设计了一个窗口，并在上面显示了字符串“Hello， World！”。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/27854f2a-ee7a-46e2-a980-d260a1415f59.gif" alt="image">图 8 实例程序 Demo 运行时界面</p><p>在 Qt Creator 中也可以对程序设置断点进行调试，但是必须以 Debug 模式编译，并以“Start Debugging”（快捷键 F5）方式运行程序。</p><p>程序调试的方法与一般 IDE 工具类似，不再详述。注意，要在 Qt Creator 里调试 MSVC2015 编译的程序，必须安装 Windows 软件开发工具包 SDK。       </p><ol start="8"><li>Qt项目管理文件（.pro）及其作用详解</li></ol><p>在 <a href="http://c.biancheng.net/qt/">Qt</a>Creator 中新建一个 Widget Application 项目 samp2_1，在选择窗口基类的页面选择 QWidget 作为窗体基类，并选中“Generate form”复选框。创建后的项目文件目录树如图 1 所示。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/5daeacb7-8528-4d7c-b081-fcdfa8fd6737.gif" alt="image"></p><p>图 1 项目文件的目录树</p><p>这个项目包含以下一些文件：</p><ul><li><p>项目管理文件 samp2_1.pro，存储项目设置的文件。</p></li><li><p>主程序入口文件 main.cpp，实现 main()函数的程序文件。</p></li><li><p>窗体界面文件 widget.ui，一个 XML 格式存储的窗体上的元件及 其布局的文件。</p></li><li><p>widget.h 是所设计的窗体类的头文件，widget.cpp 是 widget.h 里 定义类的实现文件。<a href="http://c.biancheng.net/cplus/">C++</a>中，任何窗体或界面组件都是用类封装的，一个类一般有一个头文件（.h 文件）和一个源程序文件（.cpp 文件）。</p></li></ul><p>本节先来介绍一下项目管理文件（.pro文件）。 后缀为“.pro”的文件是项目的管理文件，文件名就是项目的名称，如本项目中的 samp2_1.pro。 下面是 samp2_1.pro 文件的内容。</p><figure class="highlight plaintext"><table><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">QT       += core gui</span><br><span class="line">greaterThan(QT_MAJOR_VERSION, 4): QT += widgets</span><br><span class="line">TARGET = samp2_1</span><br><span class="line">TEMPLATE = app</span><br><span class="line">SOURCES += \</span><br><span class="line">        main.cpp \</span><br><span class="line">        widget.cpp</span><br><span class="line">HEADERS += \</span><br><span class="line">        widget.h</span><br><span class="line">FORMS += \</span><br><span class="line">        widget.ui</span><br></pre></td></tr></table></figure><p>项目管理文件用于记录项目的一些设置，以及项目包含文件的组织管理。 “Qt +&#x3D; core gui”表示项目中加入 core gui 模块。core gui 是 Qt 用于 GUI 设计的类库模块，如果创建的是控制台（Console）应用程序，就不需要添加 core gui。 Qt 类库以模块的形式组织各种功能的类，根据项目涉及的功能需求，在项目中添加适当的类库模块支持。例如，如果项目中使用到了涉及数据库操作的类就需要用到 sql 模块，在 pro 文件中需要增加如下一行：</p><p>Qt +&#x3D;sql</p><p>samp2_1.pro 中的第 2 行是：</p><p>greaterThan(Qt_MAJOR_VERSION, 4): Qt +&#x3D; widgets</p><p>这是个条件执行语句，表示当 Qt 主版本大于 4 时，才加入 widgets 模块。 “TARGET &#x3D; samp2_1”表示生成的目标可执行文件的名称，即编译后生成的可执行文件是 samp2_1.exe。 “TEMPLATE &#x3D; app”表示项目使用的模板是 app，是一般的应用程序。 后面的 SOURCES、HEADERS、FORMS 记录了项目中包含的源程序文件、头文件和窗体文件（.ui 文件）的名称。这些文件列表是 Qt Creator 自动添加到项目管理文件里面的，用户不需要手动修改。当添加一个文件到项目，或从项目里删除一个文件时，项目管理文件里的条目会自动修改。</p><ol start="9"><li>Qt项目界面文件（.ui）及其作用（超详细）</li></ol><p><a href="http://c.biancheng.net/qt/">Qt</a>项目中，后缀为“.ui”的文件是可视化设计的窗体的定义文件，如 widget.ui。双击项目文件目录树中的文件 widget.ui，会打开一个集成在 Qt Creator 中的 Qt Designer 对窗体进行可视化设计，如图 1 所示。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/48cf436f-7c10-45cb-acfb-b3c1f147aee2.gif" alt="image">图 1 集成在 Qt Creator中的 UI 设计器</p><p>本教程后面将称这个集成在 Qt Creator 中的 Qt Designer 为“UI 设计器”，以便与独立运行的 Qt Designer 区别开来。 图 1 中的 UI 设计器有以下一些功能区域：</p><ul><li><p>组件面板：窗口左侧是界面设计组件面板，分为多个组，如Layouts、Buttons、Display Widgets等，界面设计的常见组件都可以在组件面板里找到。</p></li><li><p>中间主要区域是待设计的窗体。如果要将某个组件放置到窗体上时，从组件面板上拖放一个组件到窗体上即可。例如，先放一个 Label 和一个 Push Button 到窗体上。</p></li><li><p>Signals 和 Slots 编辑器与 Action 编辑器是位于待设计窗体下方的两个编辑器。Signals 和Slots 编辑器用于可视化地进行信号与槽的关联，Action 编辑器用于可视化设计 Action。</p></li><li><p>布局和界面设计工具栏：窗口上方的一个工具栏，工具栏上的按钮主要实现布局和界面设计。</p></li><li><p>对象浏览器（Object Inspector）：窗口右上方是 Object Inspector，用树状视图显示窗体上各组件之间的布局包含关系，视图有两列，显示每个组件的对象名称（ObjectName）和类名称。</p></li><li><p>属性编辑器（Property Editor）：窗口右下方是属性编辑器，是界面设计时最常用到的编辑器。属性编辑器显示某个选中的组件或窗体的各种属性及其取值，可以在属性编辑器里修改这些属性的值。</p></li></ul><p>图 2 显示的是选中窗体上放置的标签组件后属性编辑器的内容。最上方显示的文字“LabDemo: QLabel”表示这个组件是一个 QLabel 类的组件，objectName 是LabDemo。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/7683c037-9ad3-4917-96d7-c1ee2e74001a.gif" alt="image">                                                                图 2 界面组件的属性编辑器</p><p>属性编辑器的内容分为两列，分别为属性的名称和属性的值。属性又分为多个组，实际上表示了类的继承关系，如在图 2 中，可以看出 QLabel 的继承关系是QObject→QWidget→QFrame→QLabel。 objectName 表示组件的对象名称，界面上的每个组件都需要一个唯一的对象名称，以便被引用。界面上的组件的命名应该遵循一定的法则，具体使用什么样的命名法则根据个人习惯而定，主要目的是便于区分和记忆，也要便于与普通变量相区分。 设置其他属性的值只需在属性编辑器里操作即可，如设置 LabDemo 的 text 属性为“Hello,World”，只需像图 2 那样修改 text 属性的值即可。</p><p>提示，标准 <a href="http://c.biancheng.net/cplus/">C++</a>语言里并没有 property 关键字，property 是 Qt 对标准 C++ 的扩展，使得在 Qt Designer 里就可以可视化设置类的数据。</p><p>在图 1 显示的设计窗体上，放置一个 Label 和一个 Push Button 组件，它们的主要属性设置见表 3。</p><table><thead><tr><th>ObjectName</th><th>类名称</th><th>属性设置</th><th>备注</th></tr></thead><tbody><tr><td>LabDemo</td><td>QLabel</td><td>Text&#x3D;”Hello, World” Font.PointSize&#x3D;20 Font.bold&#x3D;true</td><td>设置标签显示文字和字体</td></tr><tr><td>btnClose</td><td>QPushButton</td><td>Text&#x3D;”Close”</td><td>设置按钮的文字</td></tr></tbody></table><p>编辑完属性之后，再为 btnClose 按钮增加一个功能，就是单击此按钮时，关闭窗口，退出程序。使用 Signals 和 Slots 编辑器完成这个功能，如图 4 所示。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/fec3582a-cc3d-42a8-87c0-64d342bd36a7.gif" alt="image">                                        图 4 信号与槽编辑器中设计信号与槽的关联</p><p>在信号与槽编辑器的工具栏上单击“Add”按钮，在出现的条目中，Sender 选择 btnClose，Signal 选择 clicked()，Receiver 选择窗体 Widget，Slot 选择 close()。这样设置表示当按钮 btnClose 被单击时，就执行 Widget 的 close() 函数，实现关闭窗口的功能。 然后对项目进行编译和运行，可以出现如图 5 所示的窗口，单击“Close”按钮可以关闭程序。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/6b5f38b3-d520-4c5f-92b8-e5736ce549d9.gif" alt="image"></p><p>图 5 具有 Close 按钮的“Hello World”程序</p><p>标签的文字内容和字体被修改了，窗口标题也显示为所设置的标题，而我们并没有编写一行程序语句，Qt 是怎么实现这些功能的呢？ 为了搞清楚窗体类的定义，以及界面功能的实现原理，这里将项目进行编译。编译后在项目目录下会自动生成一个文件 ui_widget.h，这样对于一个窗体，就有 4 个文件了，各文件的功能说明见表 6。</p><table><thead><tr><th>文件名</th><th>功能</th></tr></thead><tbody><tr><td>widget.h</td><td>定义窗体类的头文件，定义了类Widget</td></tr><tr><td>widget.cpp</td><td>Widget 类的功能实现源程序文件</td></tr><tr><td>widget.ui</td><td>窗体界面文件，由UI设计器自动生成，存储了窗体上各个组件的属性设置和布局</td></tr><tr><td>ui_widget.h</td><td>编译后，根据窗体上的组件及其属性、信号与槽的关联等自动生成的一个类的定义文件，类的名称是Ui_Widget</td></tr></tbody></table><p>下面分别分析各个文件的内容及其功能，以及它们是如何联系在一起工作，实现界面的创建与显示的。</p><h2 id="widget-h-文件"><a href="#widget-h-文件" class="headerlink" title="widget.h 文件"></a>widget.h 文件</h2><p>widget.h 文件是窗体类的头文件。在创建项目时，选择窗体基类是 QWidget，在 widget.h 中定义了一个继承自 QWidget 的类 Widget。 下面是 widget.h 文件的内容：</p><figure class="highlight plaintext"><table><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">#ifndef WIDGET_H</span><br><span class="line">#define WIDGET_H</span><br><span class="line"></span><br><span class="line">#include &lt;QWidget&gt;</span><br><span class="line"></span><br><span class="line">namespace Ui &#123;</span><br><span class="line">class Widget;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Widget : public QWidget</span><br><span class="line">&#123;</span><br><span class="line">    Q_OBJECT</span><br><span class="line"></span><br><span class="line">public:</span><br><span class="line">    explicit Widget(QWidget *parent = 0);</span><br><span class="line">    ~Widget();</span><br><span class="line"></span><br><span class="line">private:</span><br><span class="line">    Ui::Widget *ui;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">#endif // WIDGET_H</span><br></pre></td></tr></table></figure><p>widget.h 文件有几个重要的部分。</p><h4 id="namespace-声明"><a href="#namespace-声明" class="headerlink" title="namespace 声明"></a>namespace 声明</h4><p>代码中有如下的一个 namespace 声明：</p><figure class="highlight plaintext"><table><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">namespace Ui &#123;</span><br><span class="line">class Widget;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这是声明了一个名称为 Ui 的命名空间（namespace），包含一个类 Widget。但是这个类 Widget 并不是本文件里定义的类 Widget，而是 ui_widget.h 文件里定义的类，用于描述界面组件的。这个声明相当于一个外部类型声明（具体要看完 ui_widget.h 文件内的解释之后才能搞明白）。</p><h4 id="Widget-类的定义"><a href="#Widget-类的定义" class="headerlink" title="Widget 类的定义"></a>Widget 类的定义</h4><p>widget.h 文件的主体部分是一个继承于 QWidget 的类 Widget 的定义，也就是本实例的窗体类。 在 Widget 类中使用了宏 Q_OBJECT，这是使用 Qt 的信号与槽（signal 和 slot）机制的类都必须加入的一个宏（信号与槽在后面详细介绍）。 在 public 部分定义了 Widget 类的构造函数和析构函数。 在 private 部分又定义了一个指针。</p><p>Ui::Widget *ui;</p><p>这个指针是用前面声明的 namespace Ui 里的 Widget 类定义的，所以指针 ui 是指向可视化设计的界面，后面会看到要访问界面上的组件，都需要通过这个指针 ui。</p><h2 id="widget-cpp-文件"><a href="#widget-cpp-文件" class="headerlink" title="widget.cpp 文件"></a>widget.cpp 文件</h2><p>widget.cpp 文件是类 Widget 的实现代码，下面是 widget.cpp 文件的内容。</p><figure class="highlight plaintext"><table><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">#include &quot;widget.h&quot;</span><br><span class="line">#include &quot;ui_widget.h&quot;</span><br><span class="line"></span><br><span class="line">Widget::Widget(QWidget *parent) :</span><br><span class="line">    QWidget(parent),</span><br><span class="line">    ui(new Ui::Widget)</span><br><span class="line">&#123;</span><br><span class="line">    ui-&gt;setupUi(this);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Widget::~Widget()</span><br><span class="line">&#123;</span><br><span class="line">    delete ui;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意到，在这个文件的包含文件部分自动加入了如下一行内容：</p><p>#include “ui_widget.h”</p><p>这个就是 Qt 编译生成的与 UI 文件 widget.ui 对应的类定义文件。 目前只有构造函数和析构函数。其中构造函数头部是：</p><p>Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)</p><p>其意义是：执行父类 QWidget 的构造函数，创建一个 Ui::Widget 类的对象 ui。这个 ui 就是 Widget 的 private 部分定义的指针变量 ui。 构造函数里只有一行语句：</p><p>ui-&gt;setupUi(this)</p><p>它是执行了 Ui::Widget 类的 setupUi() 函数，这个函数实现窗口的生成与各种属性的设置、信号与槽的关联（后面会具体介绍）。 析构函数只是简单地删除用 new 创建的指针 ui。 所以，在 ui_widget.h 文件里有一个 namespace 名称为 Ui，里面有一个类 Widget 是用于描述可视化设计的窗体，且与 widget.h 里定义的类同名。在 Widget 类里访问 Ui::Widget 类的成员变量或函数需要通过 Widget 类里的 ui 指针，如同构造函数里执行 ui-&gt;setupUi( this) 函数那样。</p><h2 id="widget-ui-文件"><a href="#widget-ui-文件" class="headerlink" title="widget.ui 文件"></a>widget.ui 文件</h2><p>widget.ui 是窗体界面定义文件，是一个 XML 文件，定义了窗口上的所有组件的属性设置、布局，及其信号与槽函数的关联等。用UI设计器可视化设计的界面都由 Qt 自动解析，并以 XML 文件的形式保存下来。在设计界面时，只需在 UI 设计器里进行可视化设计即可，而不用管 widget.ui 文件是怎么生成的。 下面是 widget.ui 文件的内容：</p><figure class="highlight plaintext"><table><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">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span><br><span class="line">&lt;ui version=&quot;4.0&quot;&gt;</span><br><span class="line">&lt;class&gt;Widget&lt;/class&gt;</span><br><span class="line">&lt;widget class=&quot;QWidget&quot; name=&quot;Widget&quot;&gt;</span><br><span class="line">  &lt;property name=&quot;geometry&quot;&gt;</span><br><span class="line">   &lt;rect&gt;</span><br><span class="line">    &lt;x&gt;0&lt;/x&gt;</span><br><span class="line">    &lt;y&gt;0&lt;/y&gt;</span><br><span class="line">    &lt;width&gt;336&lt;/width&gt;</span><br><span class="line">    &lt;height&gt;216&lt;/height&gt;</span><br><span class="line">   &lt;/rect&gt;</span><br><span class="line">  &lt;/property&gt;</span><br><span class="line">  &lt;property name=&quot;windowTitle&quot;&gt;</span><br><span class="line">   &lt;string&gt;My First Demo&lt;/string&gt;</span><br><span class="line">  &lt;/property&gt;</span><br><span class="line">  &lt;widget class=&quot;QLabel&quot; name=&quot;Lablabel&quot;&gt;</span><br><span class="line">   &lt;property name=&quot;geometry&quot;&gt;</span><br><span class="line">    &lt;rect&gt;</span><br><span class="line">     &lt;x&gt;100&lt;/x&gt;</span><br><span class="line">     &lt;y&gt;70&lt;/y&gt;</span><br><span class="line">     &lt;width&gt;141&lt;/width&gt;</span><br><span class="line">     &lt;height&gt;61&lt;/height&gt;</span><br><span class="line">    &lt;/rect&gt;</span><br><span class="line">   &lt;/property&gt;</span><br><span class="line">   &lt;property name=&quot;font&quot;&gt;</span><br><span class="line">    &lt;font&gt;</span><br><span class="line">     &lt;pointsize&gt;12&lt;/pointsize&gt;</span><br><span class="line">     &lt;weight&gt;75&lt;/weight&gt;</span><br><span class="line">     &lt;bold&gt;true&lt;/bold&gt;</span><br><span class="line">    &lt;/font&gt;</span><br><span class="line">   &lt;/property&gt;</span><br><span class="line">   &lt;property name=&quot;text&quot;&gt;</span><br><span class="line">    &lt;string&gt;Hello，World&lt;/string&gt;</span><br><span class="line">   &lt;/property&gt;</span><br><span class="line">  &lt;/widget&gt;</span><br><span class="line">  &lt;widget class=&quot;QPushButton&quot; name=&quot;btnClose&quot;&gt;</span><br><span class="line">   &lt;property name=&quot;geometry&quot;&gt;</span><br><span class="line">    &lt;rect&gt;</span><br><span class="line">     &lt;x&gt;210&lt;/x&gt;</span><br><span class="line">     &lt;y&gt;150&lt;/y&gt;</span><br><span class="line">     &lt;width&gt;75&lt;/width&gt;</span><br><span class="line">     &lt;height&gt;23&lt;/height&gt;</span><br><span class="line">    &lt;/rect&gt;</span><br><span class="line">   &lt;/property&gt;</span><br><span class="line">   &lt;property name=&quot;text&quot;&gt;</span><br><span class="line">    &lt;string&gt;Close&lt;/string&gt;</span><br><span class="line">   &lt;/property&gt;</span><br><span class="line">  &lt;/widget&gt;</span><br><span class="line">&lt;/widget&gt;</span><br><span class="line">&lt;layoutdefault spacing=&quot;6&quot; margin=&quot;11&quot;/&gt;</span><br><span class="line">&lt;resources/&gt;</span><br><span class="line">&lt;connections&gt;</span><br><span class="line">  &lt;connection&gt;</span><br><span class="line">   &lt;sender&gt;btnClose&lt;/sender&gt;</span><br><span class="line">   &lt;signal&gt;clicked()&lt;/signal&gt;</span><br><span class="line">   &lt;receiver&gt;Widget&lt;/receiver&gt;</span><br><span class="line">   &lt;slot&gt;close()&lt;/slot&gt;</span><br><span class="line">   &lt;hints&gt;</span><br><span class="line">    &lt;hint type=&quot;sourcelabel&quot;&gt;</span><br><span class="line">     &lt;x&gt;247&lt;/x&gt;</span><br><span class="line">     &lt;y&gt;161&lt;/y&gt;</span><br><span class="line">    &lt;/hint&gt;</span><br><span class="line">    &lt;hint type=&quot;destinationlabel&quot;&gt;</span><br><span class="line">     &lt;x&gt;167&lt;/x&gt;</span><br><span class="line">     &lt;y&gt;107&lt;/y&gt;</span><br><span class="line">    &lt;/hint&gt;</span><br><span class="line">   &lt;/hints&gt;</span><br><span class="line">  &lt;/connection&gt;</span><br><span class="line">&lt;/connections&gt;</span><br><span class="line">&lt;/ui&gt;</span><br></pre></td></tr></table></figure><h2 id="ui-widget-h-文件"><a href="#ui-widget-h-文件" class="headerlink" title="ui_widget.h 文件"></a>ui_widget.h 文件</h2><p>ui_widget.h 是在对 widget.ui 文件编译后生成的一个文件，ui_widget.h 会出现在编译后的目录下，或与 widget.ui 同目录（与项目的 shadow build 编译设置有关）。 文件 ui_widget.h 并不会出现在 Qt Creator 的项目文件目录树里，当然，可以手工将 ui_widget.h 添加到项目中。方法是在项目文件目录树上，右击项目名称节点，在调出的快捷菜单中选择“Add Existing Files…”，找到并添加 ui_widget.h 文件即可。 注意，ui_widget.h 是对 widget.ui 文件编译后自动生成的，widget.ui 又是通过 UI 设计器可视化设计生成的。所以，对 ui_widget.h 手工进行修改没有什么意义，所有涉及界面的修改都应该直接在UI 设计器里进行。所以，ui_widget.h 也没有必要添加到项目里。 下面是 ui_widget.h 文件的内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/********************************************************************************** Form generated from reading UI file &#x27;widget.ui&#x27;**** Created by: Qt User Interface Compiler version 5.9.1**** WARNING! All changes made in this file will be lost when recompiling UI file!********************************************************************************/#ifndef UI_WIDGET_H#define UI_WIDGET_H#include &lt;QtCore/QVariant&gt;#include &lt;QtWidgets/QAction&gt;#include &lt;QtWidgets/QApplication&gt;#include &lt;QtWidgets/QButtonGroup&gt;#include &lt;QtWidgets/QHeaderView&gt;#include &lt;QtWidgets/QLabel&gt;#include &lt;QtWidgets/QPushButton&gt;#include &lt;QtWidgets/QWidget&gt;QT_BEGIN_NAMESPACEclass Ui_Widget&#123;public:    QLabel *label;    QPushButton *btnClose;    void setupUi(QWidget *Widget)    &#123;        if (Widget-&gt;objectName().isEmpty())            Widget-&gt;setObjectName(QStringLiteral(&quot;Widget&quot;));        Widget-&gt;resize(336, 216);        label = new QLabel(Widget);        label-&gt;setObjectName(QStringLiteral(&quot;label&quot;));        label-&gt;setGeometry(QRect(100, 70, 141, 61));        QFont font;        font.setPointSize(12);        font.setBold(true);        font.setWeight(75);        label-&gt;setFont(font);        btnClose = new QPushButton(Widget);        btnClose-&gt;setObjectName(QStringLiteral(&quot;btnClose&quot;));        btnClose-&gt;setGeometry(QRect(210, 150, 75, 23));        retranslateUi(Widget);        QObject::connect(btnClose, SIGNAL(clicked()), Widget, SLOT(close()));        QMetaObject::connectSlotsByName(Widget);    &#125; // setupUi    void retranslateUi(QWidget *Widget)    &#123;        Widget-&gt;setWindowTitle(QApplication::translate(&quot;Widget&quot;, &quot;My First Demo&quot;, Q_NULLPTR));        label-&gt;setText(QApplication::translate(&quot;Widget&quot;, &quot;Hello\357\274\214World&quot;, Q_NULLPTR));        btnClose-&gt;setText(QApplication::translate(&quot;Widget&quot;, &quot;Close&quot;, Q_NULLPTR));    &#125; // retranslateUi&#125;;namespace Ui &#123;    class Widget: public Ui_Widget &#123;&#125;;&#125; // namespace UiQT_END_NAMESPACE#endif // UI_WIDGET_H</span><br></pre></td></tr></table></figure><p>查看 ui_widget.h 文件的内容，发现它主要做了以下的一些工作：</p><ol><li><p>定义了一个类 Ui_Widget，用于封装可视化设计的界面。</p></li><li><p>自动生成了界面各个组件的类成员变量定义。在 public 部分为界面上每个组件定义了一个指针变量，变量的名称就是设置的 objectName。比如，在窗体上放置了一个 QLabel 和一个 QPushButton 并命名后，自动生成的定义是：</p></li></ol><p>QLabel *LabDemo; QPushButton *btnClose;</p><ol start="3"><li>定义了 setupUi() 函数，这个函数用于创建各个界面组件，并设置其位置、大小、文字内容、字体等属性，设置信号与槽的关联。setupUi() 函数体的第一部分是根据可视化设计的界面内容，用 C++ 代码创建界面上各组件，并设置其属性。 接下来，setupUi() 调用了函数 retranslateUi(Widget)，用来设置界面各组件的文字内容属性，如标签的文字、按键的文字、窗体的标题等。将界面上的文字设置的内容独立出来作为一个函数 retranslateUi()，在设计多语言界面时会用到这个函数。 setupUi() 函数的第三部分是设置信号与槽的关联，本文件中有以下两行：</li></ol><p>QObject::connect(btnClose, SIGNAL(clicked()), Widget, SLOT(close())); QMetaObject::connectSlotsByName(Widget);</p><p>第1 行是调用 connect() 函数，将在 UI 设计器里设置的信号与槽的关联转换为语句。这里是将 btnClose 按键的 clicked() 信号与窗体 Widget 的 close() 槽函数关联起来，就是在图 4 中设置的信号与槽的关联的程序语句实现。这样，当单击 btnClose 按钮时，就会执行 Widget 的 close() 槽函数，而 close() 槽函数的功能是关闭窗口。 第 2 行是设置槽函数的关联方式，用于将 UI 设计器自动生成的组件信号的槽函数与组件信号相关联。 所以，在Widget 的构造函数里调用 ui-&gt;setupUI(this)，就实现了窗体上组件的创建、属性设置、信号与槽的关联。</p><ol start="4"><li>定义 namespace Ui，并定义一个从Ui_Widget 继承的类Widget。</li></ol><p>namespace Ui {    class Widget: public Ui_Widget {}; }</p><p>提示：ui_widget.h 文件里实现界面功能的类是 Ui_Widget。再定义一个类 Widget 从 Ui_Widget 继承而来，并定义在 namespace Ui 里，这样 Ui:: Widget 与 widget.h 里的类 Widget 同名，但是用 namespace 区分开来。所以，界面的 Ui:: Widget 类与文件 widget.h 里定义的 Widget 类实际上是两个类，但是 Qt 的处理让用户感觉不到 Ui:: Widget 类的存在，只需要知道在 Widget 类里用 ui 指针可以访问可视化设计的界面组件就可以了。</p><ol start="10"><li>Qt项目中main主函数及其作用</li></ol><p>main.cpp 是实现 main() 函数的文件，下面是 main.cpp 文件的内容。</p><figure class="highlight plaintext"><table><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">#include &quot;widget.h&quot;</span><br><span class="line">#include &lt;QApplication&gt;</span><br><span class="line">int main(int argc, char *argv[])</span><br><span class="line">&#123;</span><br><span class="line">    QApplication a(argc, argv); //定义并创建应用程序   </span><br><span class="line">  Widget w; //定义并创建窗口  </span><br><span class="line">   w.show(); //显示窗口   </span><br><span class="line">  return a.exec(); //应用程序运行</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>main() 函数是应用程序的入口。它的主要功能是创建应用程序，创建窗口，显示窗口，并运行应用程序，开始应用程序的消息循环和事件处理。     QApplication 是 <a href="http://c.biancheng.net/qt/">Qt</a>的标准应用程序类，第 1 行代码定义了一个 QApplication 类的实例 a，就是应用程序对象。     然后定义了一个 Widget 类的变量 w，Widget 是本实例设计的窗口的类名，定义此窗口后再用 w.show() 显示此窗口。     最后一行用 a.exec() 启动应用程序的执行，开始应用程序的消息循环和事件处理。</p><ol start="11"><li>Qt界面布局管理详解</li></ol><p>在上一节，通过一个简单的应用程序，分析了 <a href="http://c.biancheng.net/qt/">Qt</a>创建的 GUI 应用程序中各个文件的作用，剖析了可视化设计的UI文件是如何被转换为 <a href="http://c.biancheng.net/cplus/">C++</a>的类定义，并自动创建界面的。这些是使用 Qt Creator 可视化设计用户界面，并使各个部分融合起来运行的基本原理。         本节再以一个稍微复杂的例子来讲解设计 GUI 的常见功能，包括界面设计时布局的管理，以及程序里如何访问界面组件。</p><h2 id="实例程序功能"><a href="#实例程序功能" class="headerlink" title="实例程序功能"></a>实例程序功能</h2><p>创建一个 Widget Application 项目 samp2_2，在创建窗体时选择基类 QDialog，生成的类命名为 QWDialog，并选择生成窗体。         如此新建的项目 samp2_2 有一个界面文件 qwdialog.ui，一个头文件 qwdialog.h 和源程序文件 qwdialog.cpp。此外，还有项目文件 samp2_2.pro 和主程序文件 main.cpp。         qwdialog.ui 界面文件设计时界面如图 1 所示。程序的主要功能是对中间一个文本框的文字字体样式和颜色进行设置。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/d15367cc-9b42-4778-99a7-2bf38a1b2257.gif" alt="image">                                                                图 1 实例程序 samp2_2 设计时界面</p><p>在界面设计时，对需要访问的组件修改其 objectName，如各个按钮、需要读取输入的编辑框、需要显示结果的标签等，以便在程序里区分。对于不需要程序访问的组件则无需修改其 objectName，如用于界面上组件分组的 GroupBox、Frame、布局等，让 UI 设计器自动命名即可。 对图 1 中几个主要组件的命名、属性设置见表 2。</p><table><thead><tr><th>对象名</th><th>类名称</th><th>属性设置</th><th>备注</th></tr></thead><tbody><tr><td>txtEdit</td><td>QPlainTextEdit</td><td>Text&#x3D;”Hello, World It is my demo. “ Font.PointSize&#x3D;20</td><td>用于显示文字内容，可编辑</td></tr><tr><td>chkBoxUnder</td><td>QCheckBox</td><td>Text&#x3D;”Underline”</td><td>设置字体为下划线</td></tr><tr><td>chkBoxItalic</td><td>QCheckBox</td><td>Text&#x3D;”Italic”</td><td>设置字体为斜体</td></tr><tr><td>chkBoxBold</td><td>QCheckBox</td><td>Text&#x3D;”Bold”</td><td>设置字体为粗体</td></tr><tr><td>rBtnBlack</td><td>QRadioButton</td><td>Text&#x3D;”Black”</td><td>字体颜色为黑色</td></tr><tr><td>rBtnRed</td><td>QRadioButton</td><td>Text&#x3D;”Red”</td><td>字体颜色为红色</td></tr><tr><td>rBtnBlue</td><td>QRadioButton</td><td>Text&#x3D;”Blue”</td><td>字体颜色为蓝色</td></tr><tr><td>btnOK</td><td>QPushButton</td><td>Text&#x3D;”确定”</td><td>返回确定，并关闭窗口</td></tr><tr><td>btnCancel</td><td>QPushButton</td><td>Text&#x3D;”取消”</td><td>返回取消，并关闭窗口</td></tr><tr><td>btnClose</td><td>QPushButton</td><td>Text&#x3D;”退出”</td><td>退出程序</td></tr><tr><td>QWDialog</td><td>QWDialog</td><td>windowTitle&#x3D;”Dialog by Designer”</td><td>界面窗口的类名称是QWDialog，objectName 不要修改</td></tr></tbody></table><p>对于界面组件的属性设置，需要注意以下几点。</p><ol><li><p>objectName 是窗体上创建的组件的实例名称，界面上的每个组件需要有一个唯一的 objectName，程序里访问界面组件时都是通过其 objectName 进行访问，自动生成的槽函数名称里也有 objectName。所以，组件的 objectName 需要在设计程序之前设置好，设置好之后一般不要再改动。若设计程序之后再改动 objectName，涉及的代码需要相应的改动。</p></li><li><p>窗体的 objectName 就是窗体的类名称，在 UI 设计器里不要修改窗体的 objectName，窗体的实例名称需要在使用窗体的代码里去定义。</p></li></ol><h2 id="界面组件布局"><a href="#界面组件布局" class="headerlink" title="界面组件布局"></a>界面组件布局</h2><p>Qt 的界面设计使用了布局（Layout）功能。所谓布局，就是界面上组件的排列方式，使用布局可以使组件有规则地分布，并且随着窗体大小变化自动地调整大小和相对位置。布局管理是 GUI 设计的必备技巧，下面逐步讲解如何实现图 1 所示的界面设计。</p><h4 id="界面组件的层次关系"><a href="#界面组件的层次关系" class="headerlink" title="界面组件的层次关系"></a>界面组件的层次关系</h4><p>为了将界面上的各个组件的分布设计得更加美观，经常使用一些容器类，如 QgoupBox、QtabWidget、QFrame 等。         例如，将 3 个 CheckBox 组件放置在一个 GroupBox 组件里，该 GroupBox 组件就是这 3 个 CheckBox 的容器，移动这个 GroupBox 就会同时移动其中的 3 个 CheckBox。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/f8010e30-aa1e-4516-8996-bfa9e058a992.gif" alt="image">                                                    图 3 界面组件的放置及层次关系</p><p>图 3 显示的是设计图 1 界面的前期阶段。在窗体上放置了 2 个 GroupBox 组件，在 groupBox1 里放置 3 个 CheckBox 组件，在 groupBox2 里放置 3 个 RadioButton 组件。图 3 右侧 Object Inspector 里显示了界面上各组件之间的层次关系。</p><h4 id="布局管理"><a href="#布局管理" class="headerlink" title="布局管理"></a>布局管理</h4><p>Qt 为界面设计提供了丰富的布局管理功能，在 UI 设计器中，组件面板里有 Layouts 和 Spacers 两个组件面板，在窗体上方的工具栏里有布局管理的按钮（如图 4 所示）。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/be3253ea-59d1-437e-a7f5-0fc5c11ef85c.gif" alt="image">图 4 用于布局可视化设计的组件面板和工具栏</p><p>Layouts 和 Spacers 两个组件面板里的布局组件的功能见表 5：</p><table><thead><tr><th>布局组件</th><th>功能</th></tr></thead><tbody><tr><td>Vertical Layout</td><td>垂直方向布局，组件自动在垂直方向上分布</td></tr><tr><td>Horizontal Layout</td><td>水平方向布局，组件自动在水平方向上分布</td></tr><tr><td>Grid Layout</td><td>网格状布局，网状布局大小改变时，每个网格的大小都改变</td></tr><tr><td>Form Layout</td><td>窗体布局，与网格状布局类似，但是只有最右侧的一列网格会改变大小</td></tr><tr><td>Horizontal Spacer</td><td>一个用于水平分隔的空格</td></tr><tr><td>Vertical Spacer</td><td>一个用于垂直分隔的空格</td></tr></tbody></table><p>使用组件面板里的布局组件设计布局时，先拖放一个布局组件到窗体上，如在设计图 4 中 3 个按钮的布局时，先放一个 Horizontal Layout 到窗体上，布局组件会以红色边框显示。再往布局组件里拖放 3 个 Push Button 和 2 个 Horizontal Spacer，就可以得到图 1 中 3 个按钮的水平布局效果。 在设计窗体的上方有一个工具栏，用于调整设计器进入不同的状态，以及进行布局设计，工具栏上各按钮的功能见表 6。</p><table><thead><tr><th>按钮及快捷键</th><th>功能</th></tr></thead><tbody><tr><td>Edit Widget (F3)</td><td>界面设计进入编辑状态，就是正常的设计状态</td></tr><tr><td>Edit Signals&#x2F;Slots(F4)</td><td>进入信号与槽的可视化设计状态</td></tr><tr><td>Edit Buddies</td><td>进入伙伴关系编辑状态，可以设置一个Label 与一个组件成为伙伴关系</td></tr><tr><td>Edit Tab Order</td><td>进入Tab 顺序编辑状态，Tab 顺序是在键盘上按Tab 键时，输入焦点在界面各组件之间跳动的顺序</td></tr><tr><td>Lay Out Horizontally (Ctrl+H)</td><td>将窗体上所选组件水平布局</td></tr><tr><td>Lay Out Vertically (Ctrl+L)</td><td>将窗体上所选组件垂直布局</td></tr><tr><td>Lay Out Horizontally in Splitter</td><td>将窗体上所选组件用一个分割条进行水平分割布局</td></tr><tr><td>Lay Out Vertically in Splitter</td><td>将窗体上所选组件用一个分割条进行垂直分割布局</td></tr><tr><td>Lay Out in a Form Layout</td><td>将窗体上所选组件按窗体布局</td></tr><tr><td>Lay Out in a Grid</td><td>将窗体上所选组件网格布局</td></tr><tr><td>Break Layout</td><td>解除窗体上所选组件的布局，也就是打散现有的布局</td></tr><tr><td>Adjust Size(Ctrl+J)</td><td>自动调整所选组件的大小</td></tr></tbody></table><p>使用工具栏上的布局控制按钮时，只需在窗体上选中需要设计布局的组件，然后点击某个布局按钮即可。在窗体上选择组件时同时按住 Ctrl 键，可以实现组件多选，选择某个容器类组件，相当于选择了其内部的所有组件。         例如，在图 3 的界面中，选中 groupBox1，然后单击“Lay Out Horizontally”工具栏按钮，就可以对 groupBox1 内的 3 个 CheckBox 水平布局。 在图 4 的界面上，使 groupBox1 里的 3 个 CheckBox 水平布局，groupBox2 里的 3 个 RadioButton 水平布局，下方 3个按钮水平布局。在窗体上又放置了一个 PlainTextEdit 组件。现在，改变 groupBox1、groupBox2 或按钮的水平布局的大小，其内部组件都会自动改变大小。但是当改变窗体大小时，界面上的各组件却并不会自动改变大小。         随后还需为窗体指定一个总的布局。选中窗体（即不要选择任何组件），单击工具栏上的“Lay Out Vertically”按钮，使 4 个组件垂直分布。这样布局后，当窗体大小改变时，各个组件都会自动改变大小。 在 UI 设计器里可视化设计布局时，要善于利用水平和垂直空格组件，善于设置组件的最大、最小宽度和高度来实现某些需要的布局效果。</p><h4 id="伙伴关系与-Tab-顺序"><a href="#伙伴关系与-Tab-顺序" class="headerlink" title="伙伴关系与 Tab 顺序"></a>伙伴关系与 Tab 顺序</h4><p>在 UI 设计工具栏上单击“Edit Buddies”按钮可以进入伙伴关系编辑状态，如设计一个窗体时，进入伙伴编辑状态之后的界面如图 7 所示。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/93c4efe7-dfc7-4d6b-baae-ee964dc11e8e.gif" alt="image">图 7 编辑伙伴关系</p><p>伙伴关系（Buddy）是指界面上一个 Label 和一个组件相关联，如图 7 中的伙伴关系编辑状态，单击一个 Label，按住鼠标左键，然后拖向一个组件，就建立了 Label 和组件之间的伙伴关系。 伙伴关系是为了在程序运行时，在窗体上用快捷键快速将输入焦点切换到某个组件上。例如，在图 7 的界面上，设定“姓名”标签的 Text 属性为“姓名(&amp;N)”，其中符号“&amp;”用来指定快捷字符，界面上并不显示“&amp;”，这里指定快捷字母为 N。那么程序运行时，用户按下 Alt+N，输入焦点就会快速切换到“姓名”关联的输入框内。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/f05e917d-6c0d-491f-9d27-e4bbdbbba357.gif" alt="image">                                                                                    图 8 Tab 顺序编辑状态</p><p>在 UI 设计器工具栏上单击“Edit Tab Order”按钮进入Tab 顺序编辑状态（如图 8 所示）。Tab 顺序是指在程序运行时，按下键盘上的 Tab 键时输入焦点的移动顺序。一个好的用户界面，在按 Tab 键时，焦点应该以合理的顺序在界面上移动，而不是随意地移动。     进入 Tab 顺序编辑状态后，在界面上会显示具有 Tab 顺序组件的编号，依次按希望的顺序单击组件，就可以重排 Tab 顺序了。没有输入焦点的组件是没有 Tab 顺序的，如 Label 组件。</p><h2 id="项目功能实现"><a href="#项目功能实现" class="headerlink" title="项目功能实现"></a>项目功能实现</h2><p>下面开始设计程序功能。对于该程序，希望它的功能如下：</p><ol><li><p>单击 UnderLine、Italic、Bold 3 个 CheckBox 时，根据其状态，设置 PlainTextEdit 里的文字的字体样式；</p></li><li><p>Black、Red、Blue 3 个 RadioButton 是互斥选择的，单击某个 RadioButton 时，设置文字的颜色；</p></li><li><p>单击“确定”“取消”或“退出”按钮时，关闭窗口，退出程序。</p></li></ol><h4 id="字体样式设置"><a href="#字体样式设置" class="headerlink" title="字体样式设置"></a>字体样式设置</h4><p>窗体在<a href="http://c.biancheng.net/design_pattern/">设计模式</a>下，选中 chkBoxUnder 组件，单击右键调出其快捷菜单。在快捷菜单中单击菜单项“Go to slot…”（中文状态为“转到槽”），出现如图 9 所示的对话框。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/0642f283-94f5-4dec-afff-438d7ba3d43a.gif" alt="image"></p><p>图 9 QcheckBox的Go to slot对话框</p><p>该对话框列出了 QCheckBox 类的所有信号，第一个是 clicked()，第二个是带一个布尔类型参数的 clicked(bool)。 信号 clicked(bool) 会将 CheckBox 组件当前的选择状态作为一个参数传递，在响应代码里可以直接利用这个传递的参数。而如果用信号 clicked()，则需要在代码里读取 CheckBox 组件的选中状态。为了简化代码，选择 clicked(bool) 信号。 选择 clicked(bool)，然后单击“OK”按钮，在 QWDialog 的类定义中，会在 private slots 部分自动增加一个槽函数声明，函数名是根据发射对象及其信号名称自动命名的。</p><p>void on_chkBoxUnder_clicked(bool checked);</p><p>同时，在 qwdialog.cpp 文件中自动添加了函数 on_chkBoxUnder_clicked(bool) 的框架，在此函数中添加如下的代码，实现文本框字体下划线的控制。</p><figure class="highlight plaintext"><table><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">void QWDialog::on_chkBoxUnder_clicked(bool checked)</span><br><span class="line">&#123;</span><br><span class="line">    QFont font=ui-&gt;txtEdit-&gt;font();</span><br><span class="line">    font.setUnderline(checked);</span><br><span class="line">    ui-&gt;txtEdit-&gt;setFont(font);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以同样的方法为 Italic 和 Bold 两个 CheckBox设计槽函数，编译后运行，发现已经实现了修改字体的下划线、斜体、粗体属性的功能，说明信号与槽函数已经关联了。 但是，查看 QWDialog 的构造函数，构造函数只有简单的一条语句。</p><figure class="highlight plaintext"><table><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">QWDialog::QWDialog(QWidget *parent) : QDialog(parent), ui(new Ui::QWDialog)</span><br><span class="line">&#123;</span><br><span class="line">    ui-&gt;setupUi(this);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里没有发现用 connect() 函数进行几个 CheckBox 的信号与槽函数关联的操作。这些功能是如何实现的呢？ 查看编译生成的 ui_qwdialog.h 文件。构造函数里调用的 setupUi() 是在 ui_qwdialog.h 文件里实现的。查看 setupUi() 函数的内容，也没有发现用 connect() 函数进行几个 CheckBox 的信号与槽关联的操作，只是在 setupUI()里发现了如下的一条语句：</p><p>QMetaObject::connectSlotsByName(QWDialog);</p><p>秘密就在于这条语句。connectSlotsByName(QWDialog) 函数将搜索 QWDialog 界面上的所有组件，将信号与槽函数匹配的信号和槽关联起来，它假设槽函数的名称是：</p><p>void on_<object name>_<signal name>(<signal parameters>);</p><p>例如，通过 UI 设计器的操作，为 chkBoxUnder 自动生成的槽函数是：</p><p>void on_chkBoxUnder_clicked(bool checked);</p><p>它就正好是 chkBoxUnder 的信号 clicked(bool) 的槽函数。那么，connectSlotsByName() 就会将此信号和槽函数关联起来，如同执行了下面的这样一条语句：</p><p>connect(chkBoxUnder, SIGNAL(clicked (bool)), this, SLOT (on_chkBoxUnder_clicked (bool));</p><p>这就是用 UI 设计器可视化设计某个组件的信号响应槽函数，而不用手工去将其关联起来的原因，都是在界面类的构造函数里调用 setupUi() 自动完成了关联。</p><h4 id="字体颜色设置"><a href="#字体颜色设置" class="headerlink" title="字体颜色设置"></a>字体颜色设置</h4><p>设置字体的 3 个 RadioButton 是互斥性选择的，即一次只有一个 RadioButton 被选中，虽然也可以采用可视化设计的方式设计其 clicked() 信号的槽函数，但是这样就需要生成 3 个槽函数。这里可以简化设计，即设计一个槽函数，将 3 个 RadioButton 的 clicked() 信号关联到这一个槽函数。 为此，在 QWDialog 类的 private slots 部分增加一个槽函数定义如下：</p><p>void setTextFontColor();</p><p>提示 将鼠标光标移动到这个函数的函数名上面，单击右键，在弹出的快捷菜单中选择“Refactor”→“Add Definition in qwdialog.cpp”，就可以在 qwdialog.cpp 文件中自动为函数 setTextFontColor() 生成一个函数框架。 在 qwdialog.cpp 文件中，为 setTextFontColor() 编写实现代码如下：</p><figure class="highlight plaintext"><table><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">void QWDialog::setTextFontColor()</span><br><span class="line">&#123;</span><br><span class="line">    QPalette plet=ui-&gt;txtEdit-&gt;palette();</span><br><span class="line">    if (ui-&gt;rBtnBlue-&gt;isChecked())</span><br><span class="line">        plet.setColor(QPalette::Text,Qt::blue);</span><br><span class="line">    else if (ui-&gt;rBtnRed-&gt;isChecked())</span><br><span class="line">       plet.setColor(QPalette::Text,Qt::red);</span><br><span class="line">    else if (ui-&gt;rBtnBlack-&gt;isChecked())</span><br><span class="line">        plet.setColor(QPalette::Text,Qt::black);</span><br><span class="line">    else</span><br><span class="line">       plet.setColor(QPalette::Text,Qt::black);</span><br><span class="line">    ui-&gt;txtEdit-&gt;setPalette(plet);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>由于这个槽函数是自定义的，所以不会自动与 RadioButton 的 clicked() 事件关联，此时编译后运行程序不会实现改变字体颜色的功能。需要在 QWDialog 的构造函数中手工进行关联，代码如下：</p><figure class="highlight plaintext"><table><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">QWDialog::QWDialog(QWidget *parent) : QDialog(parent), ui(new Ui::QWDialog)</span><br><span class="line">&#123;</span><br><span class="line">    ui-&gt;setupUi(this);</span><br><span class="line">    connect(ui-&gt;rBtnBlue,SIGNAL(clicked()),this,SLOT(setTextFontColor()));</span><br><span class="line">    connect(ui-&gt;rBtnRed,SIGNAL(clicked()),this,SLOT(setTextFontColor()));</span><br><span class="line">    connect(ui-&gt;rBtnBlack,SIGNAL(clicked()),this,SLOT(setTextFontColor()));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在构造函数中将 3 个 RadioButton 的 clicked() 信号与同一个槽函数 setTextFontColor() 相关联。再编译后运行，就可以更改文字的颜色了。</p><h4 id="三个按钮的功能设计"><a href="#三个按钮的功能设计" class="headerlink" title="三个按钮的功能设计"></a>三个按钮的功能设计</h4><p>界面上还有“确定”“取消”“退出”3 个按钮，这是在对话框中常见的按钮。“确定”表示确认选择并关闭对话框，“取消”表示取消选择并关闭对话框，“退出”则直接关闭对话框。 QWDialog 是从 QDialog 继承而来的，QDialog 提供了 accept()、reject()、close() 等槽函数来表示这三种状态，只需将按钮的 clicked() 信号与相应槽函数关联即可。 下面采用可视化的方式，将按钮的 clicked() 信号与这些槽函数关联起来。在 UI 设计器里，单击上方工具栏里的“Edit Signals&#x2F;Slots”按钮，窗体进入信号与槽函数编辑状态，如图 10 所示。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/97585e5f-2e3e-4248-bbdd-bac482f506d9.gif" alt="image">图 10 窗体进入Signals&#x2F;Slot编辑状态</p><p>将鼠标移动到“确定”按钮上方，再按下鼠标左键，移动到窗体的空白区域释放左键，这时出现如图 11 所示的关联设置对话框。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/fca6cab8-18c5-428c-9f5a-278f2c89bd9c.gif" alt="image">图 11 信号与槽关联编辑对话框</p><p>在图 11 中，左侧的列表框里显示了 btnOK 的信号，选择 clicked()，右边的列表框里显示了 QWDialog 的槽函数，选择 accept()，单击“OK”按钮。 同样的方法可以将 btnCancel 的 clicked() 信号与 QWDialog 的 reject() 槽函数关联，将 btnClose 的 clicked() 信号与 QWDialog 的 close() 槽函数关联。</p><p>注意，在图 11 的右侧列表框中没有 close() 槽函数，需要勾选下方的“Show signals and slots inherited from QWidget”才会出现 close() 函数。</p><p>设置完 3 个按钮的信号与槽关联之后，在窗体下方的 Signals 和 Slots 编辑器里也显示了这 3 个关联。实际上，可以直接在 Signals 和 Slots 编辑器进行关联设置。现在编译并运行程序，单击这 3 个按钮都会关闭程序。 那么，这 3 个按钮的信号与槽函数的关联是在哪里实现的呢？答案在 setupUi() 函数里，在 setupUi() 函数里自动增加了以下 3 行代码：</p><figure class="highlight plaintext"><table><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">QObject::connect(btnOK, SIGNAL(clicked()), QWDialog, SLOT(accept()));</span><br><span class="line">QObject::connect(btnCancel, SIGNAL(clicked()), QWDialog, SLOT(reject()));</span><br><span class="line">QObject::connect(btnClose, SIGNAL(clicked()), QWDialog, SLOT(close()));</span><br></pre></td></tr></table></figure><p>这个实例程序的功能全部完成了。采用 UI 设计器设计了窗体界面，采用可视化和程序化的方式设计槽函数，设计信号与槽函数之间的关联。 从以上的设计过程可以看到，Qt Creator 和 UI 设计器为设计应用程序提供了强大的可视化设计功能。</p><ol start="12"><li>Qt信号与槽机制详解</li></ol><p>信号与槽（Signal &amp; Slot）是 <a href="http://c.biancheng.net/qt/">Qt</a>编程的基础，也是 Qt 的一大创新。因为有了信号与槽的编程机制，在 Qt 中处理界面各个组件的交互操作时变得更加直观和简单。信号（Signal）就是在特定情况下被发射的事件，例如PushButton 最常见的信号就是鼠标单击时发射的 clicked() 信号，一个 ComboBox 最常见的信号是选择的列表项变化时发射的 CurrentIndexChanged() 信号。 GUI 程序设计的主要内容就是对界面上各组件的信号的响应，只需要知道什么情况下发射哪些信号，合理地去响应和处理这些信号就可以了。槽（Slot）就是对信号响应的函数。槽就是一个函数，与一般的<a href="http://c.biancheng.net/cplus/">C++</a>函数是一样的，可以定义在类的任何部分（public、private 或 protected），可以具有任何参数，也可以被直接调用。槽函数与一般的函数不同的是：槽函数可以与一个信号关联，当信号被发射时，关联的槽函数被自动执行。 信号与槽关联是用 QObject::connect() 函数实现的，其基本格式是：</p><p>QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));</p><p>connect() 是 QObject 类的一个静态函数，而 QObject 是所有 Qt 类的基类，在实际调用时可以忽略前面的限定符，所以可以直接写为：</p><p>connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));</p><p>其中，sender 是发射信号的对象的名称，signal() 是信号名称。信号可以看做是特殊的函数，需要带括号，有参数时还需要指明参数。receiver 是接收信号的对象名称，slot() 是槽函数的名称，需要带括号，有参数时还需要指明参数。 SIGNAL 和 SLOT 是 Qt 的宏，用于指明信号和槽，并将它们的参数转换为相应的字符串。例如，在 samp2_1（前面章节中的项目）的 ui_widget.h 文件中，在 setupUi() 函数中有如下的语句：</p><p>QObject::connect(btnClose, SIGNAL(clicked()), Widget, SLOT(close()));</p><p>其作用就是将 btnClose 按钮的 clicked() 信号与窗体（Widget）的槽函数 close() 相关联，这样，当单击 btnClose 按钮（就是界面上的“Close”按钮）时，就会执行 Widget 的 close() 槽函数。 关于信号与槽的使用，有以下一些规则需要注意：</p><ul><li>一个信号可以连接多个槽，例如：</li></ul><p>connect(spinNum, SIGNAL(valueChanged(int)), this, SLOT(addFun(int)); connect(spinNum, SIGNAL(valueChanged(int)), this, SLOT(updateStatus(int));</p><p>这是当一个对象 spinNum 的数值发生变化时，所在窗体有两个槽进行响应，一个 addFun()用于计算，一个 updateStatus() 用于更新状态。 当一个信号与多个槽函数关联时，槽函数按照建立连接时的顺序依次执行。 当信号和槽函数带有参数时，在 connect()函数里，要写明参数的类型，但可以不写参数名称。</p><ul><li>多个信号可以连接同一个槽，例如在 samp2_2（前面章节中的项目）中，让三个选择颜色的 RadioButton的clicked() 信号关联到相同的一个自定义槽函数 setTextFontColor()。</li></ul><p>connect(ui-&gt;rBtnBlue,SIGNAL(clicked()),this,SLOT(setTextFontColor())); connect(ui-&gt;rBtnRed,SIGNAL(clicked()),this,SLOT(setTextFontColor())); connect(ui-&gt;rBtnBlack,SIGNAL(clicked()),this,SLOT(setTextFontColor()));</p><p>这样，当任何一个 RadioButton 被单击时，都会执行 setTextFontColor() 函数。</p><ul><li>一个信号可以连接另外一个信号，例如：</li></ul><p>connect(spinNum, SIGNAL(valueChanged(int)), this, SIGNAL (refreshInfo(int));</p><p>这样，当一个信号发射时，也会发射另外一个信号，实现某些特殊的功能。</p><ul><li><p>严格的情况下，信号与槽的参数个数和类型需要一致，至少信号的参数不能少于槽的参数。如果不匹配，会出现编译错误或运行错误。</p></li><li><p>在使用信号与槽的类中，必须在类的定义中加入宏 Q_OBJECT。</p></li><li><p>当一个信号被发射时，与其关联的槽函数通常被立即执行，就像正常调用一个函数一样。只有当信号关联的所有槽函数执行完毕后，才会执行发射信号处后面的代码。</p></li></ul><p>信号与槽机制是 Qt GUI 编程的基础，使用信号与槽机制可以比较容易地将信号与响应代码关联起来。</p><ol start="13"><li>Qt纯代码设计UI实例分析</li></ol><p>UI 的可视化设计是对用户而言的，其实底层都是 <a href="http://c.biancheng.net/cplus/">C++</a>的代码实现，只是 <a href="http://c.biancheng.net/qt/">Qt</a>巧妙地进行了处理，让用户省去了很多繁琐的界面设计工作。 由于界面设计的底层其实都是由 C++ 语言实现的，底层实现的功能比可视化设计更加强大和灵活。某些界面效果是可视化设计无法完成的，或者某些人习惯了用纯代码的方式来设计界面，就可以采用纯代码的方式设计界面，如 Qt 自带的实例基本都是用纯代码方式实现用户界面的。 所以，本节介绍一个用纯代码方式设计 UI 的实例，通过实例了解用纯代码设计 UI 的基本原理。与前面的可视化 UI 设计相对应，且称之为代码化 UI 设计。</p><h2 id="实例功能"><a href="#实例功能" class="headerlink" title="实例功能"></a>实例功能</h2><p>首先建立一个 Widget Appliation 项目 samp2_3，在创建项目向导中选择基类时，选择基类 QDialog，新类的名称命名为 QWDlgManual，关键是取消创建窗体，即不勾选“Generate form”（创建界面）复选框。创建后的项目文件目录树下没有 qwdlgmanual.ui 文件。 该项目通过代码创建一个对话框，实现与 samp2_2 类似的界面和功能。本例完成后的运行效果如图 1 所示，其界面和功能与 samp2_2 类似。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/b8c40f64-6dee-4624-9069-e2a1ce396575.gif" alt="image"></p><p>图 1 实例 samp2_3 运行效果</p><h2 id="界面创建"><a href="#界面创建" class="headerlink" title="界面创建"></a>界面创建</h2><h4 id="QWDlgManual-类定义"><a href="#QWDlgManual-类定义" class="headerlink" title="QWDlgManual 类定义"></a>QWDlgManual 类定义</h4><p>完成功能后的 qwdlgmanual.h 文件中 QWDlgManual 类的完整定义如下：</p><figure class="highlight plaintext"><table><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></pre></td><td class="code"><pre><span class="line">#include &lt;QDialog&gt;</span><br><span class="line">#include &lt;QCheckBox&gt;</span><br><span class="line">#include &lt;QRadioButton&gt;</span><br><span class="line">#include &lt;QPlainTextEdit&gt;</span><br><span class="line">#include &lt;QPushButton&gt;</span><br><span class="line">class QWDlgManual : public QDialog&#123; </span><br><span class="line">   Q_OBJEC</span><br><span class="line">Tprivate: </span><br><span class="line">   QCheckBox   *chkBoxUnder;  </span><br><span class="line">  QCheckBox   *chkBoxItalic;  </span><br><span class="line">  QCheckBox   *chkBoxBold;  </span><br><span class="line">  QRadioButton    *rBtnBlack; </span><br><span class="line">   QRadioButton    *rBtnRed;  </span><br><span class="line">  QRadioButton    *rBtnBlue;  </span><br><span class="line">  QPlainTextEdit  *txtEdit;   </span><br><span class="line"> QPushButton     *btnOK;   </span><br><span class="line"> QPushButton     *btnCancel;   </span><br><span class="line"> QPushButton     *btnClose;   </span><br><span class="line"> void    iniUI();//UI 创建与初始化 </span><br><span class="line">   void    iniSignalSlots();//初始化信号与槽的链接</span><br><span class="line">private slots:    void on_chkBoxUnder(bool checked);</span><br><span class="line"> //Underline 的clicked(bool)信号的槽函数    </span><br><span class="line">void on_chkBoxItalic(bool checked);//Italic 的clicked(bool)信号的槽函数    void on_chkBoxBold(bool checked); //Bold 的clicked(bool)信号的槽函数    void setTextFontColor(); //设置字体颜色public:    QWDlgManual(QWidget *parent = 0);    ~QWDlgManual();&#125;;</span><br></pre></td></tr></table></figure><p>在 QWDlgManual 类的 private 部分，声明了界面上的各个组件的指针变量，这些界面组件都需要在 QWDlgManual 类的构造函数里创建并在窗体上布局。 在 private 部分自定义了两个函数，iniUI() 用来创建所有界面组件，并完成布局和属性设置，iniSignalSlots() 用来完成所有的信号与槽函数的关联。 在 private slots 部分声明了 4 个槽函数，分别是 3 个 CheckBox 的响应槽函数，以及 3 个颜色设置的 RadioButton 的共同响应槽函数。</p><p>注意：与可视化设计得到的窗体类定义不同，QWDlgManual 的类定义里没有指向界面的指针 ui。</p><p>这几个槽函数的功能与例 samp2_2 中的类似，只是在访问界面组件时，无需使用 ui 指针，而是直接访问 QWDlgManual 类里定义的界面组件的成员变量即可，例如 on_chkBoxUnder() 的代码：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">void QWDlgManual::on_chkBoxUnder(bool checked)&#123;    QFont font=txtEdit-&gt;font();    font.setUnderline(checked);    txtEdit-&gt;setFont(font);&#125;</span><br></pre></td></tr></table></figure><p>界面的创建，以及信号与槽函数的关联都在 QWDlgManual 的构造函数里完成，构造函数代码如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QWDlgManual::QWDlgManual(QWidget *parent) : QDialog(parent)&#123;    iniUI(); //界面创建与布局    iniSignalSlots(); //信号与槽的关联    setWindowTitle(&quot;Form created mannually&quot;);&#125;</span><br></pre></td></tr></table></figure><p>构造函数调用 iniUI() 创建界面组件并布局，调用 iniSignalSlots() 进行信号与槽函数的关联。</p><h4 id="界面组件的创建与布局"><a href="#界面组件的创建与布局" class="headerlink" title="界面组件的创建与布局"></a>界面组件的创建与布局</h4><p>iniUI() 函数实现界面组件的创建与布局，以及属性设置。下面是 iniUI() 的完整代码：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">void QWDlgManual::iniUI()&#123;    //创建 Underline, Italic, Bold 3 个CheckBox，并水平布局    chkBoxUnder=new QCheckBox(tr(&quot;Underline&quot;));    chkBoxItalic=new QCheckBox(tr(&quot;Italic&quot;));    chkBoxBold=new QCheckBox(tr(&quot;Bold&quot;));    QHBoxLayout *HLay1=new QHBoxLayout;    HLay1-&gt;addWidget(chkBoxUnder);    HLay1-&gt;addWidget(chkBoxItalic);    HLay1-&gt;addWidget(chkBoxBold);    //创建 Black, Red, Blue 3 个RadioButton，并水平布局    rBtnBlack=new QRadioButton(tr(&quot;Black&quot;));    rBtnBlack-&gt;setChecked(true);    rBtnRed=new QRadioButton(tr(&quot;Red&quot;));    rBtnBlue=new QRadioButton(tr(&quot;Blue&quot;));    QHBoxLayout *HLay2=new QHBoxLayout;    HLay2-&gt;addWidget(rBtnBlack);    HLay2-&gt;addWidget(rBtnRed);    HLay2-&gt;addWidget(rBtnBlue);    //创建确定, 取消, 退出3 个 PushButton, 并水平布局    btnOK=new QPushButton(tr(&quot;确定&quot;));    btnCancel=new QPushButton(tr(&quot;取消&quot;));    btnClose=new QPushButton(tr(&quot;退出&quot;));    QHBoxLayout *HLay3=new QHBoxLayout;    HLay3-&gt;addStretch();    HLay3-&gt;addWidget(btnOK);    HLay3-&gt;addWidget(btnCancel);    HLay3-&gt;addStretch();    HLay3-&gt;addWidget(btnClose);    //创建文本框,并设置初始字体    txtEdit=new QPlainTextEdit;    txtEdit-&gt;setPlainText(&quot;Hello world\n\nIt is my demo&quot;);    QFont font=txtEdit-&gt;font(); //获取字体    font.setPointSize(20);//修改字体大小    txtEdit-&gt;setFont(font);//设置字体    //创建垂直布局，并设置为主布局    QVBoxLayout *VLay=new QVBoxLayout;    VLay-&gt;addLayout(HLay1); //添加字体类型组    VLay-&gt;addLayout(HLay2);//添加字体颜色组    VLay-&gt;addWidget(txtEdit);//添加PlainTextEdit    VLay-&gt;addLayout(HLay3);//添加按键组    setLayout(VLay); //设置为窗体的主布局&#125;</span><br></pre></td></tr></table></figure><p>iniUI() 函数按照顺序完成了如下的功能：</p><ul><li><p>创建 3 个 QCheckBox 组件，这 3 个组件的指针已经定义为 QWDlgManual 的私有变量，然后创建一个水平布局 HLay1，将 3 个 CheckBox 添加到这个水平布局里。</p></li><li><p>创建 3 个 QRadioButton 组件，并创建一个水平布局 HLay2，将 3 个 RadioButton 添加到这个水平布局里。</p></li><li><p>创建 3 个 QPushButton 组件，并创建一个水平布局 HLay3，将 3 个 PushButton 添加到这个水平布局里，并适当添加水平空格。</p></li><li><p>创建一个 QPlainTextEdit 组件，设置其文字内容，并设置其字体。</p></li><li><p>创建一个垂直布局 VLay，将前面创建的 3 个水平布局和文本框依次添加到此布局里。</p></li><li><p>设置垂直布局为窗体的主布局。</p></li></ul><p>如此创建组件并设置布局后，运行可以得到如图 1 所示的界面效果。这里完全是采用代码来实现组件创建与布局的设置，而这些功能在可视化设计中是由 setupUi() 函数根据界面的可视化设计结果自动实现的。 采用代码设计实现 UI 时，需要对组件的布局有个完整的规划，不如可视化设计直观，且编写代码工作量大。</p><h2 id="信号与槽的关联"><a href="#信号与槽的关联" class="headerlink" title="信号与槽的关联"></a>信号与槽的关联</h2><p>在纯代码设计 UI 时，信号与槽的关联也需要用代码来完成。函数 iniSignalSlots() 初始化所有的信号与槽的关联，其完整代码如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">void QWDlgManual::iniSignalSlots()&#123;    //三个颜色 QRadioButton 的clicked()信号与setTextFontColor()槽函数关联    connect(rBtnBlue,SIGNAL(clicked()),this,SLOT(setTextFontColor()));    connect(rBtnRed,SIGNAL(clicked()),this,SLOT(setTextFontColor()));    connect(rBtnBlack,SIGNAL(clicked()),this,SLOT(setTextFontColor()));    //三个字体设置的 QCheckBox 的clicked(bool)信号与相应的槽函数关联    connect(chkBoxUnder,SIGNAL(clicked(bool)),    this,SLOT(on_chkBoxUnder(bool)));    connect(chkBoxItalic,SIGNAL(clicked(bool)),    this,SLOT(on_chkBoxItalic(bool)));    connect(chkBoxBold,SIGNAL(clicked(bool)),    this,SLOT(on_chkBoxBold(bool)));    //三个按钮的信号与窗体的槽函数关联    connect(btnOK,SIGNAL(clicked()),this,SLOT(accept()));    connect(btnCancel,SIGNAL(clicked()),this,SLOT(reject()));    connect(btnClose,SIGNAL(clicked()),this,SLOT(close()));&#125;</span><br></pre></td></tr></table></figure><p>设计完成后，编译并运行程序，可以得到如图 1 所示的运行效果，且功能与 samp2_2 相同。很显然，采用纯代码方式实现 UI 界面是比较复杂的，代码设计的工作量大而繁琐。</p><ol start="14"><li>Qt Creator使用技巧</li></ol><p><a href="http://c.biancheng.net/qt/">Qt</a>Creator 在设计界面或编辑代码时，有一些快捷键和使用技巧，熟悉这些快捷键和使用技巧，可以提高工作效率。表 1 是 Qt Creator 的一些快捷操作的总结。</p><table><thead><tr><th>功能</th><th>快捷键</th><th>解释</th></tr></thead><tbody><tr><td>Switch Header&#x2F;Source</td><td>F4</td><td>在同名的头文件和源程序文件之间切换</td></tr><tr><td>Follow Symbol Under Cursor</td><td>F2</td><td>跟踪光标下的符号，若是变量，可跟踪到变量声明的地方；若是函数体或函数声明，可在两者之间切换</td></tr><tr><td>Switch Between Function Declaration and Definition</td><td>Shift+F2</td><td>在函数的声明（函数原型）和定义（函数实现）之间切换</td></tr><tr><td>Refactor\Rename Symbol Under Cursor</td><td>Ctrl+Shift+R</td><td>对光标处的符号更改名称，这将替换到所有用到这个符号的地方</td></tr><tr><td>Refactor\Add Definition in .cpp</td><td></td><td>为函数原型在 cpp 文件里生成函数体</td></tr><tr><td>Auto-indent Selection</td><td>Ctrl+I</td><td>为选择的文字自动进行缩进</td></tr><tr><td>Toggle Comment Selection</td><td>Ctrl+&#x2F;</td><td>为选择的文字进行注释符号的切换，即可以注释所选代码，或取消注释</td></tr><tr><td>Context Help</td><td>F1</td><td>为光标所在的符号显示帮助文件的内容</td></tr><tr><td>Save All</td><td>Ctrl+Shift+S</td><td>文件全部保存</td></tr><tr><td>Find&#x2F;Replace</td><td>Ctrl+F</td><td>调出查找&#x2F;替换对话框</td></tr><tr><td>Find Next</td><td>F3</td><td>查找下一个</td></tr><tr><td>Build</td><td>Ctrl+B</td><td>编译当前项目</td></tr><tr><td>Start Debugging</td><td>F5</td><td>开始调试</td></tr><tr><td>Step Over</td><td>F10</td><td>调试状态下单步略过，即执行当前行程序语句</td></tr><tr><td>Step Into</td><td>F11</td><td>调试状态下跟踪进入，即如果当前行里有函数，就跟踪进入函数体</td></tr><tr><td>Toggle Breakpoint</td><td>F9</td><td>设置或取消当前行的断点设置</td></tr></tbody></table><p>另外，在使用 Qt 时，要善于使用 Qt 自带的帮助文件，对于一个编程语言或类库来说，其自带的帮助文件是最全面最权威的资料。当光标停留在一个类名或函数上时，按 F1 可以调出其帮助文件的内容。 在 Qt Creator 主窗口左侧的主工具栏上有“Help”按钮，单击可以打开 Qt 的帮助文件系统（如图 2 所示），也可以使用“开始”菜单 Qt 程序组里的 Assistant 单独打开帮助系统。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/94e13ac6-45f2-432e-a3c8-2a47ecf1d89d.gif" alt="image">图 2 使用 Qt 的帮助系统查看资料</p><p>在帮助文件显示界面上，左上方工具栏中有个下拉列表框，可以选择 Bookmarks、Contents、Index 和 Search 4 种模式：</p><ol><li><p>Bookmarks 模式下，左边框里显示已存储的 Bookmarks（书签），任何帮助页面下，点击窗口上方工具栏上的“Add Bookmark”可以添加书签。</p></li><li><p>Contents 模式下，左边框里以目录树形式显示 Qt 的所有模块（如图 1 所示），可以分类浏览想看的内容。</p></li><li><p>Index 模式下，可以输入查找内容，左边框里会列出与输入内容前匹配的帮助主题列表。</p></li><li><p>Search 模式下，可以输入关键字进行搜索。</p></li></ol><p>在 Qt 帮助系统里可以搜索查看每个类的详细资料，如 QTextEdit，可以看到这个类的详细资料，包括在这个类定义的公共类型、属性、公共函数、信号、公共槽等。 另外，若要查看类的继承关系，可以访问 Qt 官网的“Inheritance Hierarchy”页面。</p><ol start="15"><li>Qt元对象和属性系统详解</li></ol><p><a href="http://c.biancheng.net/qt/">Qt</a>是一个用标准 <a href="http://c.biancheng.net/cplus/">C++</a>编写的跨平台开发类库，它对标准 C++ 进行了扩展，引入了元对象系统、信号与槽、属性等特性，使应用程序的开发变得更高效。 本节将介绍 Qt 的这些核心特点，对于理解和编写高效的 Qt C++ 程序是大有帮助的。</p><h2 id="Qt-的元对象系统"><a href="#Qt-的元对象系统" class="headerlink" title="Qt 的元对象系统"></a>Qt 的元对象系统</h2><p>Qt 的元对象系统（Meta-Object System）提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统。 元对象系统由以下三个基础组成：</p><ol><li><p>QObject 类是所有使用元对象系统的类的基类。</p></li><li><p>在一个类的 private 部分声明 Q_OBJECT宏，使得类可以使用元对象的特性，如动态属性、信号与槽。</p></li><li><p>MOC（元对象编译器）为每个 QObject 的子类提供必要的代码来实现元对象系统的特性。</p></li></ol><p>构建项目时，MOC 工具读取 C++ 源文件，当它发现类的定义里有 Q_OBJECT 宏时，它就会为这个类生成另外一个包含有元对象支持代码的 C++ 源文件，这个生成的源文件连同类的实现文件一起被编译和连接。 除了信号与槽机制外，元对象还提供如下一些功能：</p><ul><li>QObject::metaObject() 函数返回类关联的元对象，元对象类 QMetaObject 包含了访问元对象的一些接口函数，例如 QMetaObject::className() 函数可在运行时返回类的名称字符串。</li></ul><p>QObject *obj &#x3D; new QPushButton; obj-&gt;metaObject()-&gt;className (); &#x2F;&#x2F;返回”QPushButton”</p><ul><li><p>QMetaObject::newInstance() 函数创建类的一个新的实例。</p></li><li><p>QObject::inherits(const char *className) 函数判断一个对象实例是否是名称为 className 的类或 QObject 的子类的实例。例如：</p></li></ul><p>QTimer *timer &#x3D; new QTimer; &#x2F;&#x2F; QTimer 是 QObject 的子类 timer-&gt;inherits (“QTimer”); &#x2F;&#x2F; 返回 true timer-&gt;inherits (“QObject”);  &#x2F;&#x2F; 返回 true timer-&gt;inherits (“QAbstractButton”);&#x2F;&#x2F;返回 false,不是 QAbstractButton 的子类</p><ul><li><p>QObject::tr() 和 QObject::trUtf8() 函数可翻译字符串，用于多语言界面设计，后续章会专门介绍多语言界面设计。</p></li><li><p>QObject::setProperty() 和 QObject::property() 函数用于通过属性名称动态设置和获取属性值。</p></li></ul><p>对于 QObject 及其子类，还可以使用 qobject_cast() 函数进行动态投射（dynamic cast）。例如，假设 QMyWidget 是 QWidget 的子类并且在类定义中声明了 Q_OBJECT 宏。创建实例使用下面的语句：</p><p>QObject *obj &#x3D; new QMyWidget;</p><p>变量 obj 定义为 QObject 指针，但它实际指向 QMyWidget 类，所以可以正确投射为 QWidget，即：</p><p>QWidget *widget &#x3D; qobject_cast&lt;QWidget *&gt;(obj);</p><p>从 QObject 到 QWidget 的投射是成功的，因为 obj 实际是 QMyWidget 类，是 QWidget 的子类。也可以将其成功投射为 QMyWidget，即：</p><p>QMyWidget *myWidget &#x3D; qobject_cast&lt;QMyWidget *&gt;(obj);</p><p>投射为 QMyWidget 是成功的，因为 qobject_cast() 并不区分 Qt 内建的类型和用户自定义类型。但是，若要将 obj 投射为 QLabel 则是失败的，即：</p><p>QLabel * label - qobject_cast&lt;QLabel *&gt;(obj);</p><p>这样投射是失败的，返回指针 label 为 NULL，因为 QMyWidget 不是 QLabel 的子类。 使用动态投射，使得程序可以在运行时对不同的对象做不同的处理。</p><h2 id="属性系统"><a href="#属性系统" class="headerlink" title="属性系统"></a>属性系统</h2><h4 id="属性定义"><a href="#属性定义" class="headerlink" title="属性定义"></a>属性定义</h4><p>Qt 提供一个 Q_PROPERTY() 宏可以定义属性，它也是基于元对象系统实现的。Qt 的属性系统与 C++ 编译器无关，可以用任何标准的 C++ 编译器编译定义了属性的 Qt C++ 程序。 在 QObject 的子类中，用宏 Q_PROPERTY() 定义属性，其使用格式如下：</p><p>Q_PROPERTY(type name (READ getFunction [WRITE setFunction] | MEMBER meznberName [(READ getFunction | WRITE setFunction)])    [RESET resetFunction]    [NOTIFY notifySignal]    [REVISION int]    [DESIGNABLE bool]    [SCRIPTABLE bool]    [STORED bool]    [USER bool]    [CONSTANT]    [FINAL])</p><p>Q_PROPERTY 宏定义一个返回值类型为 type，名称为 name 的属性，用 READ、WRITE 关键字定义属性的读取、写入函数，还有其他的一些关键字定义属性的一些操作特性。属性的类型可以是 QVariant 支持的任何类型，也可以用户自定义类型。 Q_PROPERTY 宏定义属性的一些主要关键字的意义如下：</p><ul><li><p>READ 指定一个读取属性值的函数，没有 MEMBER 关键字时必须设置 READ。</p></li><li><p>WRITE 指定一个设定属性值的函数，只读属性没有 WRITE 设置。</p></li><li><p>MEMBER 指定一个成员变量与属性关联，成为可读可写的属性，无需再设置 READ 和 WRITE。</p></li><li><p>RESET 是可选的，用于指定一个设置属性缺省值的函数。</p></li><li><p>NOTIFY 是可选的，用于设置一个信号，当属性值变化时发射此信号。</p></li><li><p>DESIGNABLE 表示属性是否在 Qt Designer 里可见，缺省为 true。</p></li><li><p>CONSTANT 表示属性值是一个常数，对于一个对象实例，READ 指定的函数返回值是常数，但是每个实例的返回值可以不一样。具有 CONSTANT 关键字的属性不能有 WRITE 和 NOTIFY 关键字。</p></li><li><p>FINAL 表示所定义的属性不能被子类重载。</p></li></ul><p>QWidget 类定义属性的一些例子如下：</p><p>Q_PROPERTY(bool focus READ hasFocus) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)</p><h4 id="属性的使用"><a href="#属性的使用" class="headerlink" title="属性的使用"></a>属性的使用</h4><p>不管是否用 READ 和 WRITE 定义了接口函数，只要知道属性名称，就可以通过 QObject::property() 读取属性值，并通过 QObject::setProperty() 设置属性值。例如：</p><p>QPushButton *button &#x3D; new QPushButton; QObject *object &#x3D; button； object-&gt;setProperty(“flat”, true); bool isFlat&#x3D; object-&gt;property (“flat”);</p><h4 id="动态属性"><a href="#动态属性" class="headerlink" title="动态属性"></a>动态属性</h4><p>QObject::setProperty() 函数可以在运行时为类定义一个新的属性，称之为动态属性。动态属性是针对类的实例定义的。 动态属性可以使用 QObject::property() 查询，就如在类定义里用 Q_PROPERTY 宏定义的属性一样。 例如，在数据表编辑界面上，一些字段是必填字段，就可以在初始化界面时为这些字段的关联显示组件定义一个新的 required 属性，并设置值为“true”，如：</p><p>editName-&gt;setProperty(“required”, “true”); comboSex-&gt;setProperty(“required”, “true”); checkAgree-&gt;setProperty(“required”, “true”);</p><p>然后，可以应用下面的样式定义将这种必填字段的背景颜色设置为亮绿色。</p><p>*[required&#x3D;”true”]{background-color:lime}</p><h4 id="类的附加信息"><a href="#类的附加信息" class="headerlink" title="类的附加信息"></a>类的附加信息</h4><p>属性系统还有一个宏 Q_CLASSINFO()，可以为类的元对象定义“名称——值”信息，如：</p><figure class="highlight plaintext"><table><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">class QMyClass:public QObject &#123;</span><br><span class="line">    Q_OBJECT</span><br><span class="line">    Q_CLASSINFO(&quot;author&quot;, &quot;Wang&quot;)</span><br><span class="line">    Q_CLASSINFO (&quot;company&quot;, &quot;UPC&quot;)</span><br><span class="line">    Q_CLASSINFO(&quot;version &quot;, &quot;3.0.1&quot;)</span><br><span class="line">  public:</span><br><span class="line">    ...</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>用 Q_CLASSINFO() 宏定义附加类信息后，可以通过元对象的一些函数获取类的附加信息，如 classlnfo(int) 获取某个附加信息，函数原型定义如下：</p><p>QMetaClassInfo QMetaObject::classInfo(int index) const</p><p>返回值是 QMetaClassInfo 类型，有 name() 和 value() 两个函数，可获得类附加信息的名称和值。</p><ol start="16"><li>Qt全局变量、函数和宏定义详解</li></ol><p>&lt;<a href="http://c.biancheng.net/qt/">Qt</a>Global&gt; 头文件包含了 Qt 类库的一些全局定义，包括基本数据类型、函数和宏，一般的 Qt 类的头文件都会包含该文件，所以不用显式包含这个头文件也可以使用其中的定义。</p><h2 id="全局变量定义"><a href="#全局变量定义" class="headerlink" title="全局变量定义"></a>全局变量定义</h2><p>为了确保在各个平台上各数据类型都有统一确定的长度，Qt 为各种常见数据类型定义了类型符号，如 qint8 就是 signed char 的类型定义，即：</p><p>typedef signed char qint8;</p><p><QtGlobal>中定义的数据类型见表 1。</p><table><thead><tr><th>Qt 数据类型</th><th>等效定义</th><th>字节数</th></tr></thead><tbody><tr><td>qint8</td><td>signed char</td><td>1</td></tr><tr><td>qint16</td><td>signed short</td><td>2</td></tr><tr><td>qint32</td><td>signed int</td><td>4</td></tr><tr><td>qint64</td><td>long long int</td><td>8</td></tr><tr><td>qlonglong</td><td>long long int</td><td>8</td></tr><tr><td>quint8</td><td>unsigned char</td><td>1</td></tr><tr><td>quint16</td><td>unsigned short</td><td>2</td></tr><tr><td>quint32</td><td>unsigned int</td><td>4</td></tr><tr><td>quint64</td><td>unsigned long long int</td><td>8</td></tr><tr><td>qulonglong</td><td>unsigned long long int</td><td>8</td></tr><tr><td>uchar</td><td>unsigned char</td><td>1</td></tr><tr><td>ushort</td><td>unsigned short</td><td>2</td></tr><tr><td>uint</td><td>unsigned int</td><td>4</td></tr><tr><td>ulong</td><td>unsigned long</td><td>8</td></tr><tr><td>qreal</td><td>double</td><td>8</td></tr><tr><td>qfloat16</td><td></td><td>2</td></tr></tbody></table><p>其中 qreal 缺省是 8 字节 double 类型浮点数，如果 Qt 使用 -qreal float 选项进行配置，就是 4 字节 float 类型的浮点数。 qfloat16 是 Qt 5.9.0 中新增的一个类，用于表示 16 位的浮点数，要使用 qfloat16，需要包含头文件 <QFloat16>。</p><h2 id="全局函数定义"><a href="#全局函数定义" class="headerlink" title="全局函数定义"></a>全局函数定义</h2><p><QtGlobal> 头文件包含一些常用函数的定义，这些函数多以模板类型作为参数，返回相应的模板类型，模板类型可以用任何其他类型替换。若是以 double 或 float 类型数作为参数的，一般有两个参数版本的同名函数，如qFuzzyIsNull(double d) 和 qFuzzyIsNull(float f)。 表 2 是 <QtGlobal> 中常用的全局函数定义，列出了函数的输入和输出参数（若存在 double 和 float 两种参数版本，只列出 double 类型参数的版本）。</p><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>T qAbs(const T &amp;value)</td><td>返回变量 value 的绝对值</td></tr><tr><td>const T &amp;qBound(const T &amp;min, const T&amp;value, const T &amp;max)</td><td>返回 value 限定在 min 至 max 范围之内的値</td></tr><tr><td>bool qFuzzyComparc(doublc p1, double p2)</td><td>若 p1 和 p2 近似相等，返回 true</td></tr><tr><td>bool qFuzzyIsNulI(double d)</td><td>如果参数 d 约等于 0，返回 true</td></tr><tr><td>double qInf(()</td><td>返回无穷大的数</td></tr><tr><td>bool qIsFinite(double d)</td><td>若 d 是一个有限的数，返回 true</td></tr><tr><td>bool qIsInf(double d)</td><td>若 d 是一个无限大的数，返回 true</td></tr><tr><td>bool qIsNaN(double d)</td><td>若 d 不是一个数，返回 true</td></tr><tr><td>constT&amp;qMax(const T&amp;value1, const T&amp;value2)</td><td>返回 value1 和 value2 中较大的值</td></tr><tr><td>const T &amp;qMin(const T&amp;value1, const T&amp;value2)</td><td>返回 value1 和 value2 中较小的值</td></tr><tr><td>qint64 qRound64(double value)</td><td>将 value 近似为最接近的 qint64 整数</td></tr><tr><td>int qRound(double value)</td><td>将 value 近似为最接近的 int 整数</td></tr><tr><td>int qrand()</td><td>标准 <a href="http://c.biancheng.net/cplus/">C++</a> 中 rand() 函数的线程安全型版本，返回 0 至 RAND_MAX 之间的伪随机数</td></tr><tr><td>void qsrand(uint seed)</td><td>标准 C++ 中 srand() 函数的线程安全型版本，使用种子 seed 对伪随机数字序列初始化</td></tr></tbody></table><p>还有一些基础的数学运算函数在 <QtMath> 头文件中定义，比如三角运算函数、弧度与角度之间的转换函数等。</p><h2 id="全局宏定义"><a href="#全局宏定义" class="headerlink" title="全局宏定义"></a>全局宏定义</h2><p><QtGlobal>中文件中定义了很多宏，以下一些是比较常用的：</p><ul><li><p>QT_VERSION：这个宏展开为数值形式 0xMMNNPP (MM &#x3D; major, NN &#x3D; minor, PP &#x3D; patch) 表示 Qt 编译器版本，例如 Qt 编译器版本为 Qt 5.9.1，则 QT_VERSION 为 0x050901。这个宏常用于条件编译设置，根据 Qt 版本不同，编译不同的代码段。</p></li><li><p>QT_VERSION_CHECK：这个宏展开为 Qt 版本号的一个整数表示，例如：</p></li><li><p>QT_VERSION_STR：这个宏展开为 Qt 版本号的字符串，如“5.9.0”。</p></li><li><p>Q_BYTE_ORDER、Q_BIG_ENDIAN 和 Q_LITTLE_ENDIAN：Q_BYTE_ORDER 表示系统内存中数据的字节序，Q_BIG_ENDIAN 表示大端字节序，Q_LITTLE_ ENDIAN 表示小端字节序。在需要判断系统字节序时会用到，例如：</p></li><li><p>Q_DECL_IMPORT 和 Q_DECL_EXPORT：在使用或设计共享库时，用于导入或导出库的内容，后续章节有其使用实例。</p></li><li><p>Q_DECL_OVERRIDE：在类定义中，用于重载一个虚函数，例如在某个类中重载虚函数 paintEvem()，可以定义如下：</p></li></ul><p>void paintEvent(QPaintEvent*) Q_DECL_OVERRIDE;</p><p>使用 Q_DECL_OVERRIDE 宏后，如果重载的虚函数没有进行任何重载操作，编译器将会报错。</p><ul><li><p>Q_DECL_FINAL：这个宏将一个虚函数定义为最终级别，不能再被重载，或定义一个类不能再被继承，示例如下：</p></li><li><p>Q_UNUSED(name)：这个宏用于在函数中定义不在函数体里使用的参数，示例如下： 在这个函数里，id 参数没有使用。如果不用 QJJNUSED(id) 定义，编译器会出现参数未使用的警告。</p></li><li><p>foreach(variable, container)：foreach 用于容器类的遍历，例如：</p></li><li><p>forever：forever用于构造一个无限循环，例如：</p></li><li><p>qDebug(const char * message,…）：在debugger窗体显示信息，如果编译器设置了 Qt_NO_DEBUG_OUTPUT，则不作任何输出，例如：</p></li></ul><p>qDebug(“Items in list: %d”, myList.size());</p><p>类似的宏还有 qWarning、qCritical、qFatal、qInfo 等，也是用于在 debugger 窗体显示信息。</p><ol start="17"><li>Qt顺序容器类和关联容器类详解</li></ol><p><a href="http://c.biancheng.net/qt/">Qt</a>提供了多个基于模板的容器类，这些容器类可以用于存储指定类型的数据项，例如常用的字符串列表类 QStringList 就是从容器类 QLiSt<QString> 继承的，实现对字符串列表的添加、存储、删除等操作。 Qt 的容器类比标准模板库（<a href="http://c.biancheng.net/stl/">STL</a>）中的容器类更轻巧、安全和易于使用。这些容器类是隐式共享和可重入的，而且它们进行了速度和存储优化，因此可以减少可执行文件的大小。此外，它们还是线程安全的，也就是说它们作为只读容器时可被多个线程访问。 容器类是基于模板的类，如常用的容器类 QList<T>，T 是一个具体的类型，可以是 int、float 等简单类型，也可以是 Qstring、QDate 等类，但不能是 QObject 或任何其子类。T 必须是一个可赋值的类型，即T必须提供一个缺省的构造函数，一个可复制构造函数和一个赋值运算符。 例如用 QList<T> 定义一个字符串列表的容器，其定义方法是：</p><p>QList<QString> aList;</p><p>这样定义了一个 QList 容器类的变量 aList，它的数据项是 QString，所以 aList 可以用于处理字符串列表，例如：</p><p>aList.append(“Monday”); aList.append(“Tuesday”); aList.append(“Wednesday”); QString str&#x3D;aList[0];</p><p>Qt 的容器类分为顺序容器和关联容器。</p><h2 id="顺序容器类"><a href="#顺序容器类" class="headerlink" title="顺序容器类"></a>顺序容器类</h2><p>Qt 的顺序容器类有 QList、QLinkedList、QVector、QStack 和 QQueue。</p><h4 id="QList"><a href="#QList" class="headerlink" title="QList"></a>QList</h4><p>QList 是最常用的容器类，虽然它是以数组列表的形式实现的，但是在其前或后添加数据非常快，QList 以下标索引的方式对数据项进行访问。 QList 用于添加、插入、替换、移动、删除数据项的函数有：insert()、replace()、removeAt()、move()、swap()、append()、prepend()、removeFirst() 和 removeLast() 等。 QList 提供下标索引方式访问数据项，如同数组一样，也提供 at() 函数，例如：</p><p>QList<QString> list; list &lt;&lt; “one” &lt;&lt; “two” &lt;&lt; “three”; QString str1&#x3D;list[1]; &#x2F;&#x2F;str1&#x3D;&#x3D;”two” QString str0&#x3D;list.at(0); &#x2F;&#x2F;str0&#x3D;&#x3D;”one”</p><p>QList 的 isEmpty() 函数在数据项为空时返回 true，size() 函数返回数据项的个数。 QList 是 Qt 中最常用的容器类，很多函数的参数传递都是采用 QList 容器类，例如 QAudioDeviceInfo 的静态函数 availableDevices() 的函数原型是：</p><p>QList<QAudioDeviceInfo> QAudioDeviceInfo::availableDevices(QAudio::Mode mode)</p><p>其返回数据就是 QAudioDeviceInfo 类型的 QList 列表。</p><h4 id="QLinkedList"><a href="#QLinkedList" class="headerlink" title="QLinkedList"></a>QLinkedList</h4><p>QLinkedList<T> 是链式列表，数据项不是用连续的内存存储的，它基于迭代器访问数据项，并且插入和删除数据项的操作时间相同。 除了不提供基于下标索引的数据项访问外，QLinkedList 的其他接口函数与 QList 基本相同。</p><h4 id="QVector"><a href="#QVector" class="headerlink" title="QVector"></a>QVector</h4><p>QVector<T> 提供动态数组的功能，以下标索引访问数据。 QVector 的函数接口与 QList 几乎完全相同，QVector<T> 的性能比 QList<T> 更高，因为 QVector<P> 的数据项是连续存储的。</p><h4 id="QStack"><a href="#QStack" class="headerlink" title="QStack"></a>QStack</h4><p>QStack<T> 是提供类似于堆栈的后入先出（LIFO）操作的容器类，push() 和 pop() 是主要的接口函数。例如：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QStack&lt;int&gt; stack;stack.push(10);stack.push(20);stack.push(30);while (!stack.isEmpty())    cout &lt;&lt; stack.pop() &lt;&lt; endl;</span><br></pre></td></tr></table></figure><p>程序会依次输出 30, 20, 10。</p><h4 id="QQueue"><a href="#QQueue" class="headerlink" title="QQueue"></a>QQueue</h4><p>QQueue<T> 是提供类似于队列先入先出（FIFO）操作的容器类。enqueue() 和 dequeue() 是主要操作函数。例如：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QQueue&lt;int&gt; queue;queue.enqueue (10);queue.enqueue(20);queue.enqueue (30);while (!queue.isEmpty())    cout &lt;&lt; queue.dequeue() &lt;&lt; endl;</span><br></pre></td></tr></table></figure><p>程序会依次输出 10, 20，30。</p><h2 id="关联容器类"><a href="#关联容器类" class="headerlink" title="关联容器类"></a>关联容器类</h2><p>Qt 还提供关联容器类 QMap、QMultiMap、QHash、QMultiHash 和 QSet。 QMultiMap 和 QMultiHash 支持一个键关联多个值，QHash 和 QMultiHash 类使用散列函数进行查找，查找速度更快。</p><h4 id="QSet"><a href="#QSet" class="headerlink" title="QSet"></a>QSet</h4><p>QSet 是基于散列表的集合模板类，它存储数据的顺序是不定的，查找值的速度非常快。 QSet<T> 内部就是用 QHash 实现的。 定义 QSet<T> 容器和输入数据的实例代码如下：</p><p>QSet<QString> set; set &lt;&lt; “dog” &lt;&lt; “cat” &lt;&lt; “tiger”;</p><p>测试一个值是否包含于这个集合，用 contains() 函数，示例如下：</p><p>if (!set.contains(“cat”))    …    </p><h4 id="QMap"><a href="#QMap" class="headerlink" title="QMap"></a>QMap</h4><p>QMap&lt;Key, T&gt; 提供一个字典（关联数组)，一个键映射到一个值。QMap 存储数据是按照键的顺序，如果不在乎存储顺序，使用 QHash 会更快。 定义 QMap&lt;QString，int&gt; 类型变量和赋值的示例代码如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QMap&lt;QString, int&gt; map;map[&quot;one&quot;] = 1;map[&quot;two&quot;] = 2;map[&quot;three &quot;] = 3;</span><br></pre></td></tr></table></figure><p>也可以使用 insert() 函数赋值，或 remove() 移除一个键值对，示例如下：</p><p>map.insert(“four”, 4); map.remove(“two”);</p><p>要查找一个值，使用运算符“[]”或 value() 函数，示例如下：</p><p>int num1 &#x3D; map[“one”]; int num2 &#x3D; map.value(“two”);</p><p>如果在映射表中没有找到指定的键，会返回一个缺省构造值，例如，如果值的类型是字符串，会返回一个空的字符串。 在使用 value() 函数查找键值时，还可以指定一个缺省的返回值，示例如下：</p><p>timeout &#x3D; map.value(“TIMEOUT”,30);</p><p>这表示如果在 map 里找到键“TIMEOUT”，就返回关联的值，否则返回值为 30。</p><h4 id="QMultiMap"><a href="#QMultiMap" class="headerlink" title="QMultiMap"></a>QMultiMap</h4><p>QMultiMap 是 QMap 的子类，是用于处理多值映射的便利类。 多值映射就是一个键可以对应多个值。QMap 正常情况下不允许多值映射，除非使用 QMap::insertMulti() 添加键值对。 QMultiMap 是 QMap 的子类，所以 QMap 的大多数函数在 QMultiMap 都是可用的，但是有几个特殊的，QMultiMap::insert() 等效于 QMap::insertMulti() , QMultiMap::replace() 等效于 QMap::insert()。 QMultiMap 使用示例如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QMultiMap&lt;QString, int&gt; map1, map2, map3;map1.insert(&quot;plenty&quot;, 100);mapl.insert(&quot;plenty&quot;, 2000); // map1.size() == 2map2.insert(&quot;plenty&quot;, 5000); // map2.size() == 1map3 = map1 + map2; // map3.size() == 3</span><br></pre></td></tr></table></figure><p>QMultiMap 不提供“[]”操作符，使用 value() 函数访问最新插入的键的单个值。如果要获取一个键对应的所有值，使用 values() 函数，返回值是 QList<T> 类型。</p><figure class="highlight plaintext"><table><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">QList&lt;int&gt; values = map.values(&quot;plenty&quot;);</span><br><span class="line">for (int i = 0; i &lt; values.size(); ++i)</span><br><span class="line">    cout &lt;&lt; values.at(i) &lt;&lt; endl;</span><br></pre></td></tr></table></figure><h4 id="QHash"><a href="#QHash" class="headerlink" title="QHash"></a>QHash</h4><p>QHash 是基于散列表来实现字典功能的模板类，QHash&lt;Key，T&gt; 存储的键值对具有非常快的查找速度。 QHash 与 QMap 的功能和用法相似，区别在于以下几点：</p><ol><li><p>QHash 比 QMap 的查找速度快；</p></li><li><p>在 QMap 上遍历时，数据项是按照键排序的，而 QHash 的数据项是任意顺序的；</p></li><li><p>QMap 的键必须提供“&lt;”运算符，QHash 的键必须提供“&#x3D;&#x3D;”运算符和一个名称为 qHash() 的全局散列函数。</p></li></ol><h4 id="QMultiHash"><a href="#QMultiHash" class="headerlink" title="QMultiHash"></a>QMultiHash</h4><p>QMultiHash 是 QHash 的子类，是用于处理多值映射的便利类，其用法与 QMultiMap 类似。</p><ol start="18"><li>Qt迭代器（Java类型和STL类型）详解</li></ol><p>迭代器为访问容器类里的数据项提供了统一的方法，<a href="http://c.biancheng.net/qt/">Qt</a>有两种迭代器类：<a href="http://c.biancheng.net/java/">Java</a>类型的迭代器和 <a href="http://c.biancheng.net/stl/">STL</a>类型的迭代器。 两者比较，Java 类型的迭代器更易于使用，且提供一些高级功能，而 STL 类型的迭代器效率更高。</p><h2 id="Java-类型迭代器"><a href="#Java-类型迭代器" class="headerlink" title="Java 类型迭代器"></a>Java 类型迭代器</h2><p>对于每个容器类，有两个 Java 类型迭代器：一个用于只读操作，一个用于读写操作，各个Java 类型的容器类见表 1。</p><table><thead><tr><th>容器类</th><th>只读迭代器</th><th>读写迭代器</th></tr></thead><tbody><tr><td>QList<T>, QQueue<T></td><td>QListItcrator<T></td><td>QMutableListItcrator<T></td></tr><tr><td>QLinkedList<T></td><td>QLinkedListIterator<T></td><td>QMutableLinkedListIterator<T></td></tr><tr><td>QVector<T>, QStack<T></td><td>QVectorllcrator<T></td><td>QMutableVectorIterator<T></td></tr><tr><td>QSet<T></td><td>QSetItcrator<T></td><td>QMutableSetItcrator<T></td></tr><tr><td>QMap&lt;Key, T&gt;, QMultiMap&lt;Key, T&gt;</td><td>QMapIterator&lt;Key, T&gt;</td><td>QMutableMapIterator&lt;Key, T&gt;</td></tr><tr><td>QHash&lt;Key, T&gt;, QMultiHash&lt;Key, T&gt;</td><td>QHashIterator&lt;Key, T&gt;</td><td>QMutablcHashlterator&lt;Key, T&gt;</td></tr></tbody></table><p>QMap 和 QHash 等关联容器类的迭代器用法相冋，QList 和 QLinkedList、QSet 等容器类的用法相同，所以下面只以 QMap 和 QList 为例介绍迭代器的用法。</p><h4 id="顺序容器类的迭代器的使用"><a href="#顺序容器类的迭代器的使用" class="headerlink" title="顺序容器类的迭代器的使用"></a>顺序容器类的迭代器的使用</h4><p>Java 类型迭代器的指针不是指向一个数据项，而是在数据项之间，迭代器指针位置示意图如图 2 所示。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/398b8619-7ab1-4399-bfb3-a5180e8e3b12.gif" alt="image">图 2 Java类型迭代器位置示意图</p><p>下面是遍历访问一个 QList<QString> 容器的所有数据项的典型代码：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QList&lt;QString&gt; list;list &lt;&lt; &quot;A&quot; &lt;&lt; &quot;B&quot; &lt;&lt; &quot;C&quot; &lt;&lt; &quot;D&quot;;QListIterator&lt;QString&gt; i (list);while (i.hasNext())    qDebug () &lt;&lt; i.next ();</span><br></pre></td></tr></table></figure><p>QList<QString> 容器对象 list 作为参数传递给 QListIterator<QString> 迭代器 i 的构造函数，i 用于对 list 作只读遍历。起始时刻，迭代器指针在容器第一个数据项的前面（图 2 中数据项“A” 的前面)，调用 hasNext() 判断在迭代器指针后面是否还有数据项，如果有，就调用 next() 跳过一个数据项，并且 next() 函数返回跳过去的那个数据项。 也可以反向遍历，示例代码如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QListIterator&lt;QString&gt; i (list);i.toBack();while (i.hasPrevious())    qDebug() &lt;&lt; i.previous();</span><br></pre></td></tr></table></figure><p>QListItemtor 用于移动指针和读取数据的函数见表 3。</p><table><thead><tr><th>函数名</th><th>功能</th></tr></thead><tbody><tr><td>void toFront()</td><td>迭代器移动到列表的最前面（第一个数据项之前)</td></tr><tr><td>void toBack()</td><td>迭代器移动到列表的最后面（最后一个数据项之后）</td></tr><tr><td>bool hasNext()</td><td>如果迭代器不是位于列表最后位罝，返回true</td></tr><tr><td>const T&amp; next()</td><td>返回下一个数据项，并且迭代器后移一个位置</td></tr><tr><td>const T&amp; peekNext()</td><td>返回下一个数据项，但是不移动迭代器位置</td></tr><tr><td>bool hasPrevious()</td><td>如果迭代器不是位于列表的最前面，返回true</td></tr><tr><td>const T&amp; previous()</td><td>返回前一个数据项，并且迭代器前移一个位置</td></tr><tr><td>const T&amp; peekPrevious()</td><td>返回前一个数椐项，但是不移动迭代器指针</td></tr></tbody></table><p>QListIterator 是只读访问容器内数据项的迭代器，若要在遍历过程中对容器的数据进行修改， 需要使用 QMutableListlterator。例如下面的示例代码为删除容器中数据为奇数的项：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QList&lt;int&gt; list;list &lt;&lt;1&lt;&lt;2&lt;&lt;3&lt;&lt;4&lt;&lt;5;QMutableListIterator&lt;int&gt; i (list);while (i.hasNext()) &#123;    if (i.next() % 2 != 0)        i.remove();&#125;</span><br></pre></td></tr></table></figure><p>remove() 函数移除 next() 函数刚刚跳过的一个数据项，不会使迭代器失效。setValue() 函数可以修改刚刚跳过去的数据项的值。</p><h4 id="关联容器类的迭代器的使用"><a href="#关联容器类的迭代器的使用" class="headerlink" title="关联容器类的迭代器的使用"></a>关联容器类的迭代器的使用</h4><p>对于关联容器类 QMap<Key T>，使用 QMapIterator 和 QMutableMapIterator 迭代器类，它们具有表 3 所示的所有函数，主要是增加了 key() 和 value() 函数用于获取刚刚跳过的数据项的键和值。 例如，下面的代码将删除键（城市名称）里以“City”结尾的数据项：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QMap&lt;QString, QString&gt; map;map.insert(&quot;Paris&quot;, &quot;France&quot;);map.insert(&quot;New York&quot;, &quot;USA&quot;);map.insert(&quot;Mexico City&quot;, &quot;USA&quot;);map.insert(&quot;Moscow&quot;, &quot;Russia&quot;);...QMutableMapIterator&lt;QString, QString&gt; i(map);while (i.hasNext ()) &#123;    if (i.next().key().endsWith(&quot;City&quot;))        i.remove();&#125;</span><br></pre></td></tr></table></figure><p>如果是在多值容器里遍历，可以用 findNext() 或 findPrevious() 查找下一个或上一个值，如下面的代码将删除上一示例代码中 map 里值为“USA”的所有数据项：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QMutableMapIterator&lt;QString, QString&gt; i(map);、while (i.findNext(&quot;USA&quot;))    i.remove();</span><br></pre></td></tr></table></figure><h2 id="STL类型迭代器"><a href="#STL类型迭代器" class="headerlink" title="STL类型迭代器"></a>STL类型迭代器</h2><p>STL 迭代器与 Qt 和 STL 的原生算法兼容，并且进行了速度优化。具体类型见表 4。</p><table><thead><tr><th>容器类</th><th>只读迭代器</th><th>读写迭代器</th></tr></thead><tbody><tr><td>QList<T>, QQueue<T></td><td>QList<T>::const iterator</td><td>QList<T>::iterator</td></tr><tr><td>QLinkedList<T></td><td>Q1. i nked List&lt;1&gt;: :const_iterator</td><td>QLinkedList<T>::iterator</td></tr><tr><td>QVector<T>, QStack<T></td><td>QVector<T>::const_ilerator</td><td>QVector<T>::iterator</td></tr><tr><td>QSet<T></td><td>QSet<T>::const_iterator</td><td>QSet<T>::iterator</td></tr><tr><td>QMap&lt;Key, P&gt; QMultiMap&lt;Kcy, T&gt;</td><td>QMap&lt;Key, T&gt;::const_iterator</td><td>QMap&lt;Key, T&gt;:: iterator</td></tr><tr><td>QHash&lt;Key, T&gt; QMultiHash&lt;Key, T&gt;</td><td>QHash&lt;Key, T&gt;: :const_iterator</td><td>QHash&lt;Key, T&gt;::iterator</td></tr></tbody></table><p>对于每一个容器类，都有两个 STL 类型迭代器：一个用于只读访问，一个用于读写访问。无需修改数据时一定使用只读迭代器，因为它们速度更快。 注意，在定义只读迭代器和读写迭代器时的区别，它们使用了不同的关健字，const_iterator 定义只读迭代器，iterator 定义读写迭代器。此外，还可以使用 const_reverse_iterator 和 reverse_iterator 定义相应的反尚迭代器。 STL 类型的迭代器是数组的指针，所以“++”运算符使迭代器指向下一个数据项，运算符返回数据项内容。与 Java 类型的迭代器不同，STL 迭代器直接指向数据项，STL 迭代器指向位置示意图如图 5 所示。</p><p><img src="https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/collab/32M9qPzB3v9pl015/c486641f-de42-4a34-8a8f-93c7d19b75b3.gif" alt="image">图 5 STL类型迭代器位置示意图</p><p>begin() 函数使迭代器指向容器的第一个数据项，end() 函数使迭代器指向一个虚拟的表示结尾的数据项，end() 表示的数据项是无效的，一般用作循环结束条件。 下面仍然以 QList 和 QMap 为例说明 STL 迭代器的用法，其他容器类迭代器的用法类似。</p><h4 id="顺序容器类的迭代器的用法"><a href="#顺序容器类的迭代器的用法" class="headerlink" title="顺序容器类的迭代器的用法"></a>顺序容器类的迭代器的用法</h4><p>下面的示例代码将 QList<QString> list 里的数据项逐项输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QList&lt;QString&gt; list;list &lt;&lt; &quot;A&quot; &lt;&lt; &quot;B&quot; &lt;&lt; &quot;C&quot; &lt;&lt; &quot;D&quot;;QList&lt;QString&gt;::const_iterator i;for (i = list.constBegin(); i != list.constEnd(); ++i)    qDebug() &lt;&lt; *i;</span><br></pre></td></tr></table></figure><p>constBegin() 和 constEnd() 是用于只读迭代器的，表示起始和结束位置。 若使用反向读写迭代器，并将上面示例代码中 list 的数据项都改为小写，代码如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QList&lt;QString&gt;::reverse_iterator i;for (i = list.rbegin(); i != list.rend(); ++i)    *i = i-&gt;toLower();&#125;</span><br></pre></td></tr></table></figure><h4 id="关联容器类的迭代器的用法"><a href="#关联容器类的迭代器的用法" class="headerlink" title="关联容器类的迭代器的用法"></a>关联容器类的迭代器的用法</h4><p>对于关联容器类 QMap 和 QHash，迭代器的操作符返回数据项的值。如果想返回键，使用 key() 函数。对应的，用 value() 函数返回一个项的值。 例如，下面的代码将 QMap&lt;int，int&gt; map 中所有项的键和值输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QMap&lt;int, int&gt; map;...QMap&lt;int, int&gt;::const_iterator i;for (i = map.constBegin(); i != map.constEnd(); ++i)    qDebug () &lt;&lt; i.key () &lt;&lt; &#x27;:&#x27; &lt;&lt; i.value ();</span><br></pre></td></tr></table></figure><p>Qt API 包含很多返回值为 QList 或 QStringList 的函数，要遍历这些返回的容器，必须先复制。由于 Qt 使用了隐式共享，这样的复制并无多大开销。 例如，下面的代码是正确的：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">const QList&lt;int&gt; sizes = splitter-&gt;sizes();QList&lt;int&gt;::const_iterator i;for (i = sizes.begin (); i != sizes.end(); ++i)    ...</span><br></pre></td></tr></table></figure><p>提示：隐式共享是对象的管理方法。一个对象被隐式共享，只是传递该对象的一个指针给使用者，而不实际复制对象数据，只有在使用者修改数据时，才实质复制共享对象给使用者。如在上面的代码中，splitter-&gt;sizes() 返回的是一个 QList<int>M 表对象 sizes，但是实际上代码并不将 splitter-&gt;sizes() 表示的列表内容完全复制给变量 sizes，只是传递给它一个指针，只有当 sizes 发生数据修改时，才会将共享对象的数据复制给 sizes，这样避免了不必要的复制，减少了资源占用。</p><p>而下面的代码是错误的：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QList&lt;int&gt;::const_iterator i;for (i = splitter-&gt;sizes().begin(); i != splitter-&gt;sizes().end(); ++i)</span><br></pre></td></tr></table></figure><p>对于 STL 类型的迭代器，隐式共享还涉及另外一个问题，即当有一个迭代器在操作一个容器变量时，不要去复制这个容器变量。</p><ol start="19"><li>Qt foreach关键字用法（无师自通）</li></ol><p><a href="http://c.biancheng.net/qt/">Qt</a>提供一个关键字 foreach (实际是 <QtGlobal> 里定义的一个宏）用于方便地访问容器里所有数据项。 foreach 关键字用于遍历容路中所有的项，使用 foreach 的句法是：</p><p>foreach (variable, container)</p><p>使用 foreach 的代码比使用迭代器更简洁。例如，使用 foreach 遍历一个 QLinkedList<QString> 的示例代码如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QLinkedList&lt;QString&gt; list;...QString str;foreach (str, list)    qDebug() &lt;&lt; str;</span><br></pre></td></tr></table></figure><p>用于迭代的变量也可以在 foreach 语句里定义，foreach 语句也可以使用花括号，可以使用 break 退出迭代，示例代码如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QLinkedList&lt;QString&gt; list;...foreach (const QString &amp;str, list) &#123;    if (str.isEmpty())        break;    qDebug() &lt;&lt; str;&#125;</span><br></pre></td></tr></table></figure><p>对于 QMap 和 QHash，foreach 会自动访问“键-值”对里的值，所以无需调用 values()。如果需要访问键则可以调用 keys()，示例代码如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QMap&lt;QString, int&gt; map;...foreach (const QString &amp;str, map.keys())    qDebug() &lt;&lt; str &lt;&lt; &#x27;:&#x27; &lt;&lt; map.value(str);</span><br></pre></td></tr></table></figure><p>对于多值映射，可以使用两重 foreach 语句，示例代码如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">QMultiMap&lt;QString, int&gt; map;...foreach (const QString &amp;str, map.uniqueKeys()) &#123;    foreach (int i, map.values(str))        qDebug() &lt;&lt; str &lt;&lt; &#x27;:&#x27; &lt;&lt; i;&#125;</span><br></pre></td></tr></table></figure><p>注意，foreach 关徤字遍历一个容器变量是创建了容器的一个副本，所以不能修改原来容器变量的数据项。</p><ol start="20"><li><p>编写第一个Qt程序</p></li><li><p>编写第一个Qt程序</p></li><li><p>编写第一个Qt程序</p></li><li><p>编写第一个Qt程序</p></li><li><p>编写第一个Qt程序</p></li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;QT详解&quot;&gt;&lt;a href=&quot;#QT详解&quot; class=&quot;headerlink&quot; title=&quot;QT详解&quot;&gt;&lt;/a&gt;QT详解&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Qt 可以做什么？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Qt 虽然经常被当做一个 GU</summary>
      
    
    
    
    <category term="Qt" scheme="https://cpp-memory-leaks.github.io/categories/Qt/"/>
    
    
    <category term="Qt" scheme="https://cpp-memory-leaks.github.io/tags/Qt/"/>
    
  </entry>
  
  <entry>
    <title>C++代码规范</title>
    <link href="https://cpp-memory-leaks.github.io/2024/08/07/Cpp%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/"/>
    <id>https://cpp-memory-leaks.github.io/2024/08/07/Cpp%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/</id>
    <published>2024-08-07T10:57:16.000Z</published>
    <updated>2025-09-13T11:00:09.843Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-概述"><a href="#1-概述" class="headerlink" title="1.  概述"></a>1.  <strong>概述</strong></h2><p>命名，是计算机史上最难的问题之一，有很多书本都有专门的章节讨论命名规范的问题，鄙人写的总结，是作为一名刚毕业的黄毛小子从刚工作开始积累的，可能没有那些名著写的那么详细，但是贵在真实，我觉得还是有点使用价值的。</p><p>我觉得给代码命名有三要：</p><ol><li>要有区分度</li><li>要有辨识度</li><li>要够详细（最少的字表达最清楚的意思）</li></ol><p><strong>不要造字</strong>，你不是仓颉，英语里面有约定俗成的名称缩写，如info-&gt;infomation。</p><p><strong>不要为了规范而规范。</strong></p><p>目的：<strong>不影响理解、不产生歧义、不增加维护成本足以</strong></p><p>旨在<strong>提高代码的可读性、可维护性</strong>，特此制定本规范。参考《Googe C++ Stye Guide》以及《Effective C++》等规范基础上，结合项目开发经验，汇总整理成本规范。</p><h2 id="2-头文件"><a href="#2-头文件" class="headerlink" title="2.  头文件"></a>2.  <strong>头文件</strong></h2><h3 id="2-1-头文件保护"><a href="#2-1-头文件保护" class="headerlink" title="2.1   头文件保护"></a>2.1   <strong>头文件保护</strong></h3><p>所有头文件都应该使用**#define**防止头文件被重复包含，命名格式为<FieName>_H，例如：</p><figure class="highlight c++"><table><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="meta">#<span class="keyword">ifndef</span> MAINWINDOW_H</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAINWINDOW_H</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">// MAINWINDOW_H</span></span></span><br></pre></td></tr></table></figure><h3 id="2-2-前置声明"><a href="#2-2-前置声明" class="headerlink" title="2.2  前置声明"></a>2.2  <strong>前置声明</strong></h3><p>“前置声明”是类、函数和模板的纯粹声明，没伴随着其定义。</p><p>在头文件中进行前置声明，可以减少**#incude**的数量，避免多重包含，减少头文件展开的次数，有效的提高编译效率。对于库工程使用前置声明，可以减少内部类的导出。</p><p><strong>注意：前置类型的类是不完全类型，只能定义指向该类型的指针或引用，或者声明(但不能定义)以不完全类型作为参数或者返回类型的函数。</strong></p><figure class="highlight c++"><table><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">#incude <span class="string">&lt;QObject&gt;</span></span></span><br><span class="line"><span class="comment">// #incude &lt;QJsonVaue&gt;</span></span><br><span class="line"><span class="comment">// #incude &lt;QJsonObject&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用前置声明形式代替</span></span><br><span class="line">cass QJsonVaue;</span><br><span class="line">cass QJsonObject;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 库工程不希望导出的内部类才使用前置声明</span></span><br><span class="line">cass IDictionaryPrivate;</span><br><span class="line">cass SVSEMSHARE_EXPORT IDictionary : pubic QObject</span><br><span class="line">&#123;</span><br><span class="line">Q_OBJECT</span><br><span class="line"></span><br><span class="line">pubic:</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">write</span><span class="params">(<span class="type">const</span> QString&amp; key, <span class="type">const</span> QJsonVaue&amp; vaue)</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">write</span><span class="params">(<span class="type">const</span> QJsonObject&amp; vaus)</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="2-3-内联函数"><a href="#2-3-内联函数" class="headerlink" title="2.3  内联函数"></a>2.3  <strong>内联函数</strong></h3><p>当函数被声明为内联函数之后，编译器会将其内联展开，而不是按通常的函数调用机制进行调用。</p><p><strong>只要当函数只有10行甚至更少时才将其定义为内联函数</strong>，只有内联的函数体较小，内联该函数才可以令目标代码更加高效。对于存取函数以及其他函数比较短，性能关键的函数，鼓励使用内联。</p><p><strong>注意：不要内联包含循环或switch语句的函数，可能导致增加代码大小。</strong></p><h3 id="2-4-incude的路径及次序"><a href="#2-4-incude的路径及次序" class="headerlink" title="2.4  #incude的路径及次序"></a>2.4  <strong>#incude的路径及次序</strong></h3><p>项目内的头文件按照项目源代码目录树结构排列，头文件包含顺序<strong>项目内头文件</strong>、<strong>其他库头文件</strong>、<strong>Qt库</strong>、<strong>C++库</strong>、C库，通过空行分隔相关头文件。</p><figure class="highlight c++"><table><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="meta">#incude <span class="string">&quot;Base/NameSpace.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 其他库头文件</span></span><br><span class="line"><span class="meta">#incude <span class="string">&quot;foo/bar.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Qt库头文件</span></span><br><span class="line"><span class="meta">#incude <span class="string">&lt;QObject&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// C++库头文件</span></span><br><span class="line"><span class="meta">#incude <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// C库头文件</span></span><br><span class="line"><span class="meta">#incude <span class="string">&lt;unistd.h&gt;</span></span></span><br></pre></td></tr></table></figure><h2 id="3-作用域"><a href="#3-作用域" class="headerlink" title="3. 作用域"></a>3. <strong>作用域</strong></h2><h3 id="3-1-命名空间"><a href="#3-1-命名空间" class="headerlink" title="3.1  命名空间"></a>3.1  <strong>命名空间</strong></h3><p>命名空间将全局作用域细分为独立的、具名的作用域，可以有效防止全局作用域的命名冲突。举例来说，两个不同项目的全局作用域都有一个类Foo，这样在编译或运行时会造成冲突。如果每个项目将代码置于不同命名空间中，project1::Foo和project2::Foo作为不同符号自然不会冲突。命名方式参考<a href="#_%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4%E5%91%BD%E5%90%8D">7.7 命名空间命名</a>，使用方式如下：</p><p>全局定义宏包括了以下，所有项目内开发头文件均包含以下内容：</p><figure class="highlight c++"><table><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="comment">// NameSpace.h</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="meta">#<span class="keyword">define</span> CIQTEK_NAMESPACE ciqtek</span></span><br><span class="line"></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="meta">#<span class="keyword">define</span> BEGIN_NAMESPACE_CIQTEK namespace CIQTEK_NAMESPACE &#123;</span></span><br><span class="line"></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="meta">#<span class="keyword">define</span> END_NAMESPACE_CIQTEK &#125;</span></span><br><span class="line"></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="meta">#<span class="keyword">define</span> CIQTEK_QUAIFIER     ::CIQTEK_NAMESPACE::</span></span><br></pre></td></tr></table></figure><p>根据全局定义宏文件，头文件以及源文件遵循以下规则编写：</p><figure class="highlight c++"><table><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="comment">// .h文件</span></span><br><span class="line">BEGIN_NAMESPACE_CIQTEK</span><br><span class="line"></span><br><span class="line"><span class="comment">// 所有声明(前置声明除外)都置于命名空间中</span></span><br><span class="line">cass MyCass</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Foo</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">END_NAMESPACE_CIQTEK</span><br><span class="line"></span><br><span class="line"><span class="comment">// .cpp文件</span></span><br><span class="line">BEGIN_NAMESPACE_CIQTEK</span><br><span class="line"></span><br><span class="line"><span class="comment">// 函数定义都至于命名空间中</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">MyCass::Foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">BEGIN_NAMESPACE_CIQTEK</span><br></pre></td></tr></table></figure><p><strong>结论：</strong></p><p>不建议使用using指示引入整个命名空间的标识符号，在.cpp和.h文件的函数、方法或者类中，可以使用using声明；</p><figure class="highlight c++"><table><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">// 在.cpp文件中</span></span><br><span class="line"><span class="comment">// .h文件的话,必须在函数,方法或类的内部使用</span></span><br><span class="line"><span class="keyword">using</span> ::foo::bar;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不建议 -- 污染命名空间</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> foo;</span><br></pre></td></tr></table></figure><p><strong>不要在头文件中使用命名空间别名</strong>，因为头文件的别名对包含了该头文件的所有人可见，所以递归包含到其他头文件里；</p><figure class="highlight c++"><table><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="comment">// 使用别名缩短常用的命名空间——可以在.cpp文件</span></span><br><span class="line"><span class="comment">// .h文件的话,必须在函数,方法或类的内部使用,否则污染命名空间</span></span><br><span class="line"><span class="keyword">namespace</span> fbz = ::foo::bar::baz;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在.h文件里</span></span><br><span class="line"><span class="keyword">namespace</span> ibrarian &#123;</span><br><span class="line"><span class="comment">// 以下别名在所有包含了该头文件的文件中生效</span></span><br><span class="line"><span class="keyword">namespace</span> pd_s = ::pipeine_diagnostics::sidetabe;</span><br><span class="line"></span><br><span class="line"><span class="function">inine <span class="type">void</span> <span class="title">my_inine_function</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="comment">// 在函数(方法)使用命名别名,限制在函数中</span></span><br><span class="line"><span class="keyword">namespace</span> fbz = ::foo::bar::baz;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#125; <span class="comment">// namespace ibrarian</span></span><br></pre></td></tr></table></figure><h3 id="3-2-非成员函数、静态成员函数和全局函数"><a href="#3-2-非成员函数、静态成员函数和全局函数" class="headerlink" title="3.2  非成员函数、静态成员函数和全局函数"></a>3.2  <strong>非成员函数、静态成员函数和全局函数</strong></h3><p>使用静态成员函数或命名空间内的非成员函数，尽量不要用裸的全局函数。将一些列函数直接至于命名空间，不要用类的静态函数模拟出命名空间的效果，类的静态方法应该和类的实例或数据紧密相关。</p><figure class="highlight c++"><table><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="comment">// 推荐</span></span><br><span class="line"><span class="keyword">namespace</span> myproject &#123;</span><br><span class="line"><span class="keyword">namespace</span> foo_bar &#123;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Function1</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Function2</span><span class="params">()</span></span>;</span><br><span class="line">&#125;  <span class="comment">// namespace foo_bar</span></span><br><span class="line">&#125;  <span class="comment">// namespace myproject</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 不推荐,类的内部并没有静态变量,类的实体也没具体功能</span></span><br><span class="line"><span class="keyword">namespace</span> myproject &#123;</span><br><span class="line">cass FooBar </span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line"><span class="function"><span class="type">static</span> <span class="type">void</span> <span class="title">Function1</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">static</span> <span class="type">void</span> <span class="title">Function2</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line">&#125;  <span class="comment">// namespace myproject</span></span><br></pre></td></tr></table></figure><h3 id="3-3-局部变量"><a href="#3-3-局部变量" class="headerlink" title="3.3  局部变量"></a>3.3  <strong>局部变量</strong></h3><p>尽量将函数变量<strong>尽可能置于最小作用域内，并在变量声明时进行初始化</strong>。</p><figure class="highlight c++"><table><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="type">int</span> i;</span><br><span class="line">i = <span class="built_in">f</span>(); <span class="comment">// 不推荐——初始化和声明分离</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> j = <span class="built_in">g</span>(); <span class="comment">// 推荐——初始化时声明</span></span><br><span class="line"></span><br><span class="line">vector&lt;<span class="type">int</span>&gt; v;</span><br><span class="line">v.<span class="built_in">push_back</span>(<span class="number">1</span>); <span class="comment">// 不推荐——用花括号初始化更好</span></span><br><span class="line">v.<span class="built_in">push_back</span>(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">vector&lt;<span class="type">int</span>&gt; v = &#123;<span class="number">1</span>, <span class="number">2</span>&#125;; <span class="comment">// 推荐——v 一开始就初始化</span></span><br></pre></td></tr></table></figure><p>属于whie和for语句的变量尽量在这些语句中正常地声明，这样变量的作用域就被限制在这些语句中了，如：</p><figure class="highlight c++"><table><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="built_in">whie</span> (<span class="type">const</span> <span class="type">char</span>* p = <span class="built_in">strchr</span>(str, <span class="string">&#x27;/&#x27;</span>)) &#123;</span><br><span class="line">str = p + <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i) &#123;</span><br><span class="line">……</span><br><span class="line">&#125;</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="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000000</span>; ++i) &#123;</span><br><span class="line">    Foo f;                  <span class="comment">// 构造函数和析构函数分别调用 1000000 次!</span></span><br><span class="line">    f.<span class="built_in">DoSomething</span>(i);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 应该作用域外声明</span></span><br><span class="line">Foo f;                      <span class="comment">// 构造函数和析构函数只调用 1 次</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000000</span>; ++i) &#123;</span><br><span class="line">    f.<span class="built_in">DoSomething</span>(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="4-类"><a href="#4-类" class="headerlink" title="4. 类"></a>4. <strong>类</strong></h2><h3 id="4-1-构造函数的职责"><a href="#4-1-构造函数的职责" class="headerlink" title="4.1  构造函数的职责"></a>4.1  <strong>构造函数的职责</strong></h3><p><strong>构造函数不得调用虚函数</strong>，如果在构造函数内调用了自身的虚函数，这类调用是不会重定向到子类的虚函数实现的，即当前没有子类化实现，存在隐患。<strong>构造函数不能报告一个非致命错误</strong>，即<strong>构造函数必须成功</strong>，不然会获得一个初始化失败的对象，有可能进入不正常的状态。</p><p>如果对象需要进行初始化，考虑使用明确的<strong>init()<strong>方法或使用</strong>工厂模式</strong>。</p><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line">cass Foo </span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="function">boo <span class="title">init</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// init方法形式, 需要在类的注释里说明用法</span></span><br><span class="line">&#123;</span><br><span class="line">    Foo *foo = <span class="keyword">new</span> <span class="built_in">Foo</span>();</span><br><span class="line">    <span class="keyword">if</span> (!foo-&gt;<span class="built_in">init</span>()) &#123;</span><br><span class="line">        deete foo;</span><br><span class="line">        foo = nuptr;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不推荐,容易误用 — 工厂方法</span></span><br><span class="line">cass Foo </span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="function"><span class="type">static</span> Foo *<span class="title">create</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        Foo *foo = <span class="keyword">new</span> <span class="built_in">Foo</span>();</span><br><span class="line">        <span class="keyword">if</span> (!foo-&gt;<span class="built_in">init</span>()) &#123;</span><br><span class="line">            deete foo;</span><br><span class="line">            foo = nuptr;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> foo;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="built_in">Foo</span>() &#123;&#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">init</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">&#123;</span><br><span class="line">    Foo *foo = Foo::<span class="built_in">create</span>();</span><br><span class="line">    <span class="keyword">if</span>(nuptr != foo) &#123; <span class="comment">// 功能逻辑</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-2-初始化"><a href="#4-2-初始化" class="headerlink" title="4.2  初始化"></a>4.2  <strong>初始化</strong></h3><p>如果类中定义了成员变量，则必须在类中为每个类提供初始化函数或定义一个构造函数。若未声明构造函数，则编译器会生成一个默认的构造函数，这有可能导致某些成员未被初始化或初始化未不恰当的值。</p><p>所以，<strong>确保构造函数将对象的每一个成员变量进行了初始化，且初始化顺序和声明顺序保持一致。</strong></p><figure class="highlight c++"><table><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">cass PhoneNumber &#123; ... &#125;;</span><br><span class="line">cass ABEntry </span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="built_in">ABEntry</span>(<span class="type">const</span> std::string &amp;name, <span class="type">const</span> std::string &amp;address,</span><br><span class="line">            <span class="type">const</span> std::ist&lt;PhoneNumber&gt;&amp; phones);</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    std::string theName;</span><br><span class="line">    std::string theAddress;</span><br><span class="line">    std::ist&lt;PhoneNumber&gt; thePhones;</span><br><span class="line">    <span class="type">int</span> numTimesConsuted;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不推荐写法 —— 会导致多调用赋值操作函数降低效率</span></span><br><span class="line">ABEntry::<span class="built_in">ABEntry</span>(<span class="type">const</span> std::string &amp;name, <span class="type">const</span> std::string &amp;address,</span><br><span class="line">                 <span class="type">const</span> std::ist&lt;PhoneNumber&gt;&amp; phones)</span><br><span class="line">&#123;</span><br><span class="line">    theName = name;             <span class="comment">// 这些是赋值操作</span></span><br><span class="line">    theAddress = address;       <span class="comment">// 而非初始化</span></span><br><span class="line">    thePhones = phones;</span><br><span class="line">    numTimesConsuted = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 推荐写法 —— 只调用拷贝构造函数</span></span><br><span class="line">ABEntry::<span class="built_in">ABEntry</span>(<span class="type">const</span> std::string &amp;name, <span class="type">const</span> std::string &amp;address,</span><br><span class="line">                 <span class="type">const</span> std::ist&lt;PhoneNumber&gt;&amp; phones)</span><br><span class="line">    : <span class="built_in">theName</span>(name),            <span class="comment">// 这些是初始化操作</span></span><br><span class="line">      <span class="built_in">theAddress</span>(address),</span><br><span class="line">      <span class="built_in">thePhones</span>(phones),</span><br><span class="line">      <span class="built_in">numTimesConsuted</span>(<span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>或者在类的成员变量声明时进行初始化。</p><figure class="highlight c++"><table><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">cass PhoneNumber &#123; ... &#125;;</span><br><span class="line">cass ABEntry </span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="comment">// 不需要对变量进行初始化</span></span><br><span class="line">    <span class="built_in">ABEntry</span>() &#123;&#125;</span><br><span class="line">    <span class="built_in">ABEntry</span>(<span class="type">const</span> std::string &amp;name, <span class="type">const</span> std::string &amp;address,</span><br><span class="line">            <span class="type">const</span> std::ist&lt;PhoneNumber&gt;&amp; phones) </span><br><span class="line">    : <span class="built_in">theName</span>(name), <span class="built_in">theAddress</span>(address),      <span class="comment">// 重复操作,会覆盖初值</span></span><br><span class="line">      <span class="built_in">thePhones</span>(phones), <span class="built_in">numTimesConsuted</span>(<span class="number">0</span>) &#123;&#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 在声明时就初始化成员变量</span></span><br><span class="line">    std::string theName&#123;<span class="string">&quot;&quot;</span>&#125;;</span><br><span class="line">    std::string theAddress&#123;<span class="string">&quot;&quot;</span>&#125;;</span><br><span class="line">    std::ist&lt;PhoneNumber&gt; thePhones&#123;&#125;;</span><br><span class="line">    <span class="type">int</span> numTimesConsuted = <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="4-3-显式构造函数"><a href="#4-3-显式构造函数" class="headerlink" title="4.3  显式构造函数"></a>4.3  <strong>显式构造函数</strong></h3><p>对于单个参数的构造函数，不要定义为隐式类型转换，使用C++关键字<strong>expicit</strong>。</p><p><strong>隐式类型转换：</strong>即允许某种类型(称作 源类型)的对象被用于需要另一种类型(称作 目的类型)。例如将一个int类型的参数传递给需要doube类型的函数。通常只有一个参数的构造函数，被看作是一种隐式转换。</p><p>除了单参数构造函数外，也适用于除第一个参数以外的其他参数都具有默认参数的构造函数，例如Foo::Foo(string name, int id &#x3D; 42)。<strong>拷贝和移动构造函数</strong>不需要被标记为expicit，因为它们并不进行类型转换。</p><figure class="highlight c++"><table><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">cass Foo </span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="comment">// 以下都需要进行expicit进行显示转换</span></span><br><span class="line">    <span class="function">expicit <span class="title">Foo</span><span class="params">(<span class="type">const</span> <span class="type">int</span> a)</span></span>;</span><br><span class="line">    <span class="function">expicit <span class="title">Foo</span><span class="params">(<span class="type">const</span> <span class="type">char</span> a, <span class="type">const</span> <span class="type">int</span> b = <span class="number">0</span>)</span></span>;</span><br><span class="line">    <span class="comment">// 不需要使用expicit</span></span><br><span class="line"><span class="built_in">Foo</span>(<span class="type">const</span> <span class="type">int</span> a, <span class="type">const</span> foat b);</span><br><span class="line">    <span class="built_in">Foo</span>(<span class="type">const</span> Foo&amp;);        <span class="comment">// 拷贝构造</span></span><br><span class="line">    <span class="built_in">Foo</span>(Foo&amp;&amp;);             <span class="comment">// 移动构造</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>当设计目的用于其他类型进行透明封装的类来说，隐式类型转换是合适的，例如QJsonVaue和QVariant。</p><h3 id="4-4-结构体-VS类"><a href="#4-4-结构体-VS类" class="headerlink" title="4.4  结构体 VS类"></a>4.4  结构体 VS类</h3><p>在C++中struct和cass关键词几乎含义一样。对两个关键字进行进一步规定是，<strong>struct用来定义包含数据的被动式对象，也可以包含相关的常量</strong>。但除了存取数据成员以外，没有别的操作函数功能。并且<strong>存取功能通过直接访问位域，而非函数调用</strong>。除了构<strong>造函数、析构函数、initiaize初始化数据、reset重置数据、operator&#x3D;&#x3D;数据对比操作符重载等</strong>类似用于设定数据成员的函数外，不能提供其他功能函数<strong>。</strong>如果需要更多函数功能，使用cass代替struct。</p><h3 id="4-5-接口"><a href="#4-5-接口" class="headerlink" title="4.5  接口"></a>4.5  <strong>接口</strong></h3><p>当一个类满足以下要求时，称之为纯接口。类应以I为开头命名，如IFoo。</p><ul><li>只有纯虚函数(“&#x3D;0”)和静态函数(析构函数除外)；</li><li>没有定义任何构造函数。如果有，也不能带有参数，并且必须为<strong>protected</strong>；</li><li>如果它是一个子类，也只能从满足上述条件的类继承。</li></ul><figure class="highlight c++"><table><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">cass Foo</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 错误 —— 基类非接口类</span></span><br><span class="line">cass IBar : pubic Foo</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="function">virtua <span class="type">void</span> <span class="title">bar</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 只有纯虚函数 </span></span><br><span class="line">cass IProcessCommunicationCaback</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="function">virtua <span class="type">void</span> <span class="title">onConnected</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line">    <span class="function">virtua <span class="type">void</span> <span class="title">onDisconnected</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 继承于接口类才能定义为接口类</span></span><br><span class="line">cass IBar : pubic IProcessCommunicationCaback</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="function">virtua <span class="type">void</span> <span class="title">bar</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>以I为前缀可以提醒该类为纯接口类，这一点对于<strong>多重继承</strong>尤其重要。</p><p>​    由于接口类不能被直接实例化，为确保接口类的所有实现可被正确销毁，必须为之声明<strong>虚析构函数</strong>。</p><h3 id="4-6-继承"><a href="#4-6-继承" class="headerlink" title="4.6  继承"></a>4.6  继承</h3><p>当子类继承基类时，子类包含了父基类所有数据及操作的定义。继承主要用于两种场景：实现继承，子类继承父类的实现代码；接口继承，子类仅继承父类的接口名称。</p><p><strong>尽量使用pubic继承，不要使用private继承，而应该替换成把类的实例作为成员对象的方式</strong>。避免过度使用继承，要尽量做到只在“是一种”的情况下使用继承，例如QFrame是“是一种”控件，QFrame继承于QWidget。</p><figure class="highlight c++"><table><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">cass Foo </span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">cass Bar : <span class="keyword">private</span> Foo <span class="comment">// 禁止</span></span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">bar</span><span class="params">()</span> </span>&#123; <span class="built_in">foo</span>(); &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">cass Bar</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">bar</span><span class="params">()</span> </span>&#123; f.<span class="built_in">foo</span>(); &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    Foo f&#123;&#125;;    <span class="comment">// 应该以类的成员变量形式</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>如果类确定存在继承关系，作为基类应该将<strong>析构函数</strong>声明为virtua。相反，当类的设计目的不是作为基类，或不具有多态性，就不将析构函数声明为<strong>virtua</strong>。而当类中存在虚函数，则析构函数也应该声明为<strong>virtua</strong>。</p><p><strong>注意：析构函数与构造函数相同，也不应该调用虚函数或者发生错误。</strong></p><figure class="highlight c++"><table><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">// 作为基类,必须将析构函数声明为virtua</span></span><br><span class="line">cass Base</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    virtua ~<span class="built_in">Base</span>();</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">cass Derived : pubic Base</span><br><span class="line">&#123;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类中存在virtua函数,必须将析构函数声明为virtua</span></span><br><span class="line">cass Foo</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    virtua ~<span class="built_in">Foo</span>();       <span class="comment">// 定义虚函数foo,析构函数声明为virtua</span></span><br><span class="line">    <span class="function">virtua <span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span>;   </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="4-7-多重继承"><a href="#4-7-多重继承" class="headerlink" title="4.7  多重继承"></a>4.7  多重继承</h3><p>只有以下情况允许多重继承：<strong>只有一个基类是非抽象类，其他基类都是以I为前缀的纯接口类。</strong>继承顺序应从非抽象类，再到纯接口类的顺序进行继承。</p><figure class="highlight c++"><table><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">cass Foo</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">cass Bar</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">bar</span><span class="params">()</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">cass IBar</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="function">virtua <span class="type">void</span> <span class="title">bar</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 错误 —— Foo和Bar都是非抽象类</span></span><br><span class="line">cass Baz : pubic Foo, pubic Bar</span><br><span class="line">&#123;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确 —— Foo为非抽象类,IBar为纯接口类</span></span><br><span class="line">cass Baz : pubic Foo, pubic IBar</span><br><span class="line">&#123;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="4-8-声明顺序"><a href="#4-8-声明顺序" class="headerlink" title="4.8  声明顺序"></a>4.8  声明顺序</h3><p>类的访问控制区段的声明为：pubic:、protected:、private:。如果某区段没有内容可以不声明，注释方式参考<a href="#_%E6%B3%A8%E9%87%8A">8. 注释</a>。每个区段内的声明通常按以下顺序：</p><ul><li>typedefs和枚举</li><li>常量</li><li>构造函数</li><li>析构函数</li><li>成员函数，包含静态成员函数</li><li>槽函数(公有继承后跟信号)</li><li>数据成员，包含静态数据成员</li></ul><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line">cass Foo : pubic QObject</span><br><span class="line">&#123;</span><br><span class="line">    <span class="function">Q_OBJECT</span></span><br><span class="line"><span class="function">    <span class="title">Q_PROPERTY</span><span class="params">(<span class="type">int</span> m_foo READ foo)</span></span></span><br><span class="line"><span class="function">    </span></span><br><span class="line"><span class="function">pubic:</span></span><br><span class="line"><span class="function">    // 公有成员</span></span><br><span class="line"><span class="function">    Foo();</span></span><br><span class="line">    ~<span class="built_in">Foo</span>();</span><br><span class="line">    <span class="comment">// 成员函数，包含静态成员函数</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">foo</span><span class="params">()</span> <span class="type">const</span></span>;</span><br><span class="line"><span class="function"><span class="type">static</span> <span class="type">void</span> <span class="title">bar</span><span class="params">()</span></span>;</span><br><span class="line">    </span><br><span class="line">pubic Q_SOTS:</span><br><span class="line">    <span class="comment">// 公有槽函数</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">sotFoo</span><span class="params">()</span></span>;</span><br><span class="line">    </span><br><span class="line">Q_SIGNAS:</span><br><span class="line">    <span class="comment">// 信号</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">signaFoo</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">    <span class="comment">// 保护成员</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">bar</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">protected</span> Q_SOTS:</span><br><span class="line">    <span class="comment">// 保护槽函数</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">sotBar</span><span class="params">()</span></span>;</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="type">void</span> <span class="title">baz</span><span class="params">()</span></span>;</span><br><span class="line">    </span><br><span class="line"><span class="keyword">private</span> Q_SOTS:</span><br><span class="line">    <span class="comment">// 私有槽函数</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">sotBar</span><span class="params">()</span></span>;</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="type">int</span> m_foo;</span><br><span class="line">    <span class="keyword">friend</span> cass faz;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="4-9-存取控制"><a href="#4-9-存取控制" class="headerlink" title="4.9  存取控制"></a>4.9  <strong>存取控制</strong></h3><p>将<strong>所有数据成员</strong>声明为<strong>private</strong>，并根据需要提供响应的存取函数。静态常量数据成员可以不是私有成员。</p><p>存取函数一般在头文件中定义为内联函数。</p><figure class="highlight c++"><table><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">cass Foo</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="function">inine <span class="type">int</span> <span class="title">getBar</span><span class="params">()</span> <span class="type">const</span></span>;</span><br><span class="line">    <span class="function">inine <span class="type">void</span> <span class="title">setBar</span><span class="params">(<span class="type">const</span> <span class="type">int</span> vaue)</span></span>;</span><br><span class="line">    </span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="type">int</span> m_bar;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="5-函数"><a href="#5-函数" class="headerlink" title="5. 函数"></a>5. <strong>函数</strong></h2><p>函数命名规则参考<a href="#_%E5%87%BD%E6%95%B0%E5%91%BD%E5%90%8D">7.6函数命名</a>。</p><h3 id="5-1-参数"><a href="#5-1-参数" class="headerlink" title="5.1  参数"></a>5.1  <strong>参数</strong></h3><p>函数的参数顺序为：输入参数在前，输出参数包含输入输出参数在后</p><p>C&#x2F;C++中的函数参数可能是输入参数，也可能是输出参数，或者是输入输出参数。输入参数通常是<strong>const值传递</strong>或者const引用或指针*，输出参数或输入输出参数则为<strong>非const指针或引用</strong>。更多说明参考<a href="#_const%E7%94%A8%E6%B3%95">6.4 const用法</a>。</p><p>在加入新参数时不要因为它们时新参数就置于参数列表最后，而是仍然要按照输入参数在前，输入参数在后的原则。</p><figure class="highlight c++"><table><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">cass Foo</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line">    <span class="comment">// 内置类型输入参数或者ST的迭代器,用const值传递</span></span><br><span class="line">    <span class="comment">// baz作为输出参数用非const指针形式传入</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">(<span class="type">const</span> <span class="type">int</span> bar, foat *baz)</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">(<span class="type">const</span> <span class="type">int</span> *bar, foat *baz)</span></span>;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">(<span class="type">const</span> std::vector::iterator iter, foat *baz)</span></span>;</span><br><span class="line">    <span class="comment">// 当输入参数为对象,则用const引用传递</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">(<span class="type">const</span> std::string&amp; bar, foat *baz)</span></span>;<span class="comment">// 推荐—直观看出输入/输出参数</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">(<span class="type">const</span> std::string&amp; bar, foat &amp;baz)</span></span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><strong>注意：这一条不是硬性规定，属于推荐写法，实际情况复杂时，可以进行更改。</strong></p><h3 id="5-2-编写简短函数"><a href="#5-2-编写简短函数" class="headerlink" title="5.2  编写简短函数"></a>5.2  <strong>编写简短函数</strong></h3><p>函数的编写尽量简练。目的是使函数实现的逻辑结构化、清晰化，便于阅读与维护。</p><p>如果函数行数太多(超过40行，后成为长函数)，可以考虑将长函数拆分成几个短函数，是函数尽量简短，便于阅读和维护。</p><p><strong>注意：长函数按照具体情况而定，例如对于部分if和switch逻辑，分支过长就不遵行本条规定。</strong></p><h2 id="6-其他C-特性"><a href="#6-其他C-特性" class="headerlink" title="6.  其他C++特性"></a>6.  <strong>其他C++特性</strong></h2><h3 id="6-1-异常"><a href="#6-1-异常" class="headerlink" title="6.1  异常"></a>6.1  <strong>异常</strong></h3><p><strong>禁止C++异常机制</strong>，所有错误都应该通过错误值在函数之间传递并做出相应判断，而不应该通过异常进行错误处理。<strong>例外：在接管C++语言本身抛出的异常(例如new失败、ST)、第三方库(例如Qt)抛出的异常时，可以使用异常机制</strong>。</p><p>面对异常首先考虑是否为操作错误，例如参数范围问题，应该在代码进行入参检测；例如Qt库中出现私有类指针为野指针，应该从析构问题解决；这些问题都不应该从接异常解决。</p><figure class="highlight c++"><table><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="type">int</span> en = ...;</span><br><span class="line"><span class="type">char</span> *p = nuptr;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    p = <span class="keyword">new</span> <span class="type">char</span>[en];</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">catch</span> (bad_aoc) &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="built_in">abort</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-2-类型转换"><a href="#6-2-类型转换" class="headerlink" title="6.2 类型转换"></a>6.2 <strong>类型转换</strong></h3><p>使用C++的类型转换，如static_cast&lt;&gt;()。而不是使用int y &#x3D; (int)x或int y &#x3D; int(x)等转换形式。即<strong>不要使用C风格进行类型转换，而应该使用C++风格</strong>。</p><ul><li>用static_cast替代C风格的值转换，或某个类的指针需要明确向上转换为父类指针；</li><li>用const_cast去掉const限定符；</li><li>用reinterpret_cast指针类型和整型和其他类型指针进行转换；</li><li>用dynamic_cast转换存在继承关系的对象。</li></ul><h3 id="6-3-前置自增和自减"><a href="#6-3-前置自增和自减" class="headerlink" title="6.3  前置自增和自减"></a>6.3  <strong>前置自增和自减</strong></h3><p>**对于迭代器和其他模板类型使用前缀形式(++i)*的自增、自减运算符，对于简单数值(非对象)，两种都无所谓**。在不考虑返回值的情况，前置自增(++i)通常要比后置自增(i++)效率更高。因为后置自增(或自减)需要对表达式的值i进行一次拷贝。如果i是迭代器或其他非数值类型，拷贝的代价比较大。所以推荐使用前置自增。</p><figure class="highlight c++"><table><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">std::vector vec;</span><br><span class="line"><span class="keyword">for</span>(std::vector::iterator iter = vec.<span class="built_in">begin</span>();</span><br><span class="line">    iter != vec.<span class="built_in">end</span>(); ++iter); <span class="comment">// 推荐 —— 效率更高</span></span><br><span class="line"><span class="keyword">for</span>(std::vector::iterator iter = vec.<span class="built_in">begin</span>();</span><br><span class="line">    iter != vec.<span class="built_in">end</span>(); iter++); <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="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; i++); </span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i); <span class="comment">// 更推荐,保证习惯一致</span></span><br></pre></td></tr></table></figure><h3 id="6-4-const用法"><a href="#6-4-const用法" class="headerlink" title="6.4  const用法"></a>6.4  <strong>const用法</strong></h3><p>const变量、数据成员、函数和参数为编译时进行类型检测增加了一道屏障，便于尽早发现问题，参考<a href="#_%E5%8F%82%E6%95%B0">5.1 参数</a>。因此尽可能的情况下使用const，参考：</p><figure class="highlight c++"><table><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">cass Bar&#123;&#125;;</span><br><span class="line">cass Foo</span><br><span class="line">&#123;</span><br><span class="line">pubic:</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">getBar</span><span class="params">()</span> <span class="type">const</span> </span>&#123; <span class="keyword">return</span> m_bar; &#125; <span class="comment">// 访问函数</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setBar</span><span class="params">(<span class="type">const</span> <span class="type">int</span> b)</span></span>; &#123; m_bar = b &#125;; <span class="comment">// 函数传入不会修改的参数</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">sum</span><span class="params">()</span> <span class="type">const</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">getBar</span>() + m_CONST_VAUE; &#125; <span class="comment">// 未调用非const函数</span></span><br><span class="line"><span class="function"><span class="type">const</span> Bar&amp; <span class="title">getCBar</span><span class="params">()</span> <span class="type">const</span> </span>&#123; <span class="keyword">return</span> m_cBar; &#125; <span class="comment">// 返回const引用</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"><span class="type">int</span> m_bar;</span><br><span class="line">Bar m_cBar;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> m_CONST_VAUE = <span class="number">10</span>; <span class="comment">// 构造之后不会修改的值</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><ul><li>如果函数不会修改传入的引用或指针类型参数，该参数应声明为const；</li><li>尽可能将函数声明为const。访问函数应该总是const。其他不会修改任何数据成员，未调用非const函数，返回数据成员为指针或引用也应该声明成const；</li><li>如果数据成员在对象构造之后不再发生变化，可将其定义未const。</li></ul><h3 id="6-5-预处理宏"><a href="#6-5-预处理宏" class="headerlink" title="6.5   预处理宏"></a>6.5   <strong>预处理宏</strong></h3><p>宏意味着你和编译器看到的代码时不同的，这可能会导致异常行为，而且宏具有全局作用域。尽量<strong>以内联函数、枚举和常量代替宏定义</strong>。命名规则参考<a href="#_%E5%B8%B8%E9%87%8F%E5%91%BD%E5%90%8D">7.5 常量命名</a>。</p><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// const替换#define</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ASPECT_RATIO 1.653</span></span><br><span class="line"><span class="type">const</span> doube ASPECT_RATIO = <span class="number">1.653</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> AUTHOR_NAME <span class="string">&quot;Scott Meyers&quot;</span></span></span><br><span class="line"><span class="function"><span class="type">const</span> std::string <span class="title">AUTHOR_NAME</span><span class="params">(<span class="string">&quot;Scott Meyers&quot;</span>)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// inine替换#define</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CA_WITH_MAX(a,b) f((a) &gt; (b) ? (a) : (b))</span></span><br><span class="line"><span class="type">int</span> a = <span class="number">5</span>, b = <span class="number">0</span>;</span><br><span class="line"><span class="built_in">CA_WITH_MAX</span>(++a, b);       <span class="comment">// a被累加两次</span></span><br><span class="line"><span class="built_in">CA_WITH_MAX</span>(++a, b + <span class="number">10</span>);  <span class="comment">// a被累加一次</span></span><br><span class="line"><span class="function">tempate&lt;<span class="keyword">typename</span> T&gt;</span></span><br><span class="line"><span class="function">inine <span class="type">void</span> <span class="title">caWithMax</span><span class="params">(<span class="type">const</span> T&amp; a, <span class="type">const</span> T&amp; B)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">f</span>(a &gt; b ? a : b);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// enum替换#define</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> NUM_TURNS 5</span></span><br><span class="line">cass CostEstimate </span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="type">int</span> scores[NUM_TURNS];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">cass CostEstimate </span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">enum</span> &#123; NumTurns = <span class="number">5</span> &#125;;</span><br><span class="line">    <span class="type">int</span> scores[NumTurns];</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="6-6-sieof"><a href="#6-6-sieof" class="headerlink" title="6.6  sieof"></a>6.6  <strong>sieof</strong></h3><p>尽可能用sizeof(varname)代替sizeof(type)。使用sizeof(varname)时因为当代码中变量类型改变时会自动更新。当用sizeof(type)处理不涉及任何变量的代码，比如处理来自外部或内部的数据格式时，sizeof(varname)就不适用了。</p><figure class="highlight c++"><table><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">Struct data;</span><br><span class="line"><span class="built_in">memset</span>(&amp;data, <span class="number">0</span>, <span class="built_in">sizeof</span>(data));     <span class="comment">// 推荐 —— 当data改成其他类型时不影响运行</span></span><br><span class="line"><span class="built_in">memset</span>(&amp;data, <span class="number">0</span>, <span class="built_in">sizeof</span>(Struct));   <span class="comment">// 不推荐 —— 改为其他类型未做修改可能导致BUG</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 不适用sizeof(varname)情况</span></span><br><span class="line"><span class="keyword">if</span> (raw_size &lt; <span class="built_in">sizeof</span>(<span class="type">int</span>)) &#123;</span><br><span class="line">    <span class="built_in">OG</span>(ERROR) &lt;&lt; <span class="string">&quot;compressed record not big enough for count:&quot;</span> &lt;&lt; raw_size;</span><br><span class="line">    <span class="keyword">return</span> fase;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-7-auto"><a href="#6-7-auto" class="headerlink" title="6.7  auto"></a>6.7  <strong>auto</strong></h3><p>C++11中，若变量被声明为auto，类型就会被自动匹配成初始化表达式的类型。用auto绕过繁琐的类型名，只要可读性好就可以使用，但不要用在局部变量之外的地方。<strong>auto不要用在初始化列表</strong>，会导致歧义，同时要注意区分*<em>auto</em>和const* *<strong>auto</strong>&amp;*。</p><figure class="highlight c++"><table><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">sparse_hash_map&lt;string, <span class="type">int</span>&gt;::iterator iter = m.<span class="built_in">find</span>(va);</span><br><span class="line"><span class="comment">// 修改为auto显得直观(因为iter一般是迭代器变量,即m的迭代器)</span></span><br><span class="line"><span class="keyword">auto</span> iter = m.<span class="built_in">find</span>(va);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 没有auto的话，我们不得不在同一个表达式出现两次类型名</span></span><br><span class="line">diagnostics::ErrorStatus* status = <span class="keyword">new</span> diagnostics::<span class="built_in">ErrorStatus</span>(<span class="string">&quot;xyz&quot;</span>);</span><br><span class="line"><span class="comment">// 使用auto就简单且不会有歧义</span></span><br><span class="line"><span class="keyword">auto</span> status = <span class="keyword">new</span> diagnostics::<span class="built_in">ErrorStatus</span>(<span class="string">&quot;xyz&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> i = x.<span class="built_in">ookup</span>(key); <span class="comment">// 不推荐 —— 代码不能直观看出i是何种类型</span></span><br><span class="line">vector&lt;string&gt; v;</span><br><span class="line"><span class="keyword">auto</span> s1 = v[<span class="number">0</span>]; <span class="comment">// 创建一份v[0]的拷贝</span></span><br><span class="line"><span class="type">const</span> <span class="keyword">auto</span>&amp; s2 = v[<span class="number">0</span>]; <span class="comment">// s2是v[0]的一个引用</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// auto不要用在初始化列表</span></span><br><span class="line"><span class="keyword">auto</span> d = &#123;<span class="number">1.23</span>&#125;;</span><br><span class="line"><span class="keyword">auto</span> d&#123;<span class="number">1.23</span>&#125;;           <span class="comment">// d是std::initiazer_ist&lt;doube&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> d = doube&#123;<span class="number">1.23</span>&#125;;  <span class="comment">// d是doube,并非std::initiazer_ist</span></span><br></pre></td></tr></table></figure><h3 id="6-8-lambda表达式"><a href="#6-8-lambda表达式" class="headerlink" title="6.8  lambda表达式"></a>6.8  <strong>lambda表达式</strong></h3><p>ambda表达式是创建匿名函数对象的一种简易途径，常用于把函数当参数传递，例如：</p><figure class="highlight c++"><table><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">std::<span class="built_in">sort</span>(v.<span class="built_in">begin</span>(), v.<span class="built_in">end</span>(), [](<span class="type">int</span> x, <span class="type">int</span> y) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Weight</span>(x) &lt; <span class="built_in">Weight</span>(y);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>当ambda变量需要捕获识，<strong>禁止使用通用捕获，将所有的捕获都显式写出来</strong>，增加可读性。使用引用捕获时，变量名和&amp;之间不留空格</p><figure class="highlight c++"><table><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="type">int</span> n = <span class="number">0</span>;</span><br><span class="line">[=](<span class="type">int</span> x) &#123; <span class="keyword">return</span> x + n; &#125;    <span class="comment">// 不推荐—使用默认捕获</span></span><br><span class="line">[n](<span class="type">int</span> x) &#123; <span class="keyword">return</span> x + n; &#125;    <span class="comment">// 推荐—显式捕获n写出来</span></span><br><span class="line">[&amp;n](<span class="type">int</span> x) &#123; <span class="keyword">return</span> x + n; &#125;    <span class="comment">// &amp;和n不留空格</span></span><br></pre></td></tr></table></figure><p>ambda表达式用于参数传递时，如果函数体超过五行，应当将ambda表达式转换为std::function对象；如果是作为connect的槽函数，则改用函数的形式。</p><h2 id="7-命名约定"><a href="#7-命名约定" class="headerlink" title="7.  命名约定"></a>7.  <strong>命名约定</strong></h2><p>命名的风格能让我们在不需要去查找类型声明的条件下快速了解某个名字代表的含义：<strong>类型、变量、函数、常量、宏、信号、槽函数</strong>等等。</p><h3 id="7-1-通用命名约定"><a href="#7-1-通用命名约定" class="headerlink" title="7.1  通用命名约定"></a>7.1  <strong>通用命名约定</strong></h3><p>名称由<strong>字母、数字以及下划线组合而成，且第一位不能为数字，小驼峰命名方式</strong>。</p><p>尽量使用描述性的命名，少用缩写(除了一些广泛接受的缩写，例如iter表示迭代器、用T表示模板参数)。</p><figure class="highlight c++"><table><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="type">int</span> priceCountReader;       <span class="comment">// 无缩写</span></span><br><span class="line"><span class="type">int</span> numError;               <span class="comment">// &quot;num&quot;是常见的缩写</span></span><br><span class="line"><span class="type">int</span> numDNSConnection;       <span class="comment">// DNS是都知道的概念</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> n;                      <span class="comment">// 不清楚作用和意义</span></span><br><span class="line"><span class="type">int</span> nCompConns;             <span class="comment">// 缩写不知道代表何种意思</span></span><br><span class="line"><span class="type">int</span> wgcConnections;         <span class="comment">// &quot;wgc&quot;是何种意思</span></span><br><span class="line"><span class="type">int</span> pcReader;               <span class="comment">// &quot;pc&quot;可能出现歧义</span></span><br><span class="line"><span class="type">int</span> cstmrID;                <span class="comment">// 缩写若干字母</span></span><br></pre></td></tr></table></figure><h3 id="7-2-文件命名"><a href="#7-2-文件命名" class="headerlink" title="7.2  文件命名"></a>7.2  <strong>文件命名</strong></h3><p>文件命名<strong>使用大驼峰命名方式，定义类和文件名一般成对出现</strong>。</p><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// NameSpace.h</span></span><br><span class="line"><span class="comment">// 文件名说明作用,如NameSpace.h存放关于NameSpace的声明</span></span><br><span class="line"></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="meta">#<span class="keyword">define</span> CIQTEK_NAMESPACE ciqtek</span></span><br><span class="line"></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="meta">#<span class="keyword">define</span> BEGIN_NAMESPACE_CIQTEK namespace CIQTEK_NAMESPACE &#123;</span></span><br><span class="line"></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="meta">#<span class="keyword">define</span> END_NAMESPACE_CIQTEK &#125;</span></span><br><span class="line"></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="meta">#<span class="keyword">define</span> CIQTEK_QUAIFIER     ::CIQTEK_NAMESPACE::</span></span><br><span class="line"><span class="comment">// AgorithmAutogamma.h</span></span><br><span class="line"><span class="comment">// 文件名和类名一一对应,成对存在</span></span><br><span class="line">cass AgorithmAutogamma</span><br><span class="line">&#123;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="7-3-类型命名"><a href="#7-3-类型命名" class="headerlink" title="7.3  类型命名"></a>7.3  <strong>类型命名</strong></h3><p>类型命名的<strong>每个单词首字母都是大写，不包含下划线，大驼峰命名方式</strong>。所有类型命名——类、结构体、类型定义(typedef)、枚举、类型模板参数均使用本约定。</p><figure class="highlight c++"><table><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="comment">// 类和结构体</span></span><br><span class="line">cass UrTabe &#123;&#125;;</span><br><span class="line">cass UrTabeTester &#123;&#125;;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">UrTabeProperties</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类型定义</span></span><br><span class="line"><span class="keyword">typedef</span> hash_map&lt;UrTabeProperties *, string&gt; PropertiesMap;</span><br><span class="line"></span><br><span class="line"><span class="comment">// using 别名</span></span><br><span class="line"><span class="keyword">using</span> PropertiesMap = hash_map&lt;UrTabeProperties *, string&gt;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 枚举</span></span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">UrTabeErrors</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 类型模板参数</span></span><br><span class="line"><span class="function">tempate&lt;<span class="keyword">typename</span> UrInfo&gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setUr</span><span class="params">(<span class="type">const</span> UrInfo&amp;)</span></span>;</span><br></pre></td></tr></table></figure><h3 id="7-4-变量命名"><a href="#7-4-变量命名" class="headerlink" title="7.4  变量命名"></a>7.4  <strong>变量命名</strong></h3><p>变量(包含函数参数)和数据成员名<strong>一律用小驼峰命名方式，每行一个变量，单字符的变量只在循环计数中使用</strong>。对于不同作用域的变量遵循以下规则：</p><ul><li>类成员变量须在变量名前加m_前缀。</li><li>局部变量等到需要使用时再定义，且定义是必须要初始化，整数为0，实数用0.0，指针用nuptr，字符(串)用’\0’。</li><li>全局变量命名时须在变量前加g_前缀。</li><li>静态变量名以s_开头。</li></ul><table><thead><tr><th>前缀</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td>无</td><td>局部变量(oca)</td><td>odVaue</td></tr><tr><td>m_</td><td>类的成员变量(member)</td><td>int m_width</td></tr><tr><td>ms_</td><td>类的静态成员变量(static member)</td><td>static int ms_initVaue</td></tr><tr><td>s_</td><td>静态变量(static)</td><td>static int s_initVaue</td></tr><tr><td>g_</td><td>外部全局变量</td><td>int g_maxCount</td></tr><tr><td>sg_</td><td>静态全局变量(static goba)</td><td>static int sg_exampe</td></tr><tr><td>gg_</td><td>进程间共享的数据段全局变量(goba goba)</td><td>int gg_shareVaue</td></tr></tbody></table><h3 id="7-5-常量命名"><a href="#7-5-常量命名" class="headerlink" title="7.5  常量命名"></a>7.5  <strong>常量命名</strong></h3><p>常量*<em>不含前缀且应该大写，单词间由下滑线，包含constexpr</em>、const以及宏定义。例如：</p><figure class="highlight c++"><table><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="type">const</span> <span class="type">int</span> DAYS_IN_AWEEK = <span class="number">7</span>;</span><br><span class="line"><span class="keyword">constexpr</span> <span class="type">int</span> DAYS_IN_AWEEK = <span class="number">7</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DAYS_IN_AWEEK 7 <span class="comment">// 不推荐写法,命名方式按常量命名规则</span></span></span><br></pre></td></tr></table></figure><h3 id="7-6-函数命名"><a href="#7-6-函数命名" class="headerlink" title="7.6  函数命名"></a>7.6  <strong>函数命名</strong></h3><p>函数命名以及函数参数<strong>都使用小驼峰命名方式，函数名时动词或含有动词的短语，函数参数若非基础数据类型，使用对象引用</strong>。例如：</p><figure class="highlight c++"><table><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"><span class="type">int</span> <span class="title">getVaue</span><span class="params">()</span> <span class="type">const</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setVaue</span><span class="params">(<span class="type">const</span> <span class="type">int</span> vaue)</span></span>;         <span class="comment">// 基础类型(迭代器)值传递</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setCoor</span><span class="params">(<span class="type">const</span> QCoor&amp; newCoor)</span></span>;  <span class="comment">// 非基础类型引用传递</span></span><br></pre></td></tr></table></figure><p><em>当函数为信号或者槽函数时，应分别在前加上signa*\和sot前缀。</em></p><figure class="highlight c++"><table><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"><span class="type">void</span> <span class="title">signaogWritten</span><span class="params">(<span class="type">const</span> ogeve &amp;eve, <span class="type">const</span> QString &amp;og)</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">sotogWrite</span><span class="params">(<span class="type">const</span> ogeve &amp;eve, <span class="type">const</span> QString &amp;og)</span></span>;</span><br></pre></td></tr></table></figure><h3 id="7-7-命名空间命名"><a href="#7-7-命名空间命名" class="headerlink" title="7.7   命名空间命名"></a>7.7   <strong>命名空间命名</strong></h3><p>命名空间的名称是名词，<strong>用小写字母命名</strong>，每个单词以下划线分割，例如：ciqtek。</p><h3 id="7-8-枚举命名"><a href="#7-8-枚举命名" class="headerlink" title="7.8  枚举命名"></a>7.8  <strong>枚举命名</strong></h3><p>枚举名和枚举值<strong>都是名词</strong>，和常量或宏规则一致，<strong>枚举值每个字母均为大写，单词之间以下划线间隔，枚举名为大驼峰命名方式</strong>。例如：</p><figure class="highlight c++"><table><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="keyword">enum</span> <span class="title class_">MyCoor</span> <span class="comment">// 枚举名</span></span><br><span class="line">&#123;</span><br><span class="line">    WHITE, <span class="comment">// 枚举值</span></span><br><span class="line">BACK,</span><br><span class="line">SKY_BUE</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="7-9-结构体命名"><a href="#7-9-结构体命名" class="headerlink" title="7.9   结构体命名"></a>7.9   <strong>结构体命名</strong></h3><p>结构体中只定义变量，不定义函数。需要定义函数的结构体，转换成类实现。</p><p><strong>结构体名</strong>是名词，<strong>每个单词以大写字母开头，大驼峰命名方式</strong>。<strong>结构体成员</strong>是名词，以<strong>小驼峰命名方式</strong>，例如：</p><figure class="highlight c++"><table><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">struct</span> <span class="title class_">MyCoor</span></span><br><span class="line">&#123;</span><br><span class="line">    boo isMyCoor;</span><br><span class="line">    <span class="type">int</span> white;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="7-10-获取器和设置器命名"><a href="#7-10-获取器和设置器命名" class="headerlink" title="7.10   获取器和设置器命名"></a>7.10   <strong>获取器和设置器命名</strong></h3><p>获取器和设置器都是根据约定俗成的命名规则：</p><ul><li>非布尔型的获取器**coor()<strong>或者</strong>getCoor()**；</li><li>布尔型的获取器**isChecked()**；</li><li>设置器**Coor(const Coor&amp; newCoor)**。</li></ul><h3 id="7-11-界面控件命名"><a href="#7-11-界面控件命名" class="headerlink" title="7.11  界面控件命名"></a>7.11  <strong>界面控件命名</strong></h3><p>控件命名应以控件类型结尾，以说明控件的类型，例如：</p><figure class="highlight c++"><table><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">QPainTextEdit *textEdit;</span><br><span class="line">Qabe *abe;</span><br><span class="line">QineEdit *ineEdit;</span><br><span class="line"><span class="comment">// 界面中出现多种重复的控件,应将前面补充说明</span></span><br><span class="line">Qabe *contentsabe;</span><br><span class="line">QPushButton *findButton;</span><br><span class="line">QTooBar *fieTooBar;</span><br><span class="line">QComboBox *caseComboBox;</span><br><span class="line">QSpinBox *maxVisibeSpinBox;</span><br><span class="line">QCheckBox *wrapCheckBox;</span><br></pre></td></tr></table></figure><h2 id="8-注释"><a href="#8-注释" class="headerlink" title="8. 注释"></a>8. <strong>注释</strong></h2><p>一般情况下源程序有效注释量必须在20%以上，不易理解的地方都需加上注释，需要简单精炼。</p><h3 id="8-1-注释风格"><a href="#8-1-注释风格" class="headerlink" title="8.1  注释风格"></a>8.1  <strong>注释风格</strong></h3><p>头文件注释风格需要兼容Doxygen注释方式，便于生成说明文档。源文件注释使用&#x2F;&#x2F;或&#x2F;* *&#x2F;都可以。</p><h3 id="8-2-头文件注释"><a href="#8-2-头文件注释" class="headerlink" title="8.2  头文件注释"></a>8.2  <strong>头文件注释</strong></h3><p>头文件注释包括<strong>版权说明、版本号、作者、生成日期、描述信息</strong>等。</p><figure class="highlight c++"><table><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="comment">**Copyright(C), 2021-2022, Ciqtek Technoogy.</span></span><br><span class="line"><span class="comment">**Version:     1.0</span></span><br><span class="line"><span class="comment">**Author:      dingyy</span></span><br><span class="line"><span class="comment">**Date:        $DATE$</span></span><br><span class="line"><span class="comment">**Description: </span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><h3 id="8-3-类注释"><a href="#8-3-类注释" class="headerlink" title="8.3  类注释"></a>8.3  <strong>类注释</strong></h3><p>类注释<strong>包括描述信息，有必要时需将使用方法加到注释中</strong>。</p><p>简单类只需要包含简要说明信息即可，如下：</p><figure class="highlight c++"><table><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">/**</span></span><br><span class="line"><span class="comment"> * @brief    类描述信息</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><p>当类需要添加实例代码时候，按以下方式进行备注：</p><figure class="highlight c++"><table><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="comment"> * @brief    类描述信息</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> *   Exampe of usage:</span></span><br><span class="line"><span class="comment"> *   @code</span></span><br><span class="line"><span class="comment"> *       示例代码</span></span><br><span class="line"><span class="comment"> *   @endcode</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><h3 id="8-4-函数注释"><a href="#8-4-函数注释" class="headerlink" title="8.4  函数注释"></a>8.4  <strong>函数注释</strong></h3><p>函数注释主要<strong>包括描述信息、参数信息、返回值以及返回值说明，有必要时加入注解信息</strong>。</p><figure class="highlight c++"><table><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="comment"> * @brief          函数名 函数简介</span></span><br><span class="line"><span class="comment"> * @param          形参 参数说明</span></span><br><span class="line"><span class="comment"> * @return         返回说明</span></span><br><span class="line"><span class="comment"> *   @retva 0     (非必要)返回值说明</span></span><br><span class="line"><span class="comment"> *   @retva 1     (非必要)返回值说明</span></span><br><span class="line"><span class="comment"> * @note           (非必要)注释信息</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><h3 id="8-5-变量注释"><a href="#8-5-变量注释" class="headerlink" title="8.5  变量注释"></a>8.5  <strong>变量注释</strong></h3><p>类的数据成员变量按需进行注释，<strong>全局变量需要注释说明含义及用途</strong>。变量注释<strong>置于变量的上方</strong>。</p><figure class="highlight c++"><table><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">/** 回归测试用例总数</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> NUM_TEST_CASES = <span class="number">6</span>;</span><br></pre></td></tr></table></figure><h3 id="8-6-实现注释"><a href="#8-6-实现注释" class="headerlink" title="8.6  实现注释"></a>8.6  <strong>实现注释</strong></h3><p>对于实现代码中巧妙的、隐晦的、重要的地方加以注释。<strong>注意后跟一个空格</strong>。</p><p><strong>巧妙或复杂的代码在代码块上方注释</strong>，例如：</p><figure class="highlight c++"><table><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">// 将结果除以2,考虑到x包含加法的进位</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; resut-&gt;<span class="built_in">size</span>(); ++i) &#123;</span><br><span class="line">    x = (x &lt;&lt; <span class="number">8</span>) + (*resut)[i];</span><br><span class="line">    (*resut)[i] = x &gt;&gt; <span class="number">1</span>;</span><br><span class="line">    x &amp;= <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="8-7-枚举和结构体注释"><a href="#8-7-枚举和结构体注释" class="headerlink" title="8.7  枚举和结构体注释"></a>8.7  <strong>枚举和结构体注释</strong></h3><p>枚举注释需要对<strong>枚举、枚举值</strong>进行说明。</p><p>结构体注释需要对<strong>结构体、成员变量</strong>进行说明。</p><figure class="highlight c++"><table><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="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief     日志类型</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">ogeve</span> </span><br><span class="line">&#123;</span><br><span class="line">    OG_EVE_TRACE = <span class="number">0</span>,             <span class="comment">///&lt; TARCE输出 </span></span><br><span class="line">    OG_EVE_DEBUG = <span class="number">10000</span>,         <span class="comment">///&lt; DEBUG输出 </span></span><br><span class="line">    OG_EVE_INFO = <span class="number">20000</span>,          <span class="comment">///&lt; INFO输出 </span></span><br><span class="line">    OG_EVE_WARN = <span class="number">30000</span>,          <span class="comment">///&lt; WARN输出 </span></span><br><span class="line">    OG_EVE_ERROR = <span class="number">40000</span>,         <span class="comment">///&lt; ERROR输出 </span></span><br><span class="line">    OG_EVE_FATA = <span class="number">50000</span>,         <span class="comment">///&lt; FATA输出 </span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief     自定义颜色</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MyCoor</span> </span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> red;        <span class="comment">///&lt; 红色分量</span></span><br><span class="line">    <span class="type">int</span> green;      <span class="comment">///&lt; 绿色分量</span></span><br><span class="line">    <span class="type">int</span> bue;       <span class="comment">///&lt; 蓝色分量</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="8-8-TODO注释"><a href="#8-8-TODO注释" class="headerlink" title="8.8   TODO注释"></a>8.8   <strong>TODO注释</strong></h3><p>对于<strong>临时的、短期的方案，或计划中但未完成的代码，或已实现功能但待优化的代码</strong>使用TODO注释。TODO注释需要<strong>作者、日期</strong>利于后期检索，避免查找困难，例如：</p><figure class="highlight c++"><table><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="function"><span class="type">int</span> <span class="title">MyCass::test</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 计划但未完成</span></span><br><span class="line"><span class="comment">// TODO - Barmaco 2022/01/18 9:59 实现xx相关功能</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// TODO - Barmaco 2022/01/18 9:59 临时方案</span></span><br><span class="line">    <span class="keyword">return</span> (a &gt; b ? a : b);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">MyCass::doSomething</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// TODO - Barmaco 2022/01/18 9:59 待优化</span></span><br><span class="line">    <span class="type">int</span> x = (x &lt;&lt; <span class="number">8</span>) + (*resut)[i];</span><br><span class="line">    (*resut)[i] = x &gt;&gt; <span class="number">1</span>;</span><br><span class="line">    x &amp;= <span class="number">1</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="9-格式"><a href="#9-格式" class="headerlink" title="9. 格式"></a>9. <strong>格式</strong></h2><h3 id="9-1-行长度"><a href="#9-1-行长度" class="headerlink" title="9.1  行长度"></a>9.1  <strong>行长度</strong></h3><p>较长的语句(&gt;80字符)要分成多行书写，长表达式要<strong>在较低优先级操作符处划分新行，操作符放在新行之首，逗号放在一行的结束，划分出的新行要进行适当的缩进</strong>，使排版整齐，语句可读，例如：</p><figure class="highlight c++"><table><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">if</span> ((taskOne &lt; taskNumber) &amp;&amp; (taskTwo &lt; taskNumber) </span><br><span class="line">    &amp;&amp; (taskThree &lt; taskNumber) &amp;&amp;(taskFour &lt; taskNumber)) &#123;</span><br><span class="line">&#125; <span class="comment">// 操作符放在新行之首</span></span><br><span class="line">boo retva = <span class="built_in">doSomething</span>(averyveryveryveryveryveryongargument1,</span><br><span class="line">                          argument2, argument3); <span class="comment">// 后一行与第一个实参对齐</span></span><br></pre></td></tr></table></figure><h3 id="9-2-文件编码"><a href="#9-2-文件编码" class="headerlink" title="9.2  文件编码"></a>9.2  <strong>文件编码</strong></h3><p>为了统一文件编码，避免开发过程中文件编码混乱问题，文件保存过程中，<strong><em>统一使用UTF-8无签名</em>编码</strong>。编码转换时，默认在UTF-8中转换。</p><p>下载VS插件Force UTF-8(No BOM)，选择第一个点击下载→关闭并重启VS安装插件。</p><p><img src="/CodeStyle/image-20250317195151183.png" alt="image-20250317195151183"></p><h3 id="9-3-缩进"><a href="#9-3-缩进" class="headerlink" title="9.3  缩进"></a>9.3  <strong>缩进</strong></h3><p><strong>使用4个空格进行代码缩进，禁止使用制表符。</strong></p><h3 id="9-4-大括号"><a href="#9-4-大括号" class="headerlink" title="9.4  大括号"></a>9.4  大括号</h3><p>大括号的总结来说有两种使用情况，分别是{ 跟在语句后面空一格或者 { 独占一行，} 独占一行。<br>{ 必须独占一行情况，主要是定义的情况：</p><figure class="highlight c++"><table><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="keyword">class</span> <span class="title class_">Foo</span></span><br><span class="line">&#123;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 结构体定义</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Bar</span></span><br><span class="line">&#123;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 枚举定义</span></span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">Baz</span></span><br><span class="line">&#123;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 函数定义</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Foo::foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>{ 即可独占一行，或者空一格形式，主要是代码逻辑：</p><figure class="highlight c++"><table><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">// if语句</span></span><br><span class="line"><span class="keyword">if</span> (condition) &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// for循环语句</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i) &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// while和do-while循环语句</span></span><br><span class="line"><span class="keyword">while</span> (condition) &#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">do</span> &#123;</span><br><span class="line">&#125; <span class="keyword">while</span>(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// switch-case</span></span><br><span class="line"><span class="keyword">switch</span> (value) &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">0</span>: &#123;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-5-函数调用"><a href="#9-5-函数调用" class="headerlink" title="9.5  函数调用"></a>9.5  函数调用</h3><p>函数调用要么在一行写完调用，要么在圆括号里对参数分行，要么参数另起一行且缩进四格。例如：</p><figure class="highlight c++"><table><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="type">bool</span> retval = <span class="built_in">doSomething</span>(argument1, argument2, argument3);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 同一行放不下,换新行且和第一个实参对齐,左圆括号后和右圆括号前不留空格</span></span><br><span class="line"><span class="type">bool</span> retval = <span class="built_in">doSomething</span>(averyveryveryverylongargument1,</span><br><span class="line">                          argument2, argument3);</span><br><span class="line"><span class="comment">// 模拟层次多的情况</span></span><br><span class="line"><span class="keyword">if</span> (...) &#123;</span><br><span class="line">    <span class="keyword">if</span> (...) &#123;</span><br><span class="line">        <span class="built_in">doSomething</span>(</span><br><span class="line">            argument1, argument2,  <span class="comment">// 4 空格缩进</span></span><br><span class="line">            argument3, argument4);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果一些参数本身就是略复杂的表达式，那么可以直接创建临时变量描述该表达式，并传递给函数。也可以将某个参数独立成行，添加注释，增加可读性。</p><figure class="highlight plaintext"><table><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">int my_heuristic = scores[x] * y + bases[x];</span><br><span class="line">bool retval = doSomething(my_heuristic, x, y, z);</span><br><span class="line"></span><br><span class="line">bool retval = doSomething(scores[x] * y + bases[x],  // Score heuristic.</span><br><span class="line">                          x, y, z);</span><br></pre></td></tr></table></figure><p>如果一系列的参数本身就有一定的结构，可以酌情地按其结构来决定参数格式。</p><figure class="highlight c++"><table><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">// 通过 3x3 矩阵转换 widget.</span></span><br><span class="line">my_widget.<span class="built_in">Transform</span>(x1, x2, x3,</span><br><span class="line">                    y1, y2, y3,</span><br><span class="line">                    z1, z2, z3);</span><br></pre></td></tr></table></figure><h3 id="9-6-函数声明与定义"><a href="#9-6-函数声明与定义" class="headerlink" title="9.6  函数声明与定义"></a>9.6  函数声明与定义</h3><p><strong>返回类型和函数名放在同一行，参数在放不下时，对形参分行，{ }分别单独占一行。例如：</strong></p><figure class="highlight c++"><table><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="comment">// 返回值、函数名、形参在同一行</span></span><br><span class="line"><span class="function">ReturnType <span class="title">ClassName::FunctionName</span><span class="params">(Type par_name1, Type par_name2)</span> </span></span><br><span class="line"><span class="function"></span>&#123; <span class="comment">// &#123; 独占一行</span></span><br><span class="line">    <span class="built_in">doSomething</span>();</span><br><span class="line">&#125; <span class="comment">// &#125; 独占一行</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 形参换行</span></span><br><span class="line"><span class="function">ReturnType <span class="title">ClassName::ReallyLongFunctionName</span><span class="params">(Type par_name1, Type par_name2,</span></span></span><br><span class="line"><span class="params"><span class="function">                                             Type par_name3)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">doSomething</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 第一个参数都放不下,换新行4空格缩进</span></span><br><span class="line"><span class="function">ReturnType <span class="title">LongClassName::ReallyReallyReallyLongFunctionName</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">    Type par_name1,  <span class="comment">// 4空格缩进</span></span></span></span><br><span class="line"><span class="params"><span class="function">    Type par_name2,</span></span></span><br><span class="line"><span class="params"><span class="function">    Type par_name3)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="built_in">doSomething</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>未被使用参数，或者根据上下文很容易看出用途参数，可以省略参数名。<strong>但是尽量不要省略参数名。</strong></p><figure class="highlight c++"><table><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 继承于Qt的事件的函数,事件参数未被使用,可以省略参数名</span></span><br><span class="line"><span class="comment">// 这种情况大多是因为继承,重写事件而省略参数操作</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">showEvent</span><span class="params">(QShowEvent *)</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">hideEvent</span><span class="params">(QHideEvent *)</span></span>;</span><br><span class="line"><span class="comment">// 需要移除拷贝构造函数或者是赋值操作函数,可以省略参数名</span></span><br><span class="line"><span class="built_in">Foo</span>(<span class="type">const</span> Foo&amp;) = <span class="keyword">delete</span>;</span><br><span class="line">Foo&amp; <span class="keyword">operator</span>=(<span class="type">const</span> Foo &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"><span class="keyword">class</span> <span class="title class_">Shape</span> </span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">Rotate</span><span class="params">(<span class="type">double</span> radians)</span> </span>= <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Circle</span> : <span class="keyword">public</span> Shape</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">Rotate</span><span class="params">(<span class="type">double</span> radians)</span> <span class="keyword">override</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Circle::Rotate</span><span class="params">(<span class="type">double</span>)</span> </span>&#123;&#125; <span class="comment">// 不推荐 —— 省略会不清楚变量的作用</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Circle::Rotate</span><span class="params">(<span class="type">double</span> <span class="comment">/*radians*/</span>)</span> </span>&#123;&#125; <span class="comment">// 推荐 —— 说明变量作用并消除警告</span></span><br></pre></td></tr></table></figure><h3 id="9-7-条件语句"><a href="#9-7-条件语句" class="headerlink" title="9.7  条件语句"></a>9.7  条件语句</h3><p>对基本条件语句不在圆括号内使用空格，if后面空一格，{ 前空一格或换行，} 独立一行，即不跟else或者else if。例如：</p><figure class="highlight c++"><table><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="keyword">if</span> (condition) &#123; </span><br><span class="line">    ... <span class="comment">// 缩进四格</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;  <span class="comment">// else或else if另起一行</span></span><br><span class="line">    ... </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果能增强可读性，简短的条件语句允许写在同一行。只有当语句简单并且没有使用else子句时使用：</p><figure class="highlight c++"><table><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="keyword">if</span> (x == kFoo) <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Foo</span>();</span><br><span class="line"><span class="keyword">if</span> (x == kBar) <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Bar</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不允许 - 当有 ELSE 分支时 IF 块却写在同一行</span></span><br><span class="line"><span class="keyword">if</span> (x) <span class="built_in">DoThis</span>();</span><br><span class="line"><span class="keyword">else</span> <span class="built_in">DoThat</span>();</span><br></pre></td></tr></table></figure><p>如果语句中某个if-else语句使用大括号的话，其他分支也必须要使用。</p><figure class="highlight c++"><table><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="comment">// 不可以这样子 - IF 有大括号 ELSE 却没有</span></span><br><span class="line"><span class="keyword">if</span> (condition) &#123;</span><br><span class="line">    foo;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">bar;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 不可以这样子 - ELSE 有大括号 IF 却没有</span></span><br><span class="line"><span class="keyword">if</span> (condition)</span><br><span class="line">    foo;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">    bar;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 只要其中一个分支用了大括号, 两个分支都要用上大括号</span></span><br><span class="line"><span class="keyword">if</span> (condition) &#123;</span><br><span class="line">    foo;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">    bar;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-8-布尔表达式"><a href="#9-8-布尔表达式" class="headerlink" title="9.8  布尔表达式"></a>9.8  布尔表达式</h3><p>如果一个布尔表达式过长，断行方式要<strong>统一为逻辑操作符放在新行的开头。</strong>但逻辑复杂时考虑增加圆括号，增加可读性。</p><figure class="highlight c++"><table><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">if</span> (this_one_thing &gt; this_other_thing </span><br><span class="line">    &amp;&amp; (a_third_thing == a_fourth_thing) <span class="comment">// 加入圆括号增加可读性 </span></span><br><span class="line">    &amp;&amp; yet_another &amp;&amp; last_one)          <span class="comment">// 操作符放在开头对齐第一个参数</span></span><br><span class="line">&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-9-switch-case语句"><a href="#9-9-switch-case语句" class="headerlink" title="9.9  switch-case语句"></a>9.9  switch-case语句</h3><p>循环和switch-case语句的 { 可以与关键词在同一行,也可以另起单独一行与 } 对齐。后续代码不作展示说明。<br><strong>switch语句中的case块必须要大括号进行分段，以表明case之间不是连在一起的。switch应该总是包含一个default匹配。</strong></p><figure class="highlight c++"><table><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="keyword">switch</span> (var) &#123; <span class="comment">// 可以在同一行,也可以另起单独一行与&#125;对齐</span></span><br><span class="line">    <span class="keyword">case</span> <span class="number">0</span>: &#123;  <span class="comment">// 空格缩进四格</span></span><br><span class="line">        ...    <span class="comment">// 空格缩进八格</span></span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">case</span> <span class="number">1</span>: &#123;  <span class="comment">// 可以在同一行,也可以另起单独一行与&#125;对齐</span></span><br><span class="line">        ...</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">default</span>: &#123;</span><br><span class="line">        <span class="built_in">assert</span>(<span class="literal">false</span>); <span class="comment">// 如果不可能到default可以尝试添加assert语句</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-10-指针和引用表达式"><a href="#9-10-指针和引用表达式" class="headerlink" title="9.10  指针和引用表达式"></a>9.10  指针和引用表达式</h3><p>**句点或箭头前后不要有空格；指针&#x2F;地址操作符(*，&amp;)之后不能有空格。**例如：</p><figure class="highlight c++"><table><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">x = *p;</span><br><span class="line">p = &amp;x;</span><br><span class="line">x = r.y;</span><br><span class="line">x = r-&gt;y;</span><br></pre></td></tr></table></figure><p>在声明指针变量或参数时，星号与类型或变量名紧挨都可以：</p><figure class="highlight c++"><table><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="type">char</span> *c;</span><br><span class="line"><span class="type">const</span> string &amp;str;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 推荐——空格后置</span></span><br><span class="line"><span class="type">char</span>* c;</span><br><span class="line"><span class="type">const</span> string&amp; str;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> x, *y;  <span class="comment">// 不推荐——在多重声明中不能使用 &amp; 或 *</span></span><br><span class="line"><span class="type">char</span> * c;  <span class="comment">// 不推荐—— * 两边都有空格</span></span><br><span class="line"><span class="type">const</span> string &amp; str;  <span class="comment">// 不推荐—— &amp; 两边都有空格</span></span><br></pre></td></tr></table></figure><h3 id="9-11-预处理指令"><a href="#9-11-预处理指令" class="headerlink" title="9.11  预处理指令"></a><strong>9.11  预处理指令</strong></h3><p>预处理指令不要缩进, 从行首开始。</p><figure class="highlight c++"><table><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="keyword">if</span> (lopsided_score) &#123;</span><br><span class="line"><span class="meta">#<span class="keyword">if</span> DISASTER_PENDING      <span class="comment">// 正确 - 从行首开始</span></span></span><br><span class="line">    <span class="built_in">DropEverything</span>();</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">    <span class="built_in">BackToNormal</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不推荐 - 指令缩进</span></span><br><span class="line"><span class="keyword">if</span> (lopsided_score) &#123;</span><br><span class="line">    <span class="meta">#<span class="keyword">if</span> DISASTER_PENDING  <span class="comment">// 差 - &quot;#if&quot; 应该放在行开头</span></span></span><br><span class="line">    <span class="built_in">DropEverything</span>();</span><br><span class="line">    <span class="meta">#<span class="keyword">endif</span>                <span class="comment">// 差 - &quot;#endif&quot; 不要缩进</span></span></span><br><span class="line">    <span class="built_in">BackToNormal</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-12-构造函数初始值列表"><a href="#9-12-构造函数初始值列表" class="headerlink" title="9.12  构造函数初始值列表"></a>9.12  <strong>构造函数初始值列表</strong></h3><p>构造函数初始化列表放在<strong>同一行或按四格缩进并排多行。</strong></p><figure class="highlight c++"><table><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="comment">// 如果所有变量能放在同一行:</span></span><br><span class="line">MyClass::<span class="built_in">MyClass</span>(<span class="type">const</span> <span class="type">int</span> var) : <span class="built_in">m_someVar</span>(var) </span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">doSomething</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果不能放在同一行,</span></span><br><span class="line"><span class="comment">// 必须置于冒号后, 并缩进 4 个空格</span></span><br><span class="line">MyClass::<span class="built_in">MyClass</span>(<span class="type">const</span> <span class="type">int</span> var)</span><br><span class="line">    : <span class="built_in">m_someVar</span>(var), <span class="built_in">m_someOtherVar</span>(var + <span class="number">1</span>) </span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">doSomething</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果初始化列表需要置于多行, 将每一个成员放在单独的一行</span></span><br><span class="line"><span class="comment">// 并逐行对齐</span></span><br><span class="line">MyClass::<span class="built_in">MyClass</span>(<span class="type">const</span> <span class="type">int</span> var)</span><br><span class="line">    : <span class="built_in">m_someVar</span>(var),             <span class="comment">// 缩进 4 个空格</span></span><br><span class="line">      <span class="built_in">m_someOtherVar</span>(var + <span class="number">1</span>) </span><br><span class="line">&#123;  </span><br><span class="line">    <span class="built_in">doSomething</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="9-13-类格式"><a href="#9-13-类格式" class="headerlink" title="9.13  类格式"></a>9.13  类格式</h3><p><strong>访问控制块关键词的声明不需要缩进。</strong><br>类声明得基本格式如下：</p><figure class="highlight c++"><table><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="keyword">class</span> <span class="title class_">MyClass</span> : <span class="keyword">public</span> OtherClass </span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:      <span class="comment">// 注意没有缩进</span></span><br><span class="line">    <span class="built_in">MyClass</span>();  <span class="comment">// 标准的空格缩进四格</span></span><br><span class="line">    <span class="function"><span class="keyword">explicit</span> <span class="title">MyClass</span><span class="params">(<span class="type">const</span> <span class="type">int</span> var)</span></span>;</span><br><span class="line">    ~<span class="built_in">MyClass</span>() &#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">someFunction</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">someFunctionThatDoesNothing</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">setSomeVar</span><span class="params">(<span class="type">const</span> <span class="type">int</span> var)</span> </span>&#123; m_someVar = var; &#125;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">someVar</span><span class="params">()</span> <span class="type">const</span> </span>&#123; <span class="keyword">return</span> m_someVar; &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">someInternalFunction</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> m_someVar;</span><br><span class="line">    <span class="type">int</span> m_someOtherVar;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>注意事项：</p><ol><li>所有基类名应在 80 列限制下尽量与子类名放在同一行；</li><li>访问控制块关键词没有缩进，关键词后不要保留空行；</li><li>除第一个关键词 外，其他关键词前要空一行；</li><li>关于声明顺序的规则请参考 4.8 声明顺序 一节</li></ol><h2 id="10-完结撒花"><a href="#10-完结撒花" class="headerlink" title="10  完结撒花"></a>10  完结撒花</h2><p> <strong>正确性 &gt; 稳定性 &gt; 可测试性 &gt; 可读性 &gt; 全局效率 &gt; 局部效率 &gt; 个人习惯。</strong></p><ol><li>正确性，指程序要实现设计 要求的功能；</li><li>稳定性、安全性，指程序稳定、可靠、安全；</li><li>可测试性，指程序要具有良好的可测试性；</li><li>规范&#x2F;可读性，指程序书写风格、命名规则等要符合规范；</li><li>全局效率，指软件系统的整体效率；</li><li>局部效率，指某个模块&#x2F;子模块&#x2F;函数的本身效率；</li><li>个人表达方式&#x2F;个人方便性，指个人编程习惯。</li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;1-概述&quot;&gt;&lt;a href=&quot;#1-概述&quot; class=&quot;headerlink&quot; title=&quot;1.  概述&quot;&gt;&lt;/a&gt;1.  &lt;strong&gt;概述&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;命名，是计算机史上最难的问题之一，有很多书本都有专门的章节讨论命名规范的问题，鄙人</summary>
      
    
    
    
    <category term="C++" scheme="https://cpp-memory-leaks.github.io/categories/C/"/>
    
    
    <category term="C++" scheme="https://cpp-memory-leaks.github.io/tags/C/"/>
    
  </entry>
  
</feed>
