<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>EffectiveCpp on Mao's Blog</title><link>https://Mist2233.github.io/series/effectivecpp/</link><description>Recent content in EffectiveCpp on Mao's Blog</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Thu, 21 May 2026 10:32:00 +0800</lastBuildDate><atom:link href="https://Mist2233.github.io/series/effectivecpp/index.xml" rel="self" type="application/rss+xml"/><item><title>5-Implementations</title><link>https://Mist2233.github.io/posts/5-implementations/</link><pubDate>Thu, 21 May 2026 10:32:00 +0800</pubDate><guid>https://Mist2233.github.io/posts/5-implementations/</guid><description>&lt;p&gt;上一章讲的是接口设计的原则，而这一章关注具体的实现细节。&lt;/p&gt;
&lt;h2 id="item26-postpone-variable-definitions-as-long-as-possible"&gt;Item26: Postpone variable definitions as long as possible&lt;/h2&gt;
&lt;p&gt;本节告诉我们，尽可能推迟变量的定义位置。换言之，在程序逻辑不变的情况下，越晚定义变量越好。&lt;/p&gt;
&lt;p&gt;为什么？考虑到两个问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;开销问题&lt;/strong&gt;：变量定义太早，而程序由于某种原因没有继续执行（比如抛出异常）导致该变量未被使用，那么变量定义时调用构造函数的开销，相当于白费了。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码风格&lt;/strong&gt;：如果析构的位置不变，那么变量定义得越早，它的生命周期就越长，影响的代码范围就越广。而 coding 的一大原则就是“解耦”，尽可能减少每个部分对其他部分的影响。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以说，只有在你马上需要用到这个变量的时候，才去定义它，而不是提早定义。&lt;/p&gt;
&lt;p&gt;并且，最好在定义时就直接做完初始化的工作（开销更小），而不是先定义再赋值（开销更大）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;前者在构造时只需要调用一次对应的 constructor&lt;/li&gt;
&lt;li&gt;后者要调用 default constructor 和 copy assignment operator&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;作者的原话如下：&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“Not only should you postpone a variable’s definition until right before you have to use the variable, you should also try to postpone the definition until you have initialization arguments for it.” (Dulimov, p. 115)&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;意思是一定要“忍到”你能够初始化（initialization）这个变量之后，你再去定义（definition）它。一定要将两者绑定，才能实现性能和资源利用最大化。&lt;/p&gt;
&lt;p&gt;那么有人问了：如果在循环中需要使用某个变量，是在循环外部定义之后再在循环内部赋值好（方案 A），还是直接在循环内部构造好（方案 B）。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Approach A
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;some&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;dependent&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Approach B
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="nf"&gt;w&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;dependent&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;可以看下两种方案各自的开销：&lt;/p&gt;</description></item><item><title>4-Designs and Declarations</title><link>https://Mist2233.github.io/posts/4-designsanddeclarations/</link><pubDate>Sat, 09 May 2026 20:39:00 +0800</pubDate><guid>https://Mist2233.github.io/posts/4-designsanddeclarations/</guid><description>&lt;p&gt;本章将介绍设计和声明 C++ 接口的一些原则。&lt;/p&gt;
&lt;h2 id="item18-make-interfaces-easy-to-use-correctly-and-hard-to-use-incorrectly"&gt;Item18: Make interfaces easy to use correctly and hard to use incorrectly&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;核心思想&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;好的接口应该&lt;strong&gt;引导正确使用，阻止错误使用&lt;/strong&gt;。如果用户用错了，编译器应该报错，而不是运行时才发现 bug。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以，我们在设计接口的时候，最好多问自己一个问题：&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;如果别人用我的接口，最可能的几种用错方式是什么？我能不能在编译期就拦住，从而让用户正确地使用接口？&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;具体来说，主要有下面四个点需要检查：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;参数顺序容易搞混吗？ -&amp;gt; 用类型区分&lt;/li&gt;
&lt;li&gt;参数有合法范围吗？ -&amp;gt; 用 &lt;code&gt;enum&lt;/code&gt; 或者 &lt;code&gt;enum class&lt;/code&gt; 限定范围&lt;/li&gt;
&lt;li&gt;行为和内置类型一致吗？ -&amp;gt; 该 &lt;code&gt;const&lt;/code&gt; 就 &lt;code&gt;const&lt;/code&gt;，该重载运算符就重载&lt;/li&gt;
&lt;li&gt;涉及资源管理吗？ -&amp;gt; 返回智能指针，不要返回裸指针&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="参数顺序容易搞混吗"&gt;参数顺序容易搞混吗？&lt;/h3&gt;
&lt;p&gt;比如说，我有一个 &lt;code&gt;Date&lt;/code&gt; 类，它的构造函数需要三个参数：month-day-year。如果我全都定义成 &lt;code&gt;int&lt;/code&gt; 类型，那么在传错顺序时很难检查出来。&lt;/p&gt;
&lt;p&gt;所以，我可以自定义新的三个类：&lt;code&gt;Month&lt;/code&gt;、&lt;code&gt;Day&lt;/code&gt; 和 &lt;code&gt;Year&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 危险：三个 int，顺序很容易传反
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 改进：用类型区分
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Month&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;explicit&lt;/span&gt; &lt;span class="nf"&gt;Month&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Day&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;explicit&lt;/span&gt; &lt;span class="nf"&gt;Day&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Year&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;explicit&lt;/span&gt; &lt;span class="nf"&gt;Year&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Month&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Day&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Year&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这样，就可以轻松区分下面三种情况，用户很难传错参数：&lt;/p&gt;</description></item><item><title>3-Resource Management</title><link>https://Mist2233.github.io/posts/3-resourcemanagement/</link><pubDate>Sat, 02 May 2026 09:27:30 +0800</pubDate><guid>https://Mist2233.github.io/posts/3-resourcemanagement/</guid><description>&lt;p&gt;C++中，我们需要手动管理资源。最常见的资源就是动态分配的内存，但注意，它不是唯一的资源。其他资源还有：file descriptors, mutex locks, fonts and brushes in graphical user interfaces.&lt;/p&gt;
&lt;h2 id="item13-use-objects-to-manage-resouces"&gt;Item13: Use objects to manage resouces&lt;/h2&gt;
&lt;p&gt;核心思想：把资源放进对象里，用对象来管理资源，让对象的构造和析构来保证资源的获取与释放。&lt;/p&gt;
&lt;p&gt;其实，这就是 RAII。&lt;/p&gt;
&lt;p&gt;这是因为，C++没有垃圾回收机制，手动 &lt;code&gt;new&lt;/code&gt; / &lt;code&gt;delete&lt;/code&gt; 十分容易出错（比如 &lt;code&gt;delete&lt;/code&gt; 可能因为一些异常情况而没有被执行），所以我们需要一个规范化的获取和释放资源的流程。&lt;/p&gt;
&lt;h3 id="手动管理资源的做法"&gt;手动管理资源的做法&lt;/h3&gt;
&lt;p&gt;例如，下面的代码使用一个 factory function 来为所有继承自 &lt;code&gt;Investment&lt;/code&gt; 的类创建统一的调用接口（一个基类指针）。&lt;/p&gt;
&lt;p&gt;如果你手动管理资源（&lt;code&gt;new&lt;/code&gt; / &lt;code&gt;delete&lt;/code&gt;、&lt;code&gt;fopen&lt;/code&gt; / &lt;code&gt;fclose&lt;/code&gt;、&lt;code&gt;lock&lt;/code&gt; / &lt;code&gt;unlock&lt;/code&gt;），很容易出错：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Investment&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pInv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;createInvestment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// ... 中间可能有 return、异常、goto 等各种出口
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;pInv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 万一没走到这里？内存泄漏
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;当然，你可以说：啊我 coding 的时候很仔细的，不会犯这样的低级错误。&lt;/p&gt;
&lt;p&gt;但是，随着工程越来越大，代码越来越长，不规范的写法维护的成本也会越来越高。所以，为了确保资源一定能够安全释放，我们还是使用 RAII 的做法。&lt;/p&gt;
&lt;h3 id="raii-的做法"&gt;RAII 的做法&lt;/h3&gt;
&lt;p&gt;用一个对象来持有资源，对象的析构函数负责释放资源。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;auto_ptr&lt;/code&gt; 就是这样一个对象。它是一个智能指针，管理着一个裸指针；在自身析构时，会自动对自己管理的裸指针调用 &lt;code&gt;delete&lt;/code&gt;。&lt;/p&gt;
&lt;div class="callout callout-note"&gt;
&lt;p class="callout-header"&gt;再次回顾：&amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; 实际做了两件事情&lt;/p&gt;</description></item><item><title>2-Constructors, Destructors, and Assignment Operators</title><link>https://Mist2233.github.io/posts/2-constructors-destructors-and-assignment-operators/</link><pubDate>Tue, 28 Apr 2026 12:26:00 +0800</pubDate><guid>https://Mist2233.github.io/posts/2-constructors-destructors-and-assignment-operators/</guid><description>&lt;p&gt;构造函数、析构函数、赋值运算符，可以说是一个对象最重要的三个成员函数，它们分别掌管成员变量的出生、死亡以及变化。&lt;/p&gt;
&lt;h2 id="item5-know-what-functions-c-silently-writes-and-calls"&gt;Item5: Know what functions C++ silently writes and calls&lt;/h2&gt;
&lt;p&gt;虽然我们只是创建一个空类，没有写任何的函数，但是，编译器还是会默认声明一些特殊的成员函数。具体来说，包括：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;函数&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;默认构造函数&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Empty() = default;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;析构函数&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~Empty() = default;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;拷贝构造函数&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Empty(const Empty&amp;amp;) = default;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;拷贝赋值运算符&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Empty&amp;amp; operator=(const Empty&amp;amp;) = default;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;移动构造函数&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Empty(Empty&amp;amp;&amp;amp;) = default;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;移动赋值运算符&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Empty&amp;amp; operator=(Empty&amp;amp;&amp;amp;) = default;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;【补充知识】五法则&lt;/strong&gt;：&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;除了默认构造函数之外，剩下的五个特殊的成员函数，如果我们手动定义了其中一个，剩下的四个就都不会被编译器自动生成。这是因为，一旦我们需要手动定义这五个函数中的一个，就说明存在需要手动释放的资源（比如 &lt;code&gt;new&lt;/code&gt; 出的内存、打开的文件、拿到的锁等等）。既然有资源需要手动释放，那么默认的拷贝（浅拷贝）一定会出问题——两个对象会持有同一份资源，析构时双重释放。&lt;/p&gt;
&lt;p&gt;这个规则被称为“五法则”，其逻辑链是：
需要自定义某个函数 → 说明持有资源 → 拷贝/移动不能信任默认实现 → 五个都要自定义&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;回到正题。比如说，我们定义：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;但实际上，编译器生成的等价于：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 编译器实际生成的等价于
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Empty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这些函数都是 public 并且 inline 的，且只有它们在被实际调用的时候，编译器才会真正生成它们的定义（不是在声明时就生成）。&lt;/p&gt;</description></item><item><title>1-AccustomingYourselfToC++</title><link>https://Mist2233.github.io/posts/1-accustomingyourselftoc++/</link><pubDate>Thu, 23 Apr 2026 08:17:01 +0800</pubDate><guid>https://Mist2233.github.io/posts/1-accustomingyourselftoc++/</guid><description>&lt;h2 id="item1-view-cpp-as-a-federation-of-languages"&gt;Item1: View Cpp as a federation of languages&lt;/h2&gt;
&lt;p&gt;C++可以看成是下面四种不同语言的组合：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C 语言&lt;/li&gt;
&lt;li&gt;面向对象的 C++：主要针对封装、多态、继承、虚函数等等。&lt;/li&gt;
&lt;li&gt;模板 C++：主要针对泛型编程。&lt;/li&gt;
&lt;li&gt;The STL（标准模板库）：最常用且特别的模板库。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每种对应不同的策略，需要根据情况选择对应的策略。&lt;/p&gt;
&lt;div class="callout callout-note"&gt;
&lt;p class="callout-header"&gt;Note&lt;/p&gt;
&lt;div class="callout-body"&gt;&lt;p&gt;Rules for effective C++ programming vary, depending on the part of C++ you are using.&lt;/p&gt;&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="item2-prefer-consts-enums-and-inlines-to-define"&gt;Item2: Prefer &lt;code&gt;consts&lt;/code&gt;, &lt;code&gt;enums&lt;/code&gt; and &lt;code&gt;inlines&lt;/code&gt; to &lt;code&gt;#define&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;#define&lt;/code&gt; 在 preprocess 阶段完成，而前面三个关键字都是在 compile 阶段完成的。&lt;/p&gt;
&lt;p&gt;符号表：symbol table。这个概念和编译的关系是？符号表似乎就是一个存了所有变量名字的大表吧。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#define&lt;/code&gt; 出来的东西本质上不是变量，不会进到符号表里面，这给我们 debug 带来困难。&lt;/p&gt;
&lt;p&gt;代码体积更小（不会有多余的 copy），含义更加明确。&lt;/p&gt;
&lt;p&gt;两类特殊情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Constant pointer：需要指针值和指针指向的指都被定义成常量，这样这个常量才算真正的“常量”。&lt;/li&gt;
&lt;li&gt;Class-specific constants：限制一个常量的作用域在这个类内部，则这个常量是这个类的成员。并且，为了使得这个常量只有一个 copy，还要声明其为 &lt;code&gt;static&lt;/code&gt; 类型。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GamePlayer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;NumTurns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NumTurns&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;上面是对 &lt;code&gt;NumTurns&lt;/code&gt; 这个常量的 declaration，而不是 definition。这两个有什么区别吗？&lt;/p&gt;</description></item></channel></rss>