<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="https://sunirein.tech/atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CN">
  <id>https://sunirein.tech/</id>
  <title>SuniRein 的个人小站</title>
  <subtitle>一名普通技术宅的个人博客</subtitle>
  <updated>2026-05-22T02:20:52.018Z</updated>
  <generator>@vuepress/plugin-feed</generator>
  <link rel="self" href="https://sunirein.tech/atom.xml"/>
  <link rel="alternate" href="https://sunirein.tech/"/>
  <entry>
    <title type="text">Rust 中的 Niche 优化</title>
    <id>https://sunirein.tech/blog/ssaaneyj/</id>
    <link href="https://sunirein.tech/blog/ssaaneyj/"/>
    <updated>2026-05-22T02:20:07.000Z</updated>
    <summary type="html"><![CDATA[<p>Niche 优化是 Rust 编译器的一种内存优化技术。
Rust 利用类型系统和数据布局的特性，识别出某些类型中未被使用的值（Niche），并将这些值用于存储额外的信息，从而减少内存占用和提高性能。</p>
]]></summary>
    <content type="html"><![CDATA[<p>Niche 优化是 Rust 编译器的一种内存优化技术。
Rust 利用类型系统和数据布局的特性，识别出某些类型中未被使用的值（Niche），并将这些值用于存储额外的信息，从而减少内存占用和提高性能。</p>
<!-- more -->
<h2>什么是 Niche？</h2>
<p>在计算机的世界中，某些数据类型在二进制层面有一些逻辑上不会被使用的值，这些未被使用的值就是 <strong>Niche</strong>。</p>
<p>例如，Rust 规定 <code>Box</code>、引用等不为空，<code>0</code> 就是它们的 Niche。</p>
<p>Rust 编译器可以利用这些 Niche 来存储额外的信息，如枚举的标签，从而优化内存布局和性能。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">size_of</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Box</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">i32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>>>(),</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> size_of</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Box</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">i32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>>());</span></span></code></pre>
</div><div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">enum</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MyEnum2</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  Zero</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  NonZero</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">num</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">NonZeroU8</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">size_of</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">MyEnum2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(),</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> size_of</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">u8</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>());</span></span></code></pre>
</div><h2>哪些类型能被优化？</h2>
<p>理论上，只要类型存在“空隙”，就能被优化。常见的类型包括：</p>
<h3>指针与引用类型</h3>
<p>Rust 保证所有的指针和引用都是非空的，因此内存地址 <code>0</code> 就是一个天然的“空隙”。</p>
<ul>
<li>引用：<code>&amp;T</code> 和 <code>&amp;mut T</code></li>
<li>智能指针：<code>Box&lt;T&gt;</code>, <code>Rc&lt;T&gt;</code>, <code>Arc&lt;T&gt;</code></li>
<li>非空指针：<code>ptr::NonNull&lt;T&gt;</code></li>
<li>函数指针：<code>fn()</code> 等</li>
</ul>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>裸指针 <code>*const T</code>, <code>*mut T</code> 可以为空，因此不能被优化。</p>
</div>
<h3>非零整数类型</h3>
<p>标准库在 <code>std::num</code> 模块下提供了一系列专门设计的类型，它们通过类型系统保证了值不为 <code>0</code>。</p>
<ul>
<li><code>NonZeroU8</code>, <code>NonZeroU16</code>, <code>NonZeroU32</code>, <code>NonZeroU64</code>, <code>NonZeroU128</code>, <code>NonZeroUsize</code></li>
<li><code>NonZeroI8</code>, <code>NonZeroI16</code>, <code>NonZeroI32</code>, <code>NonZeroI64</code>, <code>NonZeroI128</code>, <code>NonZeroIsize</code></li>
</ul>
<h3>布尔类型</h3>
<p><code>bool</code> 只有两个可能的值：<code>true</code> 和 <code>false</code>，其余值（<code>2</code> - <code>255</code>）都是 Niche。</p>
<h3>字符类型</h3>
<p>Unicode 标准并没有使用全部的 32 位空间。
例如，范围在 0xD800 到 0xDFFF 之间的值是代理对，它们是无效的 Unicode 标量值。</p>
<p>Rust 的 <code>char</code> 类型保证会是有效的 Unicode 字符，因此可以利用这些“空隙”作为 Niche。</p>
<h3>部分标准库 IO 类型</h3>
<p>在 Unix 平台上，文件描述符是一个非负整数，<code>-1</code> 用于报告错误。因此 <code>-1</code> 就是它的 Niche。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">size_of</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">std</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">fs</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">File</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>>(),</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> size_of</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">fs</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">File</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>());</span></span></code></pre>
</div><p>很多标准库 IO 类型都具有类型的特定，例如：</p>
<ul>
<li><code>std::fs::File</code></li>
<li><code>std::net::TcpStream</code></li>
<li><code>std::process::Child</code></li>
<li><code>std::io::Stdin</code>, <code>std::io::Stdout</code>, <code>std::io::Stderr</code></li>
</ul>
<h3>枚举类型</h3>
<p>用户可以通过自定义枚举来创建具有 Niche 的类型。
只要枚举定义的变体数量少于其底层表示所能容纳的状态数量，就会产生“空隙”。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">enum</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MyEnum</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  Variant1</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  Variant2</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><h3>结构体</h3>
<p>Niche 优化具有传递性。如果一个结构体包含了一个具有 Niche 的字段，那么这个结构体本身也具有 Niche。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">struct</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MyStruct</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">  ptr</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ptr</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">NonNull</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">u32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>,</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD"> // Niche: 0</span></span>
<span class="line"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">  val</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> u32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><h3>嵌套类型</h3>
<p>此外，Niche 优化也支持嵌套枚举。一个已经被 Niche 优化的枚举如果还存在 Niche，那它依旧能被继续优化：</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">enum</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MyEnum</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  Variant1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  Variant2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">size_of</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">MyEnum</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>>(),</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> size_of</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">MyEnum</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>());</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">size_of</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">MyEnum</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>>>(),</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> size_of</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">MyEnum</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>());</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD"> // Option&#x3C;Option&#x3C;T>> 也能被优化</span></span></code></pre>
</div><p>不过，这种优化虽然节约了内存，但也可能带来一些性能上的开销。
相比于直接使用标签，编译器可能需要进行一些位操作来判断当前枚举类型，增加了运行时复杂性。</p>
<h2>如何实现自定义 Niche？</h2>
<h3>利用标准库内部特性</h3>
<p>可以使用不稳定属性 <code>#[rustc_layout_scalar_valid_range_start]</code> 和 <code>#[rustc_layout_scalar_valid_range_end]</code>
来告诉编译器该类型的有效值范围。范围之外的值就被视为 Niche。</p>
<p>这需要使用 nightly 版本的 Rust，因为这些属性不稳定（官方明确说明只供 libcore/libstd 使用）。</p>
<p>下面是一个示例，定义了 <code>NonMaxU32</code> 类型，它的有效值范围是 <code>0</code> 到 <code>u32::MAX - 1</code>。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">[</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">feature</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">rustc_attrs</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)]</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#[</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">repr</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">transparent</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)]</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#[</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">rustc_layout_scalar_valid_range_start</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)]</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#[</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">rustc_layout_scalar_valid_range_end</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">4_294_967_294</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)]</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // u32::MAX - 1</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">pub</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> struct</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> NonMaxU32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">u32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">impl</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> NonMaxU32</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">    pub</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> new</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">value</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> u32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">Self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">        if</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> value</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ==</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> u32</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">MAX</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">            None</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">        }</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> else</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">            Some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">NonMaxU32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">value</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">        }</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">    pub</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> get</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> u32</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">        self</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">    }</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>验证优化效果：</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">mem</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">size_of</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">NonMaxU32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(),</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">mem</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">size_of</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">NonMaxU32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>>(),</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><h3>使用第三方库</h3>
<ul>
<li><a href="https://docs.rs/nonany/latest/nonany/" target="_blank" rel="noopener noreferrer"><code>nomany</code></a>：<code>nonany</code> 库允许用于指定任意一个 Niche 值，其内部通过 <code>NonZero* + XOR</code> 技巧实现。</li>
<li><a href="https://docs.rs/nonmax/latest/nonmax/" target="_blank" rel="noopener noreferrer"><code>nonmax</code></a>: 提供了 <code>NonMax*</code> 等以最大值为 Niche 的整数类型。</li>
<li><a href="https://docs.rs/nook/latest/nook/" target="_blank" rel="noopener noreferrer"><code>nook</code></a>：该库提供了 <code>BalancedI*</code> 类型，具有相等范围的正负值，其 <code>Niche</code> 是最小的负数。</li>
</ul>
<h2>可视化</h2>
<p><a href="https://niche.rs/" target="_blank" rel="noopener noreferrer">Niche.rs</a> 提供了一个可视化界面，用于展示 Rust 类型内存布局与 Niche 优化细节。</p>
<h2>参考文献</h2>
<ul>
<li><a href="https://www.0xatticus.com/posts/understanding_rust_niche/" target="_blank" rel="noopener noreferrer">Niche optimizations in Rust</a></li>
</ul>
]]></content>
    <published>2026-04-03T10:34:03.000Z</published>
  </entry>
  <entry>
    <title type="text">JWT</title>
    <id>https://sunirein.tech/blog/lef5pil6/</id>
    <link href="https://sunirein.tech/blog/lef5pil6/"/>
    <updated>2025-12-08T05:34:52.000Z</updated>
    <summary type="html"><![CDATA[<p>JWT，全称 <em>JSON Web Token</em>，是一种使用加密签名保护的 JSON 数据，定义在 <a href="https://www.rfc-editor.org/rfc/rfc7519" target="_blank" rel="noopener noreferrer">RFC 7519</a> 中。
它通常用于 Web 应用和 API 中，用于验证用户身份，防止未经授权的访问。</p>
]]></summary>
    <content type="html"><![CDATA[<p>JWT，全称 <em>JSON Web Token</em>，是一种使用加密签名保护的 JSON 数据，定义在 <a href="https://www.rfc-editor.org/rfc/rfc7519" target="_blank" rel="noopener noreferrer">RFC 7519</a> 中。
它通常用于 Web 应用和 API 中，用于验证用户身份，防止未经授权的访问。</p>
<!-- more -->
<p>与传统服务端 Session 不同，JWT 是无状态的，服务端无需存储会话信息，因此非常适合分布式系统和微服务架构。</p>
<h2>JWT 的结构</h2>
<p>JWT 主要由三部分构成，每部分均采用 <code>Base64</code> 编码存储，中间用 <code>.</code> 分隔：</p>
<div class="language-" data-highlighter="shiki" data-ext style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-"><span class="line"><span>Header.Payload.Signature</span></span></code></pre>
</div><ol>
<li><em>Header</em>：包含 Token 的元数据，例如用于签名的算法。</li>
<li><em>Payload</em>：存储声明，例如用户的身份和权限、Token 过期时间等。</li>
<li><em>Signature</em>：使用密码加密后得到的签名，用于保证数据的真实性和完整性。</li>
</ol>
<p>可以在 <a href="https://www.jwt.io" target="_blank" rel="noopener noreferrer">JWT Debugger</a> 中在线尝试下 JWT 的编解码过程。</p>
<h3>Header</h3>
<p>Header 中存储了 Token 的元数据，包括签名算法和令牌类型：</p>
<div class="language-json" data-highlighter="shiki" data-ext="json" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-json"><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">{</span></span>
<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577">  "</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965">alg</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">HS256</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577">  "</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965">typ</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">JWT</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><ul>
<li><code>alg</code>：指定签名算法。常用：
<ul>
<li><code>HS256</code>（HMAC + SHA-256）：对称加密算法，兼顾效率与安全性，适合小项目。</li>
<li><code>RS256</code>（RSA + SHA-256）或 <code>ES256</code>（ECDSA + SHA-256）：非对称加密，适合高安全需求。</li>
</ul>
</li>
<li><code>typ</code>：指定类型，一般设置为 <code>JWT</code>，应用程序可通过该项来区分不同类型的对象。</li>
</ul>
<h3>Payload</h3>
<p>Payload 中可以存储任意字段，供服务端获取用户身份、检验用户权限等需求：</p>
<div class="language-json" data-highlighter="shiki" data-ext="json" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-json"><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">{</span></span>
<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577">  "</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965">sub</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 328913</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577">  "</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965">aud</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">web</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577">  "</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965">role</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">admin</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577">  "</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965">exp</span><span style="--shiki-light:#99841877;--shiki-dark:#B8A96577">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 1733631682</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>此外，RFC 7519 中定义了一些<strong>标准字段</strong>，推荐使用但不强制：</p>
<p>| 字段    | 全称            | 含义                                        |
|</p>
]]></content>
    <published>2025-12-08T05:34:52.000Z</published>
  </entry>
  <entry>
    <title type="text">Rust Result 类型方法详解</title>
    <id>https://sunirein.tech/blog/k3v8ajdo/</id>
    <link href="https://sunirein.tech/blog/k3v8ajdo/"/>
    <updated>2025-12-04T13:28:39.000Z</updated>
    <summary type="html"><![CDATA[<p>在中，我们对 <code>Option</code> 的相关方法进行了分类和总结，方便大家理解。
本文将在此基础上，继续介绍 Rust 标准库中 <code>Result</code> 的相关方法。</p>
]]></summary>
    <content type="html"><![CDATA[<p>在中，我们对 <code>Option</code> 的相关方法进行了分类和总结，方便大家理解。
本文将在此基础上，继续介绍 Rust 标准库中 <code>Result</code> 的相关方法。</p>
<!-- more -->
<h2>引言：与 Option 的比较</h2>
<p>本质上，<code>Result</code> 相当于 <code>Option</code> 的一个扩展版本。
因此，<code>Option</code> 中的方法在 <code>Result</code> 中大多也有对应的实现。</p>
<p>这些相似的方法可以分为以下几类：</p>
<ul>
<li>
<p>完全相同的方法，如 <code>unwrap</code>、<code>filter</code> 等。
它们只处理 <code>Option</code> 中的 <code>Some</code> 分支或 <code>Result</code> 中的 <code>Ok</code> 分支，本质上没有区别。</p>
</li>
<li>
<p>除了名称不同外，功能完全相同的方法。
这是由于 <code>Option</code> 和 <code>Result</code> 中的命名不同所致，例如 <code>Option::is_some</code> 对应 <code>Result::is_ok</code>。</p>
</li>
<li>
<p><code>Error</code> 所特有的方法。
与 <code>Option::None</code> 不同，<code>Result</code> 中的 <code>Err</code> 可以存储值，与 <code>Ok</code> 和 <code>Some</code> 具有相同的地位，因此也提供了类似的方法。
这些方法都以 <code>_err</code> 结尾，功能相同，例如 <code>unwrap_err</code>、<code>inspect_err</code>、<code>map_err</code> 等。</p>
</li>
</ul>
<p>因此，如果你已经熟悉 <code>Option</code> 的相关方法，可以更容易地理解 <code>Result</code> 中的方法。</p>
<p>为了避免重复，这里我们只列出 <code>Result</code> 中独有的、或与 <code>Option</code> 中不同的方法。</p>
<h2>安全提取值</h2>
<p>这两个方法用于将 <code>Result</code> 中的 <code>Ok</code> 或 <code>Err</code> 分支转换为 <code>Option</code> 类型，用于从 <code>Result</code> 中安全地提取值。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ok</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> err</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">E</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span></code></pre>
</div><h2>静态提取值</h2>
<p>这两个方法利用了类型系统本身的安全性，可以将 <code>Result</code> 转换为 <code>T</code> 或 <code>E</code> 类型，但要求另一个类型必须是不可实例化的类型 <code>!</code>（即永远不会有值的类型）。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> into_ok</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> where</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> E</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> into</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> into_err</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> E</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> where</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> into</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span></code></pre>
</div><h2>作为程序返回值</h2>
<p><code>Result</code> 实现了 <code>Termination</code> trait，可以作为主函数的返回值。
由于 <code>main</code> 函数只支持整数类型的退出码，<code>Result</code> 会通过 <code>report</code> 方法将自身转换为 <code>ExitCode</code>。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">impl</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Termination</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> E</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Debug</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Termination</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> for</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Result</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> E</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> report</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> ExitCode</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><h2>Result 中不存在的方法</h2>
<p>下面这些方法在 <code>Option</code> 中存在，但在 <code>Result</code> 中并不存在类似方法：</p>
<ul>
<li><code>is_none_or</code>（但存在 <code>is_err_and</code>）</li>
<li><code>as_pin_ref</code> 和 <code>as_pin_mut</code></li>
<li><code>as_slice</code> 和 <code>as_mut_slice</code></li>
<li><code>filter</code></li>
<li><code>zip</code>、<code>zip_with</code> 和 <code>unzip</code></li>
<li><code>insert</code>、<code>get_or_insert</code>、<code>get_or_insert_default</code> 和 <code>get_or_insert_with</code></li>
<li><code>take</code>、<code>replace</code> 和 <code>take_if</code></li>
</ul>
<p>这些方法的缺失，主要是因为 <code>Result</code> 和 <code>Option</code> 的定位不同导致的。
<code>Option</code> 更接近一个值的容器，因此提供了更多与容器相关的方法。
而 <code>Result</code> 更侧重于错误处理和结果传递，因此其方法更多地围绕这两个方面展开。</p>
<h2>参考链接</h2>
<ul>
<li><a href="https://doc.rust-lang.org/std/result/enum.Result.html" target="_blank" rel="noopener noreferrer">Enum Result | Rust std</a></li>
<li><a href="https://doc.rust-lang.org/std/result/index.html" target="_blank" rel="noopener noreferrer">Module result | Rust std</a></li>
</ul>
<p>以上两处官方文档是最权威的参考资料。
在不确定具体行为或需要查看最新稳定版 API 时，优先以官方文档为准。</p>
]]></content>
    <published>2025-12-02T11:11:31.000Z</published>
  </entry>
  <entry>
    <title type="text">从 Nginx 到 Caddy：配置简化与自动 HTTPS 的魅力</title>
    <id>https://sunirein.tech/blog/4iizatfo/</id>
    <link href="https://sunirein.tech/blog/4iizatfo/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>前不久我的云服务器快到期了，重新买了台新的服务器，但是原来的 Nginx 配置文件比较复杂，迁移起来麻烦，而且证书自动续期也得重新配置。</p>
<p>在查阅资料时，我发现了 Caddy 这个轻量级的 Web 服务器，配置文件简洁明了，内置自动 HTTPS 功能，非常适合个人和小型项目使用。</p>
]]></summary>
    <content type="html"><![CDATA[<p>前不久我的云服务器快到期了，重新买了台新的服务器，但是原来的 Nginx 配置文件比较复杂，迁移起来麻烦，而且证书自动续期也得重新配置。</p>
<p>在查阅资料时，我发现了 Caddy 这个轻量级的 Web 服务器，配置文件简洁明了，内置自动 HTTPS 功能，非常适合个人和小型项目使用。</p>
<!-- more -->
<h2>为什么选择 Caddy？</h2>
<p><a href="https://caddyserver.com/" target="_blank" rel="noopener noreferrer">Caddy</a> 是一款基于 Go 语言编写的强大且可扩展的平台，提供“开箱即用”的使用体验，相比 Ngnix 有以下优势：</p>
<ul>
<li><strong>配置文件简洁</strong>：Caddy 的配置文件非常直观，提供了合理的默认值，减少了配置的复杂度。</li>
<li><strong>自动 HTTPS</strong>：Caddy 内置了 Let's Encrypt 支持，能够自动获取和续期 SSL 证书，无需额外配置。</li>
<li><strong>文档易读</strong>：Caddy 官方文档清晰易懂，不像 Nginx 那样零散臃肿。</li>
</ul>
<p>尽管 Caddy 可能在性能上不如 Nginx，特别是在高并发场景下，但对于个人网站和小型项目来说，Caddy 已经足够强大。</p>
<h2>使用 Caddy</h2>
<p>首先，按照<a href="https://caddyserver.com/docs/install" target="_blank" rel="noopener noreferrer">官方文档</a>的指引，安装好 Caddy 并启用。</p>
<p>在当前目录下，创建名为 <code>Caddyfile</code> 的配置文件，编辑该文件：</p>
<div class="language-caddy" data-highlighter="shiki" data-ext="caddy" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-caddy"><span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">www.sunirein.tech</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  redir</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> https://sunirein.tech</span><span style="--shiki-light:#999999;--shiki-dark:#666666">{</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">uri</span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">sunirein.tech</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  root</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> *</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /var/www</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  file_server</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  encode</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> gzip</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> zstd</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">ttrss.sunirein.tech</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  reverse_proxy</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> localhost:1081</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  encode</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> gzip</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> zstd</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>上面这份配置文件实现了以下功能：</p>
<ol>
<li>将 <code>www.sunirein.tech</code> 重定向到 <code>sunirein.tech</code>。</li>
<li>为 <code>sunirein.tech</code> 提供静态文件服务，文件根目录为 <code>/var/www</code>，并启用 gzip 和 zstd 压缩。</li>
<li>将 <code>ttrss.sunirein.tech</code> 的请求反向代理到本地的 1081 端口，并启用 gzip 和 zstd 压缩。</li>
</ol>
<p>此外，Caddy 会自动为这些域名获取和续期 SSL 证书，自动处理 HTTPS 重定向等问题，无需额外配置，非常省心。</p>
<p>如果需要为其他域名添加服务，只需在 <code>Caddyfile</code> 中添加相应的块即可。</p>
<p>之后，使用以下命令加载 Caddy 配置，使其生效：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">caddy</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> reload</span></span></code></pre>
</div><h2>可选：配置阿里云 DNS</h2>
<p>Caddy 默认通过 <strong>HTTP 质询</strong>或 <strong>TLS-ALPN 质询</strong>来验证域名所有权，从而自动获取证书。</p>
<p>但是，由于我配置了 DNS 分流，国外的访问流量会被解析到 Github Page，因此 Let's Encrypt 无法验证域名所有权。</p>
<p>这时，可以通过配置 <strong>DNS 质询</strong>来解决这个问题。这里我以阿里云 DNS 为例，其他 DNS 服务商的配置方法类似。</p>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>除了上述情况外，如果你需要申请通配符证书（如 <code>*.example.com</code>），也必须使用 DNS 质询。</p>
</div>
<div class="vp-steps"><ol>
<li>
<p>首先，需要下载对应的 DNS 模块：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">caddy</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> add-package</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> github.com/caddy-dns/alidns</span></span></code></pre>
</div></li>
<li>
<p>申请阿里云 DNS 访问密钥。</p>
<p>进入 <a href="https://ram.console.aliyun.com/users" target="_blank" rel="noopener noreferrer">阿里云访问控制台</a>，并创建一个子用户，授予 <code>AliyunDNSFullAccess</code> 权限。</p>
<p>点击你新创建的子用户，进入用户详情页，为该用户创建一个 <code>AccessKey</code>，并保存好 <code>AccessKey ID</code> 和 <code>AccessKey Secret</code>。</p>
</li>
<li>
<p>在 <code>Caddyfile</code> 中配置 DNS 质询。</p>
<p>可以在顶部添加全局配置：</p>
<div class="language-caddy" data-highlighter="shiki" data-ext="caddy" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-caddy"><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">{</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  acme_dns</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> alidns</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">    access_key_id</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> YOUR_ACCESS_KEY_ID</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">    access_key_secret</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> YOUR_ACCESS_KEY_SECRET</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>或者在域名块中单独配置：</p>
<div class="language-caddy" data-highlighter="shiki" data-ext="caddy" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-caddy"><span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">your.domain</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  tls</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">    dns</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> alidns</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">      access_key_id</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> YOUR_ACCESS_KEY_ID</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">      access_key_secret</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> YOUR_ACCESS_KEY_SECRET</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">    }</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div></li>
<li>
<p>最后，重新加载 Caddy 配置：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">caddy</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> reload</span></span></code></pre>
</div><p>这样，Caddy 就会使用阿里云 DNS 来验证域名所有权，从而成功获取 SSL 证书。</p>
</li>
</ol>
</div><h2>参考资料</h2>
<ul>
<li><a href="https://caddyserver.com/docs/" target="_blank" rel="noopener noreferrer">Caddy 官方文档</a></li>
<li><a href="https://caddy2.dengxiaolong.com/docs/" target="_blank" rel="noopener noreferrer">Caddy v2 中文文档</a></li>
<li><a href="https://www.cnblogs.com/turingguo/p/caddy.html" target="_blank" rel="noopener noreferrer">caddy 配置反向代理和 ssl 证书申请 | 博客园 turingguo</a></li>
</ul>
]]></content>
    <published>2025-10-13T09:53:13.000Z</published>
  </entry>
  <entry>
    <title type="text">Rust Option 类型方法详解</title>
    <id>https://sunirein.tech/blog/mc9zsqkp/</id>
    <link href="https://sunirein.tech/blog/mc9zsqkp/"/>
    <updated>2025-12-04T13:28:39.000Z</updated>
    <summary type="html"><![CDATA[<p><code>Option</code> 和 <code>Result</code> 是 Rust 中最常接触的几个类型。
为了方便使用，Rust 提供了许多便利的方法。
不过，刚接触这些方法时，很容易被这么多的方法搞晕。
本文将对 <code>Option</code> 的相关方法进行分类和总结，帮助你更好地理解和使用它们。</p>
]]></summary>
    <content type="html"><![CDATA[<p><code>Option</code> 和 <code>Result</code> 是 Rust 中最常接触的几个类型。
为了方便使用，Rust 提供了许多便利的方法。
不过，刚接触这些方法时，很容易被这么多的方法搞晕。
本文将对 <code>Option</code> 的相关方法进行分类和总结，帮助你更好地理解和使用它们。</p>
<!-- more -->
<h2>存在性与谓词判断</h2>
<p>下面两个方法用于快速判断 <code>Option</code> 的变体或在判断时对内部值做简单谓词检查。
它们能避免显式的模式匹配，从而使代码更紧凑。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> is_some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> bool</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> is_none</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> bool</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> is_some_and</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> f</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> impl</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> bool</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> is_none_or</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> f</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> impl</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> bool</span></span></code></pre>
</div><p>不过，当判断逻辑较复杂时，仍然建议使用 <code>match</code> 或 <code>if let</code> 以提高可读性。</p>
<h2>引用适配器</h2>
<p><code>as_ref</code>/<code>as_mut</code> 用于将 <code>Option&lt;T&gt;</code> 转换为对内部值的引用形式，从而避免移动所有权。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> as_ref</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> as_mut</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span></code></pre>
</div><p><code>as_deref</code> 系列在处理 <code>Option&lt;Box&lt;String&gt;&gt;</code> 这类嵌套可解引用类型时十分便捷，能直接得到目标类型的引用，避免 <code>map(|b| b.deref())</code> 的繁琐写法。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> as_deref</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> as</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Deref</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Target</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">where</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Deref</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> as_deref_mut</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> as</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Deref</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Target</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">where</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Deref</span></span></code></pre>
</div><p><code>as_pin</code> 系列适用于与 <code>Pin</code> 相关的 API，简化相关操作。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> as_pin_ref</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Pin</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>>)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">     -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Pin</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>></span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> as_pin_mut</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Pin</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>>)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Pin</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>></span></span></code></pre>
</div><p><code>as_slice</code> 系列在内部有元素时返回一个单元素切片，为空则返回空切片。
它们可以方便地 <code>Option&lt;T&gt;</code> 作为切片，参与迭代或切片 API。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> as_slice</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x26;</span><span style="--shiki-light:#999999;--shiki-dark:#666666">[</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">]</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> as_mut_slice</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x26;mut</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> [</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">]</span></span></code></pre>
</div><h2>提取内部元素</h2>
<h3>触发 panic 的取值</h3>
<p><code>expect</code> 与 <code>unwrap</code> 在调试或明确能保证存在时使用较为方便，但在生产代码中需谨慎，建议在无法合理处理 <code>None</code> 情形时才用，以避免运行时 panic。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> expect</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> msg</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x26;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> unwrap</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span></span></code></pre>
</div><h3>提供默认值的取值</h3>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> unwrap_or</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> default</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">                  // 立即求值</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> unwrap_or_else</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> f</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> impl</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 延迟求值</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> unwrap_or_default</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> where</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Default</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">     // 使用 T:Default</span></span></code></pre>
</div><h3>不做检查的取值</h3>
<p>该方法在 <code>None</code> 时会导致未定义行为，仅应在经过严格分析并能保证不为 <code>None</code> 的极端性能路径中使用。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> unsafe</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> unwrap_unchecked</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span></span></code></pre>
</div><h2>转换、映射与链式操作</h2>
<h3>转换成 Result</h3>
<p>当需要将 <code>Option</code> 上升为 <code>Result</code> 时（例如在错误传播链中），常需要用到这两个方法。
若错误值构造代价大应选择 <code>ok_or_else</code>。</p>
<p>在链式错误处理时，<code>ok_or_else</code> 常与 <code>?</code> 操作符结合使用以保持代码简洁。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 直接映射</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ok_or</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">E</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> err</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> E</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Result</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> E</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">                       // 立即求值</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> ok_or_else</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">E</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> err</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> impl</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> E</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Result</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> E</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD"> // 延迟求值</span></span></code></pre>
</div><p><code>transpose</code> 用于进行 <code>Option&lt;Result&lt;_&gt;&gt;</code> 和 <code>Result&lt;Option&lt;_&gt;&gt;</code> 的互换，常用于从可能失败的解析或异步接口收集结果。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">impl</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> E</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Result</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> E</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> transpose</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Result</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> E</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 相当于</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">match</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> self</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  Some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Ok</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">val</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  =></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Ok</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">val</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  Some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Err</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">err</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> =></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Err</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">err</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  None</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">           =></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Ok</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">None</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><h3>条件过滤</h3>
<p><code>filter</code> 在满足谓词时返回内部值，否则返回 <code>None</code>，是函数式风格中的常见表达。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> filter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">P</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> predicate</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> P</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">where</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  P</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> bool</span></span></code></pre>
</div><h3>扁平化</h3>
<p><code>flatten</code> 能消除一层 <code>Option</code> 嵌套，用于处理多层 Option 嵌套，避免手动 <code>unwrap</code> 或嵌套匹配。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">impl</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> flatten</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><h3>观察</h3>
<p><code>inspect</code> 会使用内部值来调用 <code>f</code>，并原样返回，<strong>不做任何修改</strong>。
它适合在链式调用中添加日志、断言或调试操作等不改变原值的操作。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> inspect</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> f</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">where</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  F</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><h3>映射</h3>
<p><code>map</code> 用于对内部值进行纯映射，结果会被自动封装回 <code>Option</code>。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> map</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> f</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">where</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  F</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> U</span></span></code></pre>
</div><div class="hint-container note">
<p class="hint-container-title">注</p>
<p>当映射函数本身可能失败并返回 <code>Option</code> 时，应优先使用 <code>and_then</code> 以避免嵌套 <code>Option</code>。</p>
</div>
<p>标准库还提供了下面两个带默认值的映射版本，当输入为 <code>None</code> 时将返回提供的默认值。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> map_or</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> default</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> f</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> U</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">where</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  F</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> U</span></span></code></pre>
</div><div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> map_or_else</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> D</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> default</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> D</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> f</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> U</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">where</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  D</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  F</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> U</span></span></code></pre>
</div><h3>压缩和解压缩</h3>
<p><code>zip</code> 只有在两个 <code>Option</code> 均有值的情况下才会返回 <code>Some((s, o))</code>，其余情况均返回 <code>None</code>。
它适合需要同时存在两个可选值才能继续处理的场景，例如同时从两个来源获取配置项。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> zip</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> other</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)></span></span></code></pre>
</div><p><code>zip_with</code> 提供了更灵活的压缩行为，允许在存在两个值时直接进行合并转换，避免额外的元组构造。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> zip_with</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> R</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> other</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> f</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">R</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">where</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  F</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> R</span></span></code></pre>
</div><p><code>unzip</code> 则是 <code>zip</code> 的逆操作，将 <code>Some((s, o))</code> 解包为 <code>(Some(s), Some(o))</code></p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">impl</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> unzip</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>)</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><h2>布尔运算</h2>
<h3>与、或</h3>
<p><code>and</code> 方法当自身是 <code>Some</code> 时返回 <code>optb</code>，是 <code>None</code> 时返回 <code>None</code>（相当于什么也不做）。<br>
<code>or</code> 方法当自身是 <code>None</code> 时返回 <code>optb</code>，是 <code>Some</code> 时返回自己（相当于什么也不做）。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> and</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> optb</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> or</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> optb</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span></code></pre>
</div><p><code>and_then</code> 和 <code>or_else</code> 则是它们的函数版本。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> and_then</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> f</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">where</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">U</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> or_else</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> f</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">where</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> F</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span></code></pre>
</div><div class="hint-container info">
<p class="hint-container-title">比较 <code>map</code> 与 <code>and_then</code></p>
<p><code>map</code> 的映射函数返回 <code>T</code>，最终会被包装成 <code>Option&lt;T&gt;</code>。
而 <code>and_then</code> 的映射函数返回 <code>Option&lt;T&gt;</code>，不会再被包装，用于可能失败的转换，可以防止多层 <code>Option</code> 嵌套。</p>
<p><code>and_then</code> 的作用类似于函数式编程中常见的 <code>flatmap</code>。</p>
</div>
<div class="hint-container note">
<p class="hint-container-title">注</p>
<p>Rust 提供了强大的模式匹配语法。
在链式调用中，合理选择 <code>map</code>/<code>and_then</code> 与 <code>and</code>/<code>or</code> 能显著提升表达力，
但当逻辑变得复杂或需要多分支处理时，用 <code>match</code> 更能表达清晰的分支语义。</p>
</div>
<h3>异或</h3>
<p>除了上面的方法外，Rust 还提供了一个不太常见的 <code>xor</code> 方法。
该方法接受两个 <code>Option</code>，当其中只有一个是 <code>Some</code> 时返回该值，否则返回 <code>None</code>。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> xor</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> optb</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span></code></pre>
</div><p><code>xor</code> 在某些需要“互斥存在”的场景很有用，但在日常编码中较少见，使用时应确保语义清晰以免误解。</p>
<h2>比较运算符</h2>
<p>如果 <code>T</code> 实现了 <code>PartialOrd</code> 或 <code>Ord</code>，<code>Option&lt;T&gt;</code> 会自动派生相应的实现。</p>
<p>在这种语境下，两个 <code>Some</code> 之间的比较遵循 <code>T</code> 原有的规则，而 <code>None</code> 小于任何的 <code>Some</code>。</p>
<p>该实现主要用于简化一些比较逻辑，例如对 <code>Vec&lt;Option&lt;T&gt;&gt;</code> 进行排序。
需要注意的是，这一约定会影响排序与集合操作的结果，在需要不同语义时应显式处理 <code>None</code>（例如在比较前将 <code>None</code> 映射为特定的哨值）。</p>
<h2>作为迭代器使用</h2>
<p>你可以直接将 <code>Option</code> 作为迭代器使用。
对于 <code>Some</code>，将作为一个单值迭代器；对于 <code>None</code>，将作为一个空迭代器。</p>
<p>该用法常见于串联迭代器，例如：</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">let</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> mayby_num</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">42</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">let</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> nums</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Vec</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">i32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">..</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">chain</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">mayby_num</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">chain</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">4</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">..</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">8</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">collect</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">nums</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> [</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 42</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 6</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 7</span><span style="--shiki-light:#999999;--shiki-dark:#666666">])</span></span></code></pre>
</div><p>以这种方式串联迭代器，可以获得一致的返回值类型，从而作为 <code>impl Iterator</code> 的返回值。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> make_iter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">do_insert</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> impl</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Iterator</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Item</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> i32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">    match</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> do_insert</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">        true</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> =></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">..</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">chain</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">42</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">chain</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">4</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">..</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">8</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">        false</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> =></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">..</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">chain</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">None</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">chain</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">4</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">..</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">8</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">    }</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>这里不能使用 <code>once()</code> 和 <code>empty()</code>，否则会因为类型不匹配而报错。</p>
</div>
<h2>从迭代器聚合为 Option</h2>
<h3>容器聚合</h3>
<p><code>Option</code> 实现了 <code>FromIterator&lt;Option&lt;T&gt;&gt;</code> trait，可以将任何 <code>Option</code> 的迭代器收集成一个包含值的容器的 <code>Option</code>。</p>
<p>如果迭代器内的值全是 <code>Some</code>，将返回包含这些值的容器；否则，返回 <code>None</code>。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">let</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> v</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> [</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">11</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)];</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">let</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> res</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Vec</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> v</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">into_iter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">collect</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">res</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">vec!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">[</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 11</span><span style="--shiki-light:#999999;--shiki-dark:#666666">]));</span></span></code></pre>
</div><div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">let</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> v</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> [</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> None</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">11</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)];</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">let</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> res</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Vec</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> v</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">into_iter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">collect</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">res</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> None</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><h3>聚合运算（求积、求和）</h3>
<p><code>Option</code> 实现了 <code>Product&lt;Option&lt;T&gt;&gt;</code> 和 <code>Sum&lt;Option&lt;T&gt;&gt;</code> trait，可以通过 <code>product</code> 或 <code>sum</code> 方法来对 <code>Option</code> 迭代器求积或求和。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">let</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> input</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> [</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">123</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">invalid</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">2</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">];</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">let</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> res</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">i32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> input</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">into_iter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">map</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">|</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">s</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">|</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> s</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">parse</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">i32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ok</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">product</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">res</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> None</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">let</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> input</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> [</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">123</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">2</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">2</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">];</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">let</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> res</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">i32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> input</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">into_iter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">map</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">|</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">s</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">|</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> s</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">parse</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">i32</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ok</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">product</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">res</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Some</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">492</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><h2>就地修改与所有权操作</h2>
<h3>插入与可变引用</h3>
<p><code>insert</code> 会用新值替换内部值并返回可变引用，适合希望在容器中就地构造或更新值的场景。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> insert</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> value</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x26;mut</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span></span></code></pre>
</div><p>下面几个方法与 <code>insert</code> 类似，但仅在为 <code>None</code> 时更新内部值。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> get_or_insert</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> value</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x26;mut</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> get_or_insert_default</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x26;mut</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> where</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Default</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> get_or_insert_with</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> f</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> impl</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x26;mut</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span></span></code></pre>
</div><p>这些方法在处理缓存、懒初始化或复用 <code>Option</code> 内部存储时非常有用。</p>
<h3>移动与替换</h3>
<p><code>take</code> 将值移动出去并把容器置为 <code>None</code>，这是实现可复用容器或交换逻辑的常见手段。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> take</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span></code></pre>
</div><p><code>replace</code> 则会直接用新值替换旧值，并同时获取旧值进行处理。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> replace</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> value</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span></code></pre>
</div><p>与 <code>take</code> 不同，<code>replace</code> 保证容器最终有新值，因此常用于更新缓存或重置状态。</p>
<div class="hint-container info">
<p class="hint-container-title"><code>take_if</code> 方法</p>
<p><a href="https://github.com/rust-lang/rust/issues/98934" target="_blank" rel="noopener noreferrer">#98934</a> 添加了 <code>take_if</code> 方法：</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> take_if</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">P</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> predicate</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> P</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">where</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  P</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">:</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> FnOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;mut</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> bool</span></span></code></pre>
</div><p>该方法仅在谓词被满足时调用 <code>take</code>，为某些需要“有条件移动”的场景提供了便利。</p>
<p>不过，它也允许你直接在谓词中修改内部值，这可能有悖直觉，使用时要格外谨慎以免引入难以察觉的副作用。</p>
</div>
<h2>参考链接</h2>
<ul>
<li><a href="https://doc.rust-lang.org/std/option/enum.Option.html" target="_blank" rel="noopener noreferrer">Enum Option | Rust std</a></li>
<li><a href="https://doc.rust-lang.org/std/option/index.html" target="_blank" rel="noopener noreferrer">Module option | Rust std</a></li>
</ul>
<p>以上两处官方文档是最权威的参考资料。
在不确定具体行为或需要查看最新稳定版 API 时，优先以官方文档为准。</p>
]]></content>
    <published>2025-10-05T15:28:23.000Z</published>
  </entry>
  <entry>
    <title type="text">Rust 中如何将 Path 转换为字符串？</title>
    <id>https://sunirein.tech/blog/ai80resl/</id>
    <link href="https://sunirein.tech/blog/ai80resl/"/>
    <updated>2025-12-04T13:28:39.000Z</updated>
    <summary type="html"><![CDATA[<p>在开发中，将路径转换为字符串是一种常见的需求。
由于字符串编码的复杂性，Rust 在处理这一问题时也有些麻烦，初学者可能会遇到问题。
本文将探讨如何在 Rust 中解决这一问题，并给出项目开发实践中的一些建议。</p>
]]></summary>
    <content type="html"><![CDATA[<p>在开发中，将路径转换为字符串是一种常见的需求。
由于字符串编码的复杂性，Rust 在处理这一问题时也有些麻烦，初学者可能会遇到问题。
本文将探讨如何在 Rust 中解决这一问题，并给出项目开发实践中的一些建议。</p>
<!-- more -->
<h2>表示路径的类型</h2>
<p>Rust 中有两种表示路径的类型：</p>
<ul>
<li><code>PathBuf</code>：拥有所有权且可变的路径，类似于 <code>String</code>。</li>
<li><code>Path</code>：路径的切片，类似于 <code>str</code>。</li>
</ul>
<p>这两种类型实际上是 <code>OsString</code> 和 <code>OsStr</code> 的封装，而非使用 Rust 默认的字符串类型，因此在转换为 <code>String</code> 或 <code>str</code> 时比较麻烦。</p>
<div class="hint-container info">
<p class="hint-container-title"><code>OsString</code></p>
<p>用于存储当前平台的原生字符串类型。</p>
<p>字符串在 Unix 上通常为非零 UTF-8 编码，在 Windows 上通常为非零 UTF-16 编码。
这与 Rust 的默认字符串不匹配，因此使用 <code>OsString</code> 来弥合这种差距。</p>
<p>需要注意，<code>OsString</code> 内部不一定是按照平台原生的形式来保存字符串，只是提供了一种与原生字符串交互的途径。</p>
</div>
<h2>路径转换为字符串的方法</h2>
<p>由于 <code>PathBuf</code> 实现了 <code>Deref&lt;Target=Path&gt;</code>，因此这里仅介绍 <code>Path</code> 的方法。</p>
<h3>获取 <code>OsStr</code> 后转化</h3>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">impl</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Path</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  pub</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> as_os_str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x26;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">OsStr</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>可以调用上面提供的方法先将路径转化为 <code>OsStr</code> 再转换为字符串。</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">impl</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> OsStr</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  /// 如果为有效的 Unicode 编码，返回内部 str，否则返回 None</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  /// 该方法不会涉及拷贝或重编码，但是需要对字符串做有效性检查</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  pub</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> to_str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  /// 如果为有效的 Unicode 编码，返回内部 str，不作任何转换</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  /// 否则，会将所有无效部分替换为 U+FFFD(�) 再返回，这需要分配新的内存</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  pub</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> to_string_lossy</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Cow</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;'</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>如果最终需要得到 <code>String</code>，还需再做额外的转换，完整代码如下：</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">let</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> path</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Path</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">new</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">./foo/bar.txt</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  String</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">new</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">./foo/bar.txt</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span></span>
<span class="line"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">  path</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">as_os_str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">to_str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">unwrap</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">to_string</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  String</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">new</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">./foo/bar.txt</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span></span>
<span class="line"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">  path</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">as_os_str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">to_string_lossy</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">into_owned</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>通常如果使用 <code>to_str</code> 会搭配 <code>unwrap</code>，表示我们预期输入是合法 Unicode。</p>
<p>而如果只是想要一个可用的字符串用于打印/日志，一般建议使用 <code>to_string_lossy</code>。
它只会在必要的时候进行有损转换，大多数情况下与 <code>to_str</code> 没有性能差异。</p>
<h3>直接调用 <code>Path</code> 的转换方法</h3>
<p>由于将 <code>Path</code> 转换为字符串的操作很普遍，Rust 为上面的操作提供了以下便携方法：</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">impl</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Path</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  pub</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> to_str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Option</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  pub</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> to_string_lossy</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Cow</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;'</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>在使用时可以直接调用这两个方法，无需经过 <code>OsStr</code> 的转换。</p>
<h3><code>Display</code> 接口</h3>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">impl</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Path</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  pub</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> display</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> std</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">path</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Display</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;'</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p><code>Path</code> 提供了一个专门用于打印路径的方法，其行为类似 <code>to_string_lossy</code>，返回一个实现了 <code>Display</code> trait 的类型。
因此也可以使用该方法来获取字符串：</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">let</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> path</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Path</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">new</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">./foo/bar.txt</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">assert_eq!</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  String</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">new</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">./foo/bar.txt</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span></span>
<span class="line"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">  path</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">display</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">to_string</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>不过一般不建议使用该方法，更推荐使用意义更为明确的 <code>to_string_lossy</code>。</p>
<h2>个人实践</h2>
<p>一般我会根据需要选择 <code>to_str</code> 和 <code>to_string_lossy</code>：</p>
<ul>
<li>如果要保证输入为合法 Unicode，选择 <code>to_str</code>。</li>
<li>如果无法确认输入的编码，或者需要用于打印/日志，选择 <code>to_string_lossy</code>。</li>
</ul>
<p>使用这些方法将路径转换为字符串会比较冗长，因此我会将其封装为对应的 trait:</p>
<div class="language-rust" data-highlighter="shiki" data-ext="rust" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-rust"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">pub</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> trait</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> PathStr</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> to_utf8_string</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> String</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">impl</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> PathStr</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> for</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Path</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  fn</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> to_utf8_string</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">self</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -></span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> String</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">    self</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">to_str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">unwrap</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">to_string</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>你可以根据自己的需要调整上面函数，只需在使用时导入对应的 trait 即可。</p>
]]></content>
    <published>2025-09-10T11:54:49.000Z</published>
  </entry>
  <entry>
    <title type="text">使用 GPG/SSH 为你的提交签名</title>
    <id>https://sunirein.tech/blog/g4xq10jf/</id>
    <link href="https://sunirein.tech/blog/g4xq10jf/"/>
    <updated>2026-04-03T13:57:07.000Z</updated>
    <summary type="html"><![CDATA[<p>之前在 Github 上为某个开源项目提交 PR 的时候，发现那个项目开启了签名验证，要求提交必须经过签名。
这是我第一次知道 Git 有签名功能，就去了解了一下它是什么，以及如何设置，于是写下了这篇文章。</p>
]]></summary>
    <content type="html"><![CDATA[<p>之前在 Github 上为某个开源项目提交 PR 的时候，发现那个项目开启了签名验证，要求提交必须经过签名。
这是我第一次知道 Git 有签名功能，就去了解了一下它是什么，以及如何设置，于是写下了这篇文章。</p>
<!-- more -->
<h2>什么是签名？</h2>
<p>大家平时在 Github 上查看提交信息时，应该会注意到，在提交列表里有些提交上面会显示一个 <code>Verified</code> 状态。
这表示其经过签名且被 Github 验证通过。</p>
<p></p>
<p>签名是 Git 的一项安全机制，用于验证提交是由用户本人发起的。
用户可以在本地对 Commit 和标签进行签名，经过验证的签名便会在 Github 在被显示为 <code>Verified</code> 状态。</p>
<div class="hint-container info">
<p class="hint-container-title">签名的必要性</p>
<p>由于 Git 允许用户任意设置提交者的身份，因此签名是 Git 提供的一项用于验证提交者身份的重要工具。
不过，诸如 Github、Gitlab 这些网站本身已经提供了一套足够安全的用户账号机制。
对于仅部署在这些网站上的项目来说，实际上也没有开启该机制的必要。</p>
<p>只是由于部分开源项目对签名有要求，因此平时提交时还是建议开启签名。</p>
</div>
<h2>生成签名</h2>
<p>Git 为个人用户提供了 GPG 和 SSH 两种签名方式。</p>
<h3>GPG</h3>
<p>在命令行键入下列命令即可创建 GPG 签名：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">gpg</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --full-generate-key</span></span></code></pre>
</div><p>之后按照提示，依次输入密钥类型、密钥大小、有效期、用户信息等信息。
注意这里的用户信息应该与你在 Git 中设置的信息一致。</p>
<p>最后再输入使用该密钥所需的安全密钥，在每次使用该密钥时 GPG 都会要求你键入该安全密钥。</p>
<p>密钥生成完后可以使用下面的命令来查看已经生成的密钥：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">gpg</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --list-secret-keys</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --keyid-format=long</span></span></code></pre>
</div><p>生成的密钥类似下面这样：</p>
<div class="language-ansi" data-highlighter="shiki" data-ext="ansi" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-ansi"><span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">pub   ed25519/EFBD20CF51E1A4A0 2025-08-17 [SC]</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">      C53C71B1F960AFD0DD770485EFBD90CF51E1A4A0</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">uid                      User &#x3C;email@example.com></span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">sub   cv25519/1F3FD0F56C3D54B3 2025-08-17 [E]</span></span></code></pre>
</div><p>这里 <code>EFBD20CF51E1A4A0</code> 是该密钥的 ID。输入下列命令，替换相应的密钥 ID，以获取完整的 GPG 密钥：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">gpg</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --armor</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --export</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> EFBD20CF51E1A4A0</span></span></code></pre>
</div><p>之后打开 <a href="https://github.com/settings/keys" target="_blank" rel="noopener noreferrer">Github</a>，将生成的 GPG 密钥上传到你的账户，这样 Github 才会信任这个签名。</p>
<h3>SSH</h3>
<p>SSH 签名的生成较为简单，替换下面的邮箱为你实际使用的邮箱即可：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ssh-keygen</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -t</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> ed25519</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -C</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">your_email@example.com</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span></span></code></pre>
</div><p>如果你已经使用 SSH 生成过签名，这里 SSH 会提示你键入新的文件来保存签名，请记住这个文件的位置。</p>
<p>之后同样需要将签名上传到 <a href="https://github.com/settings/keys" target="_blank" rel="noopener noreferrer">Github</a> 上，
注意 SSH 密钥那一栏要选择 <code>Signing Key</code> 而不是 <code>Authentication Key</code>。</p>
<div class="hint-container note">
<p class="hint-container-title">注</p>
<p>你也可以复用已经生成的 <code>Authentication Key</code>，但是需要将该密钥重复上传一次，设置为 <code>Signing Key</code>。</p>
</div>
<h2>设置 Git 使用签名</h2>
<h2>在提交时使用签名</h2>
<p>在提交时，使用 <code>-S</code> 标志对该提交进行签名：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">git</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> commit</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -S</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -m</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">COMMIT_MESSAGE</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span></span></code></pre>
</div><p>如果是标签，则使用 <code>-s</code> 标志：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">git</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> tag</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -s</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> MYTAG</span></span></code></pre>
</div><p>如果设置成功，你便可以在 Github 上看到对应的 <code>Verified</code> 标志。</p>
<h3>自动签名</h3>
<p>如果你觉得手动签名比较麻烦，且容易遗忘，可以通过下面的配置让 Git 在提交和创建标签时自动帮你签名：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">git</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> config</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --global</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> commit.gpgsign</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> true</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">git</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> config</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --global</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> tag.gpgSign</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> true</span></span></code></pre>
</div><h3>为已提交的 commit 签名</h3>
<p>有时你的 commit 已经提交，而你需要为这些提交签名。</p>
<p>首先找到你需要签名的最早的 commit，选择该 commit，启动交互式变基：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">git</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> rebase</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -i</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">commit-has</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">h</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span></span></code></pre>
</div><p>之后给该 commit 及其后面的所有 commit 签名：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">git</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> commit</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --amend</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -S</span></span></code></pre>
</div><div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>使用这种方法相当于创建了新的提交，如果要上传到 Git 仓库需要加上 <code>--force</code> 标志。
如果实在没有必要，不建议使用这种方法。</p>
</div>
<h2>GPG 和 SSH 密钥如何选择？</h2>
<p>GPG 提供了更多高级的安全功能，例如密钥本身会携带用户信息、支持设置有效期等，而 SSH 则相对简单。
但两者的安全性更多取决于你使用的加密算法与密钥长度，并无优劣之分。</p>
<p>此外，GPG 密钥每次使用都需要用户键入相应的安全密钥，较为繁琐。
尽管有专门的工具可以帮你跳过这一过程，但配置起来也比较麻烦，不如 SSH 密钥简单。</p>
<p>如果你只是需要简单的签名功能以满足某些仓库的需求，配置较为简单的 SSH 密钥就已经够用了。</p>
<h2>参考文献</h2>
<ul>
<li><a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification" target="_blank" rel="noopener noreferrer">About commit signature verification | GitHub Docs</a></li>
</ul>
]]></content>
    <published>2025-08-17T07:45:28.000Z</published>
  </entry>
  <entry>
    <title type="text">GNU/Stow —— 管理编译安装程序与用户配置文件</title>
    <id>https://sunirein.tech/blog/2xg2t5km/</id>
    <link href="https://sunirein.tech/blog/2xg2t5km/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<h2>什么是 GNU/Stow？</h2>
<blockquote>
<p>GNU Stow 是一个符号链接农场管理器，它可以将位于文件系统不同目录中的不同软件包和数据，伪装成安装在同一个位置。</p>
</blockquote>
<p>使用 GNU/Stow 这一简单的工具，可以帮我们实现 Linux 上的两大难题：</p>
<ul>
<li>管理手动编译安装的程序</li>
<li>管理复杂的用户配置文件</li>
</ul>
<h2>安装</h2>
<p>各大发行版的包管理器都有 GNU/Stow，不过我更推荐直接从<a href="https://www.gnu.org/software/stow/" target="_blank" rel="noopener noreferrer">官网</a>下载源代码，
手动编译安装，以获取最新版本。</p>]]></summary>
    <content type="html"><![CDATA[<h2>什么是 GNU/Stow？</h2>
<blockquote>
<p>GNU Stow 是一个符号链接农场管理器，它可以将位于文件系统不同目录中的不同软件包和数据，伪装成安装在同一个位置。</p>
</blockquote>
<p>使用 GNU/Stow 这一简单的工具，可以帮我们实现 Linux 上的两大难题：</p>
<ul>
<li>管理手动编译安装的程序</li>
<li>管理复杂的用户配置文件</li>
</ul>
<h2>安装</h2>
<p>各大发行版的包管理器都有 GNU/Stow，不过我更推荐直接从<a href="https://www.gnu.org/software/stow/" target="_blank" rel="noopener noreferrer">官网</a>下载源代码，
手动编译安装，以获取最新版本。</p>
<h2>使用</h2>
<p>GNU/Stow 会自动在当前目录的上一级目录创建软链接。
例如，如果当前目录是 <code>~/dotfiles</code>，当我们运行 <code>stow bash</code> 时，GNU/Stow 会在上一级目录，即 <code>~</code> 中，
为 <code>~/dotfiles/bash</code> 中的所有文件创建软链接。
如果目标是文件夹，当对应文件夹存在时，GNU/Stow 会为文件夹下的所有文件创建软链接，否则直接创建该文件夹的软链接。</p>
<p>在执行具体命令前，可以使用 <code>-nv</code> 来检查将要执行的操作，而不执行这些操作。</p>
<p>使用 <code>--delete</code> 参数可以删除对应包的软链接。</p>
<p>更多的参数和用法详见<a href="https://www.gnu.org/software/stow/manual/html_node/Invoking-Stow.html" target="_blank" rel="noopener noreferrer">官方文档</a>。</p>
<h2>管理编译安装程序</h2>
<p>在 Linux 中，如果我们要手动安装一个程序，通常有两种做法：</p>
<ul>
<li>将其安装到 <code>/opt/xxx/</code> 中，并手动添加到环境目录。这可以是全局环境相对整洁，但需要手动设置大量的相关配置。</li>
<li>直接将其安装到 <code>/usr/local/</code> 中。这种方法最直接，不需要进行任何额外配置，但在卸载程序时较为麻烦。</li>
</ul>
<p>GNU/Stow 为我们提供了第三个选择。</p>
<p>这里以安装 <strong>hello</strong> 程序为例：</p>
<div class="vp-steps"><ol>
<li>
<p>首先创建 <code>stow</code> 文件夹：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> mkdir</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -p</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /usr/local/hello</span></span></code></pre>
</div></li>
<li>
<p>将程序安装到该文件夹下的对应子目录中：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">wget</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> https://ftp.gnu.org/gnu/hello/hello-2.12.tar.gz</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">tar</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -zxvf</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> hello-2.12.tar.gz</span></span>
<span class="line"><span style="--shiki-light:#998418;--shiki-dark:#B8A965">cd</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> hello-2.12</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">./configure</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --prefix=/usr/local/hello</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD"> # 这里设置安装目录</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">make</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> make</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> install</span></span></code></pre>
</div></li>
<li>
<p>然后转到 <code>stow</code> 文件夹下，运行下列命令来生成相应的软链接：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#998418;--shiki-dark:#B8A965">cd</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /usr/local/stow</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> stow</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -v</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> hello</span></span></code></pre>
</div></li>
<li>
<p>如果后续需要删除该程序，只需要删除对应的软链接：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#998418;--shiki-dark:#B8A965">cd</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /usr/local/stow</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> stow</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --delete</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> hello</span></span></code></pre>
</div></li>
</ol>
</div><div class="hint-container tip">
<p class="hint-container-title">使用技巧</p>
<ol>
<li>在运行相应命令前，请先使用 <code>-nv</code> 参数来查看对应的行为，避免意外情况。</li>
<li>由于 GNU/Stow 在遇到不存在的文件夹时会直接创建该文件夹的软链接，
推荐先在 <code>/usr/local/</code> 下创建好 <code>bin</code>、<code>lib</code>、<code>include</code>、<code>share</code>、<code>man</code> 等常见文件夹。</li>
</ol>
</div>
<h2>管理用户配置文件</h2>
<p>Linux 下用户配置文件通常以 <code>dotfiles</code> 的形式来管理。
但不同软件的配置位置通常不太一样，难以统一地管理，这时就可以借助 GNU/Stow。</p>
<p>例如，下面是一个配置文件的示例：</p>
<div class="vp-file-tree"><p class="vp-file-tree-title">~/dotfiles</p>

</div>
<p>这样就将配置文件全部集中到一个文件夹中，方便管理，之后可以使用 <code>stow package</code> 来加载对应的配置文件。</p>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>由于不同软件的配置文件之间可以存在一定的依赖关系，使用这种方法管理的不同配置文件之间难以保证完全地独立性，
因此是否要使用 GNU/Stow 来管理用户配置文件，还是取决于个人的使用习惯和需求。</p>
<p>此外，一些软件的配置文件相对比较复杂，例如 <code>neovim</code>，更推荐用一个专门的仓库来管理它。</p>
</div>
<h2>相关链接</h2>
<ul>
<li><a href="https://www.gnu.org/software/stow/" target="_blank" rel="noopener noreferrer">GNU/Stow 官网</a></li>
<li><a href="https://www.gnu.org/software/stow/manual/stow.html" target="_blank" rel="noopener noreferrer">GNU/Stow 用户手册</a></li>
</ul>
]]></content>
    <published>2025-07-18T06:44:08.000Z</published>
  </entry>
  <entry>
    <title type="text">Just —— 类似 Make 的任务运行器</title>
    <id>https://sunirein.tech/blog/46i566mf/</id>
    <link href="https://sunirein.tech/blog/46i566mf/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<h2>背景</h2>
<p>在终端日常操作中，我们经常需要使用一些<strong>重复</strong>的特定操作，而且这些操作<strong>与项目绑定</strong>，不方便放在全局环境中。
例如，我在编辑博客时，就经常需要运行 <code>vuepress dev docs</code> 来实时预览，使用 <code>vuepress build docs</code> 来构建。</p>
<p>总是键入这些命令不仅繁琐，而且一些平时使用频率相对较少的命令也容易遗忘，<code>just</code> 就是这样一个简化在命令行中重复操作的工具。</p>
<h2>介绍</h2>
<p><code>just</code> 是一个使用 <code>rust</code> 编写的，语法类似 <code>make</code> 的任务运行器。</p>]]></summary>
    <content type="html"><![CDATA[<h2>背景</h2>
<p>在终端日常操作中，我们经常需要使用一些<strong>重复</strong>的特定操作，而且这些操作<strong>与项目绑定</strong>，不方便放在全局环境中。
例如，我在编辑博客时，就经常需要运行 <code>vuepress dev docs</code> 来实时预览，使用 <code>vuepress build docs</code> 来构建。</p>
<p>总是键入这些命令不仅繁琐，而且一些平时使用频率相对较少的命令也容易遗忘，<code>just</code> 就是这样一个简化在命令行中重复操作的工具。</p>
<h2>介绍</h2>
<p><code>just</code> 是一个使用 <code>rust</code> 编写的，语法类似 <code>make</code> 的任务运行器。</p>
<p>很多人应该都用 <code>make</code> 来运行一些简单任务，但是 <code>make</code> 毕竟是一个构造系统，本身也具有一定的复杂性，例如需要指定 <code>.PHONY</code>。
<code>just</code> 避免了很多 <code>make</code> 的复杂性，更专注于任务管理。</p>
<p>与 <code>make</code> 类似，<code>just</code> 使用 <code>justfile</code> 来定义任务：</p>
<div class="code-block-title" data-title="justfile"><div class="code-block-title-bar"><span class="title">justfile</span></div><div class="language-just" data-highlighter="shiki" data-ext="just" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-just"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">dev</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    pnpm run dev --open</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">deploy</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    @echo "Deploying to aliyun..."</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    pnpm run build</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    rsync</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> -avz</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> --delete</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> -e</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ssh</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ./docs/.vuepress/dist</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> aliyun</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Page</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    @echo "Deploying to github..."</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    git push</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">lint</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    pnpm run lint</span></span></code></pre>
</div></div><p>运行 <code>just install</code> 时，<code>just</code> 会在当前目录和父目录寻找文件寻找 <code>justfile</code> 并运行它。</p>
<p>此外，<code>just</code> 还具有以下 <code>make</code> 不具备的特性：</p>
<ul>
<li>指定运行命令的 shell，甚至可以使用 <code>python</code> 解释器等</li>
<li>更友好的语法</li>
<li>每个配方使用独立的环境，互不干扰</li>
<li>接受命令行参数</li>
<li>用户友好的错误报告</li>
<li>加载 <code>.env</code> 环境变量文件</li>
</ul>
<h2>与 <code>npm</code> 脚本的比较</h2>
<p>并非每个项目都具有 <code>package.json</code>，而且 <code>npm</code> 脚本的功能有限，只能运行一些简单的命令。</p>
<h2>相关链接</h2>
<ul>
<li><a href="https://just.systems/" target="_blank" rel="noopener noreferrer">官网</a></li>
<li><a href="https://github.com/casey/just" target="_blank" rel="noopener noreferrer">Github 仓库</a></li>
</ul>
<p>建议阅读 <a href="https://just.systems/man/zh/" target="_blank" rel="noopener noreferrer">Just 用户指南</a>了解更多细节。</p>
]]></content>
    <published>2025-07-09T09:06:40.000Z</published>
  </entry>
  <entry>
    <title type="text">笔记索引</title>
    <id>https://sunirein.tech/notes/</id>
    <link href="https://sunirein.tech/notes/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<h2>文档</h2>
]]></summary>
    <content type="html"><![CDATA[<h2>文档</h2>
]]></content>
    <published>2025-06-27T08:25:51.000Z</published>
  </entry>
  <entry>
    <title type="text">欢迎使用 GoogleTest！</title>
    <id>https://sunirein.tech/gtest/welcome/</id>
    <link href="https://sunirein.tech/gtest/welcome/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<h2>译者言</h2>
<p>本系列为 GoogleTest 官方文档的非官方中文翻译，在 AI 辅助下由人工雕琢完成。</p>
<p>GoogleTest 是当下 C++ 最流行的测试框架，但却缺少较好的中文学习资料。
译者对官方文档进行了翻译，希望能对要学习 GoogleTest 的中文用户有所帮助。</p>
<p>文章尽量保留了原文的结构，对于一些原文中含糊易引发歧义或存在错误的地方进行了注释与修正。
由于官方文档本身缺乏维护，加上译者水平有限，难免存在一些翻译不好或不容易理解的地方，望谅解。</p>
<p>如果存在疑问，或发现任何问题，可在文章下面留言，译者将及时回复。</p>
<h2>文档架构</h2>]]></summary>
    <content type="html"><![CDATA[<h2>译者言</h2>
<p>本系列为 GoogleTest 官方文档的非官方中文翻译，在 AI 辅助下由人工雕琢完成。</p>
<p>GoogleTest 是当下 C++ 最流行的测试框架，但却缺少较好的中文学习资料。
译者对官方文档进行了翻译，希望能对要学习 GoogleTest 的中文用户有所帮助。</p>
<p>文章尽量保留了原文的结构，对于一些原文中含糊易引发歧义或存在错误的地方进行了注释与修正。
由于官方文档本身缺乏维护，加上译者水平有限，难免存在一些翻译不好或不容易理解的地方，望谅解。</p>
<p>如果存在疑问，或发现任何问题，可在文章下面留言，译者将及时回复。</p>
<h2>文档架构</h2>
<p>GoogleTest 是 Google 的 C++ 测试与模拟框架。
本用户指南包含以下内容：</p>
<ul>
<li>：讲授如何使用 GoogleTest 编写简单的测试。推荐初学者首先阅读该指南。</li>
<li>：当你读完初学者指南，且希望更充分利用 GoogleTest 时，阅读该指南。</li>
<li>：描述了一些 GoogleTest 的代码示例。</li>
<li>：使用时遇到问题想要获得帮助？请查看这里。</li>
<li>：讲授如何创建并在测试中使用模拟对象。</li>
<li>：包含常见模拟用例的技巧和方法。</li>
<li>：匹配器、动作和不变量等的便捷索引。</li>
<li>：包含对一些特定 gMock 问题的解答<annotation label="old-gMock" :total="1"><template #item-0><p>译注：该解答针对旧式的 gMock，一些对现在的 gMock 已经不再适用，但仍有参考价值。</p>
</template></annotation>。</li>
</ul>
]]></content>
    <published>2025-04-22T11:07:40.000Z</published>
  </entry>
  <entry>
    <title type="text">社区贡献文档</title>
    <id>https://sunirein.tech/gtest/community_created_documentation/</id>
    <link href="https://sunirein.tech/gtest/community_created_documentation/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>以下是由 Googletest 社区创建的文档链接列表（排名不分先后）：</p>
<ul>
<li><a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/googletest/insights.md" target="_blank" rel="noopener noreferrer">Googlemock Insights</a>，
作者：<a href="https://github.com/ElectricRCAircraftGuy" target="_blank" rel="noopener noreferrer">ElectricRCAircraftGuy</a></li>
</ul>]]></summary>
    <content type="html"><![CDATA[<p>以下是由 Googletest 社区创建的文档链接列表（排名不分先后）：</p>
<ul>
<li><a href="https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/googletest/insights.md" target="_blank" rel="noopener noreferrer">Googlemock Insights</a>，
作者：<a href="https://github.com/ElectricRCAircraftGuy" target="_blank" rel="noopener noreferrer">ElectricRCAircraftGuy</a></li>
</ul>
]]></content>
    <published>2025-04-21T08:54:09.000Z</published>
  </entry>
  <entry>
    <title type="text">使用 pkg-config</title>
    <id>https://sunirein.tech/gtest/pkgconfig/</id>
    <link href="https://sunirein.tech/gtest/pkgconfig/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<h2>CMake</h2>
<p>在 CMake 中使用 <code>pkg-config</code> 的典型配置如下：</p>
<div class="language-cmake" data-highlighter="shiki" data-ext="cmake" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cmake"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">find_package</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(PkgConfig)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pkg_search_module(GTEST REQUIRED gtest_main)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">add_executable</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(testapp)</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">target_sources</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(testapp </span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">PRIVATE</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> samples/sample3_unittest.cc)</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">target_link_libraries</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(testapp </span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">PRIVATE</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ${GTEST_LDFLAGS}</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">)</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">target_compile_options</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(testapp </span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">PRIVATE</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ${GTEST_CFLAGS}</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">enable_testing</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">()</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">add_test</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(first_and_only_test testapp)</span></span></code></pre>
</div>]]></summary>
    <content type="html"><![CDATA[<h2>CMake</h2>
<p>在 CMake 中使用 <code>pkg-config</code> 的典型配置如下：</p>
<div class="language-cmake" data-highlighter="shiki" data-ext="cmake" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cmake"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">find_package</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(PkgConfig)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pkg_search_module(GTEST REQUIRED gtest_main)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">add_executable</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(testapp)</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">target_sources</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(testapp </span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">PRIVATE</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> samples/sample3_unittest.cc)</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">target_link_libraries</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(testapp </span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">PRIVATE</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ${GTEST_LDFLAGS}</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">)</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">target_compile_options</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(testapp </span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">PRIVATE</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ${GTEST_CFLAGS}</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">enable_testing</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">()</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">add_test</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(first_and_only_test testapp)</span></span></code></pre>
</div><p>建议优先使用 <code>target_compile_options</code> 与 <code>_CFLAGS</code>，而非 <code>target_include_directories</code> 与 <code>INCLUDE_DIRS</code>，
因为前者不仅包含 <code>-I</code> 标志（GoogleTest 可能需要特定宏来告知内部头文件是否启用线程支持）。
此外，编译阶段可能仍需 <code>-pthread</code> 参数，若拆分 pkg-config 的 <code>Cflags</code> 变量可能导致该标志丢失。
同理，推荐使用 <code>_LDFLAGS</code> 而非更常见的 <code>_LIBRARIES</code>，以避免丢失 <code>-L</code> 标志和 <code>-pthread</code>。</p>
<p>GoogleTest 提供了 pkg-config 配置文件，可用于确定编译和链接 GoogleTest（及 GoogleMock）所需的所有标志参数。
pkg-config 采用标准化的纯文本格式，包含以下关键信息：</p>
<ul>
<li>包含目录路径（<code>-I</code>）</li>
<li>必要的宏定义（<code>-D</code>）</li>
<li>其他必需标志（<code>-pthread</code>）</li>
<li>库文件路径（<code>-L</code>）</li>
<li>待链接库名称（<code>-l</code>）</li>
</ul>
<p>当前主流构建系统均支持 pkg-config。</p>
<p>本文所有示例均假设你需要编译 <code>samples/sample3_unittest.cc</code> 示例文件。</p>
<h2>故障排除：pkg-config 找不到 GoogleTest</h2>
<p>执行上述 <code>CMakeLists.txt</code> 文件时可能会遇到以下错误：</p>
<div class="language-ansi" data-highlighter="shiki" data-ext="ansi" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-ansi"><span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">-- Checking for one of the modules 'gtest_main'</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">CMake Error at /usr/share/cmake/Modules/FindPkgConfig.cmake:640 (message):</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">  None of the required 'gtest_main' found</span></span></code></pre>
</div><p>此错误常见于用户手动安装 GoogleTest 而非通过发行版包管理器安装的情况。
解决方法是配置 pkg-config 的 <code>.pc</code> 文件搜索路径。
假设 GoogleTest 安装于 <code>/usr/local</code>，则 <code>.pc</code> 文件可能位于 <code>/usr/local/lib64/pkgconfig</code>。
此时，应设置环境变量：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">export</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> PKG_CONFIG_PATH</span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">/</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">usr</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">/</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">local</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">/</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">lib64</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">/</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">pkgconfig</span></span></code></pre>
</div><p>pkg-config 将自动搜索 <code>PKG_CONFIG_PATH</code> 路径来定位 <code>gtest_main.pc</code>。</p>
<h2>交叉编译环境配置</h2>
<p>pkg-config 同样支持交叉编译场景。
假定目标系统的最终安装前缀为 <code>/usr</code>，sysroot 路径为 <code>/home/MYUSER/sysroot</code>。
用户可按以下步骤配置安装 GoogleTest：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">mkdir</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> build</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> &#x26;&#x26;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> cmake</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -DCMAKE_INSTALL_PREFIX=/usr</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> ..</span></span></code></pre>
</div><p>使用 <code>DESTDIR</code> 参数将其安装到 sysroot：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">make</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -j</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> install</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> DESTDIR=/home/MYUSER/sysroot</span></span></code></pre>
</div><p>在继续操作前，强烈建议在交叉编译环境中定义以下两个 pkg-config 变量：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">export</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> PKG_CONFIG_ALLOW_SYSTEM_CFLAGS</span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">yes</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">export</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> PKG_CONFIG_ALLOW_SYSTEM_LIBS</span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">yes</span></span></code></pre>
</div><p>否则 pkg-config 将自动过滤针对标准前缀（如 <code>/usr</code>）的 <code>-I</code> 和 <code>-L</code> 标志
（阅读 <a href="https://bugs.freedesktop.org/show_bug.cgi?id=28264#c3" target="_blank" rel="noopener noreferrer">https://bugs.freedesktop.org/show_bug.cgi?id=28264#c3</a> 了解该过滤的作用）。</p>
<p>观察按上述操作生成的 pkg-config 文件示例：</p>
<div class="language-properties" data-highlighter="shiki" data-ext="properties" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-properties"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">libdir</span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">/usr/lib64</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">includedir</span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">/usr/include</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Name: gtest</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Description: GoogleTest (without main() function)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Version: 1.11.0</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">URL: https://github.com/google/googletest</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Libs: -L${libdir} -lgtest -lpthread</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Cflags: -I${includedir} -</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">DGTEST_HAS_PTHREAD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">1 -lpthread</span></span></code></pre>
</div><p>这里 sysroot 路径未被包含在 <code>libdir</code> 和 <code>includedir</code> 中！
若使用 <code>PKG_CONFIG_LIBDIR=/home/MYUSER/sysroot/usr/lib64/pkgconfig</code> 执行 pkg-config，则：</p>
<div class="language-shell" data-highlighter="shiki" data-ext="shell" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-shell"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">$</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> pkg-config</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --cflags</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> gtest</span></span>
<span class="line"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">-DGTEST_HAS_PTHREAD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">1</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> -lpthread</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -I/usr/include</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">$</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> pkg-config</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --libs</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> gtest</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">-L/usr/lib64</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -lgtest</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -lpthread</span></span></code></pre>
</div><p>显然输出结果有误，其指向了 <code>CBUILD</code> 而非 <code>CHOST</code> 根路径。
为正确配置交叉编译，需告知 pkg-config 将实际 sysroot 路径注入至 <code>-I</code> 和 <code>-L</code> 参数：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">export</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> PKG_CONFIG_DIR</span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">export</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> PKG_CONFIG_SYSROOT_DIR</span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">/</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">home</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">/</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">MYUSER</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">/</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">sysroot</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">export</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> PKG_CONFIG_LIBDIR</span><span style="--shiki-light:#999999;--shiki-dark:#666666">=${</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">PKG_CONFIG_SYSROOT_DIR</span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">/</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">usr</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">/</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">lib64</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">/</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">pkgconfig</span></span></code></pre>
</div><p>之后再执行 pkg-config 将获得正确结果：</p>
<div class="language-shell" data-highlighter="shiki" data-ext="shell" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-shell"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">$</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> pkg-config</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --cflags</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> gtest</span></span>
<span class="line"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">-DGTEST_HAS_PTHREAD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">1</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> -lpthread</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -I/home/MYUSER/sysroot/usr/include</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">$</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> pkg-config</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --libs</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> gtest</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">-L/home/MYUSER/sysroot/usr/lib64</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -lgtest</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -lpthread</span></span></code></pre>
</div><p>输出结果包含了正确的 sysroot 路径。
关于包含 <code>${CHOST}</code> 参数的完整配置指南，请参考 Diego Elio Pettenò 的权威教程：<a href="https://autotools.io/pkgconfig/cross-compiling.html" target="_blank" rel="noopener noreferrer">https://autotools.io/pkgconfig/cross-compiling.html</a>。</p>
]]></content>
    <published>2025-04-21T08:48:02.000Z</published>
  </entry>
  <entry>
    <title type="text">代码示例</title>
    <id>https://sunirein.tech/gtest/samples/</id>
    <link href="https://sunirein.tech/gtest/samples/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>如果你的使用场景和我们类似，可以查阅
<a href="https://github.com/google/googletest/blob/main/googletest/samples" target="_blank" rel="noopener noreferrer">GoogleTest 代码示例</a>。
示例目录包含多个注释详尽的代码样例，演示了 GoogleTest 框架的各项核心功能。</p>
<ul>
<li>示例 1 演示使用 GoogleTest 测试 C++ 函数的基本步骤。</li>
<li>示例 2 演示对包含多个成员函数的类进行复杂单元测试。</li>
<li>示例 3 使用测试夹具。</li>
<li>示例 4 指导如何联合使用 googletest 与 <code>googletest.h</code> 实现最佳实践。</li>
<li>示例 5 在基类测试夹具中封装共享测试逻辑，并在派生夹具中复用。</li>
<li>示例 6 演示类型参数化测试。</li>
<li>示例 7 介绍值参数化测试的基础应用。</li>
<li>示例 8 展示值参数化测试中 <code>Combine()</code> 方法的使用。</li>
<li>示例 9 使用监听器 API 定制 GoogleTest 控制台输出，并利用反射 API 检查测试结果。</li>
<li>示例 10 通过监听器 API 实现简单内存泄漏检测器。</li>
</ul>]]></summary>
    <content type="html"><![CDATA[<p>如果你的使用场景和我们类似，可以查阅
<a href="https://github.com/google/googletest/blob/main/googletest/samples" target="_blank" rel="noopener noreferrer">GoogleTest 代码示例</a>。
示例目录包含多个注释详尽的代码样例，演示了 GoogleTest 框架的各项核心功能。</p>
<ul>
<li>示例 1 演示使用 GoogleTest 测试 C++ 函数的基本步骤。</li>
<li>示例 2 演示对包含多个成员函数的类进行复杂单元测试。</li>
<li>示例 3 使用测试夹具。</li>
<li>示例 4 指导如何联合使用 googletest 与 <code>googletest.h</code> 实现最佳实践。</li>
<li>示例 5 在基类测试夹具中封装共享测试逻辑，并在派生夹具中复用。</li>
<li>示例 6 演示类型参数化测试。</li>
<li>示例 7 介绍值参数化测试的基础应用。</li>
<li>示例 8 展示值参数化测试中 <code>Combine()</code> 方法的使用。</li>
<li>示例 9 使用监听器 API 定制 GoogleTest 控制台输出，并利用反射 API 检查测试结果。</li>
<li>示例 10 通过监听器 API 实现简单内存泄漏检测器。</li>
</ul>
]]></content>
    <published>2025-04-21T05:59:08.000Z</published>
  </entry>
  <entry>
    <title type="text">旧式 gMock 常见问题解答</title>
    <id>https://sunirein.tech/gtest/gmock_faq/</id>
    <link href="https://sunirein.tech/gtest/gmock_faq/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<h2>我在模拟对象上调用方法时，却调用了实际对象的方法。这是为什么？</h2>
<p>要使方法能够被模拟，该方法必须声明为<strong>虚函数</strong>，除非使用。</p>
<h2>能否模拟可变参数函数？</h2>
<p>gMock 无法直接模拟可变参数函数（即使用省略号（<code>...</code>）参数的函数）。</p>
<p>问题在于，模拟对象通常<strong>无法</strong>知道可变参数方法接收的参数数量及其类型。
只有基类作者了解参数协议，而我们不能揣测其实现意图。</p>
<p>因此，要模拟此类函数，用户需要自行指导模拟对象如何解析参数数量和类型。
一种实现方式是提供函数的重载版本。</p>]]></summary>
    <content type="html"><![CDATA[<h2>我在模拟对象上调用方法时，却调用了实际对象的方法。这是为什么？</h2>
<p>要使方法能够被模拟，该方法必须声明为<strong>虚函数</strong>，除非使用。</p>
<h2>能否模拟可变参数函数？</h2>
<p>gMock 无法直接模拟可变参数函数（即使用省略号（<code>...</code>）参数的函数）。</p>
<p>问题在于，模拟对象通常<strong>无法</strong>知道可变参数方法接收的参数数量及其类型。
只有基类作者了解参数协议，而我们不能揣测其实现意图。</p>
<p>因此，要模拟此类函数，用户需要自行指导模拟对象如何解析参数数量和类型。
一种实现方式是提供函数的重载版本。</p>
<p>省略号参数继承自 C 语言，并非真正的 C++ 特性。
这类参数存在安全隐患，且不适用于带有构造函数或析构函数的参数。
因此我们建议在 C++ 中尽量避免使用。</p>
<h2>定义带有 <code>const</code> 参数的模拟方法时，MSVC 报 C4301 或 C4373 警告。这是为什么？</h2>
<p>使用 Microsoft Visual C++ 2005 SP1 编译以下代码时：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> i</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>可能出现以下警告：</p>
<div class="language-ansi" data-highlighter="shiki" data-ext="ansi" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-ansi"><span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">warning C4301: 'MockFoo::Bar': overriding virtual function only differs from 'Foo::Bar' by const/volatile qualifier</span></span></code></pre>
</div><p>这是 MSVC 的 bug。
相同代码在 gcc 等编译器上可正常编译。
若使用 Visual C++ 2008 SP1 则会出现：</p>
<div class="language-ansi" data-highlighter="shiki" data-ext="ansi" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-ansi"><span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">warning C4373: 'MockFoo::Bar': virtual function overrides 'Foo::Bar', previous versions of the compiler did not override when parameters only differed by const/volatile qualifiers</span></span></code></pre>
</div><p>在 C++ 中，若声明函数参数为 <code>const</code>，该修饰符将被忽略。
因此上述 Foo 基类代码等效于：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> i</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // int 或 const int 实际上没有区别</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>实际使用中，可以在声明 <code>Bar()</code> 时使用 <code>int</code> 参数，而定义时使用 <code>const int</code> 参数，编译器仍能正确匹配。</p>
<p>既然在方法声明中使用 <code>const</code> 修饰参数没有实际意义，我们建议在 <code>Foo</code> 和 <code>MockFoo</code> 中移除该修饰符。
这也可以绕过 VC 的编译器问题。</p>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>此处讨论的是顶层 <code>const</code> 修饰符。
若参数通过指针或引用传递，声明指针目标或引用对象为 <code>const</code> 仍有意义。
例如，以下两个声明<strong>不等效</strong>：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> p</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">        // p 和 *p 都非 const</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> p</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // p 不是 const，但 *p 是</span></span></code></pre>
</div></div>
<h2>如何排查 gMock 认为期望未满足的问题？</h2>
<p>可在运行测试时添加 <code>--gmock_verbose=info</code> 参数。
该参数会输出 gMock 接收的所有模拟函数调用信息，通过分析这些信息可定位期望未满足的原因。</p>
<p>若出现提示信息 <code>The mock function has no default action set, and its return type has no default value set.</code>，
请尝试。
由于已知问题，没有默认动作的模拟函数在遇到意外调用时，不会打印实际参数与期望参数的详细对比。</p>
<h2>程序崩溃时 <code>ScopedMockLog</code> 输出了大量信息。这是 gMock 的 bug 吗？</h2>
<p>gMock 和 <code>ScopedMockLog</code> 在此情况下的行为是符合预期的。</p>
<p>当测试崩溃时，故障信号处理器会尝试记录大量信息（如堆栈跟踪和地址映射）。
若存在多个堆栈较深的线程，信息量会剧增。
当 <code>ScopedMockLog</code> 拦截到这些信息并发现其不匹配任何期望时，会打印每条信息的错误。</p>
<p>可以选择忽略这些错误，或者通过改进期望条件增强测试鲁棒性。
例如，添加：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">AnyNumber</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Not</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // </span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">log</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Log</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Not</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EndsWith</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">/my_file.cc</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)),</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> _</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AnyNumber</span><span style="--shiki-light:#999999;--shiki-dark:#666666">());</span></span></code></pre>
</div><h2>如何断言某个函数从未被调用？</h2>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><h2>测试失败时 gMock 对同一期望报错了两次，这是否冗余？</h2>
<p>当 gMock 检测到失败时，会输出相关信息（模拟函数参数、相关期望状态等）辅助调试。
若后续再次检测到失败，会重复输出相关信息，包括期望的状态。</p>
<p>虽然有时两次输出的期望状态描述相同，但这并非冗余，因为它们对应不同的时间节点。
相同的期望报错本身即是重要的调试信息。</p>
<h2>使用模拟对象时出现堆检查错误，但使用真实对象则正常，可能是什么原因？</h2>
<p>请检查被模拟的基类（应为纯接口类）是否声明了虚析构函数。</p>
<p>当继承基类时，必须确保其析构函数为虚函数，否则会导致严重问题。
参考以下代码：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Base</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 非虚函数，但本应为虚函数</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  ~Base</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Derived</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Base</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> private</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">string value_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  Base</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> p </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> new</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Derived</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  delete</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> p</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 此处仅调用 ~Base()，未调用 ~Derived()</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">             // value_ 发生泄露</span></span></code></pre>
</div><p>将 <code>~Base()</code> 改为虚函数后，执行 <code>delete p</code> 会正确调用 <code>~Derived()</code>，堆检查即可通过。</p>
<h2>&quot;新期望覆盖旧期望&quot;规则导致编写不便。为何如此设计？</h2>
<p>开发者常抱怨如下场景：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 要求 foo.Bar() 被调用两次，首次返回 1，第二次返回 2</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 但必须逆序编写期望，非常反直觉！</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">RetiresOnSaturation</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">RetiresOnSaturation</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span></code></pre>
</div><p>问题根源在于没有采用最佳方法表达测试意图。</p>
<p>默认情况下，期望的匹配顺序是<strong>任意的</strong>。
如需指定匹配顺序，必须显式声明。
这是 gMock（及 jMock）的核心设计哲学：用户易过度指定测试顺序导致测试脆弱，因此需增加实现难度。</p>
<p>推荐两种改进方式。
其一，使用顺序块：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 这里使用顺序块，实现自然顺序编写</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  {</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    InSequence s</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">        .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">        .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">RetiresOnSaturation</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">        .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">        .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">RetiresOnSaturation</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span></code></pre>
</div><p>其二，在同一期望中声明多个动作：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">RetiresOnSaturation</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span></code></pre>
</div><div class="hint-container info">
<p class="hint-container-title">逆向搜索机制的设计初衷</p>
<p>允许用户在早期设置阶段（如模拟对象构造函数或测试夹具的 <code>SetUp</code> 中）定义通用行为，后续再通过具体测试用例进行特化。
若采用正向搜索，该模式将无法实现。</p>
</div>
<h2>设置 <code>ON_CALL</code> 但未设置 <code>EXPECT_CALL</code> 时，gMock 会警告。是否应抑制此类警告？</h2>
<p>在简洁与安全之间，gMock 选择后者，因此保留警告输出是合理的设计。</p>
<p>在模拟对象构造函数或 <code>SetUp()</code> 中设置 <code>ON_CALL</code> 作为默认行为，而具体测试用例中通过 <code>EXPECT_CALL</code> 定义特殊预期是一种常见做法。
设置了 <code>ON_CALL</code> 并不表示预期调用，若未设置 <code>EXPECT_CALL</code> 却发生调用，可能意味着潜在错误。
静默放行此类调用易导致问题不被发现。</p>
<p>如果用户确信调用是合理的，可用以下方式：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillRepeatedly</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>而非：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  ON_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillByDefault</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>这会告知 gMock 该调用属于预期行为，不再触发警告。</p>
<p>用户也可以在调试时可通过 <code>--gmock_verbose=error</code> 调节输出级别（可选值包括 <code>info</code> 或 <code>warning</code>）。
若调试时日志信息过多，只需降低输出级别。</p>
<h2>如何在动作中 <code>delete</code> 模拟函数参数？</h2>
<p>若需 <code>delete</code> 模拟函数中的指针参数，可使用 <code>testing::DeleteArg&lt;N&gt;()</code>：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">X</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Y</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> y</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_foo_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> _</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">DeleteArg</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>())</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span></code></pre>
</div><h2>如何对模拟函数参数执行自定义操作？</h2>
<p>如需执行 gMock 未直接支持的操作，可通过以下方式实现：</p>
<ul>
<li>使用  或  定义自定义操作。</li>
<li>编写桩函数并通过  调用。</li>
</ul>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Invoke</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">X</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> p</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_foo_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Invoke</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MyAction</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)));</span></span></code></pre>
</div><h2>能否模拟静态/全局函数？</h2>
<p>技术上可行，但建议重构代码。</p>
<p>静态函数调用通常意味着模块耦合度过高，同时也意味着更低的灵活性、可复用性、可测试性等。
推荐用小型接口进行封装，通过接口实现模拟。
这需要一些额外的初始投入，但很快就能感受到其带来的好处。</p>
<p>可以参考这篇 Google Testing Blog 的<a href="https://testing.googleblog.com/2008/06/defeat-static-cling.html" target="_blank" rel="noopener noreferrer">博客</a>。</p>
<h2>模拟对象需要执行复杂操作时配置繁琐——gMock 很难用！</h2>
<p>虽然这不是一个问题，但我们仍将给出解答。😃</p>
<p>使用 gMock 可以轻松创建 C++ 模拟对象。
开发者可能倾向于随处使用它们。
某些场景下它们效果良好，但有时你可能会觉得使用体验欠佳。
那么问题出在哪里？</p>
<p>当你编写不使用模拟的测试时，会执行代码并断言其返回正确值或系统处于预期状态。
这种测试方式被称为“基于状态的测试”。</p>
<p>模拟对象在“基于交互的测试”中表现卓越：
不同于最终检查系统状态的方式，模拟对象会验证其是否被正确调用，并在问题出现时立即报错，使你能精准定位错误发生的上下文环境。
相较于基于状态的测试，这种方式通常更高效、更经济。</p>
<p>若你正在进行基于状态的测试，且仅使用测试替身来模拟真实对象，则更适合使用伪对象（<em>fake</em>）。
此时使用模拟对象会导致不便，因为执行复杂操作并非模拟对象的强项。
如果你遇到此类问题，并因此认为模拟对象难用，说明你可能选错了工具，或者试图解决错误的问题。</p>
<h2>出现 <code>Uninteresting function call encountered - default action taken..</code> 警告时，需要担忧吗？</h2>
<p>完全不需要！这只是提示信息。😃</p>
<p>该警告表示：你定义的模拟函数未被设置任何期望
（根据 gMock 规则，这意味着你不关注此函数的调用，因此可被任意次数调用），
但该函数确实被调用了。
这完全正常——你从未声明禁止调用此函数！</p>
<p>但如果你实际意图是禁止调用该函数，却忘记编写 <code>EXPECT_CALL(foo, Bar()).Times(0)</code>，该怎么办？
虽然这可以归咎于用户的失误，但 gMock 仍会友好地输出提示信息。</p>
<p>因此，当你看到此消息并确信不应存在未关注的调用时，应当调查具体原因。
为便于调试，gMock 会在遇到无趣调用时输出堆栈跟踪信息。
通过该信息，可以定位具体的模拟函数及其调用方式。</p>
<h2>定义自定义动作时，应该使用 <code>Invoke()</code> 还是实现 <code>ActionInterface</code> 接口？</h2>
<p>两种方式均可，用户应根据具体场景选择最便捷的方式。</p>
<p>通常，若动作针对特定函数类型，使用 <code>Invoke()</code> 定义更简便。
若动作需适用于多种函数类型（例如 <code>Return(value)</code>），则使用 <code>MakePolymorphicAction()</code> 最合适。
当需要精确控制动作适用的函数类型时，实现 <code>ActionInterface</code> 接口是最佳选择。
具体示例可参考 <code>gmock-actions.h</code> 中 <code>Return()</code> 的实现。</p>
<h2>在 <code>WillOnce()</code> 中使用 <code>SetArgPointee()</code> 时，gcc 报错 <code>conflicting return type specified</code>。这是什么意思？</h2>
<p>此错误源于 gMock 无法确定模拟方法调用时应返回的值。
<code>SetArgPointee()</code> 仅设置副作用，未指定返回值。
你需要使用 <code>DoAll()</code> 来组合 <code>SetArgPointee()</code> 与提供适当返回值的 <code>Return()</code>。</p>
<p>更多细节和示例详见。</p>
<h2>大型模拟类导致 Microsoft Visual C++ 编译时内存不足，如何解决？</h2>
<p>我们注意到当使用 <code>/clr</code> 编译选项时，Visual C++ 会消耗 5~6 倍内存来编译模拟类。
建议在编译 C++ 模拟对象时避免使用 <code>/clr</code> 选项。</p>
]]></content>
    <published>2025-04-21T05:16:54.000Z</published>
  </entry>
  <entry>
    <title type="text">gTest 常见问题解答</title>
    <id>https://sunirein.tech/gtest/faq/</id>
    <link href="https://sunirein.tech/gtest/faq/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<h2>为什么测试套件名称和测试名称不应包含下划线？</h2>
<div class="hint-container note">
<p class="hint-container-title">注</p>
<p>除了下列原因外，GoogleTest 还保留下划线（<code>_</code>）用于特殊用途的关键字，例如
。</p>
</div>
<p>下划线（<code>_</code>）具有特殊性，因为 C++ 保留以下情况供编译器和标准库使用：</p>
<ol>
<li>任何以 <code>_</code> 开头后跟大写字母的标识符</li>
<li>任何名称中包含连续两个下划线（即 <code>__</code>）的标识符</li>
</ol>]]></summary>
    <content type="html"><![CDATA[<h2>为什么测试套件名称和测试名称不应包含下划线？</h2>
<div class="hint-container note">
<p class="hint-container-title">注</p>
<p>除了下列原因外，GoogleTest 还保留下划线（<code>_</code>）用于特殊用途的关键字，例如
。</p>
</div>
<p>下划线（<code>_</code>）具有特殊性，因为 C++ 保留以下情况供编译器和标准库使用：</p>
<ol>
<li>任何以 <code>_</code> 开头后跟大写字母的标识符</li>
<li>任何名称中包含连续两个下划线（即 <code>__</code>）的标识符</li>
</ol>
<p>用户代码被严格禁止使用此类标识符。</p>
<p>现在让我们看看这对 <code>TEST</code> 和 <code>TEST_F</code> 意味着什么。</p>
<p>目前 <code>TEST(TestSuiteName, TestName)</code> 会生成名为 <code>TestSuiteName_TestName_Test</code> 的类。
如果 <code>TestSuiteName</code> 或 <code>TestName</code> 包含 <code>_</code> 会发生什么？</p>
<ol>
<li>如果 <code>TestSuiteName</code> 以 <code>_</code> 开头后跟大写字母（例如 <code>_Foo</code>），将生成 <code>_Foo_TestName_Test</code>。</li>
<li>如果 <code>TestSuiteName</code> 以 <code>_</code> 结尾（例如 <code>Foo_</code>），将生成 <code>Foo__TestName_Test</code>。</li>
<li>如果 <code>TestName</code> 以 <code>_</code> 开头（例如 <code>_Bar</code>），将生成 <code>TestSuiteName__Bar_Test</code>。</li>
<li>如果 <code>TestName</code> 以 <code>_</code> 结尾（例如 <code>Bar_</code>），将生成 <code>TestSuiteName_Bar__Test</code>。</li>
</ol>
<p>这些都是保留标识符，不能使用。</p>
<p>因此显然 <code>TestSuiteName</code> 和 <code>TestName</code> 不能以 <code>_</code> 开头或结尾
（实际上，只要 <code>_</code> 后不跟大写字母，<code>TestSuiteName</code> 就可以以 <code>_</code> 开头。
但这样会使规则复杂化。
为简单起见，我们统一规定不能以 <code>_</code> 开头）。</p>
<p>看起来在名称中间使用 <code>_</code> 没有问题。
但考虑以下情况：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Time</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Flies_Like_An_Arrow</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Time_Flies</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Like_An_Arrow</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>这两个 <code>TEST</code> 宏会生成相同的类名（<code>Time_Flies_Like_An_Arrow_Test</code>），从而产生问题。</p>
<p>因此，为简化规则，我们要求用户在 <code>TestSuiteName</code> 和 <code>TestName</code> 中完全避免使用 <code>_</code>。
虽然该规则比实际需求更严格，但简单易记。
这也为 GoogleTest 未来的实现变化留有余地。</p>
<p>如果违反此规则，可能不会立即出现问题，
但使用新编译器（或新版本编译器）或新版本 GoogleTest 时，测试可能会崩溃。
因此最好遵守此规则。</p>
<h2>为什么 GoogleTest 支持 <code>EXPECT_EQ(NULL, ptr)</code> 和 <code>ASSERT_EQ(NULL, ptr)</code>，但不支持 <code>EXPECT_NE(NULL, ptr)</code> 和 <code>ASSERT_NE(NULL, ptr)</code>？</h2>
<p>首先，你可以使用 <code>nullptr</code> 来配合这些宏，
例如 <code>EXPECT_EQ(ptr, nullptr)</code>、<code>EXPECT_NE(ptr, nullptr)</code>、<code>ASSERT_EQ(ptr, nullptr)</code>、<code>ASSERT_NE(ptr, nullptr)</code>。
这是风格指南推荐的方式，因为 <code>nullptr</code> 没有 <code>NULL</code> 的类型问题。</p>
<p>由于 C++ 的特殊性，支持在 <code>EXPECT_XX()</code> 和 <code>ASSERT_XX()</code> 宏中使用 <code>NULL</code> 需要复杂的模板元编程技巧。
因此我们只在最需要的地方实现它（否则会使 GoogleTest 的实现更难维护且更易出错）。</p>
<p>历史上，<code>EXPECT_EQ()</code> 宏曾将<strong>预期值</strong>作为第一个参数，<strong>实际值</strong>作为第二个参数，不过现在不鼓励这种参数顺序。
用户确实需要编写 <code>EXPECT_EQ(NULL, some_expression)</code> 的需求是合理的，也确实被多次提出，因此我们实现了它。</p>
<p>对 <code>EXPECT_NE(NULL, ptr)</code> 的需求并不强烈。
当断言失败时，<code>ptr</code> 必为 <code>NULL</code>，因此打印 <code>ptr</code> 的值不会提供额外信息。
这意味着，<code>EXPECT_TRUE(ptr != NULL)</code> 同样有效。</p>
<p>如果支持 <code>EXPECT_NE(NULL, ptr)</code>，为了保持一致性也需要支持 <code>EXPECT_NE(ptr, NULL)</code>。
这将使实现中的模板元编程技巧使用次数翻倍，显著增加理解和维护难度。
我们认为收益不值得付出这样的成本。</p>
<p>最后，随着 gMock 匹配器库的发展，我们鼓励用户更多使用统一的 <code>EXPECT_THAT(value, matcher)</code> 语法。
匹配器方法的重要优势是可以轻松组合新匹配器，而 <code>EXPECT_NE</code> 等宏无法轻松组合。
因此我们更倾向于在匹配器上投入精力。</p>
<h2>我需要测试接口的不同实现是否满足共同要求，应该使用类型化测试还是值参数化测试？</h2>
<p>要验证同一接口的不同实现是否满足共同要求，类型化测试和值参数化测试均可适用。
具体选择取决于实际场景：</p>
<ul>
<li>
<p>若不同实现实例的创建方式仅类型不同则<strong>类型化测试</strong>更易编写。例如：</p>
<ul>
<li>所有实现都具有公有默认构造函数（可使用 <code>new TypeParam</code>）。</li>
<li>工厂函数形式统一（如 <code>CreateInstance&lt;TypeParam&gt;()</code>）。</li>
</ul>
</li>
<li>
<p>若不同实现实例需要不同的创建模式则<strong>值参数化测试</strong>更适用。
例如：<code>new Foo</code> vs <code>new Bar(5)</code>。
要消除这种差异，可以编写工厂函数包装器，并将函数指针作为参数传递给测试。</p>
</li>
<li>
<p>调试信息差异：</p>
<ul>
<li>类型化测试失败时会默认输出类型名称，便于快速定位问题实现。</li>
<li>值参数化测试默认仅显示失败迭代的编号。
用户需通过定义迭代名称函数，作为第三个参数传递给 <code>INSTANTIATE_TEST_SUITE_P</code> 来加强输出信息。</li>
</ul>
</li>
<li>
<p>使用类型化测试时，需确保测试是针对接口类型而非具体实现类型
（即需验证 <code>implicit_cast&lt;MyInterface*&gt;(my_concrete_impl)</code> 有效，而不仅是 <code>my_concrete_impl</code> 有效）。
值参数化测试在此方面更不易出错。</p>
</li>
</ul>
<p>建议实践两种方法以深入理解其细微差异，通过实际体验更易做出合适选择。</p>
<h2>死亡测试中的状态修改为何在测试结束后丢失？</h2>
<p>死亡测试（<code>EXPECT_DEATH</code> 等）在子进程中执行，以确保预期的崩溃不会终止主测试进程。
因此，任何内存副作用仅存在于子进程中，主进程无法观测子进程状态变化。
可以将死亡测试视为在&quot;平行宇宙&quot;中运行。</p>
<p>特别指出，若在死亡测试调用模拟方法，主进程会认为调用从未发生。
因此，可能需要将 <code>EXPECT_CALL</code> 语句移动到 <code>EXPECT_DEATH</code> 内部。</p>
<h2>在优化模式下使用 <code>EXPECT_EQ(htonl(blah), blah_blah)</code> 为何报编译错误？</h2>
<p>该问题源于 <code>htonl()</code> 的实现缺陷：</p>
<p>根据 <code>'man htonl'</code> 文档，<code>htonl()</code> 本应作为函数存在，因此可被用作函数指针。
但在优化模式下，<code>htonl()</code> 被定义为宏，这违反了规范要求。</p>
<p>更严重的是，<code>htonl()</code> 的宏实现使用了 gcc 扩展语法，不符合标准 C++ 规范。
这种非标准实现存在特定限制，
例如无法在模板参数中使用 <code>Foo&lt;sizeof(htonl(x))&gt;</code> 这样的表达式（其中 <code>Foo</code> 是接受整型参数的模板）。</p>
<p><code>EXPECT_EQ(a, b)</code> 的实现会在模板参数中使用 <code>sizeof(... a ...)</code>。
因此，当参数 <code>a</code> 包含 <code>htonl()</code> 调用时，在优化模式下将无法通过编译。
由于需要兼容不同编译器和平台，很难让 <code>EXPECT_EQ</code> 绕过这个 <code>htonl()</code> 的缺陷。</p>
<h2>编译器报 <code>undefined references</code> 错误，但我已在类内定义相应的静态常量成员变量。问题出在哪里？</h2>
<p>当类包含静态数据成员时：</p>
<div class="code-block-title" data-title="foo.h"><div class="code-block-title-bar"><span class="title">foo.h</span></div><div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  static</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> kBar </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 100</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div></div><p>仍需在 <code>foo.cc</code> 文件中进行外部定义：</p>
<div class="code-block-title" data-title="foo.cc"><div class="code-block-title-bar"><span class="title">foo.cc</span></div><div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">kBar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD"> // 此处不需要初始化</span></span></code></pre>
</div></div><p>否则将产生无效的 C++ 代码，可能导致意外错误。
特别是在 GoogleTest 的断言宏（如 EXPECT_EQ）中使用时，会引发 <code>undefined reference</code> 链接错误。
之前能运行并不意味着代码正确，只是侥幸成功。</p>
<p>若使用 <code>constexpr</code> 声明静态成员，则会隐式生成 <code>inline</code> 定义，此时无需在 <code>foo.cc</code> 中重复定义：</p>
<div class="code-block-title" data-title="foo.h"><div class="code-block-title-bar"><span class="title">foo.h</span></div><div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  static</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> constexpr</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> kBar </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 100</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 直接定义 kBar，无需在 foo.cc 中重复</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div></div><h2>能否让测试夹具继承另一个夹具？</h2>
<p>可以。</p>
<p>每个测试夹具对应唯一的一个同名测试套件，这意味着一个测试夹具只有一个测试套件可以使用。
但有时多个测试用例需要共享相似的夹具逻辑。
例如，确保 GUI 库的所有测试套件都不会泄漏字体、画笔等系统资源。</p>
<p>在 GoogleTest 中，可将共享逻辑放在基类夹具，然后为每个测试套件派生子类夹具。
使用 <code>TEST_F()</code> 编写基于派生夹具的测试。</p>
<p>典型实现如下：</p>
<div class="language-c++" data-highlighter="shiki" data-ext="c++" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-c++"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 定义基类测试夹具</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> BaseTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Test</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> protected</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 从 BaseTest 派生 FooTest 夹具</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> BaseTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> protected</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> SetUp</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    BaseTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">SetUp</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 先初始化基类夹具</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">    // ... 扩展初始化逻辑 ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> TearDown</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">    // ... FooTest 的清理逻辑 ...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    BaseTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TearDown</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 最后清理基类夹具</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // ... FooTest 的成员函数和变量 ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 使用 FooTest 夹具的测试用例</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Baz</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// ... 其他从 BaseTest 派生的夹具 ...</span></span></code></pre>
</div><p>如有需要，可继续继承派生夹具。
GoogleTest 对继承层次深度没有限制。</p>
<p>完整示例参见
<a href="https://github.com/google/googletest/blob/main/googletest/samples/sample5_unittest.cc" target="_blank" rel="noopener noreferrer">sample5_unittest.cc</a>。</p>
<h2>编译器报 <code>void value not ignored as it ought to be</code> 是什么意思？</h2>
<p>此错误通常意味着您在非 <code>void</code> 函数中使用了 <code>ASSERT_*()</code> 断言。
由于 Google 的构建系统禁用了异常机制，<code>ASSERT_*()</code> 系列断言仅可在返回类型为 <code>void</code> 的函数中使用。
更多技术细节请参阅。</p>
<h2>死亡测试卡死（或段错误）如何修复？</h2>
<p>GoogleTest 的死亡测试在子进程中运行，其工作机制较为特殊。
编写死亡测试前必须理解其工作原理——详见断言参考中的章节。</p>
<p>特别要注意，死亡测试无法兼容父进程中存在多线程的情况。
首要解决方法是消除在 <code>EXPECT_DEATH()</code> 外部创建线程的行为，例如在测试中使用模拟对象替代真实对象。</p>
<p>有时这种情况不可避免，例如必须使用的某些库在 <code>main()</code> 函数执行前就创建线程时。
这时可通过两种方式降低冲突概率：
尽可能将更多操作移至 <code>EXPECT_DEATH()</code> 内部（极端情况下需移入全部操作），或尽量减少其外部依赖。
此外，可将死亡测试风格设为 <code>&quot;threadsafe&quot;</code>，这更安全但更慢。</p>
<p>若使用 <code>&quot;threadsafe&quot;</code> 死亡测试，需注意其会在子进程中从头重新运行测试程序。
因此必须确保程序能并行执行自身副本且具有确定性行为。</p>
<p>本质上，这属于并发编程的范畴。
用户必须确保程序中不存在竞态条件或死锁。
很遗憾，此问题没有通用解决方案！</p>
<h2>我应该使用测试夹具的构造函数/析构函数还是 <code>SetUp()</code>/<code>TearDown()</code>？</h2>
<p>首先需要明确：GoogleTest 不会在多个测试之间复用同一个测试夹具对象。
对于每个 <code>TEST_F</code> 测试用例，GoogleTest 都会创建一个全新的测试夹具对象，
调用 <code>SetUp()</code> 方法，运行测试主体，调用 <code>TearDown()</code> 方法，然后删除该测试夹具对象。</p>
<p>当需要编写每个测试专用的设置和清理逻辑时，
可以选择使用测试夹具的构造函数/析构函数或 <code>SetUp()</code>/<code>TearDown()</code> 方法。
通常推荐使用前者，因为它具有以下优势：</p>
<ul>
<li>通过在构造函数中初始化成员变量，可以将其声明为 <code>const</code> 类型，
这有助于防止意外修改其值，使测试逻辑更加明确可靠。</li>
<li>当需要派生测试夹具类时，子类构造函数会首先调用基类构造函数，子类析构函数会最后调用基类析构函数。
若使用 <code>SetUp()</code>/<code>TearDown()</code> 方法，子类可能忘记调用基类的对应方法或在错误时机调用。</li>
</ul>
<p>不过，在以下场景中，则建议使用 <code>SetUp()</code>/<code>TearDown()</code>：</p>
<ul>
<li>C++ 不允许在构造函数和析构函数中调用虚函数。
虽然可以调用声明为 <code>virtual</code> 的方法，但不会使用动态派发机制，而是使用当前执行构造函数所属类中的定义。
这是因为，在派生类构造函数执行前调用虚函数可能操作未初始化数据，存在风险。
因此，若需要调用会被派生类重写的方法，必须使用 <code>SetUp()</code>/ <code>TearDown()</code> 。</li>
<li>在构造函数（或析构函数）体内无法使用 <code>ASSERT_xx</code> 断言宏。
如果设置操作可能导致需要中止当前测试的致命错误，必须使用 <code>abort</code> 终止整个测试程序，
或改用 <code>SetUp()</code> 替代构造函数。</li>
<li>如果清理操作可能抛出异常，必须使用 <code>TearDown()</code> 而非析构函数，
因为在析构函数中抛出异常会导致未定义行为（通常直接终止程序）。
注意，当编译器启用异常时，许多标准库（如 STL）都可能抛出异常。
因此，若需要编写兼容异常启用/禁用环境的可移植测试代码，应优先选择 <code>TearDown()</code> 。</li>
<li>GoogleTest 团队正考虑在启用异常的平台上（如 Windows、Mac OS 和 Linux 客户端），将断言宏改为抛出异常。
这将消除用户需要手动将子程序错误传递到调用方的需求。
因此，如果代码可能运行在此类平台，不应在析构函数中使用 GoogleTest 断言。</li>
</ul>
<h2>使用 <code>ASSERT_PRED*</code> 时编译器报错 <code>no matching function to call</code>，如何解决？</h2>
<p>请查阅断言参考中的  一节。</p>
<h2>调用 <code>RUN_ALL_TESTS()</code> 时编译器警告 <code>ignoring return value</code>，原因为何？</h2>
<p>有些开发者会忽略 <code>RUN_ALL_TESTS()</code> 的返回值，即不写：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">return</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> RUN_ALL_TESTS</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span></code></pre>
</div><p>而是写成：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">RUN_ALL_TESTS</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span></code></pre>
</div><p>这是错误且危险的。
测试框架需要通过检查 <code>RUN_ALL_TESTS()</code> 的返回值来判断测试是否通过。
如果 <code>main()</code> 函数忽略该返回值，即使存在 GoogleTest 断言失败，测试仍会被判定为成功。</p>
<p>我们已决定修复此问题（感谢 Michael Chastain 的建议）。
现在，使用 gcc 编译时，代码将无法忽略 <code>RUN_ALL_TESTS()</code> 的返回值。
如果忽略，会导致编译错误。</p>
<p>如果编译器提示 <code>ignoring return value</code>，解决方法很简单：确保将其返回值作为 <code>main()</code> 函数的返回结果。</p>
<p>但这种改动是否会破坏现有测试？
事实上，此类代码原本就是错误的，因此我们并没有破坏现有有效代码。😃</p>
<h2>编译器提示构造函数（或析构函数）不能返回值，这是怎么回事？</h2>
<p>由于 C++ 的语法限制，为了支持向 <code>ASSERT_*</code> 断言流式传输消息的语法，例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_EQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">blah blah</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span></code></pre>
</div><p>我们不得不禁止在构造函数和析构函数中使用 <code>ASSERT*</code> 和 <code>FAIL*</code> 系列宏（但 <code>EXPECT*</code> 和 <code>ADD_FAILURE*</code> 仍可用）。
解决方法是将构造函数/析构函数中的相关内容移至私有 <code>void</code> 成员函数中，或改用 <code>EXPECT_*()</code> 断言。
用户指南中的中对此有详细解释。</p>
<h2><code>SetUp()</code> 函数未被调用，原因为何？</h2>
<p>C++ 语言大小写敏感。
请检查是否误写为 <code>Setup()</code>？</p>
<p>类似地，有时开发者会将 <code>SetUpTestSuite()</code> 误拼为 <code>SetupTestSuite()</code>，导致该方法未被调用。</p>
<h2>多个测试套件共享相同的测试夹具逻辑时，必须为每个套件定义新的夹具类吗？这样似乎太繁琐了</h2>
<p>不需要重复定义。
你可以使用类型别名替代继承。</p>
<p>原始写法：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-diff vp-code"><code class="language-cpp"><span class="line diff remove"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> BaseTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Abc</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Def</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line diff remove"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> BarTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> BaseTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">BarTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Abc</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">BarTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Def</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>优化写法（使用 <code>typedef</code>）：</p>
<div class="language-c++" data-highlighter="shiki" data-ext="c++" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-diff vp-code"><code class="language-c++"><span class="line diff add"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">typedef</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> BaseTest FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Abc</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Def</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line diff add"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">typedef</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> BaseTest BarTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">BarTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Abc</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">BarTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Def</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><h2>GoogleTest 输出被大量 LOG 消息淹没，如何解决？</h2>
<p>GoogleTest 的输出设计被为简洁易读的测试报告。
若测试代码自身产生文本输出，会与框架输出混杂影响可读性。</p>
<p>由于 <code>LOG</code> 消息输出到 stderr，而 GoogleTest 的输出默认送往 stdout，可通过重定向分离两者：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">./my_test</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ></span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> gtest_output.txt</span></span></code></pre>
</div><h2>为什么应该优先使用测试夹具而非全局变量？</h2>
<p>有以下原因：</p>
<ol>
<li>测试修改全局变量状态，易导致副作用溢出进而影响其他测试，增加调试难度。
使用夹具可为每个测试提供独立变量集（同名但不同实例），确保测试的隔离性。</li>
<li>全局变量会污染全局命名空间。</li>
<li>测试夹具可通过子类化复用，而全局变量难以实现此特性。
这在多个测试套件存在共性时特别有用。</li>
</ol>
<h2><code>ASSERT_DEATH()</code> 中的 <code>statement</code> 参数可以是哪些形式？</h2>
<p><code>ASSERT_DEATH(statement, matcher)</code>（及所有死亡断言宏）可对任何有效 <em><code>statement</code></em> 使用。
<em><code>statement</code></em> 可以是当前上下文中任意有效的 C++ 语句。
它可以引用全局或局部变量，可以是：</p>
<ul>
<li>简单的函数调用（常见形式）</li>
<li>复杂表达式</li>
<li>复合语句</li>
</ul>
<p>示例如下：</p>
<div class="language-c++" data-highlighter="shiki" data-ext="c++" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-c++"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 简单的函数调用</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MyDeathTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> FunctionCall</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  ASSERT_DEATH</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Xyz</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Xyz failed</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 引用变量和函数的复杂表达式</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MyDeathTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ComplexExpression</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> c </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Condition</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  ASSERT_DEATH</span><span style="--shiki-light:#999999;--shiki-dark:#666666">((</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">c </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">?</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Func1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> :</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> object2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Method</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">test</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)),</span></span>
<span class="line"><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">               "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">(Func1|Method) failed</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 死亡断言可在函数内任意位置使用（包括循环内部）</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MyDeathTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> InsideLoop</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 验证 Foo(0) 到 Foo(4) 均会崩溃</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // Verifies that Foo(0), Foo(1), ..., and Foo(4) all die.</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  for</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">++</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    EXPECT_DEATH_M</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">i</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Foo has </span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">\\</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">d+ errors</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">                   ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Message</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">where i is </span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 死亡断言可包含复合语句</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MyDeathTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> CompoundStatement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 验证 Bar(0) 到 Bar(4) 至少有一个崩溃</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  ASSERT_DEATH</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">{</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">    for</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">++</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">      Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">i</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    }</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  }</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">  "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Bar has </span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">\\</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">d+ errors</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><h2>我定义了测试夹具类 <code>FooTest</code>，但 <code>TEST_F(FooTest, Bar)</code> 依旧报错 <code>no matching function for call to FooTest::FooTest()</code>，这是为什么？</h2>
<p>GoogleTest 需要能够创建测试夹具类的对象，因此该类必须包含默认构造函数。
通常编译器会自动生成默认构造函数，但在以下情况需要手动定义：</p>
<ul>
<li>如果为 <code>FooTest</code> 显式声明了非默认构造函数（例如使用 <code>DISALLOW_EVIL_CONSTRUCTORS()</code> 宏），
则必须定义默认构造函数，即使为空实现。</li>
<li>如果 <code>FooTest</code> 包含 <code>const</code> 修饰的非静态数据成员，
则必须定义默认构造函数，并在构造函数的初始化列表中初始化该成员。
（早期 <code>gcc</code> 版本存在不强制要求初始化 <code>const</code> 成员的缺陷，该问题已在 <code>gcc 4</code> 中修复。）</li>
</ul>
<h2>为什么使用 <code>ASSERT_DEATH</code> 时 GoogleTest 要求将整个测试套件（而非单个测试）命名为 <code>*DeathTest</code>？</h2>
<p>GoogleTest 不会交错运行不同测试套件中的测试。
也就是说，它会先运行完某个测试套件中的所有测试，在接着运行另一个测试套件中的所有测试，依此类推。
这样因为 GoogleTest 需要在首个测试运行前完成测试套件初始化，并在最后测试完成后执行清理工作。
如果拆分测试套件，会导致多次重复的初始化和清理操作，既降低效率又破坏语义清晰度。</p>
<p>如果允许测试名称为 <code>*DeathTest</code>，以下场景会产生矛盾：</p>
<div class="language-c++" data-highlighter="shiki" data-ext="c++" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-c++"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> AbcDeathTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Uvw</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">BarTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DefDeathTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">BarTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Xyz</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>由于需要保证 <code>FooTest.AbcDeathTest</code> 在 <code>BarTest.Xyz</code> 之前运行，同时不同测试套件不交叉执行，
就必须先执行完整套件 <code>FooTest</code> 再执行 <code>BarTest</code> 。
这与 <code>BarTest.DefDeathTest</code> 需要在 <code>FooTest.Uvw</code> 之前运行的要求冲突。</p>
<h2>我的测试套件同时包含死亡测试与普通测试，但我不想使用 <code>*DeathTest</code> 来命名整个套件，该怎么做？</h2>
<p>可以使用类型别名将测试套件拆分为 <code>FooTest</code> 和 <code>FooDeathTest</code>：</p>
<div class="language-c++" data-highlighter="shiki" data-ext="c++" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-c++"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Test</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Abc</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Def</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> FooDeathTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FooDeathTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Uvw</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_DEATH</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FooDeathTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Xyz</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_DEATH</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><h2>GoogleTest 只会在死亡测试失败时打印子进程的日志信息，如何在测试成功时查看日志？</h2>
<p>打印 <code>EXPECT_DEATH()</code> 中语句的日志信息会影响主进程的日志可读性，
因此 GoogleTest 只会在死亡测试失败时打印这些信息。</p>
<p>如需查看成功时的日志，可以尝试临时破坏死亡测试（例如，改变某个正则匹配要求）。
这确实有点不方便。
我们可能会在完成 <code>fork-and-exec</code> 风格死亡测试后提供一个永久解决方案。</p>
<h2>使用断言时编译器报错 <code>no match for 'operator&lt;&lt;'</code>，如何解决？</h2>
<p>当在断言中使用自定义类型 <code>FooType</code> 时，必须确保实现对应的流输出运算符，否则无法打印该类型：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">ostream</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> operator</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">&#x3C;&#x3C;</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">ostream</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> FooType</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>此外，该 <code>&lt;&lt;</code> 运算符必须定义在与 <code>FooType</code> 相应的命名空间中。
详见 <a href="https://abseil.io/tips/49" target="_blank" rel="noopener noreferrer">Tip of the Week #49</a>。</p>
<h2>如何屏蔽 Windows 平台的内存泄漏警告？</h2>
<p>由于静态初始化的 GoogleTest 单例对象需要申请堆内存，
Visual C++ 内存检测器会在程序结束时报告内存泄漏。
最简单的解决方法是使用 <code>_CrtMemCheckpoint</code> 和 <code>_CrtMemDumpAllObjectsSince</code>
来抑制所有静态初始化的堆内存对象的报告。
更多细节与堆检测/调试例程说明详见 MSDN 文档。</p>
<h2>如何让代码检测是否运行在测试环境中？</h2>
<p>若你编写代码来嗅探是否处于测试环境并据此执行不同操作，
相当于将测试专用逻辑泄漏到生产代码中，且无法保证测试专用逻辑不会意外在生产环境中运行。
这种“聪明”的做法还会导致<a href="https://en.wikipedia.org/wiki/Heisenbug" target="_blank" rel="noopener noreferrer">海森堡缺陷</a>。
因此我们强烈反对这种做法，GoogleTest 也不提供相关支持。</p>
<p>通常，推荐使用<a href="https://en.wikipedia.org/wiki/Dependency_injection" target="_blank" rel="noopener noreferrer">依赖注入</a>来实现不同环境下的差异化行为。
你可以为测试代码和生产代码分别注入不同功能。
由于生产代码完全不会链接测试逻辑（<code>BUILD</code> 目标的
<a href="https://docs.bazel.build/versions/master/be/common-definitions.html#common.testonly" target="_blank" rel="noopener noreferrer">testonly</a>
属性可确保这点），因此不会存在误执行风险。</p>
<p>不过，如果你确实别无选择，且遵循了测试程序名以 <code>_test</code> 结尾的命名规范，
则可以使用检测可执行文件名（<code>main()</code> 中的 <code>argv[0]</code>）这种<strong>极不推荐</strong>的黑客方法来判断代码是否处于测试环境。</p>
<h2>如何临时禁用某个测试？</h2>
<p>对于无法立即修复的故障测试，可在测试名前添加 <code>DISABLED_</code> 前缀。
这将禁用测试的执行。
此方法优于注释代码或使用 <code>#if 0</code>，因为被禁用的测试仍会参与编译（避免代码腐化）。</p>
<p>要执行被禁用的测试，只需在运行测试程序时添加 <code>--gtest_also_run_disabled_tests</code> 标志。</p>
<h2>是否允许在不同命名空间中定义同名 <code>TEST(Foo, Bar)</code> 测试？</h2>
<p>允许。</p>
<p>核心原则是：<strong>同一测试套件中的所有测试必须使用相同的夹具类</strong>。
以下示例<strong>允许</strong>存在，因为两个测试都使用相同的夹具类（<code>::testing::Test</code>）：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">namespace</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">CoolTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DoSomething</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  SUCCEED</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // namespace foo</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">namespace</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">CoolTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DoSomething</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  SUCCEED</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // namespace bar</span></span></code></pre>
</div><p>但以下代码<strong>不被允许</strong>，这将导致 GoogleTest 运行时错误，因为测试位于相同测试套件但使用了不同的夹具类：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">namespace</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> CoolTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Test</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {};</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // Fixture foo::CoolTest</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">CoolTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DoSomething</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  SUCCEED</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // namespace foo</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">namespace</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> CoolTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Test</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {};</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // Fixture: bar::CoolTest</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">CoolTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DoSomething</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  SUCCEED</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // namespace bar</span></span></code></pre>
</div>]]></content>
    <published>2025-04-10T13:38:21.000Z</published>
  </entry>
  <entry>
    <title type="text">动作参考</title>
    <id>https://sunirein.tech/gtest/reference/actions/</id>
    <link href="https://sunirein.tech/gtest/reference/actions/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>用于指定模拟函数被调用时应执行的操作。
本文列出 GoogleTest 提供的内置动作。
所有动作均定义在 <code>::testing</code> 命名空间内。</p>
<h2>返回值类</h2>
<table>
<thead>
<tr>
<th style="text-align:left">动作</th>
<th style="text-align:left">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><code>Return()</code></td>
<td style="text-align:left">从返回类型为 <code>void</code> 的函数中返回。</td>
</tr>
<tr>
<td style="text-align:left"><code>Return(value)</code></td>
<td style="text-align:left">返回 <code>value</code>。若 <code>value</code> 类型与模拟函数返回类型不一致，将在<strong>设置期望时</strong>（而非执行动作时）进行类型转换。</td>
</tr>
<tr>
<td style="text-align:left"><code>ReturnArg&lt;N&gt;()</code></td>
<td style="text-align:left">返回第 <code>N</code> 个（从 0 开始）参数。</td>
</tr>
<tr>
<td style="text-align:left"><code>ReturnNew&lt;T&gt;(a1, ..., ak)</code></td>
<td style="text-align:left">返回 <code>new T(a1, ..., ak)</code>；每次运行会创建新对象。</td>
</tr>
<tr>
<td style="text-align:left"><code>ReturnNull()</code></td>
<td style="text-align:left">返回空指针。</td>
</tr>
<tr>
<td style="text-align:left"><code>ReturnPointee(ptr)</code></td>
<td style="text-align:left">返回 <code>ptr</code> 指向的值。</td>
</tr>
<tr>
<td style="text-align:left"><code>ReturnRef(variable)</code></td>
<td style="text-align:left">返回 <code>variable</code> 的引用。</td>
</tr>
<tr>
<td style="text-align:left"><code>ReturnRefOfCopy(value)</code></td>
<td style="text-align:left">返回 <code>value</code> 副本的引用；该副本在动作生命周期内有效。</td>
</tr>
<tr>
<td style="text-align:left"><code>ReturnRoundRobin({a1, ..., ak})</code></td>
<td style="text-align:left">每次调用返回列表中的下一个元素，到达末尾时循环至开头。</td>
</tr>
</tbody>
</table>]]></summary>
    <content type="html"><![CDATA[<p>用于指定模拟函数被调用时应执行的操作。
本文列出 GoogleTest 提供的内置动作。
所有动作均定义在 <code>::testing</code> 命名空间内。</p>
<h2>返回值类</h2>
<p>| 动作                              | 描述                                                                                                      |
| :</p>
]]></content>
    <published>2025-04-08T10:19:34.000Z</published>
  </entry>
  <entry>
    <title type="text">匹配器参考</title>
    <id>https://sunirein.tech/gtest/reference/matchers/</id>
    <link href="https://sunirein.tech/gtest/reference/matchers/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p><strong>匹配器</strong>用于匹配<em>单个</em>参数。
你可以在 <code>ON_CALL()</code> 或 <code>EXPECT_CALL()</code> 中使用匹配器，或通过以下两个宏来直接验证值：</p>
<table>
<thead>
<tr>
<th style="text-align:left">宏</th>
<th style="text-align:left">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><code>EXPECT_THAT(actual_value, matcher)</code></td>
<td style="text-align:left">断言 <code>actual_value</code> 匹配 <code>matcher</code>。</td>
</tr>
<tr>
<td style="text-align:left"><code>ASSERT_THAT(actual_value, matcher)</code></td>
<td style="text-align:left">功能同 <code>EXPECT_THAT(actual_value, matcher)</code>，但会产生致命失败。</td>
</tr>
</tbody>
</table>]]></summary>
    <content type="html"><![CDATA[<p><strong>匹配器</strong>用于匹配<em>单个</em>参数。
你可以在 <code>ON_CALL()</code> 或 <code>EXPECT_CALL()</code> 中使用匹配器，或通过以下两个宏来直接验证值：</p>
<p>| 宏                                   | 描述                                                            |
| :</p>
]]></content>
    <published>2025-04-07T13:04:39.000Z</published>
  </entry>
  <entry>
    <title type="text">断言参考</title>
    <id>https://sunirein.tech/gtest/reference/assertions/</id>
    <link href="https://sunirein.tech/gtest/reference/assertions/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>本文档列举了 GoogleTest 为验证代码行为提供的断言宏。
使用前需添加 <code>#include &lt;gtest/gtest.h&gt;</code> 头文件。</p>
<p>大多数宏以 <code>EXPECT_</code> 和 <code>ASSERT_</code> 两种形式存在：</p>
<ul>
<li><code>EXPECT_</code> 宏在失败时生成非致命错误，允许当前函数继续执行。</li>
<li><code>ASSERT_</code> 宏在失败时生成致命错误，终止当前函数。</li>
</ul>
<p>所有断言宏都支持通过 <code>&lt;&lt;</code> 运算符流式传输自定义错误信息，例如：</p>]]></summary>
    <content type="html"><![CDATA[<p>本文档列举了 GoogleTest 为验证代码行为提供的断言宏。
使用前需添加 <code>#include &lt;gtest/gtest.h&gt;</code> 头文件。</p>
<p>大多数宏以 <code>EXPECT_</code> 和 <code>ASSERT_</code> 两种形式存在：</p>
<ul>
<li><code>EXPECT_</code> 宏在失败时生成非致命错误，允许当前函数继续执行。</li>
<li><code>ASSERT_</code> 宏在失败时生成致命错误，终止当前函数。</li>
</ul>
<p>所有断言宏都支持通过 <code>&lt;&lt;</code> 运算符流式传输自定义错误信息，例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_TRUE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">my_condition</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">My condition is not true</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span></code></pre>
</div><p>流式传输支持所有可输出到 <code>ostream</code> 的类型（特别是 C 风格字符串和 <code>string</code> 对象）。
若流式传输宽字符串（<code>wchar_t*</code>、Windows 在 Unicode 模式下的 <code>TCHAR*</code> 或 <code>std::wstring</code>），
输出时将自动转换成 UTF-8 编码。</p>
<h2>显式成功与失败</h2>
<p>本节的断言将直接生成成功或失败，而不是测试值或表达式。
这些在使用控制流（而非布尔表达式）决定测试成功或失败时非常有用，如下例所示：</p>
<div class="language-c++" data-highlighter="shiki" data-ext="c++" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-c++"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">switch</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">expression</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  case</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 1</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">:</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">    // ... 一些检查项 ...</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  case</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 2</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">:</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">    // ... 另一些检查项 ...</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  default</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">:</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    FAIL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">We shouldn't get here.</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><h3>SUCCEED</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">SUCCEED</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span></span></code></pre>
</div><p>生成成功。
这<strong>不会</strong>使整个测试成功，要使测试成功需要所有断言通过。</p>
<p>目前，<code>SUCCEED</code> 断言仅作为文档说明，不会产生可见输出。
未来版本可能会为它添加输出信息。</p>
<h3>FAIL</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">FAIL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span></span></code></pre>
</div><p>生成致命失败，并从当前函数返回。</p>
<p>仅限在返回类型为 <code>void</code> 的函数中使用。
详见。</p>
<h3>ADD_FAILURE</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ADD_FAILURE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span></span></code></pre>
</div><p>生成非致命失败，这允许当前函数继续运行。</p>
<h3>ADD_FAILURE_AT</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ADD_FAILURE_AT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">file_path</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> line_number</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>在指定文件和行号位置生成非致命失败。</p>
<h2>泛用断言</h2>
<p>本节的断言允许使用来验证值。</p>
<h3>EXPECT_THAT</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_THAT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">value</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> matcher</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_THAT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">value</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> matcher</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证值 <em><code>value</code></em> 满足 <em><code>matcher</code></em> 的条件。</p>
<p>例如，下面的代码验证了字符串 <code>value1</code> 以 <code>&quot;Hello&quot;</code> 开头，
<code>value2</code> 匹配给定正则表达式，且 <code>value3</code> 位于 5 和 10 之间：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> &#x3C;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">gmock/gmock.h</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">AllOf</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Gt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Lt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MatchesRegex</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">StartsWith</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_THAT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">value1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> StartsWith</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Hello</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_THAT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">value2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> MatchesRegex</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Line </span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">\\</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">d+</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_THAT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">value3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> AllOf</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Gt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Lt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">10</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)));</span></span></code></pre>
</div><p>匹配器形式的断言具有更好的可读性，且能生成易读的错误信息。
例如，如果上述对 <code>value1</code> 的断言失败，会产生类似下面的错误信息：</p>
<div class="language-ansi" data-highlighter="shiki" data-ext="ansi" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-ansi"><span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Value of: value1</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">  Actual: "Hi, world!"</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Expected: starts with "Hello"</span></span></code></pre>
</div><p>GoogleTest 提供了内置匹配器库（见），
同时也支持用户自定义匹配器（见）。
与匹配器配合，使得 <code>EXPECT_THAT</code> 功能强大且扩展性强。</p>
<div class="hint-container info">
<p class="hint-container-title">相关信息</p>
<p>本断言的灵感来源于 Joe Walnes 的 Hamcrest 项目，它为 JUnit 添加了 <code>assertThat()</code>。</p>
</div>
<h2>布尔条件</h2>
<p>本节的断言用于验证布尔条件。</p>
<h3>EXPECT_TRUE</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_TRUE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">condition</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_TRUE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">condition</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <em><code>condition</code></em> 为真。</p>
<h3>EXPECT_FALSE</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_FALSE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">condition</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_FALSE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">condition</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <em><code>condition</code></em> 为假。</p>
<h2>二元比较</h2>
<p>本节的断言用于比较两个值。
值参数必须支持断言对应的比较运算符，否则会导致编译错误。</p>
<p>若参数支持 <code>&lt;&lt;</code> 运算符，当断言失败时将调用该运算符以输出参数值。
否则，GoogleTest 会自动选择最佳的输出方式，
见。</p>
<p>参数只会被计算一次，因此允许存在副作用。
不过，参数的求值顺序未定义，程序不应依赖特定的求值顺序。</p>
<p>断言同时支持窄、宽字符串（<code>string</code> 和 <code>wstring</code>）。</p>
<p>要避免浮点数比较时的精度问题，请参阅<a href="#floating-point">浮点数比较</a>。</p>
<h3>EXPECT_EQ</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_EQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_EQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <code>val1 == val2</code>。</p>
<p>在比较指针时会比较其地址。
对于 C 字符串，会验证其具有相同地址，而不是相同值。
若要验证 C 字符串的值相等，请使用 <a href="#EXPECT_STREQ"><code>EXPECT_STREQ</code></a>。</p>
<p>若要验证指针为空，请使用 <code>EXPECT_EQ(ptr, nullptr)</code> 而非 <code>EXPECT_EQ(ptr, NULL)</code>。</p>
<h3>EXPECT_NE</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_NE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_NE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <code>val1 != val2</code>。</p>
<p>在比较指针时会比较其地址。
对于 C 字符串，会验证其具有不同地址，而不是不同值。
若要验证 C 字符串的值不相等，请使用 <a href="#EXPECT_STRNE"><code>EXPECT_STRNE</code></a>。</p>
<p>若要验证指针非空，请使用 <code>EXPECT_NE(ptr, nullptr)</code> 而非 <code>EXPECT_NE(ptr, NULL)</code>。</p>
<h3>EXPECT_LT</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_LT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_LT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <code>val1 &lt; val2</code>。</p>
<h3>EXPECT_LE</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_LE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_LE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <code>val1 &lt;= val2</code>。</p>
<h3>EXPECT_GT</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_GT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_GT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <code>val1 &gt; val2</code>。</p>
<h3>EXPECT_GE</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_GE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_GE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <code>val1 &gt;= val2</code>。</p>
<h2>字符串比较</h2>
<p>本节的断言用于比较两个 <strong>C 字符串</strong>。
要比较 <code>string</code>，请使用 <a href="#EXPECT_EQ"><code>EXPECT_EQ</code></a> 或 <a href="#EXPECT_NE"><code>EXPECT_NE</code></a>。</p>
<p>断言也支持宽字符串（<code>wchar_t*</code>），在失败时将输出 UTF-8 编码结果。</p>
<p>若要比较 C 字符串与 <code>NULL</code>，请使用 <code>EXPECT_EQ(c_string, nullptr)</code> 或 <code>EXPECT_NE(c_string, nullptr)</code>。</p>
<h3>EXPECT_STREQ</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_STREQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">str1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> str2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_STREQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">str1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> str2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证两 C 字符串 <em><code>str1</code></em> 与 <em><code>str2</code></em> 内容相同。</p>
<h3>EXPECT_STRNE</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_STRNE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">str1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> str2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_STRNE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">str1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> str2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证两 C 字符串 <em><code>str1</code></em> 与 <em><code>str2</code></em> 内容不同。</p>
<h3>EXPECT_STRCASEEQ</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_STRCASEEQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">str1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> str2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_STRCASEEQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">str1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> str2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证两 C 字符串 <em><code>str1</code></em> 与 <em><code>str2</code></em> 内容相同（忽略大小写）。</p>
<h3>EXPECT_STRCASENE</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_STRCASENE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">str1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> str2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_STRCASENE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">str1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> str2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证两 C 字符串 <em><code>str1</code></em> 与 <em><code>str2</code></em> 内容不同（忽略大小写）。</p>
<h2>浮点数比较</h2>
<p>本节的断言用于比较两个浮点数。</p>
<p>由于存在舍入误差，浮点数无法精确比较，因此不适用 <code>EXPECT_EQ</code>。
通常，要对浮点数进行有意义的比较，需要谨慎选择合适的误差边界（<em>error bound</em>）。</p>
<p>GoogleTest 还提供了基于最小精度单位（<em>Unit in the Last Place</em>，<em>ULP</em>）设置默认误差边界的断言。
要了解更多关于 ULP 的信息，请参阅文章
<a href="https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/" target="_blank" rel="noopener noreferrer">Comparing Floating Point Numbers</a>。</p>
<h3>EXPECT_FLOAT_EQ</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_FLOAT_EQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_FLOAT_EQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证两个 <code>FLOAT</code> 值 <em><code>val1</code></em> 和 <em><code>val2</code></em> 近似相等，彼此相差不超过 4 个 ULP。
无穷大与最大有限浮点数值视为相差 1 个 ULP。</p>
<h3>EXPECT_DOUBLE_EQ</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_DOUBLE_EQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_DOUBLE_EQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证两个 <code>double</code> 值 <em><code>val1</code></em> 和 <em><code>val2</code></em> 近似相等，彼此相差不超过 4 个 ULP。
无穷大与最大有限双精度浮点数值视为相差 1 个 ULP。</p>
<h3>EXPECT_NEAR</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_NEAR</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> abs_error</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_NEAR</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> abs_error</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <em><code>val1</code></em> 和 <em><code>val2</code></em> 的差值不超过绝对误差限 <em><code>abs_error</code></em>。</p>
<p>若 <em><code>val1</code></em> 和 <em><code>val2</code></em> 为符号相同的无穷大，则差值视为 0。
否则，若任一值为无穷大，则差值视为无穷大。
所有非 NaN 值（包括无穷大）都不会超过无穷大的 <em><code>abs_error</code></em>。</p>
<h2>异常断言</h2>
<p>本节的断言用于验证代码段抛出（或不抛出）异常。
使用时需保证构建环境启用了异常机制。</p>
<div class="hint-container note">
<p class="hint-container-title">注</p>
<p>被测代码片段可以是复合语句，例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_NO_THROW</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">{</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  DoSomething</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">}</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div></div>
<h3>EXPECT_THROW</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_THROW</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> exception_type</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_THROW</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> exception_type</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <em><code>statement</code></em> 抛出类型为 <em><code>exception_type</code></em> 的异常。</p>
<h3>EXPECT_ANY_THROW</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_ANY_THROW</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_ANY_THROW</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <em><code>statement</code></em> 抛出任意类型的异常。</p>
<h3>EXPECT_NO_THROW</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_NO_THROW</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_NO_THROW</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <em><code>statement</code></em> 不抛出任何异常。</p>
<h2>谓词断言</h2>
<p>本节的断言用于验证复杂谓词，相比单独使用 <code>EXPECT_TRUE</code> 能提供更清晰的错误信息。</p>
<h3>EXPECT_PRED*</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_PRED1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_PRED2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_PRED3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_PRED4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_PRED5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证谓词 <em><code>pred</code></em> 在接受给定参数时返回 <code>true</code>。</p>
<p>参数 <em><code>pred</code></em> 是一个接受相应宏参数数量的函数或仿函数。
若 <em><code>pred</code></em> 对给定参数返回 <code>true</code>，则断言成功，否则断言失败。</p>
<p>断言失败时会打印每个参数的值。
保证每个参数只会被计算一次。</p>
<p>示例：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 当 m 和 n 没有非 1 公因数时返回 true</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> MutuallyPrime</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> m</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> a </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> b </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> c </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 10</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MutuallyPrime</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> a</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> b</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 成功</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MutuallyPrime</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> b</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> c</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 失败</span></span></code></pre>
</div><p>上述示例中，第二个断言失败，将输出：</p>
<div class="language-ansi" data-highlighter="shiki" data-ext="ansi" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-ansi"><span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">MutuallyPrime(b, c) is false, where</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">b is 4</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">c is 10</span></span></code></pre>
</div><p>若给定谓词是重载函数或模板函数，断言宏可能无法解析出合适的谓词，需要显示指定类型。
例如，对于接受 <code>int</code> 或 <code>double</code> 类型的 <code>IsPositive</code> 重载函数，需使用：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">static_cast&#x3C;bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">IsPositive</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">static_cast&#x3C;bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">double</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">IsPositive</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 3.14</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>直接使用 <code>EXPECT_PRED1(IsPositive, 5)</code> 会导致编译错误。
类似地，使用模板函数需指定模板参数：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">template</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> &#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">typename</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> IsNegative</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  return</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> x </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">IsNegative</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;int></span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> -</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 必须指定模板参数</span></span></code></pre>
</div><p>多个模板参数需用括号包裹以确保宏正确解析：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_PRED2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">((</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MyPredicate</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int></span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><h3>EXPECT_PRED_FORMAT*</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED_FORMAT1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred_formatter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED_FORMAT2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred_formatter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED_FORMAT3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred_formatter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED_FORMAT4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred_formatter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED_FORMAT5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred_formatter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_PRED_FORMAT1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred_formatter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_PRED_FORMAT2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred_formatter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_PRED_FORMAT3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred_formatter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_PRED_FORMAT4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred_formatter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_PRED_FORMAT5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pred_formatter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证谓词格式化器 <em><code>pred_formatter</code></em> 在接受给定参数时返回 <code>true</code>。</p>
<p>参数 <em><code>pred_formatter</code></em> 是谓词格式化器，其签名为：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">AssertionResult</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> PredicateFormatter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> expr1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">                                            const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> expr2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">                                            ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">                                            const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> exprn</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">                                            T1</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">                                            T2</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">                                            ...</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">                                            Tn</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> valn</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>其中 <em><code>val1</code></em>、<em><code>val2</code></em>、...、<em><code>valn</code></em> 是参数值，<em><code>expr1</code></em>、<em><code>expr2</code></em>、...、<em><code>exprn</code></em> 是源代码中相应的表达式。
<code>T1</code>、<code>T2</code>、...、<code>Tn</code> 既可以是值类型，也可以是引用类型。
关于返回类型 <code>testing::AssertionResult</code>，请参阅
。</p>
<p>示例：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 返回 m 和 n 的最小公质因数，互质则返回 1</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> SmallestPrimeCommonDivisor</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> m</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 当 m 和 n 没有非 1 公因数时返回 true</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> MutuallyPrime</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> m</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 断言两个整数互质的谓词格式化器</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">AssertionResult</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> AssertMutuallyPrime</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> m_expr</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">                                             const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n_expr</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">                                             int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> m</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">                                             int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  if</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MutuallyPrime</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">m</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> return</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AssertionSuccess</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  return</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AssertionFailure</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> m_expr </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> and </span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n_expr</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">      &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> (</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> m </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> and </span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">) are not mutually prime, </span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">      &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">as they have a common divisor </span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> SmallestPrimeCommonDivisor</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">m</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> a </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> b </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> c </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 10</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED_FORMAT2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">AssertMutuallyPrime</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> a</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> b</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 成功</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED_FORMAT2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">AssertMutuallyPrime</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> b</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> c</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 失败</span></span></code></pre>
</div><p>上述示例中，第二个断言失败，将输出：</p>
<div class="language-ansi" data-highlighter="shiki" data-ext="ansi" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-ansi"><span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">b and c (4 and 10) are not mutually prime, as they have a common divisor 2</span></span></code></pre>
</div><h2>Windows HRESULT 断言</h2>
<p>本节的断言用于验证 <code>HARSULT</code> 成功或失败。
例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">CComPtr</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">IShellDispatch2</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> shell</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_HRESULT_SUCCEEDED</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">shell</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">CoCreateInstance</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">L"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Shell.Application</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">CComVariant empty</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_HRESULT_SUCCEEDED</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">shell</span><span style="--shiki-light:#999999;--shiki-dark:#666666">-></span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ShellExecute</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">CComBSTR</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">url</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> empty</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> empty</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> empty</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> empty</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><p>断言失败时将输出可读的 <code>HRESULT</code> 错误信息。</p>
<h3>EXPECT_HRESULT_SUCCEEDED</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_HRESULT_SUCCEEDED</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">expression</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_HRESULT_SUCCEEDED</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">expression</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <em><code>expression</code></em> 是成功的 <code>HRESULT</code>。</p>
<h3>EXPECT_HRESULT_FAILED</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_HRESULT_FAILED</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">expression</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_HRESULT_FAILED</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">expression</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <em><code>expression</code></em> 是失败的 <code>HRESULT</code>。</p>
<h2>死亡断言</h2>
<p>本节的断言用于验证代码段导致进程终止。
详见。</p>
<p>这些断言会创建新进程来执行被测代码。
断言的具体行为取决于平台和变量 <code>::testing::GTEST_FLAG(death_test_style)</code>
（通过命令行参数 <code>--gtest_death_test_style</code> 初始化）。</p>
<ul>
<li>在 POSIX 系统中，使用 <code>fork()</code>（Linux 上为 <code>clone()</code>）创建子进程，之后：
<ul>
<li>若变量值为 <code>&quot;fast&quot;</code>，立即执行死亡测试语句。</li>
<li>若变量值为 <code>&quot;threadsafe&quot;</code>，子进程会重新执行测试程序，但只运行单个死亡测试。</li>
</ul>
</li>
<li>在 Windows 上，使用 <code>CreateProcess()</code> 创建子进程，行为类似 POSIX 的 <code>&quot;threadsafe&quot;</code> 模式。</li>
</ul>
<p>其它变量值视为无效值，会导致死亡测试失败。
目前，变量值默认为 <code>&quot;fast&quot;</code>。</p>
<p>若死亡测试语句执行完毕后未终止，子进程依旧会终止，而断言失败。</p>
<div class="hint-container note">
<p class="hint-container-title">注</p>
<p>被测代码片段可以是复合语句，例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_DEATH</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">{</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  DoSomething</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">}</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Error on line .* of DoSomething()</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div></div>
<h3>EXPECT_DEATH</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_DEATH</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> matcher</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_DEATH</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> matcher</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <em><code>statement</code></em> 导致进程以非零退出码终止，且 <code>stderr</code> 输出匹配 <em><code>matcher</code></em>。</p>
<p>参数 <em><code>matcher</code></em> 可以是 <code>const std::string&amp;</code> 的，
或正则表达式（参见）。
裸字符串 <code>s</code> 将被视为 ，
而非 。</p>
<p>例如，下列代码验证 <code>DoSomething(42)</code> 导致进程终止并输出包含 <code>&quot;My error&quot;</code> 的错误信息：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_DEATH</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">DoSomething</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">42</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">My error</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><h3>EXPECT_DEATH_IF_SUPPORTED</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_DEATH_IF_SUPPORTED</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> matcher</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_DEATH_IF_SUPPORTED</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> matcher</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>若支持死亡测试，行为同 <a href="#EXPECT_DEATH"><code>EXPECT_DEATH</code></a>，否则不作验证。</p>
<h3>EXPECT_DEBUG_DEATH</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_DEBUG_DEATH</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> matcher</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_DEBUG_DEATH</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> matcher</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>调试模式下，行为同 <a href="#EXPECT_DEATH"><code>EXPECT_DEATH</code></a>。
非调试模式（定义了 <code>NDEBUG</code> 宏）下，仅执行 <em><code>statement</code></em>。</p>
<h3>EXPECT_EXIT</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_EXIT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> predicate</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> matcher</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ASSERT_EXIT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">statement</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> predicate</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> matcher</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>验证 <em><code>statement</code></em> 导致进程终止，且退出状态满足 <em><code>predicate</code></em>，<code>stderr</code> 输出匹配 <em><code>matcher</code></em>。</p>
<p>参数 <em><code>predicate</code></em> 是接受 <code>int</code> 退出状态并返回 <code>bool</code> 的函数或仿函数。
GoolgeTest 提供了两个常用谓词：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 当程序以指定退出码正常终止时返回 true</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ExitedWithCode</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">exit_code</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 当程序被以指定信号终止时 返回 true（Windows 上不可用）</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">KilledBySignal</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">signal_number</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>参数 <em><code>matcher</code></em> 可以是 <code>const std::string&amp;</code> 的，
或正则表达式（参见）。
裸字符串 <code>s</code> 将被视为 ，
而非 。</p>
<p>例如，下列代码验证 <code>NormalExit()</code> 导致进程输出包含 <code>&quot;Success&quot;</code> 的错误信息并以状态码 0 退出：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_EXIT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">NormalExit</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(),</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ExitedWithCode</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Success</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div>]]></content>
    <published>2025-04-06T15:40:42.000Z</published>
  </entry>
  <entry>
    <title type="text">模拟参考</title>
    <id>https://sunirein.tech/gtest/reference/mocking/</id>
    <link href="https://sunirein.tech/gtest/reference/mocking/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>本文档列举了 GoogleTest 为创建和操作模拟对象提供的功能设施。
使用前需添加 <code>#include &lt;gmock/gmock.h&gt;</code> 头文件。</p>
<h2>宏</h2>
<p>GoogleTest 定义了以下宏来辅助操作模拟对象。</p>
<h3>MOCK_METHOD</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">return_type</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> method_name</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">args...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">return_type</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> method_name</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">args...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">specs...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div>]]></summary>
    <content type="html"><![CDATA[<p>本文档列举了 GoogleTest 为创建和操作模拟对象提供的功能设施。
使用前需添加 <code>#include &lt;gmock/gmock.h&gt;</code> 头文件。</p>
<h2>宏</h2>
<p>GoogleTest 定义了以下宏来辅助操作模拟对象。</p>
<h3>MOCK_METHOD</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">return_type</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> method_name</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">args...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">return_type</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> method_name</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">args...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">specs...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><p>在模拟类中定义具有参数 <em><code>arg..</code></em> 和返回类型 <em><code>return_type</code></em> 的模拟方法 <em><code>method_name</code></em>。</p>
<p><code>MOCK_METHOD</code> 的参数与方法的声明结构对应。
可选的第四个参数 <em><code>specs...</code></em> 是以逗号分隔的限定符列表。
接受的限定符包括：</p>
<p>| 限定符               | 含义                                                                                     |
|</p>
]]></content>
    <published>2025-04-05T10:45:14.000Z</published>
  </entry>
  <entry>
    <title type="text">测试参考</title>
    <id>https://sunirein.tech/gtest/reference/testing/</id>
    <link href="https://sunirein.tech/gtest/reference/testing/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>本文档列举了 GoogleTest 为编写测试程序提供的各项功能。
使用前需添加 <code>#include &lt;gtest/gtest.h&gt;</code> 头文件。</p>
<h2>宏</h2>
<p>GoogleTest 定义了以下宏来辅助编写测试。</p>
<h3>TEST</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">TestSuiteName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> TestName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // ... statements ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div>]]></summary>
    <content type="html"><![CDATA[<p>本文档列举了 GoogleTest 为编写测试程序提供的各项功能。
使用前需添加 <code>#include &lt;gtest/gtest.h&gt;</code> 头文件。</p>
<h2>宏</h2>
<p>GoogleTest 定义了以下宏来辅助编写测试。</p>
<h3>TEST</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">TestSuiteName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> TestName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // ... statements ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>在测试套件 <em><code>TestSuiteName</code></em> 中定义一个名为 <em><code>TestName</code></em> 的测试单元，包含指定的语句集合。</p>
<p>参数 <em><code>TestSuiteName</code></em> 和 <em><code>TestName</code></em> 必须是有效的 C++ 标识符，且不得包含下划线（<code>_</code>）。
位于不同测试套件的测试单元可以拥有相同的名称。</p>
<p>测试体内的语句可以是任意被测代码。
测试结果由测试体内使用的决定。</p>
<h3>TEST_F</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">TestFixtureName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> TestName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // ... statements ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>定义一个使用测试夹具类 <em><code>TestFixtureName</code></em> 的测试单元 <em><code>TestName</code></em>。
相应测试套件的名称为 <em><code>TestFixtureName</code></em>。</p>
<p>参数 <em><code>TestFixtureName</code></em> 和 <em><code>TestName</code></em> 必须是有效的 C++ 标识符，且不得包含下划线（<code>_</code>）。
<em><code>TestFixtureName</code></em> 必须是测试夹具类的名称（参见）。</p>
<p>测试体内的语句可以是任意被测代码。
测试结果由测试体内使用的决定。</p>
<h3>TEST_P</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_P</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">TestSuiteName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> TestName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // ... statements ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>定义一个使用值参数化测试夹具类 <em><code>TestFixtureName</code></em> 的测试单元 <em><code>TestName</code></em>。
相应测试套件的名称为 <em><code>TestFixtureName</code></em>。</p>
<p>参数 <em><code>TestSuiteName</code></em> 和 <em><code>TestName</code></em> 必须是有效的 C++ 标识符，且不得包含下划线（<code>_</code>）。
<em><code>TestFixtureName</code></em> 必须是值参数化测试夹具类的名称（参见）。</p>
<p>测试体内的语句可以是任意被测代码。
在测试体内，可通过 <code>GetParam()</code> 函数来访问测试参数（参见<a href="#WithParamInterface"><code>WithParamInterface</code></a>）。
例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_P</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MyTestSuite</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DoesSomething</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_TRUE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">DoSomething</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">GetParam</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()));</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>测试结果由测试体内使用的决定。</p>
<p>另见 <a href="#INSTANTIATE_TEST_SUITE_P"><code>INSTANTIATE_TEST_SUITE_P</code></a>。</p>
<h3>INSTANTIATE_TEST_SUITE_P</h3>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">INSTANTIATE_TEST_SUITE_P</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">InstantiationName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> TestSuiteName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> param_generator</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">INSTANTIATE_TEST_SUITE_P</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">InstantiationName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> TestSuiteName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> param_generator</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> name_generator</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>实例化使用 <a href="#TEST_P"><code>TEST_P</code></a> 定义的值参数化测试套件 <em><code>TestSuiteName</code></em>。</p>
<p>参数 <em><code>InstantiationName</code></em> 是该测试套件实例的唯一标识，用于区分不同实例。
在测试输出中，实例名称将作为前缀添加到测试套件名称 <em><code>TestSuiteName</code></em> 中。
若 <em><code>InstantiationName</code></em> 为空（<code>INSTANTIATE_TEST_SUITE_P(, ...)</code>），则不添加前缀。</p>
<p><span id="param-generators"></span></p>
<p>参数 <em><code>param_generator</code></em> 必须是下列 GoogleTest 提供的参数生成函数之一（均定义于 <code>::testing</code> 命名空间）：</p>
<p>| 参数生成器                                              | 行为                                                                                                                                                                                        |
|</p>
]]></content>
    <published>2025-04-04T13:56:59.000Z</published>
  </entry>
  <entry>
    <title type="text">gMock 速查表</title>
    <id>https://sunirein.tech/gtest/gmock_cheat_sheet/</id>
    <link href="https://sunirein.tech/gtest/gmock_cheat_sheet/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<h2>定义模拟类</h2>
<h3>模拟普通类</h3>
<p>给定</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ~Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetSize</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> string</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Describe</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> name</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> string</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Describe</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> type</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Process</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Bar</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> elem</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> count</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div>]]></summary>
    <content type="html"><![CDATA[<h2>定义模拟类</h2>
<h3>模拟普通类</h3>
<p>给定</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ~Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetSize</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> string</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Describe</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> name</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> string</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Describe</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> type</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Process</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Bar</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> elem</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> count</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>我们可以定义其模拟类为：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> &#x3C;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">gmock/gmock.h</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetSize</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">string</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Describe</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> name</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">string</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Describe</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> type</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Process</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Bar elem</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> count</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>要创建忽略所有无趣调用的“友好”模拟、警告所有无趣调用的“唠叨”模拟，或将无趣调用视为失败的“严格”模拟：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">NiceMock</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">NaggyMock</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">StrictMock</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">NiceMock</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MockFoo</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> nice_foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">      // 类型为 MockFoo 的子类</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">NaggyMock</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MockFoo</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> naggy_foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">    // 类型为 MockFoo 的子类</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">StrictMock</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MockFoo</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> strict_foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 类型为 MockFoo 的子类</span></span></code></pre>
</div><div class="hint-container note">
<p class="hint-container-title">注</p>
<p>目前，模拟对象默认采用唠叨模式。
未来可能会改为友好模式。</p>
</div>
<h3>模拟类模板</h3>
<p>类模板可以像普通类一样进行模拟。</p>
<p>给定模板类</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">template</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> &#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">typename</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Elem</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> StackInterface</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ~StackInterface</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetSize</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Push</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Elem</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>（注意所有需要模拟的成员函数，包括 <code>~StackInterface()</code> 必须为虚函数）</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">template</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> &#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">typename</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Elem</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockStack</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> StackInterface</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Elem</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetSize</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Push</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Elem</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><h3>指定模拟函数的调用约定</h3>
<p>若需为模拟函数指定非默认调用约定，可为 <code>MOCK_METHOD</code> 添加第四个参数 <code>Calltype(convention)</code>。
例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Calltype</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">STDMETHODCALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">double</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> double</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> y</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">            (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Calltype</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">STDMETHODCALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)));</span></span></code></pre>
</div><p>其中 <code>STDMETHODCALLTYPE</code> 由 Windows 的 <code>&lt;objbase.h&gt;</code> 头文件定义。</p>
<h2>在测试中使用模拟对象</h2>
<p>典型的工作流程如下：</p>
<div class="vp-steps"><ol>
<li>导入需要使用的 gMock 名称。
所有 gMock 符号均位于 <code>testing</code> 命名空间（宏和其他特殊标注的除外）。</li>
<li>创建模拟对象。</li>
<li>（可选）设置模拟对象的默认操作。</li>
<li>在模拟对象上设置期望（调用方式？执行操作？）。</li>
<li>执行使用模拟对象的代码，必要时使用 googletest 断言验证结果。</li>
<li>当模拟对象析构时，gMock 会自动验证所有期望是否满足。</li>
</ol>
</div><p>示例：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">                          // #1</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">BarTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DoesThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  MockFoo foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">                                    // #2</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  ON_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetSize</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">                         // #3</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillByDefault</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // ... 其他默认动作 ...</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Describe</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">                   // #4</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillRepeatedly</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Category 5</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // ... 其他期望 ...</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_EQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MyProductionFunction</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">good</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // #5</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">                                                 // #6</span></span></code></pre>
</div><h2>设置默认动作</h2>
<p>对于返回类型为 <code>void</code>、<code>bool</code>、数值类型或指针的函数，gMock 提供了内置默认动作。
另外，在 C++11 中，若类型存在默认构造函数，gMock 将返回默认构造值。</p>
<p>如果要为返回类型为 <code>T</code> 的函数定制默认动作，可使用 。
例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 设置返回类型 std::unique_ptr&#x3C;Buzz> 的默认动作为每次创建新 Buzz 对象</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">DefaultValue</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">unique_ptr</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Buzz</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>>::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">SetFactory</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">    []</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> return</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">make_unique</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Buzz</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AccessLevel</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">kInternal</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> });</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 当触发时，将运行 MakeBuzz() 的默认动作，创建新 Buzz 对象</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_buzzer_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> MakeBuzz</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">hello</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)).</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AnyNumber</span><span style="--shiki-light:#999999;--shiki-dark:#666666">());</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">auto</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> buzz1 </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> mock_buzzer_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MakeBuzz</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">hello</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">auto</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> buzz2 </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> mock_buzzer_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MakeBuzz</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">hello</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_NE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">buzz1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> nullptr</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_NE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">buzz2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> nullptr</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_NE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">buzz1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> buzz2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 重置 std::unique_ptr&#x3C;Buzz> 的默认动作，避免影响其他测试</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">DefaultValue</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">unique_ptr</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Buzz</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>>::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Clear</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span></code></pre>
</div><p>要为特定模拟对象的某个方法定制默认操作，可使用 。
<code>ON_CALL</code> 的语法与 <code>EXPECT_CALL</code> 类似，但不要求方法必须被调用。
详见。</p>
<h2>设置期望</h2>
<p>参阅模拟参考中的  一节。</p>
<h2>匹配器</h2>
<p>参阅。</p>
<h2>动作</h2>
<p>参阅。</p>
<h2>基数</h2>
<p>参阅模拟参考中 <code>EXPECT_CALL</code> 的  一节。</p>
<h2>期望的顺序</h2>
<p>默认情况下，期望可以按任意顺序匹配。
如需指定部分或全部期望的匹配顺序，可使用 <code>EXPECT_CALL</code> 的
、
，或者使用
。</p>
<h2>验证并重置模拟对象</h2>
<p>gMock 会在模拟对象析构时自动验证期望，你也可手动提前验证：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Mock</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 验证并清除 mock_obj 上的期望，返回期望是否匹配</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Mock</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">VerifyAndClearExpectations</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_obj</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 验证并清除 mock_obj 上的期望及 ON_CALL 设置的默认动作，返回期望是否匹配</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Mock</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">VerifyAndClear</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_obj</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>验证并重置模拟对象后，请勿再设置新的期望。
在已执行模拟操作的代码后设置期望将导致未定义行为。
详见。</p>
<p>你也可以允许模拟对象内存泄漏且无需验证：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Mock</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AllowLeak</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_obj</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><h2>模拟函数类</h2>
<p>gMock 定义了便捷的模拟函数类模板：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFunction</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">R</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">A1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> An</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">R</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Call</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">A1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> An</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>具体应用示例可参考。</p>
<h2>命令行标志</h2>
<p>| 标志                           | 描述                                                      |
| :</p>
]]></content>
    <published>2025-03-28T09:14:48.000Z</published>
  </entry>
  <entry>
    <title type="text">gMock 高级技巧</title>
    <id>https://sunirein.tech/gtest/gmock_cook_book/</id>
    <link href="https://sunirein.tech/gtest/gmock_cook_book/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>本指南涵盖 gMock 的高级使用技巧。
建议先阅读 掌握基本概念。</p>
<div class="hint-container note">
<p class="hint-container-title">命名空间说明</p>
<p>gMock 定义在 <code>testing</code> 命名空间中。
为提高代码的可读性，建议在使用 gMock 定义的名称之前，先在文件中使用 <code>using ::testing::Foo;</code> 声明。
本文为简洁省略了这些声明，但实际开发中应这样做。</p>
</div>
<h2>创建模拟类</h2>
<p>模拟类的定义与普通类类似，但需使用 <code>MOCK_METHOD</code> 宏来声明模拟方法。
该宏接受 3 个或 4 个参数：</p>]]></summary>
    <content type="html"><![CDATA[<p>本指南涵盖 gMock 的高级使用技巧。
建议先阅读 掌握基本概念。</p>
<div class="hint-container note">
<p class="hint-container-title">命名空间说明</p>
<p>gMock 定义在 <code>testing</code> 命名空间中。
为提高代码的可读性，建议在使用 gMock 定义的名称之前，先在文件中使用 <code>using ::testing::Foo;</code> 声明。
本文为简洁省略了这些声明，但实际开发中应这样做。</p>
</div>
<h2>创建模拟类</h2>
<p>模拟类的定义与普通类类似，但需使用 <code>MOCK_METHOD</code> 宏来声明模拟方法。
该宏接受 3 个或 4 个参数：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MyMock</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">ReturnType</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MethodName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Args...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">ReturnType</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MethodName</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Args...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Specs...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>前 3 个参数对应方法声明的三个部分。
第 4 个参数（可选）为修饰符列表，支持：</p>
<ul>
<li><code>const</code> - 添加 <code>const</code> 标记。重写 <code>const</code> 方法时必须使用。</li>
<li><code>override</code> - 添加 <code>override</code> 标记。重写虚函数时建议使用。</li>
<li><code>noexcept</code> - 添加 <code>noexcept</code> 标记。重写 <code>noexcept</code> 方法时必须使用。</li>
<li><code>Calltype(...)</code> - 指定调用约定（<em>call type</em>，例如 <code>STDMETHODCALLTYPE</code>），针对 Window 系统。</li>
<li><code>ref(...)</code> - 指定引用限定符，如 <code>ref(&amp;)</code> 或 <code>ref(&amp;&amp;)</code>。重写带引用限定符的方法时必须使用。</li>
</ul>
<h3>处理未受保护的逗号</h3>
<p>当返回类型或参数类型包含未用括号包裹的逗号时，会导致 <code>MOCK_METHOD</code> 宏解析错误：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line highlighted error"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">pair</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetPair</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> ());</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 编译失败</span></span>
<span class="line highlighted error"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> CheckMap</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">map</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> double></span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 编译失败！</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><h4>解决方案 1：括号包裹</h4>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-focused-lines vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line has-focus"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">((</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pair</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int></span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetPair</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> ());</span></span>
<span class="line has-focus"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> CheckMap</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> ((</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">map</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> double></span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><div class="hint-container note">
<p class="hint-container-title">注</p>
<p>虽然常规 C++ 语法不支持类型外包裹括号，但 <code>MOCK_METHOD</code> 会自动去除这些括号。</p>
</div>
<h4>解决方案 2：类型别名</h4>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-focused-lines vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line has-focus"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  using</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> BoolAndInt</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">pair</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>;</span></span>
<span class="line has-focus"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">BoolAndInt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetPair</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> ());</span></span>
<span class="line has-focus"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  using</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MapIntDouble</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">map</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> double</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>;</span></span>
<span class="line has-focus"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> CheckMap</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MapIntDouble</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><h3>模拟私有/保护方法</h3>
<p>无论被模拟的方法在基类中的访问权限如何，模拟方法都必须被声明在模拟类的 <code>public</code> 区段，
以便 <code>ON_CALL</code> 和 <code>EXPECT_CALL</code> 能在模拟类外部使用这些模拟方法。（C++ 允许子类修改基类中虚函数的访问级别。）</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Transform</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Gadget</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> g</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> protected</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line highlighted"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Resume</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> private</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line highlighted"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetTimeOut</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Transform</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Gadget</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> g</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 基类中的 protected 方法</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Resume</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 基类中的 private 方法</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetTimeOut</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><h3>模拟重载方法</h3>
<p>重载方法可以正常模拟，无需任何特殊处理：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 注意这里必须是虚函数</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ~Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 根数参数类型和数量重载</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Add</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Element</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Add</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Element</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 根据 const 属性重载</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Bar</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetBar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Bar</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetBar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Add</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Element x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Add</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Element x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Bar</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetBar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Bar</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetBar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>如果没有覆盖方法的所有重载版本，编译器会警告说基类中的某些方法被隐藏了。
要解决这个问题，可以使用 <code>using</code> 将这些方法引入作用域：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-focused-lines vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line has-focus"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  using</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Add</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line has-focus"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Add</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Element x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line has-focus"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 我们不打算模拟 int Add(int times, Element x)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div></div>
<h3>模拟类模板</h3>
<p>模拟类模板与模拟普通类相同：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">template</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> &#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">typename</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Elem</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> StackInterface</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ~StackInterface</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetSize</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Push</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Elem</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">template</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> &#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">typename</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Elem</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockStack</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> StackInterface</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Elem</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetSize</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Push</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Elem</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><h3>模拟非虚方法</h3>
<p>gMock 可以通过模板模拟非虚方法，实现高性能依赖注入。
在这种情况下，模拟类与真实类并不需要共同基类，只需包含具有相同签名的方法。</p>
<p>模拟非虚方法的语法与模拟虚方法相同，只是不需要添加 <code>override</code>：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 一个简单的数据包流类，它的成员都不是虚函数</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> ConcretePacketStream</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> AppendPacket</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Packet</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> new_packet</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  const</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Packet</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetPacket</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">size_t</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> packet_number</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  size_t</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> NumberOfPackets</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 一个模拟的数据包流类，它不继承任何类，但定义了 GetPacket() 和 NumberOfPackets()</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockPacketStream</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Packet</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetPacket</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">size_t</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> packet_number</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">size_t</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> NumberOfPackets</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><div class="hint-container note">
<p class="hint-container-title">注</p>
<p>与真实类不同，这里模拟类没有定义 <code>AppendPacket()</code>。
只要测试不需要调用它，这样做没问题的。</p>
</div>
<p>接下来，需要一种方式来表示在生产代码中使用 <code>ConcretePacketStream</code>，而在测试中使用 <code>MockPacketStream</code>。
由于这些函数不是虚函数，且两个类没有关联，必须在<strong>编译时</strong>（而不是运行时）指定它们。</p>
<p>我们可以通过模板参数切换来实现。
具体来说，即为代码提供一个模板类型参数来表示数据包流的类型。
在生产代码中，使用 <code>ConcretePacketStream</code> 来实例化模板；在测试中，则使用 <code>MockPacketStream</code>。
例如，可以这样定义：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line highlighted"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">template</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> &#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> PacketStream</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> CreateConnection</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">PacketStream</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> stream</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">template</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> &#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> PacketStream</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> PacketReader</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ReadPackets</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">PacketStream</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> stream</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> size_t</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> packet_num</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>然后，在生产代码中使用 <code>CreateConnection&lt;ConcretePacketStream&gt;()</code> 和 <code>PacketReader&lt;ConcretePacketStream&gt;</code>，
而在测试中使用 <code>CreateConnection&lt;MockPacketStream&gt;()</code> 和 <code>PacketReader&lt;MockPacketStream&gt;</code>。</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MockPacketStream mock_stream</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_stream</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 在 mock_stream 上设置更多期望 ...</span></span>
<span class="line highlighted"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">PacketReader</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">MockPacketStream</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> reader</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">mock_stream</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 测试 reader ...</span></span></code></pre>
</div><h3>模拟自由函数</h3>
<p>要直接模拟一个自由函数（如 C 风格函数或静态方法）是不可能的。</p>
<p>如果需要这样做，建议使用接口来重写代码——
我们不直接调用自由函数（例如 <code>OpenFile</code>），而是通过引入接口，并创建一个具体子类来使用它：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> FileInterface</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Open</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> path</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> mode</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> File</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> FileInterface</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  bool</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Open</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> path</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> mode</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">     return</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> OpenFile</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">path</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> mode</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD"> // 调用自由函数</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>所有代码应该使用 <code>FileInterface</code> 接口来打开文件。
现在，要模拟这个函数就很容易了。</p>
<p>这样做看起来可能比较麻烦，但在实际开发中，通常会有多个相关联的函数，可以被放在同一个接口中，平均下来语法开销就低很多。</p>
<p>如果担心虚函数带来的性能开销，且性能分析证实了这份担忧，可以使用<a href="#mocking-non-virtual-methods">模拟非虚方法</a>一节中的技巧。</p>
<p>或者，也可以不引入新的接口，而是重写代码，使其接受 <code>std::function</code> 作为参数而不是直接使用自由函数，
然后通过 <a href="#mock-function">MockFunction</a> 来模拟 <code>std::function</code>。</p>
<h3>旧式 <code>MOCK_METHODn</code> 宏</h3>
<p>在通用的 <code>MOCK_METHOD</code> 宏<a href="https://github.com/google/googletest/commit/c5f08bf91944ce1b19bcf414fa1760e69d20afc2" target="_blank" rel="noopener noreferrer">于 2018 年引入</a>之前，
模拟方法是通过一系列名为 <code>MOCK_METHODn</code> 的宏创建的。
这些宏目前仍受支持，但我们建议迁移到新式的 <code>MOCK_METHOD</code>。</p>
<p><code>MOCK_METHODn</code> 系列的宏与 <code>MOCK_METHOD</code> 有以下不同：</p>
<ul>
<li>通用语法是 <code>MOCK_METHODn(MethodName, ReturnType(Args))</code>，而不是 <code>MOCK_METHOD(ReturnType, MethodName, (Args))</code>。</li>
<li>必须指定数字 <code>n</code> 表示参数的数量。</li>
<li>当模拟 <code>const</code> 方法时，必须使用 <code>MOCK_CONST_METHODn</code>。</li>
<li>当模拟类模板时，宏名称必须以 <code>_T</code> 结尾。</li>
<li>为了指定调用约定，宏名称必须以 <code>_WITH_CALLTYPE</code> 结尾，并且将调用约定作为宏的第一个参数。</li>
</ul>
<p>以下是旧式宏与等效的新式宏的对比：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-diff vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 一般方法</span></span>
<span class="line diff remove"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line diff add"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// const 方法</span></span>
<span class="line diff remove"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_CONST_METHOD1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line diff add"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 类模板中的方法</span></span>
<span class="line diff remove"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD1_T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line diff add"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 类模板中的 const 方法</span></span>
<span class="line diff remove"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_CONST_METHOD1_T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line diff add"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 指定调用约定的方法</span></span>
<span class="line diff remove"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD1_WITH_CALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">STDMETHODCALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line diff add"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Calltype</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">STDMETHODCALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)))</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 指定调用约定的 const 方法</span></span>
<span class="line diff remove"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_CONST_METHOD1_WITH_CALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">STDMETHODCALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line diff add"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Calltype</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">STDMETHODCALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)))</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 类模板中指定调用约定的方法</span></span>
<span class="line diff remove"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD1_T_WITH_CALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">STDMETHODCALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line diff add"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Calltype</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">STDMETHODCALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)))</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 类模板中指定调用约定的 const 方法</span></span>
<span class="line diff remove"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_CONST_METHOD1_T_WITH_CALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">STDMETHODCALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line diff add"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Calltype</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">STDMETHODCALLTYPE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)))</span></span></code></pre>
</div><h3>友好模式、严格模式和唠叨模式</h3>
<p>如果某个模拟方法没有声明 <code>EXPECT_CALL</code> 但却被调用了，我们称这种调用为<mark>无趣调用</mark>（<em>uninteresting call</em>），
此时会执行方法的默认行为（可以通过 <code>ON_CALL()</code> 指定）。
默认情况下，如果发生无趣调用，gMock 还会打印警告来提醒用户。</p>
<p>然而，有时可能希望忽略这些无趣调用，有时也可能希望将它们视为错误。
gMock 允许在单个模拟对象级别上决定如何处理无趣调用。</p>
<p>假设测试中使用了模拟类 <code>MockFoo</code>：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  MockFoo mock_foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">());</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 使用 mock_foo 的代码 ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>当 <code>mock_foo</code> 中除 <code>DoThis()</code> 外的方法被调用，gMock 会生成一个警告。
可以使用 <code>NiceMock&lt;MockFoo&gt;</code> 来改写测试，抑制警告信息：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-focused-lines vp-code"><code class="language-cpp"><span class="line has-focus"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">NiceMock</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line has-focus"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  NiceMock</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MockFoo</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> mock_foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">());</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 使用 mock_foo 的代码 ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p><code>NiceMock&lt;MockFoo&gt;</code> 被实现为 <code>MockFoo</code> 的子类，因此可以在任何接受 <code>MockFoo</code> 的地方使用。</p>
<p>如果 <code>MockFoo</code> 的构造函数需要一些参数，<code>NiceMock&lt;MockFoo&gt;</code> 也可以正常工作，因为它继承了 <code>MockFoo</code> 的构造函数：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-focused-lines vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">NiceMock</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line has-focus"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  NiceMock</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MockFoo</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> mock_foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">hi</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 调用 MockFoo(5, "hi")</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">());</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 使用 mock_foo 的代码 ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p><code>StrictMock</code> 的用法与之类似，但它会将所有无趣调用视为失败：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-focused-lines vp-code"><code class="language-cpp"><span class="line has-focus"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">StrictMock</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line has-focus"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  StrictMock</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MockFoo</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> mock_foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">());</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 使用 mock_foo 的代码 ...</span></span>
<span class="line"></span>
<span class="line has-focus"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 如果 mock_foo 中除 DoThis() 以外的其他方法被调用，测试将失败</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><div class="hint-container note">
<p class="hint-container-title">注</p>
<p><code>NiceMock</code> 和 <code>StrictMock</code> 仅影响<strong>无趣调用</strong>（没有设置期望的调用），但不会影响<strong>意外调用</strong>（设置了期望但不匹配的调用）。
详见<a href="#uninteresting-vs-unexpected">理解无趣调用与意外调用</a>。</p>
</div>
<p>不过，由于 C++ 语法的限制，目前有一些需要注意的地方：</p>
<ol>
<li><code>NiceMock&lt;MockFoo&gt;</code> 和 <code>StrictMock&lt;MockFoo&gt;</code> 仅对直接在 <code>MockFoo</code> 类中使用 <code>MOCK_METHOD</code> 宏定义的模拟方法有效。
如果模拟方法是在 <code>MockFoo</code> 的基类中定义的，则可能不会生效，具体取决于编译器。
特别指出，不支持嵌套 <code>NiceMock</code> 和 <code>StrictMock</code>（例如 <code>NiceMock&lt;StrictMock&lt;MockFoo&gt;&gt;</code>）。</li>
<li>如果 <code>MockFoo</code> 的析构函数不是虚函数，<code>NiceMock&lt;MockFoo&gt;</code> 和 <code>StrictMock&lt;MockFoo&gt;</code> 可能无法正常工作。
我们希望修复这个问题，但这会影响现有的测试。<annotation label="clean-up-existing-tests" :total="1"><template #item-0><p>原文为“We would like to fix this, but it requires cleaning up existing tests.”译者不太清楚这里的具体含义。</p>
</template></annotation></li>
</ol>
<p>最后，对于何时使用 <code>naggy</code> 或 <code>strict</code> 要保持谨慎态度，因为这往往会使测试更加脆弱且难以维护。
理想情况下，当重构代码而不改变其外部可见行为时，应该不影响任何测试。
然而，如果测试与 <code>naggy</code> 模拟对象交互，可能会因为代码的更改而产生大量警告。
更糟糕的是，如果测试与 <code>strict</code> 模拟对象交互，甚至会直接失败，需要手动修复。
我们建议在大多数情况下使用 <code>nice</code>（目前还不是默认行为），
在开发或调试测试时使用 <code>naggy</code>（目前默认行为），而 <code>strict</code> 仅作为最后的手段使用。</p>
<h3>简化接口而不破坏现有代码</h3>
<p>有时，可能有某个方法的参数列表很长，但大部分参数对测试并不重要。
例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> LogSink</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> send</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">LogSeverity</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> severity</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> full_filename</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">                    const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> base_filename</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> line</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">                    const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> struct</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> tm</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> tm_time</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">                    const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> message</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> size_t</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> message_len</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>这个方法的参数列表冗长且不方便使用（<code>message</code> 参数甚至不是以 <code>\0</code> 结尾的）。
如果直接模拟它，将给测试带来很多麻烦。
但是，如果我们尝试简化这个接口，就需要修复所有依赖它的代码，这通常来说不可行。</p>
<p>有一个巧妙的做法——我们可以在模拟类中重新分发这个方法：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> ScopedMockLog</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> LogSink</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> send</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">LogSeverity</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> severity</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> full_filename</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">                    const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> base_filename</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> line</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> tm</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> tm_time</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">                    const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> message</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> size_t</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> message_len</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">    // 我们只关心日志的严重性、完整文件名和日志消息</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    Log</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">severity</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> full_filename</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">string</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">message</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> message_len</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 实现模拟方法：</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  //   void Log(LogSeverity severity,</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  //            const string&#x26; file_path,</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  //            const string&#x26; message);</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Log</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">              (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">LogSeverity severity</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> string</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> file_path</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">               const</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> string</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> message</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>通过定义更简洁的新模拟方法，模拟类变得更易于使用。</p>
<p>这种做法还可以用于合并重载方法。
例如，当使用重载实现默认参数时：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockTurtleFactory</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> TurtleFactory</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  Turtle</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> MakeTurtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> length</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> weight</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">  Turtle</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> MakeTurtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> length</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> weight</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> speed</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 将上述重载方法委托给这个模拟方法：</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Turtle</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> DoMakeTurtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> ());</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>这样，那些不关心哪个重载被调用的测试就无需指定参数匹配器：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ON_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">factory</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DoMakeTurtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillByDefault</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MakeMockTurtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()));</span></span></code></pre>
</div><h3>模拟具体类的替代方案</h3>
<p>通常，很多类可能没有实现接口。
为了测试使用这些类（我们称之为<strong>具体类</strong>，<em>concrete class</em>）的代码，你可能会尝试将方法改为虚函数，然后再对其进行模拟。</p>
<p>但是，请尽量不要这样做！</p>
<p>将非虚函数改为虚函数将造成巨大的影响。
它创建了一个扩展点，子类可以通过扩展点来改变类的行为。
这会削弱开发者对类的控制，因为现在维护类的不变量将变得更加困难。
你应该只在有充分理由要让子类重写某个函数时，才将其设为虚函数。</p>
<p>直接模拟具体类会造成很多问题，因为这会增加类和测试之间的耦合——
类中的任何微小更改都可能导致测试失效，从而增加维护的难度。</p>
<p>为了避免这样的问题，许多程序员选择了“面向接口编程”——
代码不直接与具体类交互，而是定义一个接口并与之交互。
开发者在具体类的基础上实现该接口的适配器。
在测试中，可以方便地模拟该接口，以观察代码的行为。</p>
<p>这种技术会带来一些开销：</p>
<ul>
<li>需要承担虚函数调用的成本（这通常不是问题）。</li>
<li>程序员需要学习更多的抽象概念。</li>
</ul>
<p>然而，除了更强的可测试性之外，它也会带来很多显著的好处：</p>
<ul>
<li>具体类的 API 可能并不完全适合特定的问题领域，因为开发者可能不是它唯一的用户。
通过设计自己的接口，开发者能够根据需求定制它——添加更高级的功能、重命名内容等，而不仅仅是裁剪类。
作为接口的使用者，这允许开发者以更自然的方式编写代码，提高代码的可读性与可维护性，并提升工作效率。</li>
<li>当具体类的实现更改时，不必重写所有使用它的地方。
相反，仅需在接口的实现中适配这些更改，而其他代码和测试将不受影响。</li>
</ul>
<p>有人会担心，如果每个人都使用这种技术，最终会产生大量冗余代码。
这种担心完全可以理解，但却是不必要的：</p>
<ul>
<li>不同的项目可能需要以不同的方式使用具体类，它们的最佳接口实践也会不同。
因此，每个项目都会在具体类之上设计自己领域的特定接口，这些接口不会是相同的代码。</li>
<li>如果有足够多的项目希望使用相同的接口，可以选择共享它，就像共享具体类一样。
可以将接口和适配器放在具体类附近（例如在 <code>contrib</code> 子目录中），让其他项目使用它。</li>
</ul>
<p>你需要根据具体问题仔细权衡利弊，但我们保证：
Java 社区长期以来一直在实践这种技术，它是一种经过充分验证的、适用于各种场景的有效技术。</p>
<h3>将调用委托给伪对象</h3>
<p>有时，你可能有一个复杂接口的伪实现。例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ~Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {}</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> s</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> p</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> FakeFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  char</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">    return</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">n </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ?</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> '</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">+</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> :</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">           (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">n </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ?</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> '</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">-</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> :</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> '</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">0</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> s</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> p</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">    *</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">p </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> strlen</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">s</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>现在，你希望模拟这个接口，以便对其设置期望，
但同时也希望使用 <code>FakeFoo</code> 作为默认行为，因为在模拟对象中重复实现这些逻辑会造成冗余。</p>
<p>在使用 gMock 定义模拟类时，可以通过以下模式将其默认行为委托给已有的伪类：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 正常使用 gMock 定义模拟方法</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">char</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> s</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int*</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> p</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 将方法的默认行为委托给 FakeFoo 对象</span></span>
<span class="line highlighted"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 此方法必须在自定义的 ON_CALL() 语句之前被调用</span></span>
<span class="line highlighted"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DelegateToFake</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    ON_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">this</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">).</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillByDefault</span><span style="--shiki-light:#999999;--shiki-dark:#666666">([</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">this</span><span style="--shiki-light:#999999;--shiki-dark:#666666">](</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line highlighted"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">      return</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> fake_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line highlighted"><span style="--shiki-light:#999999;--shiki-dark:#666666">    });</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    ON_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">this</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">).</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillByDefault</span><span style="--shiki-light:#999999;--shiki-dark:#666666">([</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">this</span><span style="--shiki-light:#999999;--shiki-dark:#666666">](</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> s</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> p</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line highlighted"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">      fake_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">s</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> p</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line highlighted"><span style="--shiki-light:#999999;--shiki-dark:#666666">    });</span></span>
<span class="line highlighted"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> private</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  FakeFoo fake_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 在模拟对象中保存伪对象实例</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>这样，就可以在测试中正常使用 <code>MockFoo</code>。
如果没有使用 <code>ON_CALL()</code> 或 <code>EXPECT_CALL()</code> 显式设置行为，就会调用伪对象来执行默认行为：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">AbcTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Xyz</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  MockFoo foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">  foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">DelegateToFake</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 启用委托</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 如果需要，请在这里添加 `ON_CALL(foo, ...)`</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 未指定动作，将使用默认行为</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> _</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_EQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> '</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">+</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 调用 FakeFoo::DoThis()</span></span>
<span class="line"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">  foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Hi</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 调用 FakeFoo::DoThat()</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_EQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><div class="hint-container tip">
<p class="hint-container-title">提示</p>
<ul>
<li>
<p>如果需要，依旧可以通过 <code>ON_CALL()</code> 或 <code>.WillOnce()</code>、<code>.WillRepeatedly()</code> 来覆盖默认行为。</p>
</li>
<li>
<p>在 <code>DelegateToFake()</code> 中，只需要委托那些打算使用伪对象来实现的方法。</p>
</li>
<li>
<p>这里讨论的技术也适用于重载方法，但需要告诉编译器具体使用的重载版本。
要消除模拟方法的歧义（在 <code>ON_CALL()</code> 中指定的函数），可以使用<a href="#select-overload">此技术</a>；
要消除伪函数的歧义（在 <code>Invoke()</code> 中调用的函数），可以使用 <code>static_cast</code>。</p>
<p>例如，如果类 <code>Foo</code> 有方法 <code>char DoThis(int n)</code> 和 <code>bool DoThis(double x) const</code>，而你希望调用后者，
则需要写</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Invoke</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">fake_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> static_cast&#x3C;bool</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">FakeFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">double</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const></span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">FakeFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span></code></pre>
</div><p>而不是</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Invoke</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">fake_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x26;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">FakeFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p><code>static_cast</code> 尖括号中的内容是第二个 <code>DoThis()</code> 方法的函数指针类型。</p>
</li>
<li>
<p>混合使用模拟对象和伪对象通常是一个不好的信号。
可能你还没有习惯基于交互的测试方式，或者你的接口承担了太多职责，应该进行拆分。
因此，<strong>不要滥用此技术</strong>。
我们建议仅在重构代码时将其作为中间步骤使用。</p>
<p>这里有一个例子可以说明为什么这可能是一个不好的信号：
假设有一个用于低级系统操作的类 <code>System</code>，它执行文件和 I/O 操作。
假设你想测试你的代码如何使用 <code>System</code> 进行 I/O 操作，而希望文件操作正常工作。
如果你模拟了整个 <code>System</code> 类，则需要为文件操作的部分提供伪实现，这表明 <code>System</code> 承担了太多职责。</p>
<p>相反，你可以定义 <code>FileOps</code> 接口和 <code>IOOps</code> 接口，将 <code>System</code> 的功能拆分为两部分，
然后仅模拟 <code>IOOps</code> 而无需模拟 <code>FileOps</code>。</p>
</li>
</ul>
</div>
<h3>将调用委托给真实对象</h3>
<p>在使用测试替身（mocks、fakes、stubs 等）时，它们的行为有时会与真实对象的行为不同。
这种差异可能是故意的（例如模拟错误行为以测试错误处理代码）或无意的。
这种不一致可能会导致代码通过测试但在生产环境中失败。</p>
<p>可以使用<strong>委托给真实对象</strong>的技术来确保模拟对象的行为与真实对象一致，同时保留验证调用的能力。
这种技术与<a href="#delegating-to-fake">委托给伪对象</a>类似，区别在于我们使用真实对象而不是伪对象。
以下是一个示例：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">AtLeast</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line highlighted"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">    // 默认情况下，所有调用都委托给真实对象</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    ON_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">this</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">).</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillByDefault</span><span style="--shiki-light:#999999;--shiki-dark:#666666">([</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">this</span><span style="--shiki-light:#999999;--shiki-dark:#666666">](</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line highlighted"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">      return</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> real_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line highlighted"><span style="--shiki-light:#999999;--shiki-dark:#666666">    });</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    ON_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">this</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">).</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillByDefault</span><span style="--shiki-light:#999999;--shiki-dark:#666666">([</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">this</span><span style="--shiki-light:#999999;--shiki-dark:#666666">](</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> s</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> p</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line highlighted"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">      real_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">s</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> p</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line highlighted"><span style="--shiki-light:#999999;--shiki-dark:#666666">    });</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    ...</span></span>
<span class="line highlighted"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">char</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ...</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> private</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  Foo real_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  MockFoo mock</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Hi</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AtLeast</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 在测试中使用模拟对象 ...</span></span></code></pre>
</div><p>通过这种方式，gMock 会验证代码是否正确调用了方法（正确的参数、顺序、次数等），
而真实对象会响应这些调用（因此行为将与生产环境一致）。
这是一种两全其美的方案。</p>
<h3>将调用委托给基类</h3>
<p>理想情况下，应采用面向接口编程，接口的所有方法都是纯虚函数。
但在实际情况中，确实可能需要模拟一个非纯虚函数（即它已经有实现）。
例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ~Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Pure</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line highlighted"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Concrete</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 模拟纯虚方法</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Pure</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line highlighted"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 模拟具体方法；Foo::Concrete() 被隐藏</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Concrete</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>有时，你可能希望调用 <code>Foo::Concrete()</code> 而不是 <code>MockFoo::Concrete()</code>。
也许你想将其作为 stub 操作的一部分，或者你的测试根本不需要模拟 <code>Concrete()</code>
（但如果每次不需要模拟某个方法时都必须定义一个新的模拟类，那将非常麻烦）。</p>
<p>gMock 允许在动作中指定调用 <code>Foo::Concrete()</code>：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Concrete</span><span style="--shiki-light:#999999;--shiki-dark:#666666">).</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">([</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">](</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">    return</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">Foo</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Concrete</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  });</span></span></code></pre>
</div><p>或者告诉模拟对象你不想要模拟 <code>Concrete()</code>：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  ON_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Concrete</span><span style="--shiki-light:#999999;--shiki-dark:#666666">).</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillByDefault</span><span style="--shiki-light:#999999;--shiki-dark:#666666">([</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">](</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> char*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">    return</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">Foo</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Concrete</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">str</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  });</span></span></code></pre>
</div><div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>这里不能使用 <code>{ return foo.Concrete(str); }</code>。
如果这样做，实际上会调用 <code>MockFoo::Concrete()</code>，造成无限递归。
这是由于 C++ 本身的语法规定。</p>
</div>
<h2>使用匹配器</h2>
<h3>精确匹配参数值</h3>
<p>可以直接指定模拟方法期望接收的具体参数值：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">a</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Hello</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><h3>使用简单的匹配器</h3>
<p>可以通过匹配器来验证参数的特定性质：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">NotNull</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 参数必须 >=5</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Ge</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)))</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">a</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 第二个参数必须为非空指针</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Hello</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> NotNull</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()));</span></span></code></pre>
</div><p>通配符 <code>_</code> 可用于匹配任意参数：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> NotNull</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()));</span></span></code></pre>
</div><h3>组合匹配器</h3>
<p>通过 <code>AllOf()</code>、<code>AllOfArray()</code>、<code>AnyOf()</code>、<code>AnyOfArray()</code> 和 <code>Not()</code> 等组合逻辑，可以构建复杂的匹配器：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">AllOf</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Gt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">HasSubstr</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Ne</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Not</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 参数必须 >5 且 !=10</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AllOf</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Gt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">                                Ne</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">10</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 第一个参数不能含有 "blah" 字串</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThat</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Not</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">HasSubstr</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">blah</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)),</span></span>
<span class="line highlighted"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">                          NULL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><p>匹配器实际上是函数对象，因此可以像任何函数一样进行组合。
不过，由于它们的类型名称冗长且缺乏有意义的语义信息，建议配合 C++14 的通用 lambda 表达式来使用：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Contains</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Property</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">inline</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> constexpr</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> auto</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> HasFoo </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> [](</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> auto&#x26;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> f</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line highlighted"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  return</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Property</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">foo</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x26;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">MyClass</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Contains</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">f</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line highlighted"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_THAT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> HasFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">blah</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><h3>安全转换匹配器类型</h3>
<p>gMock 的匹配器具有严格的类型检查，如果你使用了错误的类型（例如，使用 <code>Eq(5)</code> 来匹配一个 <code>string</code> 类型的参数），会产生编译错误。</p>
<p>不过，有时你明确知道自己的行为，并希望编译器能够稍微放宽限制。
例如，你可能有一个用于 <code>long</code> 类型的匹配器，而想要匹配的参数是 <code>int</code> 类型。
虽然这两种类型并不完全相同，但使用 <code>Matcher&lt;long&gt;</code> 来匹配 <code>int</code> 类型并没有什么问题 ——
毕竟，我们可以将 <code>int</code> 参数无损地转换为 <code>long</code>，然后再传递给匹配器。</p>
<p>为了满足这种需求，gMock 提供了 <code>SafeMatcherCast&lt;T&gt;(m)</code> 函数。
它可以将匹配器 <code>m</code> 转换为 <code>Matcher&lt;T&gt;</code> 类型。
为了确保类型转换的安全性，gMock 会检查以下条件（假设 <code>U</code> 是原匹配器接收的类型）：</p>
<ol>
<li>类型 <code>T</code> 可<strong>隐式</strong>转换为 <code>U</code>。</li>
<li>当 <code>T</code> 和 <code>U</code> 为内置算术类型（<code>bool</code>、整数和浮点数）时，<code>T</code> 到 <code>U</code> 的转换是无损的。
也就是说，任何 <code>T</code> 可以表示的值，<code>U</code> 也可以表示。</li>
<li>当 <code>U</code> 是一个非常量引用时，<code>T</code> 也必须是一个引用。
这是因为底层的匹配器可能需要使用 <code>U</code> 的地址。</li>
</ol>
<p>如果上述任一条件不满足，代码将无法编译。</p>
<p>下面是一个示例：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">SafeMatcherCast</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 一个基类与其派生类</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Base</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Derived</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Base</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ... </span><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Derived</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> derived</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  MockFoo foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 这里 m 的类型是 `Matcher&#x3C;Base*>`</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">SafeMatcherCast</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Derived</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">m</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)));</span></span></code></pre>
</div><p>如果你觉得 <code>SafeMatcherCast&lt;T&gt;(m)</code> 的限制太多，可以使用更宽松的 <code>MatcherCast&lt;T&gt;(m)</code>。
只要类型 <code>T</code> 可以通过 <code>static_cast</code> 转换为类型 <code>U</code>，<code>MatcherCast</code> 就可以工作。</p>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p><code>MatcherCast</code> 实质上允许你绕过 C++ 的类型系统（<code>static_cast</code> 并不总是安全的，因为它可能丢弃部分信息），
所以在使用时请保持警惕。</p>
</div>
<h3>选择重载函数版本</h3>
<p>当你想要调用一个重载函数时，编译器可能需要一些信息来确定要调用的版本。</p>
<p>使用 <code>Const</code> 参数包装器（<em>argument wrapper</em>），可以消除<code>const</code> 属性导致的重载歧义：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">ReturnRef</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockFoo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Bar</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetBar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Bar</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetBar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  MockFoo foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  Bar bar1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> bar2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetBar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">         // 非 const 版本的 GetBar()</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ReturnRef</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">bar1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetBar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // const 版本的 GetBar()</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ReturnRef</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">bar2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><div class="hint-container note">
<p class="hint-container-title">注</p>
<p><code>Const()</code> 由 gMock 定义，返回参数的常量引用。</p>
</div>
<p>通过将匹配器包装在 <code>Matcher&lt;type&gt;()</code> 中或使用指明具体类型的匹配器（如 <code>TypeEq&lt;type&gt;()</code>、<code>An&lt;type&gt;()</code> 等），
可以消除参数类型带来的重载歧义：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">An</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Matcher</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">TypedEq</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockPrinter</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Printer</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Print</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Print</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">char</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> c</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">PrinterTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Print</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  MockPrinter printer</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">printer</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Print</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">An</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>()));</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">            // void Print(int);</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">printer</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Print</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Matcher</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Lt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))));</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // void Print(int);</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">printer</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Print</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TypedEq</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">char</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">a</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)));</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">   // void Print(char);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">  printer</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Print</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">3</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">  printer</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Print</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">6</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">  printer</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Print</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">a</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><h3>基于参数值指定不同行为</h3>
<p>当模拟方法被调用时，将选中最后一个匹配到的有效期望。
因此，可以让方法具有基于参数值的不同行为，例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Lt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 默认情况</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillRepeatedly</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">b</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 更特定的情况</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> DoThis</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Lt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)))</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillRepeatedly</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">a</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">'</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><p>这里，如果 <code>foo.DoThis()</code> 的参数小于 5，将返回 <code>'a'</code>，否则返回 <code>'b'</code>。</p>
<h3>将多参数作为整体进行匹配</h3>
<p>有时，仅对参数进行单独地匹配是不够的。
例如，我们可能希望第一个参数必须小于第二个参数。
<code>With()</code> 子句允许我们在匹配时将方法的所有参数视为整体，进行相应操作。
例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Ne</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Lt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> InRange</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Ne</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> _</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">With</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Lt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">());</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 译注：这里的 Lt() 跟平时使用的不太一样，参数类型为 std::tuple&#x3C;A1, A2></span></span></code></pre>
</div><p>这表示 <code>InRange()</code> 的第一个参数不能为 <code>0</code>，且必须小于第二个参数。</p>
<p><code>With()</code> 接受类型为 <code>Matcher&lt;std::tuple&lt;A1, A2, ..., An&gt;&gt;</code> 的匹配器，
其中 <code>A1</code>, ..., <code>An</code> 为模拟方法的参数类型。</p>
<p>可以使用 <code>AllArgs(m)</code> 来代替 <code>m</code>。
这两种形式是等价的，但 <code>.With(AllArgs(m))</code> 具有更好的可读性。</p>
<p>可以使用 <code>Args&lt;k1, ..., kn&gt;(m)</code> 来提取特定的参数组合。
例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">AllOf</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Args</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Lt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Blah</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line highlighted"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">With</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AllOf</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Args</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Lt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()),</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Args</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Lt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())));</span></span></code></pre>
</div><p>这表示 <code>Blah</code> 的参数 <code>x</code>、<code>y</code>、<code>z</code> 必须满足 <code>x &lt; y &lt; z</code>。</p>
<p>为了让使用便利，gMock 提供了一些用于 2 元组的匹配器，包括上面提到的 <code>Lt()</code> 匹配器。
完整的列表请参阅。</p>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>如果你想将参数传递给自定义谓词（例如 <code>.With(Args&lt;0, 1&gt;(Truly(&amp;MyPredicate)))</code>），该谓词必须接受 <code>std::tuple</code>；
gMock 会将选定的 <code>n</code> 个参数作为一个元组传递给谓词。</p>
</div>
<h3>将匹配器转换为谓词</h3>
<p>你是否注意到，匹配器实际上就是一种能描述自身的谓词？
许多算法接受谓词作为参数（例如 <code>&lt;algorithm&gt;</code> 头文件中定义的算法），如果不能使用 gMock 的匹配器，将非常可惜。</p>
<p>为此，gMock 提供了 <code>Matcher()</code> 函数，可以通过匹配器转换为一元谓词。
例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> &#x3C;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">algorithm</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">></span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> &#x3C;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">vector</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Matches</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Ge</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">vector</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;int></span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> v</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// v 中有多少个元素 >= 10？</span></span>
<span class="line highlighted"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> count </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> count_if</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">v</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">begin</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(),</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> v</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">end</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(),</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Matches</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Ge</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">10</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)));</span></span></code></pre>
</div><p>由于在 gMock 中可以轻松地构建出复杂的匹配器，
这提供了一种构造复合谓词的简单方法（使用 <code>&lt;functional&gt;</code> 头文件实现同样的功能则非常麻烦）。
例如，以下是一个判断数字大于等于 <code>0</code>、小于等于 <code>100</code> 且不等于 <code>50</code> 的谓词：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">AllOf</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Ge</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Le</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Matches</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Ne</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Matches</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AllOf</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Ge</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Le</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">100</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Ne</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">50</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)))</span></span></code></pre>
</div><h3>在断言中使用匹配器</h3>
<p>请参阅断言参考中的  一节。</p>
<h3>将谓词转换为匹配器</h3>
<p>gMock 提供了一组内置的匹配器，用于将参数与期望值进行匹配——更多信息请参阅。
如果内置的匹配器无法满足需求，gMock 提供了 <code>Truly()</code> 函数，可以将任意一元谓词函数或函数对象转换为匹配器：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Truly</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> IsEven</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> return</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">n </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">%</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ==</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ?</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 1</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> :</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> }</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // Bar() 必须接受一个偶数</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Truly</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">IsEven</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)));</span></span></code></pre>
</div><p>这里，谓词函数或函数对象不要求返回 <code>bool</code> 类型。
只要返回值可以作为 <code>if (condition) ...</code> 语句中的条件，就能正常转换。</p>
<h3>处理不可拷贝的参数</h3>
<p>当执行 <code>EXPECT_CALL(mock_obj, Foo(bar))</code> 时，gMock 会保存 <code>bar</code> 的副本，
并在调用 <code>Foo()</code> 时，将 <code>Foo()</code> 的参数与保存的 <code>bar</code> 副本进行比较。
因此，你不用担心 <code>bar</code> 在 <code>EXPECT_CALL()</code> 执行后被修改或销毁而导致的问题。
对于 <code>Eq(bar)</code>、<code>Le(bar)</code> 等匹配器时，情况同样如此。</p>
<p>但如果 <code>bar</code> 不可拷贝（即没有拷贝构造函数），该怎么办？
你可以定义自己的匹配器函数，配合 <code>Truly()</code> 来作为匹配器使用，就像<a href="#predicate-to-matcher">前面的示例</a>那样。
或者，如果你能保证 <code>bar</code> 在 <code>EXPECT_CALL()</code> 执行后不会被修改，可以让 gMock 只保存 <code>bar</code> 的引用。
方法如下：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Eq</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Lt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 期待 Foo() 的参数 == bar</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_obj</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Eq</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ref</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 期待 Foo() 的参数 &#x3C; bar</span></span>
<span class="line highlighted"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_obj</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Lt</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">ref</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))));</span></span></code></pre>
</div><div class="hint-container caution">
<p class="hint-container-title">警告</p>
<p>如果你这样做，请不要在调用 <code>EXPECT_CALL()</code> 之后修改 <code>bar</code>，否则结果未定义。</p>
</div>
<h3>验证对象成员</h3>
<p>许多模拟方法接受一个对象的引用。
在匹配参数时，你可能不希望将对象作为整体进行匹配，因为这样做过于严格。
为此，gMock 提供了 <code>Field()</code> 和 <code>Property()</code>，允许单独验证对象的成员变量或方法的返回值。
具体来说：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Field</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> m</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>匹配 <code>bar</code> 成员变量满足匹配器 <code>m</code> 的 <code>Foo</code> 对象。</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Property</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">baz</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> m</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span></code></pre>
</div><p>匹配 <code>baz()</code> 方法的返回值满足匹配器 <code>m</code> 的 <code>Foo</code> 对象。</p>
<p>例如：</p>
<p>| 表达式                                       | 描述                                        |
| :</p>
]]></content>
    <published>2025-03-23T04:22:53.000Z</published>
  </entry>
  <entry>
    <title type="text">gMock 入门基础</title>
    <id>https://sunirein.tech/gtest/gmock_for_dummies/</id>
    <link href="https://sunirein.tech/gtest/gmock_for_dummies/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<h2>什么是 gMock？</h2>
<p>在编写原型（<em>prototype</em>）或测试时，完全依赖真实对象的行为往往不可行或不够明智。
<mark>模拟对象</mark>（<em>mock object</em>）实现了与真实对象相同的接口，可以替代真实对象。
你可以在运行时指定和观察模拟对象的行为，如“哪些方法会被调用？”“按什么顺序调用？”“调用多少次？”“使用什么参数？”“返回什么值？”等等。</p>
<p>人们常常会混淆<mark>伪对象</mark>（<em>fake object</em>）与模拟对象的概念。
在测试驱动开发（<em>TDD</em>）领域中，这两者有着本质区别：</p>]]></summary>
    <content type="html"><![CDATA[<h2>什么是 gMock？</h2>
<p>在编写原型（<em>prototype</em>）或测试时，完全依赖真实对象的行为往往不可行或不够明智。
<mark>模拟对象</mark>（<em>mock object</em>）实现了与真实对象相同的接口，可以替代真实对象。
你可以在运行时指定和观察模拟对象的行为，如“哪些方法会被调用？”“按什么顺序调用？”“调用多少次？”“使用什么参数？”“返回什么值？”等等。</p>
<p>人们常常会混淆<mark>伪对象</mark>（<em>fake object</em>）与模拟对象的概念。
在测试驱动开发（<em>TDD</em>）领域中，这两者有着本质区别：</p>
<ul>
<li><strong>伪对象</strong>实现了原有对象的功能，但通常采用某些简化方式（可能是为了降低运行成本），因此不适合生产环境。
内存文件系统就是一个典型案例。</li>
<li><strong>模拟对象</strong>只是按照对象原本的行为，预先设定了一些<mark>期望</mark>（<em>expectation</em>），而没有实现完整的功能。</li>
</ul>
<p>如果这些概念对你来说过于抽象，不必担心——你现在只需记住：模拟对象允许你验证其与被测代码之间的<strong>交互行为</strong>。
一旦开始实际使用，你就能清晰感受到这两者的区别。</p>
<p><mark>gMock</mark> 是一个用于创建和使用模拟对象类（以下简称“模拟类”）的库（有时也将其称为&quot;框架&quot;以彰显其专业性）。
它为 C++ 提供了与 Java 中的 jMock/EasyMock 类似的功能，虽然实现方式有所不同，但核心理念相通。</p>
<p>使用 gMock 时：</p>
<ol>
<li>首先通过一些简单的宏来描述你需要模拟的接口，这些宏会自动展开成模拟类的实现；</li>
<li>接着创建模拟对象，使用直观的语法来描述对象的期望（<em>expectation</em>）与行为；</li>
<li>最后执行那些使用模拟对象的代码。
gMock 会即时捕捉任何违反期望的情况。</li>
</ol>
<h2>为什么选择 gMock？</h2>
<p>虽然模拟对象能减少测试中不必要的依赖，使测试更加高效可靠，但在 C++ 中手动实现模拟对象十分<strong>困难</strong>：</p>
<ul>
<li>需要人工编写模拟类实现。这类工作通常枯燥且容易出错，许多开发者都避而远之。</li>
<li>手写编写的模拟对象质量参差不齐。
你可能看到精心设计的实现，也可能遇到仓促完成、充斥各种限制的版本。</li>
<li>通过使用某个模拟对象获得的经验难以运用到其他场景。</li>
</ul>
<p>相比之下，Java 和 Python 开发者拥有优秀的模拟框架（jMock、EasyMock 等），可以自动创建模拟对象。
在这些语言社区里，模拟被证明是一种高效的技术，在开发中得到广泛运用。
使用合适的工具为这些开发者带来了质的改变。</p>
<p>gMock 专为 C++ 开发者打造。
其设计灵感源自 jMock 和 EasyMock，同时充分考虑了 C++ 的语言特性。
如果你正面临以下问题，gMock 将成为你的得力助手：</p>
<ul>
<li>受困于某些次优设计，希望趁为时未晚进行更多原型验证，但 C++ 的原型开发缺乏效率。</li>
<li>测试因依赖过多库或使用昂贵资源（如数据库）而运行缓慢。</li>
<li>测试因使用不可靠资源（如网络）而不稳定。</li>
<li>需要测试代码对故障（如文件校验错误）的处理逻辑，但难以人为制造故障。</li>
<li>需要验证模块间的正确交互，但难以直接观测交互过程，因此只能通过观测最终副作用来间接判断。</li>
<li>希望模拟依赖项，但它们没有模拟版本，或者你对那些手写的笨拙模拟不感兴趣。</li>
</ul>
<p>我们建议你从两个维度来使用 gMock：</p>
<ul>
<li>作为<strong>设计工具</strong>：让你能够及早且更频繁地进行接口设计测试。更多迭代带来更优设计。</li>
<li>作为<strong>测试工具</strong>：减少测试的外部依赖；探究模块与其协作者之间的交互细节。</li>
</ul>
<h2>快速开始</h2>
<p>gMock 与 googletest 捆绑发布。</p>
<h2>模拟对象使用案例</h2>
<p>我们通过一个具体案例来理解模拟对象的作用。
假设你正在开发一个依赖 <a href="https://en.wikipedia.org/wiki/Logo_programming_language" target="_blank" rel="noopener noreferrer">LOGO</a> 风格 API 的绘图程序，
你会如何验证其绘制逻辑的正确性？
直接运行程序并与基准屏幕快照（<em>golden screen snapshot</em>）对比看似可行，但这种测试方式存在明显缺陷：</p>
<ul>
<li>测试的运行成本高昂。</li>
<li>测试结果容易被破坏：如果你更换了一张新显卡，抗锯齿性能更好，你需要重新生成所有的基准图像。</li>
</ul>
<p>如果所有测试都采用这种方式，维护成本将难以承受。
<a href="https://en.wikipedia.org/wiki/Dependency_injection" target="_blank" rel="noopener noreferrer">依赖注入（<em>Dependency Injection</em>）</a>
提供了一种更好的解决方案：通过抽象接口隔离系统 API，解耦对象逻辑与具体实现。
我们可定义如下的 <code>Turtle</code> 接口：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> ~Turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {}</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> PenUp</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> PenDown</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Forward</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> distance</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Turn</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> degrees</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GoTo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> y</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetX</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  virtual</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetY</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p><code>Turtle</code> 类的析构函数必须被声明为虚函数，否则将无法正确使用基类指针来删除派生类对象。</p>
</div>
<p>你可以用 <code>PenUp()</code> 和 <code>PenDown()</code> 来控制 <code>Trutle</code> 对象在移动时是否产生轨迹，
用 <code>Forward()</code>、<code>Turn()</code>、<code>GoTo()</code> 来控制对象的移动，
并用 <code>GetX()</code> 和 <code>GetY()</code> 来获取当前所处位置。</p>
<p>通过在实际运行中使用真实实现，而在测试中使用模拟实现，你可以</p>
<ul>
<li>精确获取程序调用不同绘图指令的顺序与调用参数。</li>
<li>增强测试的鲁棒性：测试不会因图形硬件差异而产生不同结果。</li>
<li>使测试易于阅读和维护：测试逻辑直接展现在代码中，而不是图像文件中。</li>
<li>大大提高测试运行速度。</li>
</ul>
<h2>编写模拟类</h2>
<p>有时你可能足够幸运，已经有人为你实现需要使用的模拟类了。
不过，即使你需要自己编写，也不必担心，gMock 使这个任务几乎成为一场有趣的游戏。</p>
<h3>如何定义模拟类？</h3>
<p>以 <code>Turtle</code> 接口为例，你需要遵循以下步骤：</p>
<div class="vp-steps"><ul>
<li>从 <code>Turtle</code> 派生出 <code>MockTurtle</code> 类。</li>
<li>选取要模拟的虚函数（尽管 gmock 支持，
但实现起来较为复杂）。</li>
<li>在 <code>public:</code> 区域使用 <code>MOCK_METHOD()</code> 宏。</li>
<li>现在到了最重要的一步：
将函数签名拆分成返回类型、函数名和参数列表，分别作为宏的三个参数。</li>
<li>如果该函数为 <code>const</code> 修饰，添加 <code>(const)</code> 作为第四个参数（这里括号是必要的）。</li>
<li>由于你是在重写一个虚函数，我们建议给该函数添加 <code>override</code> 修饰。
如果函数已被 <code>const</code> 修饰，则将第四个参数改为 <code>(const, override)</code>，否则直接使用 <code>(override)</code>。</li>
<li>重复上述步骤，处理你要模拟的所有函数。
注意，即使不需要模拟，你也必须为所有纯虚函数提供一份实现。</li>
</ul>
</div><p>完成上述步骤后，你会得到类似下面的示例：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> &#x3C;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">gmock/gmock.h</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">></span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 导入 gMock 库</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> MockTurtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 模拟无参数方法</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> PenUp</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> PenDown</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 模拟有参数方法</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Forward</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> distance</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Turn</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> degrees</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GoTo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> y</span><span style="--shiki-light:#999999;--shiki-dark:#666666">),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 模拟 const 方法</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetX</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  MOCK_METHOD</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> GetY</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (),</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p><code>MOCK_METHOD</code> 宏已经自动生成了所有必要的代码，因此你无需手动实现这些模拟方法。</p>
<h3>模拟类的存放位置</h3>
<p>你需要慎重考虑模拟类的存放位置。</p>
<p>很多人会将它直接放在 <code>_test.cc</code> 文件中。
当接口（这里假定为 <code>Foo</code>）与测试同属一个人或团队时，这样做是可行的。
否则，当 <code>Foo</code> 的所有者修改了接口了，测试可能会失效。
（你不能指望 <code>Foo</code> 的所有者来修复所有相关的测试。）</p>
<p>一般而言，你不应该模拟不属于你的类。
如果真的有这样做的必要，将定义存放在 <code>Foo</code> 所属的 Bazel 包中：
在同一目录或其 <code>testing</code> 子目录下创建一个 <code>.h</code> 头文件，并相应创建 <code>testonly=true</code> 的 <code>cc_library</code>。
这样每个人都可以在他们的测试中引入该模拟类，而一旦有变更发生，只需要修改这一处。</p>
<p>另一种更推荐的方式是引入一个 <code>FooAdaptor</code> 中间层——它完全属于你，因此你可以从容处理 <code>Foo</code> 接口变化的影响。
尽管这会增加初始开发成本，但一个合理的中间层相比 <code>Foo</code> 更能满足你的特定需求，从而可以提高代码的可读性和可维护性。</p>
<h2>在测试中使用模拟对象</h2>
<p>使用模拟对象的典型工作流程如下：</p>
<div class="vp-steps"><ol>
<li>从 <code>testing</code> 命名空间中导入需要使用的 gMock 符号。</li>
<li>创建模拟对象的实例。</li>
<li>设置模拟对象的期望，如方法的调用次数、调用参数和行为模式等。</li>
<li>运行使用模拟对象的测试代码，可搭配 googletest 断言来验证结果。</li>
<li>当模拟对象析构时，gMock 会自动检测所有期望是否满足。</li>
</ol>
</div><p>以下为完整示例（对应步骤已用编号标注好）：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">path/to/mock-turtle.h</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> &#x3C;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">gmock/gmock.h</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">></span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> &#x3C;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">gtest/gtest.h</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">AtLeast</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">                      // #1</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">PainterTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> CanDrawSomething</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  MockTurtle turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">                           // #2</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> PenDown</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">               // #3</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AtLeast</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  Painter </span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">painter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">                    // #4</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_TRUE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">painter</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">DrawCircle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 10</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">                                              // #5</span></span></code></pre>
</div><p>上述测试验证 <code>PenDown()</code> 方法会被调用至少一次。
如果 <code>painter</code> 实际上没有调用 <code>PenDown()</code> 方法，测试会失败并输出以下信息：</p>
<div class="language-ansi" data-highlighter="shiki" data-ext="ansi" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-ansi"><span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">path/to/my_test.cc:119: Failure</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Actual function call count doesn't match this expectation:</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Actually: never called;</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Expected: called at least once.</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Stack trace:</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">...</span></span></code></pre>
</div><div class="hint-container tip">
<p class="hint-container-title">提示</p>
<p>在 Emacs 中可直接在错误行号上敲击回车，以跳转到未通过的期望的位置。</p>
</div>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>如果模拟对象没有被析构，对期望值的验证将不会发生。
因此，最好在测试中启用堆检查器。
<code>gtest_main</code> 中已经自动启用该机制。</p>
</div>
<div class="hint-container important">
<p class="hint-container-title">设置期望的注意事项</p>
<p>gMock 要求必须在模拟方法被调用<strong>之前</strong>设置期望，否则将导致<strong>未定义行为</strong>。
禁止在模拟方法的调用过程中穿插设置期望，以及在调用结束后设置期望。</p>
<p>这意味着 <code>EXPECT_CALL()</code> 应被理解为对<strong>未来</strong>调用的期望，而不是对过去行为的验证。
gMock 采用这种设计，旨在当期望未通过时能立即报错，同时保留完整的运行上下文（如堆栈信息），提高调试效率。</p>
</div>
<p>尽管本例较为简单，即使不用 gMock 也能轻松实现类似的效果，但 gMock 的威力远不止于此。
在后面的进一步学习后，你便能对 gMock 的强大有所体会。</p>
<h2>设置期望</h2>
<p>设置合适的期望是正确用好模拟对象的关键。
如果期望设置得过于严格，测试可能会因无关的更改而失败；
如果设置得过于宽松，又可能无法发现潜在的错误。
你需要找到合适平衡点，让测试能精确捕获应该发现的错误类型。
gMock 提供了实现这种平衡的必要工具。</p>
<h3>基本语法</h3>
<p>在 gMock 中，我们使用 <code>EXPECT_CALL()</code> 宏来为模拟方法设置期望。其基本语法为：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_object</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> method</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">matchers</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">cardinality</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">action</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillRepeatedly</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">action</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>该宏接受两个参数，第一个是模拟对象，第二个是要模拟的方法和参数。
出于技术原因，你应该用 <code>,</code> 而不是用 <code>.</code> 隔开两者。
若方法没有重载，<code>matcher</code> 可以被省略：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">mock_object</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> non</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">-</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">overloaded</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">-</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">method</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">cardinality</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">action</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillRepeatedly</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">action</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>这种语法允许测试编写者在表示“允许用任意参数调用该方法”时，无需显式指定参数数量或类型。
为避免产生歧义，该语法仅适用于无重载的方法。</p>
<p>这两种形式的语法后均可接若干子句，用来提供更详细的期望描述。
我们将在后续章节中讨论不同子句的作用。</p>
<p>这种语法设计旨在让期望设置读起来像自然语言。
例如，你可能猜到以下代码：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetX</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">5</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">100</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">150</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillRepeatedly</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">200</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><p>表示 <code>Turtle</code> 对象的 <code>GetX()</code> 方法将被调用五次：
第一次返回 100，第二次返回 150，剩下每次都返回 200。
有些人将这种语法风格称为<strong>领域特定语言</strong>（<em>Domain-Specific Language</em>, <em>DSL</em>）。</p>
<div class="hint-container info">
<p class="hint-container-title">为什么使用宏来设置期望？</p>
<p>这主要出于两个目的：
一是让期望易于识别，无论是通过 <code>grep</code> 等工具还是人工阅读代码；
二是允许 gMock 在错误消息中包含期望的源码位置，便于调试。</p>
</div>
<h3>匹配器：对参数值的期望</h3>
<p>当模拟方法接收参数时，我们可以指定期望的参数值，例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 期望 turtle 前进 50 个单位</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Forward</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">100</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><p>但通常我们不希望参数限定过于严格——这会降低测试的稳定性。
因此我们建议只做必要的参数限定。
如果你不关心参数值，可以使用 <code>_</code> 作为参数。</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 期望 turtle 跳转到 x=50 轴线上的任意位置</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GoTo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">50</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> _</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><p><code>_</code> 是表示“允许任意值”的<mark>匹配器</mark>（<em>matcher</em>）。
匹配器类似于谓词，可以验证参数是否符合预期。
在 <code>EXPECT_CALL()</code> 中，任何需要函数参数的地方都可以使用匹配器。</p>
<p>上面示例中的 <code>100</code> 和 <code>50</code> 也是匹配器，它们等价于 <code>Eq(100)</code> 和 <code>Eq(50)</code>，表示函数参数必须与匹配器参数相等（使用 <code>operator==</code>）。
gMock 为常见类型提供了许多，同时也支持。
例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Ge</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 期望 turtle 前进至少 100 个单位</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Forward</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Ge</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">100</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)));</span></span></code></pre>
</div><p>如果你完全不关心参数值，可以省略参数列表，而不必将每个参数标注为 <code>_</code>：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// Expects the turtle to move forward.</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Forward</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// Expects the turtle to jump somewhere.</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> GoTo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>不过，这种语法仅适用于没有重载的方法。
如果方法存在重载，需通过指定参数数量甚至来帮助 gMock 解析要使用的重载版本。</p>
<h3>基数：对调用次数的期望</h3>
<p><code>EXPECT_CALL()</code> 后第一个可以指定的子句是 <code>Times()</code>。
它的参数称为<mark>基数</mark>（<em>cardinality</em>），表示对方法<strong>调用次数</strong>的期望。
使用该子句可以避免为相同的方法调用重复书写相同的期望。
不仅如此，基数还可以是模糊（<em>fuzzy</em>）值，具有很高的灵活性，能帮助用户精确表达测试意图。</p>
<p>基数 <code>Times(0)</code> 具有特殊含义，它表明带指定参数的调用不应发生。
若发生此类调用，gMock 将产生一个失败。</p>
<p>在前面的示例中，我们已经接触过像 <code>AtLeast(n)</code> 这样的模糊基数。
你可以在查阅完整的内置基数列表。</p>
<p><code>Times()</code> 子句可以省略。
若省略 <code>Times()</code>，gMock 会根据以下规则自动推断基数：</p>
<ul>
<li>若 <code>WillOnce()</code> 和 <code>WillRepeatedly()</code> 都未给定，推断为 <code>Times(1)</code>。</li>
<li>若存在 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo stretchy="false">(</mo><mo>≥</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">n (\ge 1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">n</span><span class="mopen">(</span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mclose">)</span></span></span></span> 个 <code>WillOnce()</code>，但不存在 <code>WillRepeatedly()</code>，推断为 <code>Times(n)</code>。</li>
<li>若存在 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo stretchy="false">(</mo><mo>≥</mo><mn>0</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">n (\ge 0)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">n</span><span class="mopen">(</span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">0</span><span class="mclose">)</span></span></span></span> 个 <code>WillOnce()</code> 和一个 <code>WillRepeatedly()</code>，推断为 <code>Times(AtLeast(n))</code>。</li>
</ul>
<div class="hint-container note">
<p class="hint-container-title">思考题</p>
<p>若某方法预期调用两次但实际调用四次，会发生什么？</p>
</div>
<h3>动作：设置方法的行为</h3>
<p>模拟对象本身并不具备真正的功能实现，作为用户，我们需要指定方法被调用时的行为。</p>
<p>gMock 为模拟方法提供了默认行为，若用户未指定动作，则采用默认行为：</p>
<ul>
<li>当返回类型是内置类型或指针时，<code>void</code> 函数直接返回，<code>bool</code> 函数返回 <code>false</code>，其他类型返回 0。</li>
<li>在 C++11 及以上版本中，若返回类型具有默认构造函数，返回默认构造的值。</li>
</ul>
<p>如果模拟方法没有默认行为，或者默认行为并不适用，可以使用 <code>WillOnce()</code> 和 <code>WillRepeatedly()</code> 子句来定制方法行为。
例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetX</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">     .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">100</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">     .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">200</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">     .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">300</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><p>表示 <code>turtle.GetX()</code> 会被调用恰好三次（由 gMock 自动推断），且分别返回 100、200 和 300。</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetY</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">     .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">100</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">     .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">200</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">     .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillRepeatedly</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">300</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><p>表示 <code>turtle.GetY()</code> 会被调用至少两次（由 gMock 自动推断），前两次分别返回 100 和 200，之后始终返回 300。</p>
<p>如果你显示指定了 <code>Times()</code>，gMock 将不再自动推断基数。
这时，如果你指定的基数超出 <code>WillOnce()</code> 描述的数量，且没有指定 <code>WillRepeatedly()</code>，在耗尽所有 <code>WillOnce()</code> 后，方法将执行默认行为。</p>
<p>在设置动作时，除了 <code>Return()</code> 外，还可以使用 <code>RuturnRef(</code><em><code>variable</code></em><code>)</code> 来返回引用，或者执行其他。</p>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p><code>EXPECT_CALL()</code> 语句仅会执行动作子句一次，即使该动作可能被多次执行，因此需谨慎对待副作用。</p>
<p>例如，以下代码将无法实现预期效果：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 100</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetX</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillRepeatedly</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">n</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">++</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><p><code>n++</code> 语句只会被执行一次，因此该方法将始终返回 100，而不是 100、101、102、...。
类似地，<code>Return(new Foo)</code> 会在 <code>EXPECT_CALL()</code> 执行时创建一个 <code>Foo</code> 对象，之后每次都会返回相同的指针。
如果你希望副作用能够正常发生，可以使用自定义动作，这部分将在 中提及。</p>
</div>
<div class="hint-container note">
<p class="hint-container-title">思考题</p>
<p>思考下面代码的含义：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetY</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">4</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">100</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><p><strong>答案</strong>：
</p>
</div>
<h3>设置多重期望</h3>
<p>在前面的示例中，我们只展示了使用一个期望的情况。
在实际测试中，我们可能需要为模拟对象的某一方法设置多重期望。
默认情况下，gMock 会按照期望定义的<strong>逆序</strong>来匹配，直到遇到第一个匹配的期望（即“后定义的期望具有更高优先级”）。
如果所有期望都不匹配，则会产生一个超限错误（<em>upper-bound-violated failure</em>）。</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Forward</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // #1</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Forward</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">10</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // #2</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>以上述代码为例，如果连续调用 3 次 <code>Forward(10)</code> ，就会产生一个错误，因为它匹配到了期望 <code>#2</code>。
如果第 3 次调用改为 <code>Forward(20)</code>，则没有问题，因为这时匹配到的是期望 <code>#1</code>。</p>
<div class="hint-container info">
<p class="hint-container-title">设计目的</p>
<p>gMock 选用<strong>逆序</strong>来匹配期望，是因为这样允许用户在模拟对象的构造函数或测试套件的 <code>SetUp</code> 方法中提供默认期望，
然后在测试单元中设置更特定（<em>specific</em>）的期望来定制模拟对象。
因此，如果某个方法有两个期望，你应该将匹配器更加特定的期望放在后面，否则它会被更通用的规则给隐藏掉。</p>
</div>
<div class="hint-container tip">
<p class="hint-container-title">兜底期望</p>
<p>为方法设置一个<strong>兜底期望</strong>（<em>catch-all expectation</em>，即匹配所有参数并设为 <code>Times(AnyNumber())</code>）的行为非常普遍。
这使得方法的任何调用都能满足预期。
对于那些根本不会涉及的方法（称为“无趣”，<em>uninteresting</em>）来说，这样做没有必要；
但对于那些设置了一些期望、同时也允许其他调用方式的方法来说非常有用。
详见。</p>
</div>
<h3>有序调用 vs 无序调用</h3>
<p>默认情况下，即使先声明的期望尚未满足，后声明的期望依旧可以被匹配到。
也就是说，方法的实际调用顺序不需要与期望声明的顺序一致。</p>
<p>然而，有时我们希望方法能够严格按照期望声明的顺序调用。
在 gMock 中，可以通过 <code>InSequence</code> 方便地实现这一功能：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">InSequence</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FooTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DrawsLineSegment</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">    InSequence seq</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> PenDown</span><span style="--shiki-light:#999999;--shiki-dark:#666666">());</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Forward</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">100</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> PenUp</span><span style="--shiki-light:#999999;--shiki-dark:#666666">());</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>在创建 <code>InSequence</code> 的实例后，其作用域内的所有期望会形成一个顺序链，必须按照声明顺序匹配。
由于 <code>InSequence</code> 的功能仅依赖其构造和析构函数，因此变量名无关紧要。</p>
<p>在这个示例中，我们验证了 <code>Foo()</code> 严格按照声明顺序调用这三个方法。
如果调用顺序错乱，将产生一个错误。</p>
<div class="hint-container note">
<p class="hint-container-title">注</p>
<p>如果你只关心部分方法的调用顺序，而不是所有方法，gMock 也允许你指定任意的偏序关系（<em>arbitrary partial order</em>）。
你可以在了解更多细节。</p>
</div>
<h3>期望的持久性</h3>
<p>现在，让我们通过一个简单的问题来检验你对 gMock 的理解：
如何验证 <code>turtle</code> 恰好被要求移动到原点两次，同时忽略其他指令？</p>
<p>在查看参考答案前，请先尝试自行解答：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">AnyNumber</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GoTo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">_</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> _</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // #1</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">     .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AnyNumber</span><span style="--shiki-light:#999999;--shiki-dark:#666666">());</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GoTo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // #2</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">     .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Times</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>如果 <code>turtle.GoTo(0, 0)</code> 实际被调用三次，这三次调用都会匹配期望 <code>#2</code>（因为 gMock 优先匹配后定义的可用期望）。
此时，由于调用次数超出限制，gMock 将立即报错。
这部分内容我们已经在<a href="#multi-expectations">设置多重期望</a>一节中讨论过。</p>
<p>上面的示例说明了 gMock 的期望默认具有<mark>持久性</mark>（<em>sticky</em>）：即使达到调用次数上限，期望仍然保持活跃状态。
这是 gMock 与其他模拟框架的一个重要区别（为何如此设计？因为我们认为此规则能简化大多数情况的表达）。</p>
<p>你觉得这很简单？
让我们看看你是否真正理解了这一规则——请阐述下面代码的作用：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">for</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">--</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetX</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">10</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">i</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>如果你认为此代码期望 <code>turtle.GetX()</code> 被调用 <code>n</code> 次并依次返回 10、20、30、...，不妨重新思考一下。
我们前面提到，期望具有持久性，
因此，第二次调用 <code>turtle.GetX()</code> 时，仍会匹配到最后一次循环定义的期望（即 <code>i=1</code> 时的 <code>Return(10)</code>）：
这会立刻产生一个超限错误——此代码存在严重的逻辑缺陷。</p>
<p>要正确实现“依次返回 10、20、30、...”的行为，你应该显式地让期望在饱和（<em>saturated</em>）后立即失效（<em>retire</em>）：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">for</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">--</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetX</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">10</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">i</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">      .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">RetiresOnSaturation</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>不过，这里我们可以使用顺序链来更优雅地解决问题：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">InSequence</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">{</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  InSequence s</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  for</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;=</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> i</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">++</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    EXPECT_CALL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">turtle</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetX</span><span style="--shiki-light:#999999;--shiki-dark:#666666">())</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">        .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">WillOnce</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Return</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">10</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">i</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">        .</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">RetiresOnSaturation</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">    // 译注：原文这里还调用了 `RetiresOnSaturation()`，但我认为这是不必要的，这可能是一处笔误。</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>在顺序链中，期望会在饱和后自动失效，以便后续的期望能够被匹配。
这是使期望不具备持久性的另一种方法。</p>
<h3>处理不重要方法</h3>
<p>模拟对象可能会有很多方法，但并非所有方法都值得我们关注。
例如，在某些测试中，我们可能不关心 <code>GetX()</code> 和 <code>GetY()</code> 的调用次数。</p>
<p>在 gMock 中，对于不关注的方法，无需设置任何期望。
当这些方法被调用时，测试会输出警告信息，但不会导致测试失败。
这种行为称为“唠叨（<em>naggy</em>）模式”。
如果你想改变该默认行为，请参考。</p>
]]></content>
    <published>2025-03-13T11:45:09.000Z</published>
  </entry>
  <entry>
    <title type="text">进阶主题</title>
    <id>https://sunirein.tech/gtest/advanced/</id>
    <link href="https://sunirein.tech/gtest/advanced/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<h2>引言</h2>
<p>现在你已经阅读了并学会了如何使用 GoogleTest 来编写测试，是时候学习一些新技巧了。
本文将向你展示更多种类的断言，以及如何构建复杂的失败信息、传播致命失败、复用和加速你的测试夹具，并在测试中使用各种标志。</p>
<h2>更多种类的断言</h2>
<p>本节将介绍一些不太常用但依旧重要的断言。</p>
<h3>显式的成功与失败</h3>
<p>请参阅断言参考的（<em>Explicit Success and Failure</em>）一节。</p>
<h3>异常断言</h3>
<p>请参阅断言参考的（<em>Exception Assertions</em>）一节。</p>]]></summary>
    <content type="html"><![CDATA[<h2>引言</h2>
<p>现在你已经阅读了并学会了如何使用 GoogleTest 来编写测试，是时候学习一些新技巧了。
本文将向你展示更多种类的断言，以及如何构建复杂的失败信息、传播致命失败、复用和加速你的测试夹具，并在测试中使用各种标志。</p>
<h2>更多种类的断言</h2>
<p>本节将介绍一些不太常用但依旧重要的断言。</p>
<h3>显式的成功与失败</h3>
<p>请参阅断言参考的（<em>Explicit Success and Failure</em>）一节。</p>
<h3>异常断言</h3>
<p>请参阅断言参考的（<em>Exception Assertions</em>）一节。</p>
<h3>谓词断言——获得更好的错误信息</h3>
<p>尽管 GoogleTest 提供了丰富的断言集，但它们永远不可能覆盖所有场景，因为预测用户可能遇到的所有情况是不可能的（也不是一个好主意）。
因此，有时用户没有更好的宏可用，不得不使用 <code>EXPECT_TRUE()</code> 来检验一个复杂的表达式。
这样做的问题是它不会显示表达式中各个部分的值，使人难以理解是哪里出了问题。
作为一种变通方法，一些用户选择自己构建失败信息，并将其流式传输到 <code>EXPECT_TRUE()</code> 中。
然而，当表达式有副作用或计算成本高时，这样做就不太方便了。</p>
<p>GoogleTest 为你提供了三种不同的选项来解决这个问题。</p>
<h4>使用现有的布尔函数</h4>
<p>如果你已经有一个返回值为 <code>bool</code>（或可以隐式转换为 <code>bool</code> 的类型）的函数或函数对象，
你可以在<strong>谓词断言</strong>（<em>predicate assertion</em>）中使用它，方便地打印函数的参数。
详细信息请参阅断言参考中的 。</p>
<h4>使用返回 AssertionResult 的函数</h4>
<p>虽然 <code>EXPECT_PRED*()</code> 及其相关宏用起来很方便，但语法却不令人满意：你必须为不同的参数数量使用不同的宏，这更像是 Lisp 而不是 C++。
<code>::testing::AssertionResult</code> 类解决了这个问题。</p>
<p><code>AssertionResult</code> 对象表示断言的结果（成功或失败，以及相关的信息）。
你可以使用以下工厂函数来创建 <code>AssertionResult</code>：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">namespace</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 返回一个 AssertionResult 对象以指示断言成功</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">AssertionResult</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> AssertionSuccess</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 返回一个 AssertionResult 对象以指示断言失败</span></span>
<span class="line"><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">AssertionResult</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> AssertionFailure</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>然后你可以使用 <code>&lt;&lt;</code> 运算符将信息流式传输到 <code>AssertionResult</code> 对象中。</p>
<p>编写一个返回 <code>AssertionResult</code> 而不是 <code>bool</code> 的谓词函数，能够在布尔断言（例如 <code>EXPECT_TRUE()</code>）中提供更易读的信息。
例如，如果你将 <code>IsEven()</code> 定义为：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">AssertionResult</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> IsEven</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  if</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> ((</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">n </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">%</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ==</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">    return</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AssertionSuccess</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  else</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">    return</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AssertionFailure</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> is odd</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>而不是：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">bool</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> IsEven</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  return</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> (</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">n </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">%</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ==</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>断言 <code>EXPECT_TRUE(IsEven(Fib(4)))</code> 失败时将打印：</p>
<div class="language-ansi" data-highlighter="shiki" data-ext="ansi" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-ansi"><span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Value of: IsEven(Fib(4))</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">  Actual: false (3 is odd)</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Expected: true</span></span></code></pre>
</div><p>而不是更晦涩的：</p>
<div class="language-ansi" data-highlighter="shiki" data-ext="ansi" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-ansi"><span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Value of: IsEven(Fib(4))</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">  Actual: false</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Expected: true</span></span></code></pre>
</div><p>如果你希望在 <code>EXPECT_FALSE</code> 和 <code>ASSERT_FALSE</code> 中也提供详细信息（Google 代码库中三分之一的布尔断言是否定形式的），
并且不介意在断言成功的情况下使执行变慢，你可以同时提供一个成功信息：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">AssertionResult</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> IsEven</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> n</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  if</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> ((</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">n </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">%</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ==</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">    return</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AssertionSuccess</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> is even</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">  else</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">    return</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">AssertionFailure</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> n </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> is odd</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>这样断言 <code>EXPECT_FALSE(IsEven(Fib(6)))</code> 失败时将打印：</p>
<div class="language-ansi" data-highlighter="shiki" data-ext="ansi" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-ansi"><span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee"> Value of: IsEven(Fib(6))</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">   Actual: true (8 is even)</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee"> Expected: false</span></span></code></pre>
</div><h4>使用谓词格式化器</h4>
<p>如果你对  和

生成的默认信息不太满意，或者你的谓词中的某些参数不能流式传输到 <code>ostream</code>，
你可以使用<strong>谓词格式化断言</strong>（<em>predicate-formatter assertion</em>）来完全自定义信息的格式。
详细信息请参阅断言参考中的  一节。</p>
<h3>浮点数比较</h3>
<p>请参阅断言参考中的一节。</p>
<h4>浮点数谓词格式化函数</h4>
<p>一些浮点数操作很有用，但并不常用。
为了避免宏数量的爆炸式增长，我们将它们作为谓词格式化函数（<em>predicate-format function</em>）提供，
可以在谓词断言宏  中使用，例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FloatLE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">DoubleLE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED_FORMAT2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FloatLE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_PRED_FORMAT2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">DoubleLE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> val2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>上述代码验证 <code>val1</code> 小于或近似等于 <code>val2</code>。</p>
<h3>使用 gMock 匹配器（<em>Matcher</em>）进行断言</h3>
<p>请参阅断言参考中的  一节。</p>
<h3>更多字符串断言</h3>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>请先阅读<a href="#asserting-using-gmock-matchers">上一节</a>再阅读本节内容。</p>
</div>
<p>你可以用 gMock 的与

来实现更多字符串验证方法（子字符串、前缀、后缀、正则表达式等）。
例如，</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">HasSubstr</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">using</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">MatchesRegex</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  ASSERT_THAT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">foo_string</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> HasSubstr</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">needle</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  EXPECT_THAT</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">bar_string</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> MatchesRegex</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">\\</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">w*</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">\\</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">d+</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">));</span></span></code></pre>
</div><h3>Windows HRESULT 断言</h3>
<p>请参阅中断言参考中的 一节。</p>
<h3>类型断言</h3>
<p>你可以使用函数</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">StaticAssertTypeEq</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">T1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>();</span></span></code></pre>
</div><p>来断言类型 <code>T1</code> 和 <code>T2</code> 相同。
如果断言满足，函数将什么也不做。
如果类型不同，函数将无法编译，编译错误消息会显示 <code>T1 and T2 are not the same type</code>，
并且很可能显示 <code>T1</code> 和 <code>T2</code> 对应的实际类型（取决于编译器）。
这在模板代码中特别有用。</p>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>当在类模板或函数模板的成员函数中使用时，<code>StaticAssertTypeEq&lt;T1, T2&gt;()</code> 只有在函数被实例化时才有效。
例如，给定：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">template</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> &#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">typename</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">StaticAssertTypeEq</span><span style="--shiki-light:#999999;--shiki-dark:#666666">&#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">int</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> T</span><span style="--shiki-light:#999999;--shiki-dark:#666666">>();</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> }</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span></code></pre>
</div><p>下列代码</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Test1</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;bool></span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> }</span></span></code></pre>
</div><p>不会生成编译错误，因为 <code>Foo&lt;bool&gt;::Bar()</code> 未被实例化。
你需要使用</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> Test2</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> Foo</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;bool></span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Bar</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> }</span></span></code></pre>
</div><p>来引发编译错误。</p>
</div>
<h3>可以使用断言的范围</h3>
<p>你可以在任何 C++ 函数中使用断言，这个函数不必是测试夹具类中的方法。
我们对此唯一的限制是，会产生致命失败的断言（<code>FAIL*</code> 和 <code>ASSERT_*</code>）只能在返回值为 <code>void</code> 的函数中使用。
这是因为 Google 不使用异常。
如果你将其放在非 <code>void</code> 函数中，你会得到一个令人困惑的编译错误，
如 <code>&quot;error: void value not ignored as it ought to be&quot;</code> 或 <code>&quot;cannot initialize return object of type 'bool' with an rvalue of type 'void'&quot;</code>
或 <code>&quot;error: no viable conversion from 'void' to 'string'&quot;</code>。</p>
<p>如果你需要在返回非 <code>void</code> 的函数中使用这些断言，你可以让函数通过输出参数返回值。
例如，将 <code>T2 Foo(T1 x)</code> 重写为 <code>void Foo(T1 x, T2* result)</code>。
你需要确保即使函数提前返回，<code>*result</code> 也包含一些合理的值。
由于函数现在返回 <code>void</code>，你可以在其中使用任何断言。</p>
<p>如果你不想更改函数的类型，你应该只使用生成非致命失败的断言，如 <code>ADD_FAILURE*</code> 和 <code>EXPECT_*</code>。</p>
<div class="hint-container note">
<p class="hint-container-title">注</p>
<p>根据 C++ 语言规范，构造函数和析构函数不被视为返回 <code>void</code> 的函数，因此你不能在其中使用致命断言。
如果你试图这样做，你会得到一个编译错误。
因此，你要么调用 <code>abort</code> 并使整个测试崩溃，要么将致命断言放在 <code>SetUp</code>/<code>TearDown</code> 中。
详细信息请参阅。</p>
</div>
<div class="hint-container caution">
<p class="hint-container-title">警告</p>
<p>在构造函数或析构函数中调用的辅助函数中的致命断言不会像你认为的那样终止当前测试：
它只会从构造函数或析构函数中提前返回，这可能会让你的对象处于部分构造或部分销毁的状态！</p>
</div>
<h2>跳过测试的执行</h2>
<p>你可以使用 <code>GTEST_SKIP()</code> 宏在运行时跳过后续测试的执行。
在你需要在运行时检查被测系统的部分条件并以合适的方式跳过后续测试时，这将非常有用。</p>
<p><code>GTEST_SKIP()</code> 可以在单个测试单元中使用，也可以在派生自 <code>::testing::Environment</code> 或 <code>::testing::Test</code> 的类的 <code>SetUp()</code> 方法中使用。
例如：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">SkipTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> DoesSkip</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  GTEST_SKIP</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Skipping single test</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  FAIL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 测试不会失败；它不会被执行</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> SkipFixture</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> :</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> public</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> ::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Test</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> protected</span><span style="--shiki-light:#999999;--shiki-dark:#666666">:</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> SetUp</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> override</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    GTEST_SKIP</span><span style="--shiki-light:#999999;--shiki-dark:#666666">()</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Skipping all tests for this fixture</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// SkipFixture 中的测试不会被执行</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST_F</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">SkipFixture</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> SkipsOneTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  FAIL</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div><p>与断言宏类似，你可以将自定义信息流式传输到 <code>GTEST_SKIP()</code> 中。</p>
<h2>告诉 GoogleTest 如何打印参数值</h2>
<p>当试断言（如 <code>EXPECT_EQ</code>）失败时，GoogleTest 会打印参数值以帮助你调试。
这是通过用户可扩展的打印器（<em>user-extensible value printer</em>）来实现的。</p>
<p>该打印器能打印 C++ 内置类型、原生数组、STL 容器以及任何支持 <code>&lt;&lt;</code> 运算符的类型。
对于其他类型，它会打印对应的原始字节，并希望用户能够理解。</p>
<p>如前所述，这个打印器是<strong>可扩展的</strong>。
这意味着你可以告诉它如何更好地打印特定类型的值，而不是仅打印原始字节。
你只需要为你的类型重载一个作为友元函数模板的<code>AbslStringify()</code> 方法。</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">namespace</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Point</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 我们希望 GoogleTest 能够打印此类的实例</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // 提供一个友元重载</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  template</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> &#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">typename</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Sink</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  friend</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> AbslStringify</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Sink</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> sink</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> const</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Point</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> point</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    absl</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Format</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">sink</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">(</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">%d</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">, </span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">%d</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">)</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> point</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> point</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">y</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> y</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 如果你不能在类中声明该函数，请将 AbslStringify 重载定义在与 Point 相同的命名空间中</span></span>
<span class="line highlighted"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// C++ 的函数查找规则依赖于这一点（即 ADL 机制）</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">enum</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> EnumWithStringify</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> kMany</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 0</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> kChoices</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> =</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 1</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> };</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">template</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> &#x3C;</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">typename</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Sink</span><span style="--shiki-light:#999999;--shiki-dark:#666666">></span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> AbslStringify</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">Sink</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> sink</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> EnumWithStringify</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> e</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">  absl</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">Format</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">sink</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">%s</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> e </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">==</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> EnumWithStringify</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">kMany </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">?</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Many</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> :</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">Choices</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // namespace foo</span></span></code></pre>
</div><div class="hint-container note">
<p class="hint-container-title">注</p>
<p><code>AbslStringify()</code> 使用通用的 <code>sink</code> 缓冲区来构建字符串。
有关 <code>AbslStringify()</code> 中 <code>sink</code> 支持的操作的更多信息，请参阅 <a href="https://abseil.io/docs/cpp/guides/abslstringify" target="_blank" rel="noopener noreferrer">AbslStringify</a>。</p>
</div>
<p><code>AbslStringify()</code> 还可以在 <code>absl::StrFormat</code> 中使用万能的 <code>%v</code> 类型说明符来执行自动类型推断。
例如， <code>Point</code> 可以被格式化为 <code>&quot;(%v, %v)&quot;</code>，其中 <code>int</code> 会被自动推断为 <code>%d</code>。</p>
<p>有时，你的团队可能希望类在测试环境下打印时带有额外调试信息，而不是使用 <code>AbslStringify()</code>。
这时，你可以改为定义一个 <code>PrintTo()</code> 函数。</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark has-highlighted vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> &#x3C;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">ostream</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">namespace</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> foo</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">class</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Point</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  ...</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  friend</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> PrintTo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Point</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> point</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">ostream</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> os</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">    *</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">os </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> point</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">x</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> point</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">y</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">)</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> x</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">  int</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> y</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">};</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 如果你不能在类中声明该函数，请将 PrintTo() 定义在与 Point 相同的命名空间中</span></span>
<span class="line highlighted"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// C++ 的函数查找规则依赖于这一点</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">void</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> PrintTo</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">const</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994"> Point</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x26;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> point</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> std</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#2E8F82;--shiki-dark:#5DA994">ostream</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">*</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> os</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">    *</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">os </span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> point</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">x</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A"> point</span><span style="--shiki-light:#999999;--shiki-dark:#666666">.</span><span style="--shiki-light:#B07D48;--shiki-dark:#BD976A">y</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">)</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">  // namespace foo</span></span></code></pre>
</div><p>当你同时定义了 <code>AbslStringify()</code> 和 <code>PrintTo()</code>，GoogleTest 将使用后者。
这允许你自定义参数值在 GoogleTest 中的输出方式，而不影响那些依赖 <code>AbslStringify()</code> 行为的代码。</p>
<p>类似的，如果你有一个 <code>&lt;&lt;</code> 运算符重载并定义了 <code>AbslStringify()</code>，GoogleTest 将使用后者。</p>
<p>你还可以调用 <code>::testing::PrintToString()</code> 来获取 GoogleTest 中的值打印器打印的值，该函数返回 <code>std::string</code>：</p>
<div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">vector</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">pair</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">Point</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> int>></span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> point_ints </span><span style="--shiki-light:#999999;--shiki-dark:#666666">=</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> GetPointIntVector</span><span style="--shiki-light:#999999;--shiki-dark:#666666">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">EXPECT_TRUE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">IsCorrectPointIntVector</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">point_ints</span><span style="--shiki-light:#999999;--shiki-dark:#666666">))</span></span>
<span class="line"><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">    &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">point_ints = </span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> testing</span><span style="--shiki-light:#999999;--shiki-dark:#666666">::</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">PrintToString</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">point_ints</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span></code></pre>
</div><p>有关 <code>AbslStringify()</code> 及其与其他库集成的更多详细信息，请参阅 <a href="https://abseil.io/docs/cpp/guides/abslstringify" target="_blank" rel="noopener noreferrer">AbslStringify</a>。</p>
<h2>正则表达式语法</h2>
<p>当使用 Bazel 构建并使用 Abseil 时，GoogleTest 使用 <a href="https://github.com/google/re2/wiki/Syntax" target="_blank" rel="noopener noreferrer">RE2</a> 语法。
否则，对于 POSIX 系统（Linux、Cygwin、Mac），GoogleTest 使用
<a href="https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html#tag_09_04" target="_blank" rel="noopener noreferrer">POSIX 扩展正则表达式</a> 语法。
你可以阅读这个<a href="https://en.wikipedia.org/wiki/Regular_expression#POSIX_extended" target="_blank" rel="noopener noreferrer">维基百科条目</a>来了解 POSIX 语法。</p>
<p>在 Windows 上，GoogleTest 使用自己实现的简单正则表达式。
该实现缺少许多功能。
例如，不支持或运算（<code>&quot;x|y&quot;</code>）、分组（<code>&quot;(xy)&quot;</code>）、字符组（<code>&quot;[xy]&quot;</code>）和重复计数（<code>&quot;x{5,7}&quot;</code>）等。
以下是我们支持的内容（<code>A</code> 表示任意字面字符、<code>.</code> 或一个 <code>\\</code> 转义序列；<code>x</code> 和 <code>y</code> 表示正则表达式）：</p>
<p>| 表达式 | 含义                               |
|</p>
]]></content>
    <published>2025-03-10T07:16:32.000Z</published>
  </entry>
  <entry>
    <title type="text">初学者指南</title>
    <id>https://sunirein.tech/gtest/primer/</id>
    <link href="https://sunirein.tech/gtest/primer/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<h2>引言：为什么选择 GoogleTest？</h2>
<p><mark>GoogleTest</mark> 可以帮助你编写更好的 C++ 测试。</p>
<p>GoogleTest 是由 Google 的测试技术团队在考虑了 Google 的特定需求和约束后开发的测试框架。
无论你是在 Linux、Windows 还是 Mac 上工作，只要你在编写 C++ 代码，GoogleTest 都能帮助你。
它支持<strong>任何</strong>类型的测试，而不仅仅是单元测试。</p>
<p>那么，什么是一个好的测试，GoogleTest 又如何与之契合呢？我们相信：</p>
<ol>
<li>
<p>测试应该是<strong>独立的</strong>和<strong>可重复的</strong>。
调试一个会因其他测试而成功或失败的测试是很痛苦的。
GoogleTest 通过在不同的对象上运行测试来隔离不同的测试单元。
当一个测试单元失败时，GoogleTest 允许你单独运行它以便快速调试。</p>
</li>
<li>
<p>测试应该<strong>组织良好</strong>，并反映被测试代码的结构。
GoogleTest 将相关测试单元分组到测试套件（<em>test suite</em>）中，这些套件可以共享数据和子程序。
这种常见的模式易于识别，并使测试易于维护。
这样的一致性在人们切换项目并开始处理新代码库时尤其有帮助。</p>
</li>
<li>
<p>测试应该是<strong>可移植的</strong>和<strong>可复用的</strong>。
Google 有许多平台无关（platform-neutral）的代码，因此测试也应该是平台无关的。
GoogleTest 可以在不同的操作系统、不同的编译器、启用或不启用异常的情况下运行，因此可以与多种配置一起工作。</p>
</li>
<li>
<p>当测试失败时，它应该尽可能多地提供有关问题的<strong>信息</strong>。
GoogleTest 不会在第一次测试失败后停止，它只会停止当前测试单元并继续运行下一个测试单元。
你还可以测试一些非致命问题，即使这些测试未通过，当前测试单元依旧能继续运行。
因此，你可以在一个运行 - 编辑 - 编译周期中检测并修复多个错误。</p>
</li>
<li>
<p>测试框架应该将测试编写者从琐事中解放出来，让他们专注于<strong>测试内容</strong>本身。
GoogleTest 自动跟踪用户定义的测试单元，不需要用户手动枚举以运行它们。</p>
</li>
<li>
<p>测试应该是<strong>快速的</strong>。
使用 GoogleTest，你可以在测试单元之间复用共享资源，并且只需支付一次设置（<em>set-up</em>）/拆卸（<em>tear-down</em>）的成本，而不会使测试单元相互依赖。</p>
</li>
</ol>]]></summary>
    <content type="html"><![CDATA[<h2>引言：为什么选择 GoogleTest？</h2>
<p><mark>GoogleTest</mark> 可以帮助你编写更好的 C++ 测试。</p>
<p>GoogleTest 是由 Google 的测试技术团队在考虑了 Google 的特定需求和约束后开发的测试框架。
无论你是在 Linux、Windows 还是 Mac 上工作，只要你在编写 C++ 代码，GoogleTest 都能帮助你。
它支持<strong>任何</strong>类型的测试，而不仅仅是单元测试。</p>
<p>那么，什么是一个好的测试，GoogleTest 又如何与之契合呢？我们相信：</p>
<ol>
<li>
<p>测试应该是<strong>独立的</strong>和<strong>可重复的</strong>。
调试一个会因其他测试而成功或失败的测试是很痛苦的。
GoogleTest 通过在不同的对象上运行测试来隔离不同的测试单元。
当一个测试单元失败时，GoogleTest 允许你单独运行它以便快速调试。</p>
</li>
<li>
<p>测试应该<strong>组织良好</strong>，并反映被测试代码的结构。
GoogleTest 将相关测试单元分组到测试套件（<em>test suite</em>）中，这些套件可以共享数据和子程序。
这种常见的模式易于识别，并使测试易于维护。
这样的一致性在人们切换项目并开始处理新代码库时尤其有帮助。</p>
</li>
<li>
<p>测试应该是<strong>可移植的</strong>和<strong>可复用的</strong>。
Google 有许多平台无关（platform-neutral）的代码，因此测试也应该是平台无关的。
GoogleTest 可以在不同的操作系统、不同的编译器、启用或不启用异常的情况下运行，因此可以与多种配置一起工作。</p>
</li>
<li>
<p>当测试失败时，它应该尽可能多地提供有关问题的<strong>信息</strong>。
GoogleTest 不会在第一次测试失败后停止，它只会停止当前测试单元并继续运行下一个测试单元。
你还可以测试一些非致命问题，即使这些测试未通过，当前测试单元依旧能继续运行。
因此，你可以在一个运行 - 编辑 - 编译周期中检测并修复多个错误。</p>
</li>
<li>
<p>测试框架应该将测试编写者从琐事中解放出来，让他们专注于<strong>测试内容</strong>本身。
GoogleTest 自动跟踪用户定义的测试单元，不需要用户手动枚举以运行它们。</p>
</li>
<li>
<p>测试应该是<strong>快速的</strong>。
使用 GoogleTest，你可以在测试单元之间复用共享资源，并且只需支付一次设置（<em>set-up</em>）/拆卸（<em>tear-down</em>）的成本，而不会使测试单元相互依赖。</p>
</li>
</ol>
<p>由于 GoogleTest 基于流行的 xUnit 架构，如果你以前使用过 JUnit 或 PyUnit，你会感到非常熟悉。
如果没有，你只需花大约 10 分钟来学习基础知识就可以开始使用。
那么，让我们开始吧！</p>
<h2>注意术语</h2>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>由于术语 <em>Test</em>、<em>Test Case</em> 和 <em>Test Suite</em> 的不同定义可能会引起一些混淆，因此请注意不要误解这些术语。</p>
</div>
<p>历史上，GoogleTest 开始使用术语 <em>Test Case</em> 来分组相关测试，
而当前的出版物，包括国际软件测试资格委员会（<a href="https://www.istqb.org/" target="_blank" rel="noopener noreferrer">ISTQB</a>）材料和各种关于软件质量的教科书，
则使用术语 [<em>Test Suite</em>][istqb test suite] 来表示这一点。</p>
<p>在 GoogleTest 中使用的相关术语 <em>Test</em> 对应于 ISTQB 术语 [<em>Test Case</em>][istqb test case]。</p>
<p>术语 <em>Test</em> 通常具有足够广泛的意义，也包括 ISTQB 对 <em>Test Case</em> 的定义，所以在这里问题不大。
但 Google Test 中使用的术语 <em>Test Case</em> 具有矛盾的意义，因此容易引起混淆。</p>
<p>GoogleTest 最近开始将术语 <em>Test Case</em> 替换为 <em>Test Suite</em>。
目前首选的 API 是 <code>TestSuite</code>。较旧的 <code>TestCase</code> API 正在逐步弃用和重构。</p>
<p>因此，请注意术语的不同定义：</p>
<p>| 含义                                     | GoogleTest 术语        | <a href="https://www.istqb.org/" target="_blank" rel="noopener noreferrer">ISTQB</a> 术语 |
| :</p>
]]></content>
    <published>2025-03-05T07:31:32.000Z</published>
  </entry>
  <entry>
    <title type="text">快速开始：Bazel</title>
    <id>https://sunirein.tech/gtest/quickstart-bazel/</id>
    <link href="https://sunirein.tech/gtest/quickstart-bazel/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>本教程旨在帮助你使用 Bazel 构建系统快速上手 GoogleTest。
如果你是第一次使用 GoogleTest 或是需要复习，我们推荐你从本教程开始。</p>
<h2>先决条件</h2>
<p>要完成本教程，你需要：</p>
<ul>
<li>一个兼容的操作系统（例如 Linux、macOS、Windows）。</li>
<li>一个兼容的 C++ 编译器，至少支持 C++14。</li>
<li><a href="https://bazel.build/" target="_blank" rel="noopener noreferrer">Bazel</a> 7.0 或更高版本，这是 GoogleTest 团队首选的构建系统。</li>
</ul>]]></summary>
    <content type="html"><![CDATA[<p>本教程旨在帮助你使用 Bazel 构建系统快速上手 GoogleTest。
如果你是第一次使用 GoogleTest 或是需要复习，我们推荐你从本教程开始。</p>
<h2>先决条件</h2>
<p>要完成本教程，你需要：</p>
<ul>
<li>一个兼容的操作系统（例如 Linux、macOS、Windows）。</li>
<li>一个兼容的 C++ 编译器，至少支持 C++14。</li>
<li><a href="https://bazel.build/" target="_blank" rel="noopener noreferrer">Bazel</a> 7.0 或更高版本，这是 GoogleTest 团队首选的构建系统。</li>
</ul>
<p>有关与 GoogleTest 兼容的平台的更多信息，请参阅。</p>
<p>如果你尚未安装 Bazel，请参阅 <a href="https://bazel.build/install" target="_blank" rel="noopener noreferrer">Bazel 安装指南</a>。</p>
<div class="hint-container note">
<p class="hint-container-title">注</p>
<p>本教程中的终端命令均采用 Unix shell 格式。</p>
</div>
<h2>设置 Bazel 工作区</h2>
<p><a href="https://docs.bazel.build/versions/main/build-ref.html#workspace" target="_blank" rel="noopener noreferrer">Bazel 工作区（Bazel workspace）</a>
是你文件系统上的一个目录，用于管理你要构建的项目的源文件。
每个工作区目录都有一个名为 <code>MODULE.bazel</code> 的文本文件，该文件包含构建程序所需的外部依赖项的描述。</p>
<p>首先，为创建一个目录作为你的工作区：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">mkdir</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> my_workspace</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> &#x26;&#x26;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965"> cd</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> my_workspace</span></span></code></pre>
</div><p>接下来，创建 <code>MODULE.bazel</code> 文件以指定依赖项。
从 Bazel 7.0 开始，推荐通过 <a href="https://registry.bazel.build/modules/googletest" target="_blank" rel="noopener noreferrer">Bazel 中央注册表（Bazel Central Registry）</a> 使用 GoogleTest。
为此，请在你的 Bazel 工作区的根目录下创建 <code>MODULE.bazel</code> 文件，内容如下：</p>
<div class="code-block-title" data-title="MODULE.bazel"><div class="code-block-title-bar"><span class="title">MODULE.bazel</span></div><div class="language-txt" data-highlighter="shiki" data-ext="txt" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-txt"><span class="line"><span># 这里的 version 请填入 https://registry.bazel.build/modules/googletest 上可用的最新版本</span></span>
<span class="line"><span>bazel_dep(name = "googletest", version = "</span><span class="highlighted-word">1.16.0</span><span>")</span></span></code></pre>
</div></div><p>现在你已经做好了构建使用 GoogleTest 的 C++ 项目的准备。</p>
<h2>创建并运行测试</h2>
<p>设置好 Bazel 工作区后，你现在可以在自己的项目中使用 GoogleTest。</p>
<p>作为一个示例，在你的 <code>my_workspace</code> 目录中创建一个名为 <code>hello_test.cc</code> 的文件，内容如下：</p>
<div class="code-block-title" data-title="hello_test.cc"><div class="code-block-title-bar"><span class="title">hello_test.cc</span></div><div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> &#x3C;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">gtest/gtest.h</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 演示一些基本的断言。</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">HelloTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> BasicAssertions</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">    // 期望两个字符串不相等。</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    EXPECT_STRNE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">hello</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">world</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">    // 期望相等。</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    EXPECT_EQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">7</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> *</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 6</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 42</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div></div><p>GoogleTest 为你提供了来测试代码的行为。
上面的示例中包含了 GoogleTest 的主要头文件，并演示了一些基本的断言。</p>
<p>要构建该测试，请在同一目录中创建一个名为 <code>BUILD</code> 的文件，内容如下：</p>
<div class="code-block-title" data-title="BUILD"><div class="code-block-title-bar"><span class="title">BUILD</span></div><div class="language-txt" data-highlighter="shiki" data-ext="txt" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-txt"><span class="line"><span>cc_test(</span></span>
<span class="line"><span>    name = "hello_test",</span></span>
<span class="line"><span>    size = "small",</span></span>
<span class="line"><span>    srcs = ["hello_test.cc"],</span></span>
<span class="line"><span>    deps = [</span></span>
<span class="line"><span>        "@googletest//:gtest",</span></span>
<span class="line"><span>        "@googletest//:gtest_main",</span></span>
<span class="line"><span>    ],</span></span>
<span class="line"><span>)</span></span></code></pre>
</div></div><p>此 <code>cc_test</code> 规则声明了你要构建的 C++ 测试文件，
并链接 GoogleTest 库（<code>@googletest//:gtest</code>）和 <code>main()</code> 函数（<code>@googletest//:gtest_main</code>）。
有关 Bazel <code>BUILD</code> 文件的更多信息，请参阅 <a href="https://docs.bazel.build/versions/main/tutorial/cpp.html" target="_blank" rel="noopener noreferrer">Bazel C++ 教程</a>。</p>
<div class="hint-container note">
<p class="hint-container-title">注</p>
<p>在下面的示例中，我们假定使用 Clang 或 GCC，并设置 <code>--cxxopt=-std=c++14</code> 以确保 GoogleTest 以 C++14 而不是编译器的默认设置（可能是 C++11）进行编译。
对于 MSVC，等效的设置是 <code>--cxxopt=/std:c++14</code>。
有关支持的语言版本的更多详细信息，请参阅。</p>
</div>
<p>现在你可以构建并运行你的测试：</p>
<div class="language-ansi" data-highlighter="shiki" data-ext="ansi" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-ansi"><span class="line highlighted"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">$ bazel test --cxxopt=-std=c++14 --test_output=all //:hello_test</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">INFO: Analyzed target //:hello_test (26 packages loaded, 362 targets configured).</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">INFO: Found 1 test target...</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">INFO: From Testing //:hello_test:</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">==================== Test output for //:hello_test:</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Running main() from gmock_main.cc</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">[==========] Running 1 test from 1 test suite.</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">[</span></span></code></pre>
</div>]]></content>
    <published>2025-03-04T07:06:53.000Z</published>
  </entry>
  <entry>
    <title type="text">快速开始：CMake</title>
    <id>https://sunirein.tech/gtest/quickstart-cmake/</id>
    <link href="https://sunirein.tech/gtest/quickstart-cmake/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>本教程旨在帮助你使用 CMake 快速上手 GoogleTest。
如果你是第一次使用 GoogleTest 或需要复习，我们推荐你从本教程开始。
如果你的项目使用 Bazel，请参阅。</p>
<h2>先决条件</h2>
<p>要完成本教程，你需要：</p>
<ul>
<li>
<p>一个兼容的操作系统（例如 Linux、macOS、Windows）。</p>
</li>
<li>
<p>一个兼容的 C++ 编译器，至少支持 C++14。</p>
</li>
<li>
<p><a href="https://cmake.org/" target="_blank" rel="noopener noreferrer">CMake</a> 和一个兼容的构建工具来构建项目。</p>
<p>兼容的构建工具包括 <a href="https://www.gnu.org/software/make/" target="_blank" rel="noopener noreferrer">Make</a>、<a href="https://ninja-build.org/" target="_blank" rel="noopener noreferrer">Ninja</a> 等
——更多信息请参阅 <a href="https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html" target="_blank" rel="noopener noreferrer">CMake 生成器</a>。</p>
</li>
</ul>]]></summary>
    <content type="html"><![CDATA[<p>本教程旨在帮助你使用 CMake 快速上手 GoogleTest。
如果你是第一次使用 GoogleTest 或需要复习，我们推荐你从本教程开始。
如果你的项目使用 Bazel，请参阅。</p>
<h2>先决条件</h2>
<p>要完成本教程，你需要：</p>
<ul>
<li>
<p>一个兼容的操作系统（例如 Linux、macOS、Windows）。</p>
</li>
<li>
<p>一个兼容的 C++ 编译器，至少支持 C++14。</p>
</li>
<li>
<p><a href="https://cmake.org/" target="_blank" rel="noopener noreferrer">CMake</a> 和一个兼容的构建工具来构建项目。</p>
<p>兼容的构建工具包括 <a href="https://www.gnu.org/software/make/" target="_blank" rel="noopener noreferrer">Make</a>、<a href="https://ninja-build.org/" target="_blank" rel="noopener noreferrer">Ninja</a> 等
——更多信息请参阅 <a href="https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html" target="_blank" rel="noopener noreferrer">CMake 生成器</a>。</p>
</li>
</ul>
<p>有关与 GoogleTest 兼容的平台的更多信息，请参阅。</p>
<p>如果你尚未安装 CMake，请参阅 <a href="https://cmake.org/install" target="_blank" rel="noopener noreferrer">CMake 安装指南</a>。</p>
<div class="hint-container note">
<p class="hint-container-title">注</p>
<p>本教程中的终端命令均采用 Unix shell 格式。</p>
</div>
<h2>设置项目</h2>
<p>CMake 使用名为 <code>CMakeLists.txt</code> 的文件来配置项目的构建。
你将使用此文件来设置项目并声明对 GoogleTest 的依赖。</p>
<p>首先，为你的项目创建一个目录：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">mkdir</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> my_project</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> &#x26;&#x26;</span><span style="--shiki-light:#998418;--shiki-dark:#B8A965"> cd</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> my_project</span></span></code></pre>
</div><p>接下来，创建 <code>CMakeLists.txt</code> 文件并声明对 GoogleTest 的依赖。
在 CMake 生态中有许多表达依赖的方式，本指南将使用 <a href="https://cmake.org/cmake/help/latest/module/FetchContent.html" target="_blank" rel="noopener noreferrer">CMake 的 <code>FetchContent</code> 模块</a>。
为此，在你的项目目录中创建一个名为 <code>CMakeLists.txt</code> 的文件，内容如下：</p>
<div class="code-block-title" data-title="CMakeLists.txt"><div class="code-block-title-bar"><span class="title">CMakeLists.txt</span></div><div class="language-cmake" data-highlighter="shiki" data-ext="cmake" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cmake"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">cmake_minimum_required</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665">VERSION</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> 3.14)</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">project</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(my_project)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD"># GoogleTest 要求至少 C++14</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">set</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(CMAKE_CXX_STANDARD 14)</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">set</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(CMAKE_CXX_STANDARD_REQUIRED </span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">ON</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(FetchContent)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FetchContent_Declare(</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  googletest</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">)</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">set</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(gtest_force_shared_crt </span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">ON</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> CACHE</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375"> BOOL</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> ""</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> FORCE) </span><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD"># 针对 Windows：防止覆盖父项目的编译器/链接器设置</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">FetchContent_MakeAvailable(googletest)</span></span></code></pre>
</div></div><p>上述配置声明了对 GoogleTest 的依赖，该依赖将从 GitHub 上下载。
<code>03597a01ee50ed33e9dfd640b249b4be3799d395</code> 是我们要使用的 GoogleTest 版本的 Git 提交哈希。
我们建议经常性更新该哈希值以指向最新版本。</p>
<p>有关如何创建 <code>CMakeLists.txt</code> 文件的更多信息，请参阅 <a href="https://cmake.org/cmake/help/latest/guide/tutorial/index.html" target="_blank" rel="noopener noreferrer">CMake 教程</a>。</p>
<h2>创建并运行测试</h2>
<p>声明了对 GoogleTest 的依赖后，你现在可以在项目中使用 GoogleTest。</p>
<p>作为一个示例，在你的 <code>my_workspace</code> 目录中创建一个名为 <code>hello_test.cc</code> 的文件，内容如下：</p>
<div class="code-block-title" data-title="hello_test.cc"><div class="code-block-title-bar"><span class="title">hello_test.cc</span></div><div class="language-cpp" data-highlighter="shiki" data-ext="cpp" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cpp"><span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">#</span><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> &#x3C;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">gtest/gtest.h</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">// 演示一些基本的断言。</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">TEST</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">HelloTest</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE"> BasicAssertions</span><span style="--shiki-light:#999999;--shiki-dark:#666666">)</span><span style="--shiki-light:#999999;--shiki-dark:#666666"> {</span></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">    // 期望两个字符串不相等。</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    EXPECT_STRNE</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">hello</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">world</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD">    // 期望相等。</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">    EXPECT_EQ</span><span style="--shiki-light:#999999;--shiki-dark:#666666">(</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">7</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> *</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 6</span><span style="--shiki-light:#999999;--shiki-dark:#666666">,</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 42</span><span style="--shiki-light:#999999;--shiki-dark:#666666">);</span></span>
<span class="line"><span style="--shiki-light:#999999;--shiki-dark:#666666">}</span></span></code></pre>
</div></div><p>GoogleTest 为你提供了来测试代码的行为。
上面的示例中包含了 GoogleTest 的主头文件，并演示了一些基本的断言。</p>
<p>要构建代码，请在你的 <code>CMakeLists.txt</code> 文件末尾添加以下内容：</p>
<div class="code-block-title" data-title="CMakeLists.txt"><div class="code-block-title-bar"><span class="title">CMakeLists.txt</span></div><div class="language-cmake" data-highlighter="shiki" data-ext="cmake" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-cmake"><span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">...</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">enable_testing</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">()</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">add_executable</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  hello_test</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  hello_test.cc</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">)</span></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">target_link_libraries</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  hello_test</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">  GTest::gtest_main</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">(GoogleTest)</span></span>
<span class="line"><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">gtest_discover_tests(hello_test)</span></span></code></pre>
</div></div><p>:::</p>
<p>上述配置在 CMake 中启用了测试，声明了你要构建的 C++ 测试（<code>hello_test</code>），并将其链接到 GoogleTest（<code>gtest_main</code>）。
最后两行启用了 CMake 的测试运行器，
使用 <a href="https://cmake.org/cmake/help/git-stage/module/GoogleTest.html" target="_blank" rel="noopener noreferrer">CMake 的 <code>GoogleTest</code> 模块</a>来发现项目中包含的测试。</p>
<p>现在你可以构建并运行你的测试：</p>
<div class="language-ansi" data-highlighter="shiki" data-ext="ansi" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-ansi"><span class="line highlighted"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">$ cmake -S . -B build</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">-- The C compiler identification is GNU 10.2.1</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">-- The CXX compiler identification is GNU 10.2.1</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">...</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">-- Build files have been written to: .../my_project/build</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">$ cmake --build build</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Scanning dependencies of target gtest</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">...</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">[100%] Built target gmock_main</span></span>
<span class="line"></span>
<span class="line highlighted"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">$ cd build &#x26;&#x26; ctest</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Test project .../my_project/build</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Start 1: HelloTest.BasicAssertions</span></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">1/1 Test #1: HelloTest.BasicAssertions ........ Passed 0.00 sec</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">100% tests passed, 0 tests failed out of 1</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#393a34;--shiki-dark:#dbd7caee">Total Test time (real) = 0.01 sec</span></span></code></pre>
</div><p>恭喜！你成功使用 GoogleTest 构建并运行了一个测试。</p>
<h2>下一步</h2>
<ul>
<li>
<p>：开始学习如何编写简单的测试。</p>
</li>
<li>
<p>：获取更多使用各种 GoogleTest 功能的示例。</p>
</li>
</ul>
]]></content>
    <published>2025-03-04T07:06:53.000Z</published>
  </entry>
  <entry>
    <title type="text">支持的平台</title>
    <id>https://sunirein.tech/gtest/platforms/</id>
    <link href="https://sunirein.tech/gtest/platforms/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>GoogleTest 遵循 Google 的<a href="https://opensource.google/documentation/policies/cplusplus-support" target="_blank" rel="noopener noreferrer">基础 C++ 支持政策</a>。
请参阅<a href="https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md" target="_blank" rel="noopener noreferrer">此表</a>，
以查看当前受支持的编译器、平台和构建工具的版本列表。</p>]]></summary>
    <content type="html"><![CDATA[<p>GoogleTest 遵循 Google 的<a href="https://opensource.google/documentation/policies/cplusplus-support" target="_blank" rel="noopener noreferrer">基础 C++ 支持政策</a>。
请参阅<a href="https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md" target="_blank" rel="noopener noreferrer">此表</a>，
以查看当前受支持的编译器、平台和构建工具的版本列表。</p>
]]></content>
    <published>2025-03-04T04:23:47.000Z</published>
  </entry>
  <entry>
    <title type="text">使用 Certbot 自动部署免费 SSL 证书</title>
    <id>https://sunirein.tech/blog/6tznb746/</id>
    <link href="https://sunirein.tech/blog/6tznb746/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>前几天个人网站的 SSL 证书过期了，导致很多部署在上面的服务突然就用不了了。
免费证书 90 天的有效期还是太短了，要是每年都要这样折腾几次就太麻烦了。
于是我最后花了点时间用 Certbot 来自动部署证书，从此一劳永逸地解决了这个问题。</p>
<p>在部署证书的过程中我也踩了一些坑，这里把我的经验分享出来，希望能帮到需要的人。</p>
]]></summary>
    <content type="html"><![CDATA[<p>前几天个人网站的 SSL 证书过期了，导致很多部署在上面的服务突然就用不了了。
免费证书 90 天的有效期还是太短了，要是每年都要这样折腾几次就太麻烦了。
于是我最后花了点时间用 Certbot 来自动部署证书，从此一劳永逸地解决了这个问题。</p>
<p>在部署证书的过程中我也踩了一些坑，这里把我的经验分享出来，希望能帮到需要的人。</p>
<!-- more -->
<h2>环境需求</h2>
<p>本文假定你拥有以下条件：</p>
<ul>
<li>
<p>一台可以访问互联网的 Linux 服务器</p>
</li>
<li>
<p>服务器的 <code>root</code> 权限</p>
</li>
<li>
<p>使用 Nginx 作为 Web 服务器</p>
</li>
</ul>
<h2>什么是 Let's Encrypt？</h2>
<p><a href="https://letsencrypt.org/zh-cn/" target="_blank" rel="noopener noreferrer">Let's Encrypt</a>
是一个免费的、自动化的、开放的 证书颁发机构（CA，Certificate Authority），由 Internet Security Research Group（ISRG）运营。
它为网站提供 TLS/SSL 证书，使网站能够启用 HTTPS 加密，提高安全性和隐私保护。</p>
<p>Let's Encrypt 证书的有效期较短，仅有 90 天，但可以通过自动化的方式来更新证书。
这也是官方推荐的方式，因为这样可以保证证书的及时更新，提高网站的安全性。<annotation label="证书有效期" :total="1"><template #item-0><p>详见 <a href="https://letsencrypt.org/2015/11/09/why-90-days/" target="_blank" rel="noopener noreferrer">https://letsencrypt.org/2015/11/09/why-90-days/</a></p>
</template></annotation></p>
<h2>什么是 Certbot？</h2>
<p><a href="https://certbot.eff.org" target="_blank" rel="noopener noreferrer">Certbot</a> 是一个用于自动化管理 Let's Encrypt SSL/TLS 证书 的开源工具。</p>
<p>Certbot 可以做到以下几件事：</p>
<ul>
<li>申请证书：通过 ACME 协议与 Let's Encrypt 服务器通信，申请证书。</li>
<li>验证域名：通过 HTTP 或 DNS 验证域名的所有权。</li>
<li>安装证书：将证书安装到 Web 服务器中。</li>
<li>自动续期：定期检查证书的有效期，自动续期。</li>
</ul>
<p>Certbot 也是 Let's Encrypt 官方推荐的工具，使用起来非常方便。</p>
<h2>安装 Certbot</h2>
<p>Certbot 提供了 <code>pip</code> 和 <code>snap</code> 两种安装方式。
这里我选择使用 <code>pip</code>。</p>
<div class="vp-steps"><ol>
<li>
<p>安装系统依赖。</p>
<p>Certbot 需要 Python 3.6 或更高版本。
如果你使用 Apache，还需要安装相应的插件。</p>
</li>
<li>
<p>移除旧版本的 Certbot（如果有的话）。</p>
</li>
<li>
<p>设置虚拟环境。</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> python3</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -m</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> venv</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /opt/certbot/</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /opt/certbot/bin/pip</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> install</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> --upgrade</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> pip</span></span></code></pre>
</div></li>
<li>
<p>安装 Certbot。</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /opt/certbot/bin/pip</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> install</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> certbot</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> certbot-nginx</span></span></code></pre>
</div></li>
<li>
<p>创建软链接。</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> ln</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -s</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /opt/certbot/bin/certbot</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /usr/bin/certbot</span></span></code></pre>
</div></li>
</ol>
</div><h2>申请证书</h2>
<p>要申请泛域名证书，我们需要使用 DNS 验证方式。
这里我以阿里云为例，介绍如何配置 DNS 验证并申请证书。</p>
<div class="vp-steps"><ol>
<li>
<p>安装对应 DNS 厂商的 Certbot 插件。</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD"># 这里 &#x3C;provider> 替换为你的 DNS 厂商，如 aliyun、cloudflare 等</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /opt/certbot/bin/pip</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> install</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> certbot-dns-</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">&#x3C;</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">provide</span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">r</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676">></span></span></code></pre>
</div></li>
<li>
<p>申请阿里云 DNS 访问密钥。</p>
<p>进入 <a href="https://ram.console.aliyun.com/users" target="_blank" rel="noopener noreferrer">阿里云访问控制台</a>，并创建一个子用户，授予 <code>AliyunDNSFullAccess</code> 权限。</p>
<p>点击你新创建的子用户，进入用户详情页，为该用户创建一个 <code>AccessKey</code>，并保存好 <code>AccessKey ID</code> 和 <code>AccessKey Secret</code>。</p>
</li>
<li>
<p>本地保存访问密钥。</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD"># 这里将 AccessKey ID 和 AccessKey Secret 替换为你创建的 API 密钥</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> cat</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ></span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /opt/certbot/credentials.ini</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">EOF</span></span>
<span class="line"><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">certbot_dns_aliyun:dns_aliyun_access_key = &#x3C;AccessKey ID></span></span>
<span class="line"><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">certbot_dns_aliyun:dns_aliyun_access_key_secret = &#x3C;AccessKey Secret></span></span>
<span class="line"><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">EOF</span></span></code></pre>
</div><p>保存完密钥后，记得修改文件权限，防止泄露。</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> chmod</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91"> 600</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /opt/certbot/credentials.ini</span></span></code></pre>
</div></li>
<li>
<p>申请证书。</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD"># 这里将 &#x3C;yourdomain.com> 替换为你的域名</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">certbot</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> certonly</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> \</span></span>
<span class="line"><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">  -a</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> certbot-dns-aliyun:dns-aliyun</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> \</span></span>
<span class="line"><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">  --certbot-dns-aliyun:dns-aliyun-credentials</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /opt/certbot/credentials.ini</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> \</span></span>
<span class="line"><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076">  -d</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">*.&#x3C;yourdomain.com>,&#x3C;yourdomain.com></span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span></span></code></pre>
</div><p>我们申请的证书同时包含了主域名和泛域名，这样就可以使用同一个证书来保护所有子域名。
申请成功后，证书会保存在 <code>/etc/letsencrypt/live/&lt;yourdomain.com&gt;/</code> 目录下。</p>
<p>你可以通过以下命令查看证书的详细信息：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> certbot</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> certificates</span></span></code></pre>
</div><div class="hint-container note">
<p class="hint-container-title">可能遇到的问题</p>
<p>如果运行上述的命令后出现报错 <code>Invalid version. The only valid version for X509Req is 0</code>，可以运行以下命令修复：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /opt/certbot/bin/pip3</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> install</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> pyOpenSSL==</span><span style="--shiki-light:#2F798A;--shiki-dark:#4C9A91">23.1.1</span></span></code></pre>
</div></div>
</li>
<li>
<p>配置自动续订。</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#998418;--shiki-dark:#B8A965">echo</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">0 0,12 * * * root python -c 'import random; import time; time.sleep(random.random() * 3600)' &#x26;&#x26; certbot renew -q</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> |</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> tee</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -a</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /etc/crontab</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ></span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /dev/null</span></span></code></pre>
</div><p>这里我们使用 <code>crontab</code> 配置了在每天的 0 点和 12 点自动续订证书。
在运行前，我们先随机等待 0 到 1 小时，以避免所有服务器在同一时间请求 Let's Encrypt 服务器。</p>
<p>Certbot 会在证书过期前 30 天自动续订证书。</p>
<div class="hint-container warning">
<p class="hint-container-title">注意</p>
<p>证书续订后还需要重新加载 nginx 才会生效，可以手动运行：</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> systemctl</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> reload</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> nginx.service</span></span></code></pre>
</div><p>或者将上述语句加到 <code>crontab</code> 中，即</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#998418;--shiki-dark:#B8A965">echo</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77"> "</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">0 0,12 * * * root python -c 'import random; import time; time.sleep(random.random() * 3600)' &#x26;&#x26; certbot renew -q &#x26;&#x26; systemctl reload nginx.service</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">"</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> |</span><span style="--shiki-light:#59873A;--shiki-dark:#80A665"> sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> tee</span><span style="--shiki-light:#A65E2B;--shiki-dark:#C99076"> -a</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /etc/crontab</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ></span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /dev/null</span></span></code></pre>
</div></div>
</li>
</ol>
</div><h2>配置 Nginx</h2>
<p>将 SSL 相关配置写入 Nginx 配置文件，并在 Nginx 配置中启用 HTTPS。
完成后记得重启 Nginx 服务。</p>
<div class="language-bash" data-highlighter="shiki" data-ext="bash" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-bash"><span class="line"><span style="--shiki-light:#A0ADA0;--shiki-dark:#758575DD"># 这里将 &#x3C;yourdomain.com> 替换为你的域名</span></span>
<span class="line"><span style="--shiki-light:#59873A;--shiki-dark:#80A665">sudo</span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> cat</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> ></span><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D"> /etc/nginx/conf.d/ssl.conf</span><span style="--shiki-light:#AB5959;--shiki-dark:#CB7676"> &#x3C;&#x3C;</span><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">EOF</span></span>
<span class="line"><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">ssl_certificate /etc/letsencrypt/live/&#x3C;yourdomain.com>/fullchain.pem;</span></span>
<span class="line"><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">ssl_certificate_key /etc/letsencrypt/live/&#x3C;yourdomain.com>/privkey.pem;</span></span>
<span class="line"><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">include /etc/letsencrypt/options-ssl-nginx.conf;</span></span>
<span class="line"><span style="--shiki-light:#B56959;--shiki-dark:#C98A7D">ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;</span></span>
<span class="line"><span style="--shiki-light:#B5695977;--shiki-dark:#C98A7D77">EOF</span></span></code></pre>
</div><div class="hint-container note">
<p class="hint-container-title">注</p>
<p>如果你的 Nginx 默认不读取 <code>/etc/nginx/conf.d/</code> 目录下的配置文件，可以在 <code>nginx.conf</code> 文件中添加如下配置：</p>
<div class="language-nginx" data-highlighter="shiki" data-ext="nginx" style="--shiki-light:#393a34;--shiki-dark:#dbd7caee;--shiki-light-bg:#ffffff;--shiki-dark-bg:#121212"><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code class="language-nginx"><span class="line"><span style="--shiki-light:#1E754F;--shiki-dark:#4D9375">include </span><span style="--shiki-light:#393A34;--shiki-dark:#DBD7CAEE">/etc/nginx/conf.d/*.conf</span><span style="--shiki-light:#999999;--shiki-dark:#666666">;</span></span></code></pre>
</div><p>如果你不想全局启用该 SSL 配置，也可以仅在对应的 <code>server</code> 块中添加。</p>
</div>
<p>如果一切顺利，现在你就可以通过 HTTPS 访问你的网站了。</p>
<h2>参考资料</h2>
<ul>
<li>
<p><a href="https://certbot.eff.org/instructions" target="_blank" rel="noopener noreferrer">Certbot 官方文档</a></p>
</li>
<li>
<p><a href="https://www.cnblogs.com/bbling/p/12807642.html" target="_blank" rel="noopener noreferrer">使用 Certbot 自动申请并续订阿里云 DNS 免费泛域名证书 | 博客园 tabsp</a></p>
</li>
<li>
<p><a href="https://www.cnblogs.com/michaelshen/p/18538178" target="_blank" rel="noopener noreferrer">使用 Let’s Encrypt 免费申请泛域名 SSL 证书，并实现自动续期 | 博客园 平元兄</a></p>
</li>
</ul>
]]></content>
    <published>2025-03-03T05:52:36.000Z</published>
  </entry>
  <entry>
    <title type="text">B 树</title>
    <id>https://sunirein.tech/blog/313wmwqo/</id>
    <link href="https://sunirein.tech/blog/313wmwqo/"/>
    <updated>2026-04-03T13:57:07.000Z</updated>
    <summary type="html"><![CDATA[<p>在本文中，我将深入解析 B 树这一广泛应用于数据库与文件系统的多路平衡搜索树。
与传统的二叉树不同，B 树通过多分支和高扇出的特性，显著减少磁盘 I/O 次数，从而高效管理海量数据。
本文将系统讲解 B 树的核心设计思想、平衡维护机制及其关键操作（查找、插入、删除），
探讨 B 树的适用场景，帮助读者理解其在大规模存储系统中的核心地位。</p>
]]></summary>
    <content type="html"><![CDATA[<p>在本文中，我将深入解析 B 树这一广泛应用于数据库与文件系统的多路平衡搜索树。
与传统的二叉树不同，B 树通过多分支和高扇出的特性，显著减少磁盘 I/O 次数，从而高效管理海量数据。
本文将系统讲解 B 树的核心设计思想、平衡维护机制及其关键操作（查找、插入、删除），
探讨 B 树的适用场景，帮助读者理解其在大规模存储系统中的核心地位。</p>
<!-- more -->
<div class="hint-container note">
<p class="hint-container-title">注</p>
<p>有些文章中会把 B 树写作 B-树，这来源与 B 树的英文 B-tree。
我个人认为这种写法不是很好，容易被误解为是“B 减树”，但其实这里的 '-' 只是表示连接的意义，并不是作为减号。</p>
</div>
<h2>什么是 B 树？</h2>
<p>B 树是一种多路平衡搜索树，广泛应用于数据库系统和文件系统中，特别是在需要频繁进行磁盘读写操作的场景中。
不同于常见的二叉平衡树，B 树中的一个节点能存放多个数据，且具有多个子节点。</p>
<div style="text-align:center"><p></p>
</div><h2>B 树的核心特性</h2>
<h3>节点结构</h3>
<p>在 B 树中，节点可以分为<mark>内部节点</mark>和<mark>叶子节点</mark>。
内部节点存储数据和指向子节点的指针，而叶子节点只存储数据，不存储指针。</p>
<p>通常来说，一个存有 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">n</span></span></span></span> 个数据的内部节点有 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo>+</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">n+1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span> 个子节点，反之亦然。</p>
<p>B 树节点内部的数据在维持自身有序性的同时，与子节点也存在一定的有序性。
<strong>对于一个升序节点中的第 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">n</span></span></span></span> 个关键字，第 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">n</span></span></span></span> 个子节点中的关键字全部小于它，第 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo>+</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">n+1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span> 个子节点中的关键字全部大于它。</strong>
因此，我们可以将这 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo>+</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">n+1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span> 个子节点看作恰好散落插入在 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">n</span></span></span></span> 个节点数据中。</p>
<h3>阶数</h3>
<p>B 树中节点允许的最大度值称为 B 树的<mark>阶数</mark>（Order），它是 B 树的一个重要特性。<sup class="footnote-ref"><a href="#footnote1">[1]</a><a class="footnote-anchor" id="footnote-ref1"></a></sup></p>
<p>一棵 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi></mrow><annotation encoding="application/x-tex">m</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">m</span></span></span></span> 阶的 B 树具有以下性质：</p>
<ol>
<li>每个节点最多有 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">m - 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">m</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span> 个关键字和 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi></mrow><annotation encoding="application/x-tex">m</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">m</span></span></span></span> 个子节点。</li>
<li>除根节点外，每一个非叶子节点最少有 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">⌊</mo><mfrac><mi>m</mi><mn>2</mn></mfrac><mo stretchy="false">⌋</mo></mrow><annotation encoding="application/x-tex">\lfloor \frac{m}{2} \rfloor</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.095em;vertical-align:-0.345em;"></span><span class="mopen">⌊</span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6954em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.394em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">m</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose">⌋</span></span></span></span> 个子节点。</li>
<li>如果根节点不是叶子节点，那么它至少有两个子节点。</li>
<li>所有的叶子节点位于同一层。</li>
</ol>
<h3>节点分裂与节点合并</h3>
<p>B 树通过<mark>节点分裂</mark>和<mark>节点合并</mark>来维护其平衡性，这两种操作是 B 树区分于传统二叉树的特有机制。</p>
<h4>节点分裂</h4>
<p>在执行插入操作时，若某个节点的关键字数量超过上限，则需要分裂。</p>
<div class="vp-steps"><ol>
<li>
<p>定位中间关键字。</p>
</li>
<li>
<p>创建两个左右子节点。</p>
<p>左节点保留中间关键字之前的关键字和对应的子节点，右节点保留中间关键字之后的关键字和对应的子节点。</p>
</li>
<li>
<p>提升中间关键字。</p>
<p>将中间关键字插入到父节点中，若不存在父节点则创建一个。
同时，删除父节点中原有节点的指针，建立父节点到左右节点的连接。</p>
</li>
<li>
<p>递归检查父节点。</p>
<p>由于中间关键字的插入，父节点可能发生溢出，需继续分裂直至满足条件。</p>
</li>
</ol>
</div><h4>节点合并</h4>
<p>在执行删除操作时，若某个节点的关键字低于下限，则需要合并。</p>
<div class="vp-steps"><ol>
<li>
<p>判断兄弟节点可否借取。</p>
<p>若<strong>邻近</strong>的兄弟节点有富余的关键字，可以向它们借取一个，同时调整对应的关系。
否则，需要合并。</p>
</li>
<li>
<p>合并兄弟节点。</p>
<p>选取一个相邻的兄弟节点，以及父节点中对应的关键字，合并为一个新的节点，然后重新调整父节点与新节点的关系。</p>
</li>
<li>
<p>递归检查父节点。</p>
<p>合并完后父节点可能因关键字下移而不足，需继续合并直至满足约束。</p>
<p>若父节点为根节点且合并后为空，删除父节点，选择合并后的新节点为新根。此时树高减少。</p>
</li>
</ol>
</div><h2>B 树的基本操作</h2>
<h3>查找</h3>
<p>B 树的查找过程类似于二叉搜索树，我们从根节点开始，自顶向下逐步搜索，直至找到目标关键字。
不同之处在于由于每个节点拥有多个关键字，我们需要遍历这些关键字来确定要查找节点所在的位置。</p>
<h3>插入</h3>
<div class="vp-steps"><ol>
<li>
<p>检验存在性。</p>
<p>插入一个元素时，首先要验证该元素在 B 树中是否存在。</p>
</li>
<li>
<p>寻找要插入的叶子节点。</p>
<p><strong>插入操作一定发生在叶子节点</strong>。
我们自顶向下搜索，找到能用来插入该节点的叶子节点。</p>
</li>
<li>
<p>插入关键字。</p>
<p>在叶子节点中找到合适的位置，插入该关键字。
此时如果溢出，则进行<a href="#%E8%8A%82%E7%82%B9%E5%88%86%E8%A3%82">节点分裂</a>。<sup class="footnote-ref"><a href="#footnote2">[2]</a><a class="footnote-anchor" id="footnote-ref2"></a></sup></p>
</li>
</ol>
</div><h3>删除</h3>
<p>相比插入操作，删除操作实现起来较为复杂。</p>
<div class="vp-steps"><ol>
<li>
<p>定位要被删除的关键字。</p>
</li>
<li>
<p>借位策略。</p>
<p>当被删除的关键字位于非叶子节点时，用前驱或后驱替换之<sup class="footnote-ref"><a href="#footnote3">[3]</a><a class="footnote-anchor" id="footnote-ref3"></a></sup>，目标节点改为前驱或后继所在节点。
<strong>前驱与后继必然位于叶子节点上。</strong></p>
</li>
<li>
<p>删除关键字。</p>
<p>此时目标节点必然为叶子节点，删除时只需重新调整关键字的顺序。
若删除后关键字的数量不足，则进行<a href="#%E8%8A%82%E7%82%B9%E5%90%88%E5%B9%B6">节点合并</a>。</p>
</li>
</ol>
</div><h2>为什么需要 B 树</h2>
<h3>磁盘 I/O 的瓶颈与二叉树的局限性</h3>
<p>传统二叉树在处理大规模数据存储上具有天然劣势。</p>
<p>大数据一般存储在磁盘上，相比键值比较所用的时间，从磁盘上读取数据的时间占支配地位。
磁盘上的数据检索以页作为基本单位，当分页大小为 4KB 时，读取 1B 数据和读取 4KB 数据都需要进行一次磁盘 I/O，耗时几乎相同。
而像 AVL 树、红黑树这种二叉树结构，每个节点可能分布在不同的磁盘页上，一次访问需要进行多次磁盘 I/O。</p>
<p>B 树的每个节点可以存储大量关键字，连续分布在相同的磁盘页上，仅需进行一次 I/O，读取效率高。
而且它能有效较少树高，降低一次查询中磁盘 I/O 的次数，大大提高查询性能。</p>
<h3>自平衡机制的稳定性</h3>
<p>AVL 树、红黑树通过旋转操作维护平衡，但旋转可能涉及多个节点调整（例如子树的高度变化）。
B 树通过节点分裂和节点合并实现平衡，操作仅影响局部节点，减少全局调整的开销。</p>
<p>此外，B 树的平衡还是自适应的，不需要像 AVL 树、红黑树那样维护额外的节点信息。</p>
<h3>局部性原理的充分利用</h3>
<p>现代 CPU 普遍采用缓存机制，访问连续内存的时间要比随机访问快得多。
B 树高扇出与局部性优化的特性，充分利用了局部性原理，在现代 CPU 多级缓存架构下同样展现出显著优势。
在很多场合下，B 树表现出优于传统 BST 的性能。</p>
<p>新兴语言 Rust 标准库的 <code>BTreeMap</code> 采用 B 树来实现，正是基于这个原理。<sup class="footnote-ref"><a href="#footnote4">[4]</a><a class="footnote-anchor" id="footnote-ref4"></a></sup></p>
<h2>B 树的应用</h2>
<h3>数据库索引</h3>
<p>主流数据库（如 MySQL、PostgreSQL）普遍使用 <mark>B+ 树</mark>（B 树的变种）作为索引结构。</p>
<h3>文件系统</h3>
<p>文件系统（如 NTFS、ReiserFS）用 B 树管理目录和文件块指针。</p>
<h3>内存受限的嵌入式系统</h3>
<p>B 树的高扇出特性在内存中能减少指针数量，降低内存碎片。</p>
<h2>参考资料</h2>
<ul>
<li><a href="https://zh.wikipedia.org/wiki/B%E6%A0%91/" target="_blank" rel="noopener noreferrer">B 树 | 维基百科</a></li>
<li><a href="https://oi-wiki.org/ds/b-tree/" target="_blank" rel="noopener noreferrer">B 树 | OI Wiki</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/463641767" target="_blank" rel="noopener noreferrer">B 树详解与实现 | 知乎专栏</a></li>
</ul>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="footnote1" class="footnote-item"><p>在有些文献中，B 树的阶数被定义为最大关键字个数，与本文所采用的定义略有不同。
还有些文献采用节点的最小度数来定义 B 树的阶数。 <a href="#footnote-ref1" class="footnote-backref">↩︎</a></p>
</li>
<li id="footnote2" class="footnote-item"><p>有时为了实现的方便，会选择先分裂后插入。 <a href="#footnote-ref2" class="footnote-backref">↩︎</a></p>
</li>
<li id="footnote3" class="footnote-item"><p>因为位于非叶子节点上，所以前驱或后继必然存在。 <a href="#footnote-ref3" class="footnote-backref">↩︎</a></p>
</li>
<li id="footnote4" class="footnote-item"><p>见 <a href="https://rustwiki.org/zh-CN/std/collections/struct.BTreeMap.html" target="_blank" rel="noopener noreferrer">https://rustwiki.org/zh-CN/std/collections/struct.BTreeMap.html</a>。 <a href="#footnote-ref4" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
]]></content>
    <published>2025-02-14T12:42:17.000Z</published>
  </entry>
  <entry>
    <title type="text">Splay 树</title>
    <id>https://sunirein.tech/blog/mdyrqsgl/</id>
    <link href="https://sunirein.tech/blog/mdyrqsgl/"/>
    <updated>2026-04-03T13:57:07.000Z</updated>
    <summary type="html"><![CDATA[<p>在本文中，我将介绍 Splay 树，一种与 类似但有所不同的自适应二叉查找树。
与 AVL 树通过严格平衡因子维护树的高度不同，Splay 树通过伸展操作将最近访问的节点移动到根节点，从而自适应地优化频繁访问的数据。
这种灵活性使得 Splay 树在缓存、数据压缩等场景中表现优异。
本文将详细讲解 Splay 树的核心原理、操作实现及其与 AVL 树的异同，帮助读者深入理解这一自平衡树结构的独特优势。</p>
]]></summary>
    <content type="html"><![CDATA[<p>在本文中，我将介绍 Splay 树，一种与 类似但有所不同的自适应二叉查找树。
与 AVL 树通过严格平衡因子维护树的高度不同，Splay 树通过伸展操作将最近访问的节点移动到根节点，从而自适应地优化频繁访问的数据。
这种灵活性使得 Splay 树在缓存、数据压缩等场景中表现优异。
本文将详细讲解 Splay 树的核心原理、操作实现及其与 AVL 树的异同，帮助读者深入理解这一自平衡树结构的独特优势。</p>
<!-- more -->
<h2>什么是 Splay 树？</h2>
<p><mark>Splay 树</mark>，又名<mark>伸展树</mark>，是一种自平衡二叉搜索树。
它通过<mark>伸展</mark>（Splay）操作不断将某个节点旋转到根节点的位置，维持整体的平衡，
能在均摊时间 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>log</mi><mo>⁡</mo><mi>N</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(\log N)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.109em;">N</span><span class="mclose">)</span></span></span></span> 内完成插入、删除、查找操作。</p>
<p>Splay 树由 Daniel Sleator 和 Robert Tarjan 于 1985 年发明。</p>
<h2>伸展——Splay 树的核心</h2>
<p>伸展（Splay）是 Splay 树这一数据结构的核心，正如之于 AVL 树。</p>
<h3>何为伸展？</h3>
<p>假设要对一个搜索树进行一系列的查找操作，被查找频率高的节点就应当经常位于靠近树根的位置。
于是干脆采用一种简单的方法，在每次查找之后调整树的结构，将要查找的节点搬到离树根较近的位置。</p>
<p>基于这样的朴素思想，Splay 树应运而生。
在每次节点操作中，它会沿着目标节点与树根之间的路径，通过树旋转将其逐渐搬运到树根处。
这个过程被形象地取名为<mark>伸展</mark>。</p>
<h3>单旋与双旋</h3>
<p>在 Splay 树中，我们把一次普通的树旋转成为<mark>单旋</mark>。
每次伸展操作我们需要将给定节点通过旋转移动根节点的位置。
显然，我们可以通过连续的单旋来轻松实现。
但这样可能会导致更高的时间复杂度，并且可能无法有效控制树的高度。
因此，Splay 树引入了一种特有的<mark>双旋</mark>操作。</p>
<p>我们将要伸展的目标节点称为 X，记其父节点为 P，祖父结点为 G，并假定它们存在（否则无需进行双旋，最多进行一次单旋）。</p>
<p>为了表述清晰和便于理解，对于单次旋转操作，我们不再区分左右旋转，而是按照节点 X 相对于父节点 P 的位置来判断旋转的方向。</p>
<h4>zig-zig</h4>
<p>当 X 与 P 同向，即 X 和 P 都是左孩子或都是右孩子时，我们先旋转 P，后旋转 X。
这种情况称为 zig-zig。</p>
<div style="text-align:center"><p></p>
<p></p>
</div><h4>zig-zag</h4>
<p>当 X 与 P 反向，即 X 和 P 分别是不同方向的孩子时，我们连续旋转 X 两次。
这种情况称为 zig-zag。</p>
<div style="text-align:center"><p></p>
<p></p>
</div><p>通常，一次伸展操作由若干次双旋与可能的一次单旋构成，其中单旋操作仅发生在最后一次旋转时 P 是根节点，无法满足双旋条件的情况下。</p>
<h2>Splay 树的基本操作</h2>
<h3>查找</h3>
<p>Splay 树的查找操作与普通二叉搜索树基本无异，不同之处在与找到目标节点后要将其伸展至根节点处。</p>
<h3>插入</h3>
<p>Splay 树的插入操作也和二叉搜索树类似，但是在插入节点或者发现节点已经存在后要伸展该节点。</p>
<h3>删除</h3>
<p>删除操作的实现较为复杂，大致可分为以下几步：</p>
<div class="vp-steps"><ol>
<li>
<p>找到要删除的节点，伸展之。</p>
</li>
<li>
<p>删除伸展后的根节点，切割出左右子树。</p>
</li>
<li>
<p>伸展左子树的最右节点（或右子树的最左节点）。</p>
</li>
<li>
<p>此时左子树的根节点没有右儿子，直接将右子树作为左子树的右儿子。</p>
</li>
</ol>
</div><h2>时间复杂度分析</h2>
<p>我们用势能法来证明 Splay 树拥有 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>log</mi><mo>⁡</mo><mi>N</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(\log N)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.109em;">N</span><span class="mclose">)</span></span></span></span> 的均摊时间复杂度。</p>
<h3>势能函数</h3>
<p>使用 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>T</mi></mrow><annotation encoding="application/x-tex">T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span></span></span></span> 来描述完整的一棵树，用小写字母 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span>、<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span></span></span>、<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi></mrow><annotation encoding="application/x-tex">z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span></span></span></span> 等表示树上的节点。
记 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>T</mi><mi mathvariant="normal">∣</mi></mrow><annotation encoding="application/x-tex">|T|</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span><span class="mord">∣</span></span></span></span> 为 Splay 树上的节点数目，<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>x</mi><mi mathvariant="normal">∣</mi></mrow><annotation encoding="application/x-tex">|x|</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mord">∣</span></span></span></span> 为以 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span> 为根的子树的节点数目（包括 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span>）。</p>
<p>定义如下的势能函数：</p>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mi>log</mi><mo>⁡</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mi mathvariant="normal">∣</mi></mrow><annotation encoding="application/x-tex">\phi (x) = \log |x|
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mord">∣</span></span></span></span></span></p>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">Φ</mi><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo><mo>=</mo><munder><mo>∑</mo><mrow><mi>x</mi><mo>∈</mo><mi>T</mi></mrow></munder><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\Phi (T) = \sum_{x \in T} \phi (x)
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">Φ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.3717em;vertical-align:-1.3217em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.05em;"><span style="top:-1.8557em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">x</span><span class="mrel mtight">∈</span><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span></span></span></span><span style="top:-3.05em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.3217em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span></span></p>
<p>其中 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Φ</mi><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\Phi(T)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">Φ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span><span class="mclose">)</span></span></span></span> 为整棵树的势能函数，<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\phi(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span> 为节点 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span> 对势能的贡献。</p>
<p>初始时刻，<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Φ</mi><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\Phi (T) = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">Φ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">0</span></span></span></span>。对于任意时刻，都有<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Φ</mi><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo><mo>≥</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\Phi (T) \ge 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">Φ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">0</span></span></span></span>。因此势能函数合法。</p>
<h3>单旋的摊还代价</h3>
<p>单旋中发生势能变化的只有目标节点 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span> 及其父节点 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span></span></span>，因此</p>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">Δ</mi><mi mathvariant="normal">Φ</mi><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo><mo>=</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>+</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>y</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>y</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\Delta \Phi (T) = \phi (x&#x27;) + \phi (y&#x27;) - \phi (x) - \phi (y)
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">ΔΦ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0519em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0519em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mclose">)</span></span></span></span></span></p>
<p>由单旋的性质可知，<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>y</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>&lt;</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>=</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>y</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\phi (y&#x27;) \lt \phi (x&#x27;) = \phi (y)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&lt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mclose">)</span></span></span></span>，
因此</p>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">Δ</mi><mi mathvariant="normal">Φ</mi><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo><mo>&lt;</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mi>O</mi><mo stretchy="false">(</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\Delta \Phi (T) \lt \phi (x&#x27;) - \phi (x) = O(\phi (x&#x27;) - \phi (x))
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">ΔΦ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&lt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0519em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0519em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">))</span></span></span></span></span></p>
<p>则单旋操作的摊还代价为 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo>+</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1 + \phi (x&#x27;) - \phi (x))</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">))</span></span></span></span>，其中 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span> 表示旋转本身的复杂度。</p>
<p>由于一次伸展中只有至多一次单旋，<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span> 不会对分析产生影响，我们可以只关心其中的 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(\phi (x&#x27;) - \phi (x))</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">))</span></span></span></span>。</p>
<h3>zig-zig 的摊还代价</h3>
<p>zig-zig 中发生势能变化的有操作节点 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span>，父节点 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span></span></span>，及其祖父节点 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi></mrow><annotation encoding="application/x-tex">z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span></span></span></span>，因此</p>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">Δ</mi><mi mathvariant="normal">Φ</mi><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo><mo>=</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>+</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>y</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>+</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>y</mi><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>z</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\Delta \Phi (T) = \phi (x&#x27;) + \phi (y&#x27;) + \phi (z&#x27;) - \phi (x) - \phi (y) - \phi (z)
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">ΔΦ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0519em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0519em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0519em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="mclose">)</span></span></span></span></span></p>
<p>同样地，有 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>y</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>&lt;</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>=</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>z</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\phi (y&#x27;) \lt \phi (x&#x27;) = \phi (z)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&lt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="mclose">)</span></span></span></span>，<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>&lt;</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>y</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\phi (x) \lt \phi (y)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&lt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mclose">)</span></span></span></span>，因此</p>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">Δ</mi><mi mathvariant="normal">Φ</mi><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo><mo>≤</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>+</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mn>2</mn><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\Delta \Phi (T) \le \phi (x&#x27;) + \phi (z&#x27;) - 2 \phi (x)
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">ΔΦ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0519em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0519em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">2</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span></span></p>
<p>这里我们需要用到一个小结论：<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>+</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mn>2</mn><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>≤</mo><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\phi (x) + \phi (z&#x27;) - 2 \phi(x&#x27;) \le - 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord">2</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">−</span><span class="mord">1</span></span></span></span>。</p>
<details class="hint-container details"><summary>证明</summary>
<p>设 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>x</mi><mi mathvariant="normal">∣</mi><mo>=</mo><mi>a</mi></mrow><annotation encoding="application/x-tex">|x| = a</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">a</span></span></span></span>，<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mi mathvariant="normal">∣</mi><mo>=</mo><mi>b</mi></mrow><annotation encoding="application/x-tex">|z&#x27;| = b</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">b</span></span></span></span>，则 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mi mathvariant="normal">∣</mi><mo>≥</mo><mi>a</mi><mo>+</mo><mi>b</mi></mrow><annotation encoding="application/x-tex">|x&#x27;| \ge a + b</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord">∣</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">b</span></span></span></span>（结合 zig-zig 的过程易得）。</p>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>+</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mn>2</mn><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow/><mo>=</mo><mi>log</mi><mo>⁡</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mi mathvariant="normal">∣</mi><mo>+</mo><mi>log</mi><mo>⁡</mo><mi mathvariant="normal">∣</mi><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mi mathvariant="normal">∣</mi><mo>−</mo><mn>2</mn><mi>log</mi><mo>⁡</mo><mi mathvariant="normal">∣</mi><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mi mathvariant="normal">∣</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow/></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow/><mo>=</mo><mi>log</mi><mo>⁡</mo><mrow><mo fence="true">(</mo><mfrac><mrow><mi mathvariant="normal">∣</mi><mi>x</mi><mi mathvariant="normal">∣</mi><mi mathvariant="normal">∣</mi><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mi mathvariant="normal">∣</mi></mrow><mrow><mi mathvariant="normal">∣</mi><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><msup><mi mathvariant="normal">∣</mi><mn>2</mn></msup></mrow></mfrac><mo fence="true">)</mo></mrow></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow/></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow/><mo>≤</mo><mi>log</mi><mo>⁡</mo><mrow><mo fence="true">(</mo><mfrac><mrow><mi>a</mi><mi>b</mi></mrow><mrow><mo stretchy="false">(</mo><mi>a</mi><mo>+</mo><mi>b</mi><msup><mo stretchy="false">)</mo><mn>2</mn></msup></mrow></mfrac><mo fence="true">)</mo></mrow></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow/></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow/><mo>≤</mo><mi>log</mi><mo>⁡</mo><mrow><mo fence="true">(</mo><mfrac><mrow><mi>a</mi><mi>b</mi></mrow><mrow><mn>2</mn><mi>a</mi><mi>b</mi></mrow></mfrac><mo fence="true">)</mo></mrow></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow/></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow/><mo>=</mo><mo>−</mo><mn>1</mn></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{aligned}
\phi (x) + \phi (z&#x27;) - 2 \phi (x&#x27;)
&amp;= \log |x| + \log |z&#x27;| - 2\log |x&#x27;| \\
&amp;= \log \left(\frac{|x||z&#x27;|}{|x&#x27;|^2}\right) \\
&amp;\le \log \left(\frac{a b}{(a+b)^2}\right) \\
&amp;\le \log \left(\frac{a b}{2 a b}\right) \\
&amp;= -1
\end{aligned}
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:10.8001em;vertical-align:-5.15em;"></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:5.65em;"><span style="top:-8.26em;"><span class="pstrut" style="height:3.45em;"></span><span class="mord"><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">2</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-6.15em;"><span class="pstrut" style="height:3.45em;"></span><span class="mord"></span></span><span style="top:-3.45em;"><span class="pstrut" style="height:3.45em;"></span><span class="mord"></span></span><span style="top:-0.75em;"><span class="pstrut" style="height:3.45em;"></span><span class="mord"></span></span><span style="top:1.34em;"><span class="pstrut" style="height:3.45em;"></span><span class="mord"></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:5.15em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:5.65em;"><span style="top:-8.26em;"><span class="pstrut" style="height:3.45em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">∣</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord">∣</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">2</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">∣</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord">∣</span></span></span><span style="top:-6.15em;"><span class="pstrut" style="height:3.45em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">(</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.4289em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">∣</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6779em;"><span style="top:-2.989em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord"><span class="mord">∣</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7401em;"><span style="top:-2.989em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">∣</span><span class="mord mathnormal">x</span><span class="mord">∣∣</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord">∣</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">)</span></span></span></span></span><span style="top:-3.45em;"><span class="pstrut" style="height:3.45em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">(</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mopen">(</span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">b</span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7401em;"><span style="top:-2.989em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal">ab</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">)</span></span></span></span></span><span style="top:-0.75em;"><span class="pstrut" style="height:3.45em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">(</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">2</span><span class="mord mathnormal">ab</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal">ab</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">)</span></span></span></span></span><span style="top:1.34em;"><span class="pstrut" style="height:3.45em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord">−</span><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:5.15em;"><span></span></span></span></span></span></span></span></span></span></span></span></p>
</details>
<p>因此，<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>−</mo><mo stretchy="false">(</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>+</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mn>2</mn><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>+</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">-(\phi (x) + \phi (z&#x27;) - 2 \phi(x&#x27;) + 1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">−</span><span class="mopen">(</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord">2</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mclose">)</span></span></span></span> 是一个非负数。
将上面的式子加上这个非负数，可得</p>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mi mathvariant="normal">Δ</mi><mi mathvariant="normal">Φ</mi><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow/><mo>≤</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>+</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mn>2</mn><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow/></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow/><mo>≤</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>+</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mn>2</mn><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>−</mo><mo stretchy="false">(</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>+</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>z</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mn>2</mn><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>+</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow/></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow/><mo>=</mo><mn>3</mn><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mn>3</mn><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>−</mo><mn>1</mn></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{aligned}
\Delta \Phi (T)
&amp;\le \phi (x&#x27;) + \phi (z&#x27;) - 2 \phi (x) \\
&amp;\le \phi (x&#x27;) + \phi (z&#x27;) - 2 \phi (x) - (\phi (x) + \phi (z&#x27;) - 2 \phi(x&#x27;) + 1) \\
&amp;= 3 \phi (x&#x27;) - 3 \phi (x) - 1
\end{aligned}
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:4.2em;vertical-align:-1.85em;"></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.35em;"><span style="top:-4.51em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">ΔΦ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span><span class="mclose">)</span></span></span><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span><span style="top:-1.51em;"><span class="pstrut" style="height:3em;"></span><span class="mord"></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.85em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.35em;"><span style="top:-4.51em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">2</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">2</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mopen">(</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">2</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">1</span><span class="mclose">)</span></span></span><span style="top:-1.51em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord">3</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8019em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">3</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.85em;"><span></span></span></span></span></span></span></span></span></span></span></span></p>
<p>则 zig-zig 的摊还代价为 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo>+</mo><mi>O</mi><mo stretchy="false">(</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1) + O(\phi (x&#x27;) - \phi (x) - 1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mclose">)</span></span></span></span>。
通过增大我们所加的非负数以及势的单位，可以抵消 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span> 中的常数。
故摊还代价可记为 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(\phi (x&#x27;) - \phi (x))</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">))</span></span></span></span>。</p>
<h3>zig-zag 的摊还代价</h3>
<p>其分析过程与 zig-zig 类似，结论不变。</p>
<h3>伸展操作的摊还代价</h3>
<p>除了最后一次旋转可能增加 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span> 的代价外，其他旋转操作的摊还代价均为 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msup><mi>x</mi><mo mathvariant="normal" lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(\phi (x&#x27;) - \phi (x))</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0019em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7519em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">))</span></span></span></span>。</p>
<p>假设一共执行了 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">n</span></span></span></span> 次旋转操作，<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">x_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 表示第 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi></mrow><annotation encoding="application/x-tex">i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6595em;"></span><span class="mord mathnormal">i</span></span></span></span> 次旋转后 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span> 的状态，则伸展操作的总摊还代价为</p>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo>+</mo><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></munderover><mo stretchy="false">(</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msub><mi>x</mi><mi>i</mi></msub><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msub><mi>x</mi><mrow><mi>i</mi><mo>−</mo><mn>1</mn></mrow></msub><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo>=</mo><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo>+</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msub><mi>x</mi><mi>n</mi></msub><mo stretchy="false">)</mo><mo>−</mo><mi>ϕ</mi><mo stretchy="false">(</mo><msub><mi>x</mi><mn>0</mn></msub><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1 + \sum_{i = 1}^{n} (\phi(x_{i}) - \phi (x_{i-1}))) = O(1 + \phi (x_n) - \phi (x_0))
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:2.9291em;vertical-align:-1.2777em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6514em;"><span style="top:-1.8723em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.05em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.3em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.2777em;"><span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2083em;"><span></span></span></span></span></span></span><span class="mclose">)))</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">))</span></span></span></span></span></p>
<p>此时 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mi>n</mi></msub></mrow><annotation encoding="application/x-tex">x_n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 是树根，故 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϕ</mi><mo stretchy="false">(</mo><msub><mi>x</mi><mi>n</mi></msub><mo stretchy="false">)</mo><mo>=</mo><mi>log</mi><mo>⁡</mo><mi mathvariant="normal">∣</mi><mi>T</mi><mi mathvariant="normal">∣</mi></mrow><annotation encoding="application/x-tex">\phi (x_n) = \log |T|</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">ϕ</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span><span class="mord">∣</span></span></span></span>。</p>
<p>因此，一次伸展操作的摊还代价为 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>log</mi><mo>⁡</mo><mi mathvariant="normal">∣</mi><mi>T</mi><mi mathvariant="normal">∣</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(\log |T|)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">∣</span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span><span class="mord">∣</span><span class="mclose">)</span></span></span></span>。</p>
<h2>与 AVL 树的比较</h2>
<p>相比于 AVL 树，Splay 树不需要存储额外的一些节点信息，如树高，对空间的利用率更高。
另外，尽管 Splay 树看似需要经常性调整自身结构，但却能保持对数级的均摊时间复杂度，
且它没有 AVL 树那样严格的平衡条件，因此在实际应用中通常表现出更优良的性能。</p>
<p>事实上，Splay 树是 AVL 树的<mark>自适应形式</mark>。</p>
<p>不过，由于 Splay 树的每个操作，哪怕是“只读”的访问，也会改变树的结构。
这给在多线程环境下使用 Splay 树带来了挑战。</p>
<h2>扩展进阶：自顶向下的操作</h2>
<p><a href="https://peteryuhang.github.io/posts/Splay-%E6%A0%91-%E5%9D%87%E6%91%8A%E6%97%B6%E9%97%B4%E4%B8%8B%E7%9A%84%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/" target="_blank" rel="noopener noreferrer">Splay 树：均摊时间下的平衡二叉搜索树</a>
中给出了一种自顶向下的 Splay 树实现。
与本文中所给出的实现方法——先找到节点，然后进行伸展——不同，
这种自顶向下的实现在向下查找的过程中，一边查找，一边调整树的结构，不需要进行回溯。</p>
<p>这样实现有两个好处：</p>
<ul>
<li>我们不需要在节点中维护额外的信息（父节点），代码实现起来更简洁。</li>
<li>在实际应用中，这种方法的运行效率会更好。</li>
</ul>
<h2>参考资料</h2>
<ul>
<li>
<p><a href="https://www.cnblogs.com/baijian0212/p/splay.html" target="_blank" rel="noopener noreferrer">伸展树（Splay）详解 | 博客园</a></p>
</li>
<li>
<p><a href="https://oi-wiki.org/ds/splay/" target="_blank" rel="noopener noreferrer">Splay 树 | OI Wiki</a></p>
</li>
<li>
<p><a href="https://www.cnblogs.com/Elaina-0/p/18275500" target="_blank" rel="noopener noreferrer">关于 Spaly 树 | 博客园 伊蕾娜的小窝</a></p>
</li>
<li>
<p><a href="https://zh.wikipedia.org/wiki/%E4%BC%B8%E5%B1%95%E6%A0%91" target="_blank" rel="noopener noreferrer">伸展树 | 维基百科</a></p>
</li>
<li>
<p><a href="https://mr-spade.github.io/study/OI/%E4%BC%B8%E5%B1%95%E6%A0%91-splay-%E5%A4%8D%E6%9D%82%E5%BA%A6%E8%AF%81%E6%98%8E/" target="_blank" rel="noopener noreferrer">伸展树（Splay）复杂度证明 | Mr_Spade</a></p>
</li>
</ul>
]]></content>
    <published>2025-02-02T03:19:13.000Z</published>
  </entry>
  <entry>
    <title type="text">AVL 树</title>
    <id>https://sunirein.tech/blog/dnnlipz6/</id>
    <link href="https://sunirein.tech/blog/dnnlipz6/"/>
    <updated>2026-04-03T13:57:07.000Z</updated>
    <summary type="html"><![CDATA[<p>在本文中，我将详细介绍 AVL 树这一自平衡二叉查找树的基本概念、核心原理以及其操作过程，
帮助读者理解其如何保持高效的查询、插入和删除操作。
同时，我还将通过代码示例演示 AVL 树的实现，并分析其在实际应用中的优势与局限。
本文适合那些有一定数据结构基础，并希望深入了解自平衡树及其应用的读者。</p>
]]></summary>
    <content type="html"><![CDATA[<p>在本文中，我将详细介绍 AVL 树这一自平衡二叉查找树的基本概念、核心原理以及其操作过程，
帮助读者理解其如何保持高效的查询、插入和删除操作。
同时，我还将通过代码示例演示 AVL 树的实现，并分析其在实际应用中的优势与局限。
本文适合那些有一定数据结构基础，并希望深入了解自平衡树及其应用的读者。</p>
<!-- more -->
<h2>什么是 AVL 树？</h2>
<p><strong>AVL 树</strong>是计算机科学中最早被发明的平衡二叉树。
它通过<strong>平衡因子 (Balance Factor)</strong> 来衡量树的平衡程度，并通过<strong>树旋转</strong>来维护平衡。</p>
<p>AVL 树得名于它的发明者 G. M. Adelson-Velsky 和 E. M. Landis。</p>
<h2>平衡因子</h2>
<p><mark>平衡因子</mark>是 AVL 树用来衡量自身平衡程度的标准。</p>
<p>一个节点的平衡因子定义为节点<em>左右子树的高度差</em>。
当节点的平衡因子绝对值小于等于<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn></mrow><annotation encoding="application/x-tex">1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span>时，称该节点为<mark>平衡节点</mark>，否则为<mark>失衡节点</mark>。
AVL 树中所有的节点都是平衡节点。</p>
<div class="hint-container note">
<p class="hint-container-title">AVL 树的高度</p>
<p>设 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>f</mi><mi>h</mi></msub></mrow><annotation encoding="application/x-tex">f_h</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.1076em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">h</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 为高度 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>h</mi></mrow><annotation encoding="application/x-tex">h</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">h</span></span></span></span> 的 AVL 树所包含的最少节点数，则</p>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>f</mi><mi>h</mi></msub><mo>=</mo><mrow><mo fence="true">{</mo><mtable rowspacing="0.36em" columnalign="left left" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mn>1</mn><mo separator="true">,</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>h</mi><mo>=</mo><mn>1</mn></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mn>2</mn><mo separator="true">,</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>h</mi><mo>=</mo><mn>2</mn></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>f</mi><mrow><mi>h</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><msub><mi>f</mi><mrow><mi>h</mi><mo>−</mo><mn>2</mn></mrow></msub><mo>+</mo><mn>1</mn><mo separator="true">,</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>h</mi><mo>≥</mo><mn>3</mn></mrow></mstyle></mtd></mtr></mtable></mrow></mrow><annotation encoding="application/x-tex">f_h =
\begin{cases}
    1, &amp; h = 1 \\
    2, &amp; h = 2 \\
    f_{h-1} + f_{h-2} + 1, &amp; h \ge 3
\end{cases}
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.1076em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">h</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:4.32em;vertical-align:-1.91em;"></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.35em;"><span style="top:-2.2em;"><span class="pstrut" style="height:3.15em;"></span><span class="delimsizinginner delim-size4"><span>⎩</span></span></span><span style="top:-2.192em;"><span class="pstrut" style="height:3.15em;"></span><span style="height:0.316em;width:0.8889em;"><svg xmlns="http://www.w3.org/2000/svg" width="0.8889em" height="0.316em" style="width:0.8889em" viewBox="0 0 888.89 316" preserveAspectRatio="xMinYMin"><path d="M384 0 H504 V316 H384z M384 0 H504 V316 H384z"/></svg></span></span><span style="top:-3.15em;"><span class="pstrut" style="height:3.15em;"></span><span class="delimsizinginner delim-size4"><span>⎨</span></span></span><span style="top:-4.292em;"><span class="pstrut" style="height:3.15em;"></span><span style="height:0.316em;width:0.8889em;"><svg xmlns="http://www.w3.org/2000/svg" width="0.8889em" height="0.316em" style="width:0.8889em" viewBox="0 0 888.89 316" preserveAspectRatio="xMinYMin"><path d="M384 0 H504 V316 H384z M384 0 H504 V316 H384z"/></svg></span></span><span style="top:-4.6em;"><span class="pstrut" style="height:3.15em;"></span><span class="delimsizinginner delim-size4"><span>⎧</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.85em;"><span></span></span></span></span></span></span><span class="mord"><span class="mtable"><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.41em;"><span style="top:-4.41em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord">1</span><span class="mpunct">,</span></span></span><span style="top:-2.97em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord">2</span><span class="mpunct">,</span></span></span><span style="top:-1.53em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.1076em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">h</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2083em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.1076em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">h</span><span class="mbin mtight">−</span><span class="mord mtight">2</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2083em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">1</span><span class="mpunct">,</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.91em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:1em;"></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.41em;"><span style="top:-4.41em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord mathnormal">h</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord">1</span></span></span><span style="top:-2.97em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord mathnormal">h</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord">2</span></span></span><span style="top:-1.53em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord mathnormal">h</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≥</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.91em;"><span></span></span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p>
<p>显然，<span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">{</mo><msub><mi>f</mi><mi>h</mi></msub><mo>+</mo><mn>1</mn><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">\{f_h + 1\}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">{</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.1076em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">h</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">1</span><span class="mclose">}</span></span></span></span> 是一个斐波那契数列。我们可以算出它的通项为</p>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>f</mi><mi>h</mi></msub><mo>=</mo><mfrac><mrow><mn>5</mn><mo>+</mo><mn>2</mn><msqrt><mn>5</mn></msqrt></mrow><mn>5</mn></mfrac><mo stretchy="false">(</mo><mfrac><mrow><mn>1</mn><mo>+</mo><msqrt><mn>5</mn></msqrt></mrow><mn>2</mn></mfrac><msup><mo stretchy="false">)</mo><mi>h</mi></msup><mo>+</mo><mfrac><mrow><mn>5</mn><mo>−</mo><mn>2</mn><msqrt><mn>5</mn></msqrt></mrow><mn>5</mn></mfrac><mo stretchy="false">(</mo><mfrac><mrow><mn>1</mn><mo>−</mo><msqrt><mn>5</mn></msqrt></mrow><mn>2</mn></mfrac><msup><mo stretchy="false">)</mo><mi>h</mi></msup><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">f_h = \frac{5+2\sqrt{5}}{5} (\frac{1+\sqrt{5}}{2})^h + \frac{5-2\sqrt{5}}{5} (\frac{1-\sqrt{5}}{2})^h - 1
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.1076em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">h</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.2702em;vertical-align:-0.686em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.5842em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">5</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">5</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">2</span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9072em;"><span class="svg-align" style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord" style="padding-left:0.833em;"><span class="mord">5</span></span></span><span style="top:-2.8672em;"><span class="pstrut" style="height:3em;"></span><span class="hide-tail" style="min-width:0.853em;height:1.08em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.08em" viewBox="0 0 400000 1080" preserveAspectRatio="xMinYMin slice"><path d="M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1328em;"><span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mopen">(</span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.5842em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">2</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9072em;"><span class="svg-align" style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord" style="padding-left:0.833em;"><span class="mord">5</span></span></span><span style="top:-2.8672em;"><span class="pstrut" style="height:3em;"></span><span class="hide-tail" style="min-width:0.853em;height:1.08em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.08em" viewBox="0 0 400000 1080" preserveAspectRatio="xMinYMin slice"><path d="M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1328em;"><span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8991em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">h</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:2.2702em;vertical-align:-0.686em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.5842em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">5</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">5</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">2</span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9072em;"><span class="svg-align" style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord" style="padding-left:0.833em;"><span class="mord">5</span></span></span><span style="top:-2.8672em;"><span class="pstrut" style="height:3em;"></span><span class="hide-tail" style="min-width:0.853em;height:1.08em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.08em" viewBox="0 0 400000 1080" preserveAspectRatio="xMinYMin slice"><path d="M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1328em;"><span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mopen">(</span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.5842em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">2</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9072em;"><span class="svg-align" style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord" style="padding-left:0.833em;"><span class="mord">5</span></span></span><span style="top:-2.8672em;"><span class="pstrut" style="height:3em;"></span><span class="hide-tail" style="min-width:0.853em;height:1.08em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.08em" viewBox="0 0 400000 1080" preserveAspectRatio="xMinYMin slice"><path d="M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1328em;"><span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8991em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">h</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span></span></p>
<p>当 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>h</mi></mrow><annotation encoding="application/x-tex">h</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">h</span></span></span></span> 足够大时</p>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>f</mi><mi>h</mi></msub><mo>∼</mo><mfrac><mrow><mn>5</mn><mo>+</mo><mn>2</mn><msqrt><mn>5</mn></msqrt></mrow><mn>5</mn></mfrac><mo stretchy="false">(</mo><mfrac><mrow><mn>1</mn><mo>+</mo><msqrt><mn>5</mn></msqrt></mrow><mn>2</mn></mfrac><msup><mo stretchy="false">)</mo><mi>h</mi></msup></mrow><annotation encoding="application/x-tex">f_h \sim \frac{5+2\sqrt{5}}{5} (\frac{1+\sqrt{5}}{2})^h
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.1076em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">h</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∼</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.2702em;vertical-align:-0.686em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.5842em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">5</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">5</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">2</span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9072em;"><span class="svg-align" style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord" style="padding-left:0.833em;"><span class="mord">5</span></span></span><span style="top:-2.8672em;"><span class="pstrut" style="height:3em;"></span><span class="hide-tail" style="min-width:0.853em;height:1.08em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.08em" viewBox="0 0 400000 1080" preserveAspectRatio="xMinYMin slice"><path d="M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1328em;"><span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mopen">(</span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.5842em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">2</span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9072em;"><span class="svg-align" style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord" style="padding-left:0.833em;"><span class="mord">5</span></span></span><span style="top:-2.8672em;"><span class="pstrut" style="height:3em;"></span><span class="hide-tail" style="min-width:0.853em;height:1.08em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.08em" viewBox="0 0 400000 1080" preserveAspectRatio="xMinYMin slice"><path d="M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1328em;"><span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8991em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">h</span></span></span></span></span></span></span></span></span></span></span></span></p>
<p>因此，对于有 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">n</span></span></span></span> 个节点的 AVL 树，其高度</p>
<p v-pre class="katex-block"><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>h</mi><mo>≤</mo><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mfrac><mrow><mn>1</mn><mo>+</mo><msqrt><mn>5</mn></msqrt></mrow><mn>2</mn></mfrac></msub><mrow><mfrac><mn>5</mn><mrow><mn>5</mn><mo>+</mo><mn>2</mn><msqrt><mn>5</mn></msqrt></mrow></mfrac><mi>n</mi></mrow><mo>=</mo><mi>O</mi><mo stretchy="false">(</mo><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">h \le \log_{\frac{1+\sqrt{5}}{2}}{\frac{5}{5+2\sqrt{5}} n} = O (\log n)
</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8304em;vertical-align:-0.136em;"></span><span class="mord mathnormal">h</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.2514em;vertical-align:-0.93em;"></span><span class="mop"><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3448em;"><span style="top:-2.5983em;margin-right:0.05em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight"><span class="mopen nulldelimiter sizing reset-size3 size6"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.0664em;"><span style="top:-2.656em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mtight">2</span></span></span></span><span style="top:-3.2255em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line mtight" style="border-bottom-width:0.049em;"></span></span><span style="top:-3.4143em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mbin mtight">+</span><span class="mord sqrt mtight"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9128em;"><span class="svg-align" style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mtight" style="padding-left:0.833em;"><span class="mord mtight">5</span></span></span><span style="top:-2.8728em;"><span class="pstrut" style="height:3em;"></span><span class="hide-tail mtight" style="min-width:0.853em;height:1.08em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.08em" viewBox="0 0 400000 1080" preserveAspectRatio="xMinYMin slice"><path d="M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1272em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.344em;"><span></span></span></span></span></span><span class="mclose nulldelimiter sizing reset-size3 size6"></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.6424em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em;"><span style="top:-2.2028em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">5</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">2</span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9072em;"><span class="svg-align" style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord" style="padding-left:0.833em;"><span class="mord">5</span></span></span><span style="top:-2.8672em;"><span class="pstrut" style="height:3em;"></span><span class="hide-tail" style="min-width:0.853em;height:1.08em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.08em" viewBox="0 0 400000 1080" preserveAspectRatio="xMinYMin slice"><path d="M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1328em;"><span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">5</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.93em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mord mathnormal">n</span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span></span></p>
<p>因此 AVL 树的高度为 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(\log n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">O</span><span class="mopen">(</span><span class="mop">lo<span style="margin-right:0.0139em;">g</span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>，始终能保持较好的平衡。</p>
</div>
<h2>树旋转</h2>
<p>在 AVL 树中，插入或删除节点会改变树的高度，产生失衡节点。
这时，我们用<mark>树旋转</mark>来重新维护节点的平衡。</p>
<p>由于修改前 AVL 的节点平衡因子大小不超过 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn></mrow><annotation encoding="application/x-tex">1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span>，如果平衡被破坏，
则失衡节点的平衡因子只可能是 <span v-pre class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>±</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">\pm 2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">±</span><span class="mord">2</span></span></span></span>。</p>
<h3>左旋与右旋</h3>
<p>根据旋转方向的不同，我们可以把树旋转分为<mark>左旋</mark>和<mark>右旋</mark>。</p>
<p>左旋是将父节点经旋转变成左儿子，而其原先的右儿子变成新的父节点的过程。</p>
<div style="text-align:center"><p></p>
</div><p>右旋则与左旋相反，是将父节点变成右儿子，原先的左儿子变成新的父节点的过程，相当于做一次左旋的逆变换。</p>
<h3>不同旋转策略的采用</h3>
<p>我们自底向上研究节点变化后的 AVL 树，找到第一个失衡节点，称该节点为 node。
该节点必有子节点，称其中高度较大的子节点为 child。</p>
<p>根据 node 与 child 平衡因子情况的不同，我们要采取不同的旋转策略。</p>
<p>| 失衡节点的平衡因子 | 子节点的平衡因子 | 所属情况 | 应采取的旋转方法 |
| :</p>
]]></content>
    <published>2025-01-21T02:23:48.000Z</published>
  </entry>
  <entry>
    <title type="text">2024 年度总结</title>
    <id>https://sunirein.tech/blog/7lg93ww2/</id>
    <link href="https://sunirein.tech/blog/7lg93ww2/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>第一次写年度总结，也不知道该写点什么，就随便写写吧。</p>
]]></summary>
    <content type="html"><![CDATA[<p>第一次写年度总结，也不知道该写点什么，就随便写写吧。</p>
<!-- more -->
<h2>2024 年的三大主线</h2>
<p>纵观 2024 一整年，我的人生轨迹基本可以分为三条清晰的主线：<strong>娱乐</strong>、<strong>开发</strong>与<strong>博客</strong>。</p>
<h3>娱乐</h3>
<p>与以往相比，2024 年我的娱乐方式和时间都极大地扩展了。
这也是相比高中，大学散漫生活给我带来的最大的变化。</p>
<h4>番剧</h4>
<p>一直以来，我都自认为只算是半个二次元。
虽然我喜欢二次元文化，但是我看过的二次元番剧、小说可谓少之甚少。
而今年，我就很好地补足了这个短板。</p>
<p>2024 年，我全年总共看番 130 部，其中上半年主要是看一些近几年来的新番，
下半年则转战各种老番，看番频率也越来越高。
最频繁的时候我一天差不多要看完两部番。
在平时学习中我居然可以挤出这么多时间，现在来回顾我还会觉得很震惊。
那些我以前一直听说过的知名番剧——《龙与虎》、凉宫春日系列、物语系列等都在今年成功补完了。</p>

<h4>网络小说 -&gt; 轻小说</h4>
<p>从小到大，我都是看网络小说长大的。
以前最疯狂的时候，差不多一周就要看完一部三四百万字的网文。
不过今年开始，我不知为啥慢慢失去了看网文的欲望了。
年初的时候我还有看几部网文，后面基本就没看了。</p>
<p>受到番剧的影响，我转而开始看起了以前很少看的轻小说。
最开始看的那些轻小说都是从动漫被吸引过来的，包括七月新番里的《败犬》、《义妹》和《小市民系列》这几部。
相比于改编后的动漫，轻小说原著有着更丰富的细节，给我带来了很多与直接看动漫不同的体验。
后面我就逐渐被这个体裁吸引了，也开始阅读一些纯轻小说作品。</p>
<p>要说为什么会发生这个转变，我觉得可能跟<strong>娱乐媒介的变化</strong>有关。
高中的时候我的主要媒介是手机，看网络小说就非常合适。
而现在我手机用得越来越少了，平时更多是用电脑和电纸书。
在我看来，这两者都不太适合看网络小说。
电脑更适合看番剧或其他影视作品，而电纸书则适合阅读出版书以及轻小说。
不过这也只是我的个人看法。</p>
<h4>游戏</h4>
<p>对我来说，游戏方面最大的变化应该是找到了一起玩游戏的伙伴，从单机变成了多人联机。</p>
<p>一直以来，我都是一个玩游戏三分钟热情的人。
可能突然就迷上了某个游戏，一下子就非常热爱这个游戏，把自己所有的空闲时间都投入其中。
不过，在玩了几天后，出于某种契机，我暂时中断了玩游戏，之后我就再也没碰过那个游戏了。
光今年我这样突然爱上然后抛弃的游戏就有原神、崩铁、七日杀、泰拉瑞亚等。
唯一突破这个周期的游戏只有 MC，我已经玩了十几年，而且应该会一直玩下去。</p>
<p>不过，今年我迎来了改变这个周期的契机：我有了和我一起玩游戏、一起开黑的伙伴。
很多游戏本身对我来说事实上并没有那么大吸引力，我自己一个人有空的话一般也不会去玩。
但是，和朋友联机，我却有了一直玩下去的兴趣。
尽管联机的过程中会遇到网络延迟、网络不稳定等各种问题，体验可能不如单机，
但比起游戏，我更享受的是这种朋友联机本身给我带来的快乐。
难怪有人会说：<strong>“最好的游戏配置就是朋友”</strong>。</p>
<h3>开发</h3>
<p>我喜欢开发各种程序，享受创造程序给我带来的乐趣。当然，目前来说，我的开发中遇到最多的还是各种失败。</p>
<h4>几次失败的经历</h4>
<p>以前我就有过好几个写着写着就没动力写下去了的项目，今年也不例外。</p>
<p>今年我第一个失败的项目是 <mark>pstring</mark>。
项目名是仿照 Github 上的项目 <a href="https://github.com/imageworks/pystring" target="_blank" rel="noopener noreferrer">pystring</a> 取的，主要内容是在 <mark>C++</mark> 中实现 <mark>Python</mark> 的字符串 API。
当时想写这个项目主要是因为觉得它简单，想拿它来练手，但是没想到字符串的 API 也有那么多讲究。
我之前这些 API 用得也比较少，本身对它们就不怎么熟悉，最后就放弃了这个项目。</p>
<p>第二个我打算开发的项目是一个 GUI 程序——<mark>bilixUI</mark>。
当然并不是从头造起，而是给一个现成的命令行程序 <a href="https://hfrost0.github.io/bilix/" target="_blank" rel="noopener noreferrer">bilix</a>，写一个 GUI 界面。
因为我平时也经常用它来下载 B 站的视频，所以这样一个程序对我来说还是有些帮助的，感觉写起来会更有动力。
这个项目最终失败的原因有两个：一是 bilix 后面缺乏维护，很多 B 站视频下载不了了；
二是 bilix 提供的 API 交互并不友好，不方便对它进行包装。</p>
<p>之后我还有试过另一个项目——<mark>inicpp</mark>，即写一个 <mark>ini</mark> 文件的解析器。
不过由于我野心太大，想要给这个解析器添加过多的自定义配置，使得它后面越来越臃肿，最后放弃了。</p>
<h4>目前正在开发的项目</h4>
<p>在 bilixUI 失败后，本来我是有“要不干脆我自己来写一个 bilix”的想法。
不过最终我放弃了这个想法，因为已经有了更成熟的工具——<a href="https://github.com/yt-dlp/yt-dlp" target="_blank" rel="noopener noreferrer">yt-dlp</a>。
它不仅功能更强大，而且还提供了非常友好的输出选项，便于外部程序的交互。
在替代 bilix 使用了一段时间后，我决定来给它写一个 GUI 界面——<mark>yt-dlp-web</mark>。</p>
<p>在开发 bilixUI 的时候，我选择用纯 C++ 的方式来开发，使用 <mark>OpenGL3</mark> + <mark>ImGui</mark> 框架。
当时我只熟悉 C++ 这门语言，对它有种一种偏爱。
但是用 C++ 来开发 UI 框架非常痛苦，而且 UI 界面本身也不是很好看。
因此这次开发我选定了 <a href="https://webui.me/" target="_blank" rel="noopener noreferrer">WebUI</a>，使用 C++ 后端配合 Web 前端的方式来开发。</p>
<p>前后端分离的开发模式非常舒服。
从没有写过前端项目的我在这次开发过程中不断学习，也增长了很多前端知识。
我学习了 <mark>TypeScript</mark>，学习了 <mark>Vue3</mark>，学会了用 <mark>pnpm</mark> 来管理项目。
自我开始这个项目来已经一个多月了，基本上我每天都会花一两个小时以上的时候来投入开发。
这也是我有史以来开发的最用心的项目，真心希望这个项目最后不要烂尾。</p>
<h4>项目开发中学到了什么</h4>
<p>对我来说，项目开发是一种乐趣，也是一种学习的方式。</p>
<p>首先，我学到了很多项目管理的知识。
我知道了如何写合适的单元测试，如何用 <mark>Git</mark> 来管理项目，
我知道了如何更好地利用 <mark>Github</mark> 的各种功能——PR、Github Action 等——来辅助管理好自己的项目。
我知道了如何管理项目中的各种资源文件，知道了那些在实际开发项目中会遇到的具体的问题。
我知道了如何写出更好的、更容易维护的代码，知道了如何通过调试来解决问题。
这些都是只能在实践中学习的，仅靠书本知识很难学会的东西。</p>
<p>此外，项目开发也是促使我学习各种知识的动力。
计算机的知识领域众多，五花八门，再怎么学习也不可能样样精通。
只是通过知识的学习，一个是容易忘记，一个是很难有继续深入的动力。
而在项目中学习，能够切实感受到自己学习的反馈，可以促使自己不断钻研。
例如，正是为了开发 yt-dlp-web，我迈向了自己未成涉足的前端领域，
迈向了自己不甚了解的多线程开发，受益匪浅。</p>
<h4>一些项目开发的心得</h4>
<ul>
<li>
<p><strong>要开发有用的、自己有需求的项目，而不是制作一个“精致的玩具”。</strong></p>
<p>在开发项目之前，不妨先问下自己：这个项目真的有需求吗？我会使用这个项目吗？
如何单纯只是为了学习某样东西而开发，很难享受到项目开发带来的乐趣，也很难有继续深入开发的动力。
而且，连自己都不用的项目，只是自娱自乐罢了。</p>
</li>
<li>
<p><strong>在实践中学习。</strong></p>
<p>以前我在开发前总是会犹豫，觉得自己的技术还不够，应该等技术再好一点再来写这个项目，可这样做的结果就是我最后什么项目也开发不出来。
没有人能在一开始就做到最好，也没有人规定一个项目必须全部是最好的。
如果后面技术精进了，发现前面的设计有问题，<strong>重构</strong>就好了呗。
只要开始写了，在实践中得到的成长会比光学习纸面知识得到的多很多。</p>
</li>
<li>
<p><strong>敢于重构，勤于重构。</strong></p>
<p>不要妄想有一套设计能一直通用到最后。
随着项目进度的推移、项目规模的扩大、或是个人见识的增长，总能发现有一些设计不太实用了，这时候就该<strong>重构</strong>了。
臃肿的、守旧的设计只会给开发添加麻烦，重构方能给项目带来新的生命力。</p>
<p>我在开发 yt-dlp-web 的时候，前端页面就经历了“原生 Web 技术 -&gt; TypeScript -&gt; Vue3 -&gt; NaiveUI 框架”这几次技术框架方面的更新，
小的重构更是不胜枚举。
对我来说，重构并没有给开发带来多大的麻烦，反而我的开发效率在每次重构后都有很大提升。
而且也正是我在原先的框架上遇到的问题，才使我知晓了重构的方向，也学到了很多东西。
这个重构前的开发过程也是必需的。</p>
</li>
<li>
<p><strong>拒绝过度设计。</strong></p>
<p>要明确自己项目开发的方向，先开发好眼前的功能，再去着眼未来。
要添加一个功能时，先问问自己：这个功能真的需要吗？你现在有能力做好这个功能吗？
在这个基础上，再去思考项目的发展规划。</p>
</li>
</ul>
<h3>博客</h3>
<p>2024 年，我终于实现了我长期以来的愿望——有一个自己的个人博客。</p>
<h4>博客形式的发展</h4>
<p>很久以前，当第一次接触 <mark>HTML</mark> 的时候，我就有了想要一个个人博客的想法。
不过当时的我还能稚嫩，只懂得用纯 HTML 来写网页，而且也只能在本地浏览，最后没坚持多久就放弃了。
后面我也接触到了一些博客平台，也尝试过在博客园写了几篇文章，但都不太满意。</p>
<p>今年 3 月，我接触到了 <mark>Obsidian</mark>，知道了有很多人用 Obsidian 写文章，于是我也开始尝试写一些文章。
不过它毕竟是用来记笔记的，而不是为博客设计的，有很多功能不尽人意。
加上它是在本地运行的，虽然也能部署到网络上，但是很麻烦，最终我就放弃了。</p>
<p>到了 8 月，正值暑假，在家无聊，我开始研究起了个人博客。
刚好阿里云也有给学生提供免费的云服务器试用，感觉试一试我不错，我就开始搭建起了我的个人博客。
当时选择的是 <mark>Hexo</mark> 框架，外加 <mark>Hexo Next</mark> 主题，
虽然主题本身不太美观，但我当时的想法是认为博客最重要的还是写文章，主题什么的外在不太重要。
<s>事实证明这个想法是错的。</s>
虽说如此，这也是我最成功的一次，成功搭建起了一个个人博客，并写了一定的文章。
至于域名、备案之类的相关事项，也在这段时间学习并弄好了。</p>
<p>不过，实践证明，当时框架的选择过于草率，Hexo 我越用越不爽。
于是，在年末的时候，我选择，也就是我现在的博客。
这次的迁移在我看来还是挺满意的，至于之前的文章没迁移，则是因为我打算有时间重新修改下里面一些稚嫩的观点，润色一遍再发布。</p>
<h4>为什么要写博客</h4>
<p>对我来说，写博客的主要目的有两个：<strong>记录生活</strong>、<strong>督促自己</strong>。</p>
<p>一直以来，我都有写日记的习惯。
这起源于我多次发现自己经常遗忘了很多重要的事情，因此想写日记，记录自己度过的时光。
但日记的时间单位终究还是太短了，一两天的时间也很难总结出一些东西来。
博客就可以让我随心地写一些文章，自由地表达和分享自己的观点。
我相信，以后的我回味我现在写的文章，一定是一件很有趣的事。</p>
<p>同时，博客也是一种督促我学习的方式。
要写一篇博客的文章，分享技术，就需要我对要分享的内容有更深入的了解，可以形成自己的观点，
而不是只知其表面，这样也写不出什么有趣的、有用的文章。
定期地写一些博客文章，就相当于让自己有不断学习新事物、探索新事物的动力。
而博客中这些成长的记录也是一种很好的正反馈，是我没有虚度时光的证明。</p>
<h4>个人博客与博客平台的区别</h4>
<p>个人博客在我看来，象征着完全的控制和极大的自由。
我可以随心随意定制自己的博客界面，设计各种功能。
我可以相对自由地表达自己的观点。
我对自己的数据有完全的控制权，不用担心哪天平台倒闭我的数据也跟着消失了。</p>
<p>个人博客对个人的技术要求也会更高，网站的众多事项都需要你亲自来管理。
博客托管在互联网上需要一台云服务器，有一定的经济成本。
此外，个人博客也代表着失去了平台本身的流量。</p>
<p>不过，我写文章其实主要是为了自己。
至于让别人看到我的文章，对我来说其实更算是添头——有当然很好，没有也没关系。
搭建个人博客，在我看来还是很值得的。</p>
<h2>零散分布的支线</h2>
<p>把这些归类为支线并不代表它们就不重要，只是它们相对比较零散，不像主线那样延续，能够撑起我生活的一面。</p>
<h3>宝藏 APP——人升</h3>
<p>在今年开头的时候，我发现了一个宝藏手机 APP——<a href="https://www.lifeupapp.fun/zh/index.html" target="_blank" rel="noopener noreferrer">人升</a>。
我可以在这个 APP 中自定义任务，每个任务都有一定的奖励和惩罚；
完成任务就可以获得金币，可以在商城里购买一些自定义的物品。</p>
<p>我最开始其实是把它当成一个待办事项软件的，但后面发现它其实更适合用来辅助习惯的养成：
可以设计一系列不同周期任务来辅助你养成某些习惯，用金币和任务没能完成的惩罚来表明不同任务的优先级；
可以在商城中设计一系列商品，例如打一小时游戏、吃雪糕等，用完成人物的金币来兑换。
这能够很好地约束你的行为，使你更加自律。</p>
<p>有的人会认为，这些都是一种自我约束，自律的人自然就能做到，不自律的人也不会去遵守。
最开始我也是这么想的，只约束了我自己的部分行为，一些我不想约束的行为也不会放在这里面。
但随着时间推移，我越来越适应它来控制我的行为习惯，逐渐把更多的事情也放进它的管理之中。
我和它在磨合的过程中，也变得越来越自律。
我想，哪怕对于本来就很自律的人来说，有一个辅助记录自己目标的工具，也是很重要的吧。</p>
<h3>云服务器</h3>
<p>今年 8 月快结束的时候，我在阿里云上购买了一台 2 核 2G 的云服务器。
当时我只是把这台服务器作为搭建个人博客的副产物使用，后面觉得只是用来托管博客有点太浪费了，就部署了一些别的服务。</p>
<p>在这个过程中，我也学习了 <mark>Docker</mark> 和 <mark>Nginx</mark> 等工具的使用方法，学到了一些服务器运维方面的知识。
相比之下，我觉得 99 元一年的成本还是挺值得的，更别说前三年还有学生优惠。
不过可惜服务器的配置较低，有条件的话还是自己搭建个人服务器会比较划算，可玩性也更高。</p>
<h3>自组移动机械硬盘</h3>
<p>我平时一直有把要看的番剧、影视作品之类的下载下来的习惯 <s>（所谓收藏癖）</s>。
随着资料的增大，原有的硬盘空间有些捉襟见肘了。
于是，3 月份的时候我花四百大洋买了一个 4T 的机械硬盘，顺带还买了硬盘盒，组装成一个移动机械硬盘。。</p>
<p>不过，硬盘装好后平时却一直是在吃灰的状态。
因为我主要存放的是一些冷数据，也就定期将看完的视频转移过去的时候会拿出来用一下，以及偶尔存放一些数据备份。
一年下来，4T 的容量只用了 700G 左右，着实浪费。</p>
<p>机械硬盘盒首先体积比较大，还需要外置电源，而且平时使用也会产生噪音，不适合长时间供电，使用起来还是不太方便。
如果要搭我这样的影视库感觉自建 NAS 会更合适，可惜我在学生宿舍，没有组装 NAS 的条件。</p>
<h3>RSS</h3>
<p>要说 2024 年有哪些技术对我的生活影响最大，<mark>RSS</mark> 就绝对是其中一门。</p>
<p>在以前，我想获取每日最新的资讯更新，需要挨个点开不同平台的首页，再从中筛选哪些是我没有看过的。
这个过程需要大量的人工操作，不仅繁琐，而且很容易遗漏。
另外，很多优质的独立网站可能几周甚至几个月才更新一次，不定期去查看也很浪费精力。</p>
<p>RSS 就很好地解决了这个问题，使我能够统一在 RSS 阅读器中接受不同平台的资讯。
很多平台都有提供专门的 RSS 服务，我可以直接订阅使用。
没有提供官方 RSS 服务的平台，也可以依靠 <a href="https://rsshub.app/" target="_blank" rel="noopener noreferrer">RSSHub</a> 来捕获。
就连微信公众号这种相对封闭的平台，都可以凭借 <a href="https://github.com/cooderl/wewe-rss" target="_blank" rel="noopener noreferrer">WeWeRss</a> 抓取。
同时，依靠在服务器上自建的 <a href="https://tt-rss.org/" target="_blank" rel="noopener noreferrer">Tiny Tiny RSS</a>，我还可以在不同的设备间同步阅读进度。
不过，目前过多的 RSS 订阅也造成了一定的<strong>信息过载</strong>，需要我后续再去做一些筛选工作。</p>
<h3>AI</h3>
<p>早在之前 AI 刚火爆的时候，其实我就有在关注 AI 了。
不过当时我并不觉得 AI 能给我带来什么实际的好处，我对 AI 绘画、AI 聊天之类的也不感兴趣，就没继续关注。
直到今年，我突发兴致想去试下 <mark>Github Copilot</mark>，方相见恨晚。</p>
<p>这几个月使用下来，我对 AI 的作用也有了比较清晰的定位：一种增强的更方便的辅助工具。
Github Copilot 虽然不能帮我写出真正实用的项目代码，但它可以帮我完成很多枯燥的代码工作，提供更强的代码补全。
ChatGPT、Claude 等知名语言模型更像是一个增强的搜索引擎，能极大提高我检索信息的效率，
而不是作为一种有创造力的工具。
在这些定位的基础上，我对 AI 的使用体验还是非常满意的，它确实很大程度提高了我的效率。</p>
<h3>Linux 上的实用软件</h3>
<p>作为一个 Linux 爱好者，我的日常使用环境都是 Linux 桌面系统。
今年接触到了许多好用的软件，极大提高了我在 Linux 上的使用体验。</p>



<h3>那些半途而废的“宏伟大计”</h3>
<p>除此之外，今年我立下了很多“宏伟大计”，最终却因总总原因没有实施，甚是可惜。</p>
<ul>
<li>
<p>制定良好的健身计划，积极锻炼身体。</p>
</li>
<li>
<p>考到驾照，体验自驾游。</p>
</li>
<li>
<p>学习唱歌，培养音乐素养。</p>
</li>
<li>
<p>不要总是宅在宿舍/家里，定期来一次简单的旅行。</p>
</li>
<li>
<p>去电影院看一次电影。</p>
</li>
<li>
<p>...</p>
</li>
</ul>
<h2>展望新的一年</h2>
<p>回顾完过去，立足于当下，接下来就要说下我对今年的期望了。
这里面，有一些是我去年想做，却没能做成的，有一些则是我对自己的期许。</p>
<ul>
<li>
<p>开源节流，克制冲动消费。</p>
</li>
<li>
<p>突破交际圈，维护好原有的人际关系，然后尝试向外扩展。</p>
</li>
<li>
<p>保持好一定的博客更新频率，最好每周都能写点东西。</p>
</li>
<li>
<p>继续开发并完善我对 <a href="https://github.com/sunirein/yt-dlp-web" target="_blank" rel="noopener noreferrer">yt-dlp-web</a> 项目。</p>
</li>
<li>
<p>学习了解 AI 领域，尝试做一些 AI 项目。</p>
</li>
<li>
<p>多参加竞赛，给自己一点挑战。</p>
</li>
<li>
<p>了解行业现状，明确自己未来的发展方向。</p>
</li>
</ul>
<p>顺带 <a href="#%E9%81%97%E6%86%BE">2024 年那些没能完成的遗憾</a>，今年我也希望能努力把它们实现。</p>
<p>希望 2026 年，我回望过去一年的时候，脸上会是满意的笑容。</p>
]]></content>
    <published>2025-01-03T14:19:24.000Z</published>
  </entry>
  <entry>
    <title type="text">博客迁移</title>
    <id>https://sunirein.tech/blog/p1wolfdw/</id>
    <link href="https://sunirein.tech/blog/p1wolfdw/"/>
    <updated>2025-10-14T00:37:40.000Z</updated>
    <summary type="html"><![CDATA[<p>今天将博客从 <mark>Hexo</mark> 迁移到了 <mark>Vuepress</mark>，有所感慨，故作此文。</p>
]]></summary>
    <content type="html"><![CDATA[<p>今天将博客从 <mark>Hexo</mark> 迁移到了 <mark>Vuepress</mark>，有所感慨，故作此文。</p>
<!-- more -->
<h2>为何当初选择了 <mark>Hexo</mark></h2>
<p>我开始写个人博客是在今年 8 月，但想写个人博客的念头却是很早就有了。
当时 Hexo 很流行，我也一直有耳闻它的大名。
今年 8 月我开始想写博客的时候，便去了解了下目前流行的博客框架，
知道了 <mark>Hexo</mark>、<mark>WordPress</mark>、<mark>Hugo</mark>、<mark>Vuepress</mark> 这几个知名框架。</p>
<p>WordPress 首先就被我排除掉了，我不喜欢它的臃肿，而且它对 <mark>Markdown</mark> 也不友好。
当时我对 <mark>Vue</mark> 也不了解，当然就没有选择 Vuepress。
最终剩下的框架里，我选择了相对更知名的 Hexo。
我看过很多人用 Hexo 写博客，也听说 Hexo 的教程很多，遇到问题好解决，于是就选择了它。</p>
<p>事实证明这个选择过于草率，这给我后续写博客也带来了很多麻烦。
不过现在这么想也算是事后诸葛亮了。</p>
<h2>我用 <mark>Hexo</mark> 面临的问题</h2>
<h3>年代久远，缺乏生命力</h3>
<p>Hexo 毕竟是一个“年龄”非常大的框架了，也有了很多历史沉积带来的问题。
一方面网上的教程时间跨度大，很多老教程里面的方法现在已经不适用了，
新教程讲的也可以是一些老方法，不断试错的过程非常痛苦。
另一方面，很多以前知名的插件现在也可能不维护了，出现了很多问题，
而新的替代品则很难找，生态缺乏生命力。</p>
<p>Hexo 自身的语法能力有限，缺乏如代码高亮、数学公式等这些博客常用的功能。
而官方文档对这部分的描述又过于简单，需要自己去摸索，非常痛苦。
而且 Hexo 默认的 <mark>Markdown</mark> 渲染引擎也不太行，没办法进行扩展，
第三方的扩展引擎挺多也有年久失修了。
当初为了解决这个问题我折腾了很久，其中艰辛，不堪回首。</p>
<p>相比之下，Vuepress 的生态更加活跃，文档也更加完善，
语法高亮、数学公式等功能可以开箱即用，自带的扩展语法也足够强大，
减少了很多不必要的折腾。</p>
<h3>配置复杂</h3>
<p>Hexo 使用 <mark>YAML</mark> 作为配置文件，本身配置项繁多，每次使用都需要查文档。
一些配置项位置较深，查找麻烦，而且配置项之间的关系也不太清晰。
不同插件之间的配置也不太统一，风格多样。</p>
<p>Vuepress 使用 <mark>TypeScript</mark> 作为配置文件，配置项清晰，且可以通过 TypeScript 的类型检查来减少配置错误，
使用起来相当方便。</p>
<h3>功能有限</h3>
<p>Hexo 本身只是一个博客框架，只适合用来写博客。
最初我只是想要写简单的个人博客，倒也无所谓。
但最近我的需求有所增加，想要把这个博客发展成我的个人网站，扩展一些非博客的功能。
Hexo 自然就不太适合了。</p>
<h3>缺乏足够的扩展性</h3>
<p>Hexo 中的语法扩展能力有限，大多都是通过纯字符串的形式来实现。
例如我想要写多标签页，就需要<code>{% tabs title %} ... {% endtabs %}</code>这样的语法，
一旦博客内容多了，可读性骤然下降。
而 Vuepress 可以通过 Vue 组件来实现扩展原生语法，
功能更强，可读性也会更高。</p>
<h2>最终选择 <mark>Vuepress</mark> 的原因</h2>
<p>除了上面提到的 Vuepress 的优点外，促使我最终选择 Vuepress，
主要还是我对前端技术了解的加深。</p>
<p>以前的我对前端技术了解甚少，只知道 <mark>HTML</mark>、<mark>CSS</mark>、<mark>JavaScript</mark> 这些基础知识。
最近我在写一个项目，需要用前端来实现用户界面，于是就开始学习前端技术。
在这个过程中，我逐渐了解了 <mark>Vue</mark> 这个前端框架，也写过很多 <mark>Vue</mark> 组件。
知道得多了，我对这门技术也就更加感兴趣了，最终就促使我选择了 Vuepress。</p>
<p>如果让今年 8 月的我来选择，我可能还是会选择 Hexo。
毕竟当时的我还真是个前端小白，别说 Vue 了，就连 <mark>TypeScript</mark>，
甚至用 <mark>Npm</mark> 来管理依赖都不会。</p>
<h2>迁移过程</h2>
<p>从我决定迁移博客到搭建好完整的 Vuepress 博客，只用了 2 个小时不到的时间。
这固然也有我对前端了解加深的原因，但也说明了 Vuepress 的易用性。
开箱即用的功能，清晰的文档，强大的扩展性，给我留下了很好的印象。</p>
<p>我之前写的博客中用到了很多 Hexo 的特有语法，没办法直接迁移。
不过以我现在的眼光来看，当时博客里面写的内容也有些过时了，
我也打算趁这个机会重新整理一下博客内容。</p>
<p>之后我还会继续完善这个博客，增加一些新的功能，并分享下我部署个人网站的经验。
希望这个博客能够帮助到更多的人。</p>
]]></content>
    <published>2024-12-28T13:38:09.000Z</published>
  </entry>
  <entry>
    <title type="text">Google Test 非官方中文文档</title>
    <id>https://sunirein.tech/gtest/</id>
    <link href="https://sunirein.tech/gtest/"/>
    <updated>2026-05-22T02:20:52.608Z</updated>
  </entry>
</feed>