@@ -421,7 +421,7 @@ <h2 id="index-_1">前言</h2>
421
421
<blockquote>
422
422
<p><img src="../img/bulb.png" height="30px" width="auto" style="margin: 0; border: none"/> 本书还在持续更新中……要追番的话,可以在 <a href="https://github.com/parallel101/cppguidebook">GitHub</a> 点一下右上角的 “Watch” 按钮,每当小彭老师提交新 commit,GitHub 会向你发送一封电子邮件,提醒你小彭老师更新了。</p>
423
423
</blockquote>
424
- <p>更新时间:2024年10月31日 18:07:26 (UTC+08:00)</p>
424
+ <p>更新时间:2024年11月01日 12:54:16 (UTC+08:00)</p>
425
425
<p><a href="https://parallel101.github.io/cppguidebook">在 GitHub Pages 浏览本书</a> | <a href="https://142857.red/book">在小彭老师自己维护的镜像上浏览本书</a></p>
426
426
<h2 id="index-_2">格式约定</h2>
427
427
<blockquote>
@@ -13181,6 +13181,10 @@ <h3 id="design_gamedev-_3">封装在类内部</h3>
13181
13181
13182
13182
Game::instance.updatePlayers();
13183
13183
</code></pre>
13184
+ <blockquote>
13185
+ <p><img src="../img/warning.png" height="30px" width="auto" style="margin: 0; border: none"/> 警告:只定义在头文件中并使用 <code>inline</code> 这种写法,不适用于多 DLL 的情况!这会使 DLL 和 EXE 各自持有一份互不共通的 <code>instance</code>。如果需要在多 DLL 环境中使用这种饿汗模式单例,请乖乖<a href="#symbols">分离声明和定义</a>,别用 <code>inline</code> 了。</p>
13186
+ <p><img src="../img/question.png" height="30px" width="auto" style="margin: 0; border: none"/> 这是因为 Windows 中的每个 DLL 和 EXE 都是一座孤岛,互相不知道对方有没有这个符号,所以 <code>inline</code> 的效果从“全局只保留一份定义”变成在每个“孤岛”内各自在内部只保留一份,从而 DLL 和 EXE 各自一份,总共有两份了,互相内容不互通,从而不是单例模式。而 Linux 没有这个问题,因为 SO 动态库是运行时才由 <code>ld-linux.so</code> 完成链接的,SO 内部仍保有编译时产生的函数符号信息,为的是被可执行 ELF 加载进来以后,<code>ld-linux.so</code> 可以自动根据把可执行 ELF 和 SO 内部的 <code>call</code> 指令后的地址更新为加载后的符号的动态地址。而 Windows 的 DLL 中所有符号在编译时就已经被 <code>ld</code> 已经焊死,无法修改,这就是为什么 Windows 的每个 DLL 都会自动额外生成一个同名 LIB 文件,这个 LIB 里面实际上是一个个“插桩”函数,这些函数名字和 DLL 中的相同,但是函数的内容,是会动态 <code>LoadLibrary</code> 加载同名 DLL,并通过 <code>GetProcAddress</code> 动态获取所有 <code>dllexport</code> 的函数,而链接时候指定的实际上是原 DLL 对应的这个插桩 LIB,DLL 本身是无法被链接器链接的。</p>
13187
+ </blockquote>
13184
13188
<ol start="2">
13185
13189
<li>作为函数内部的 static 变量(懒汗模式)</li>
13186
13190
</ol>
@@ -13201,6 +13205,9 @@ <h3 id="design_gamedev-_3">封装在类内部</h3>
13201
13205
13202
13206
Game::instance().updatePlayers();
13203
13207
</code></pre>
13208
+ <blockquote>
13209
+ <p><img src="../img/warning.png" height="30px" width="auto" style="margin: 0; border: none"/> 警告:这种写法同样不适用于多 DLL 的情况!如果需要在多 DLL 环境中使用,请乖乖<a href="#symbols">分离声明和定义</a>。</p>
13210
+ </blockquote>
13204
13211
<h3 id="design_gamedev-_4">通用的单例模式模板</h3>
13205
13212
<pre><code class="language-cpp">template <class T>
13206
13213
inline T &singleton() { // 这里的 inline 可以省略,因为就地实现的模板函数自带 inline 效果
@@ -13214,6 +13221,9 @@ <h3 id="design_gamedev-_4">通用的单例模式模板</h3>
13214
13221
singleton<Other>().someMethod();
13215
13222
</code></pre>
13216
13223
<p>任何类型 T,只要以 <code>singleton<T>()</code> 形式获取,都能保证每个 T 都只有一份对象。(前提是你不要再 <code>T()</code> 创建对象)</p>
13224
+ <blockquote>
13225
+ <p><img src="../img/warning.png" height="30px" width="auto" style="margin: 0; border: none"/> 警告:这种写法同样不适用于多 DLL 的情况!如果需要在多 DLL 环境中使用,请乖乖<a href="#symbols">分离模板的声明和定义</a>。</p>
13226
+ </blockquote>
13217
13227
<h2 id="design_gamedev-_5">模板模式</h2>
13218
13228
<blockquote>
13219
13229
<p>注意:模板模式和 C++ 的模板并没有必然关系!模板模式只是一种思想,可以用模板实现,也可以用虚函数实现(大多反而是用虚函数实现的)</p>
0 commit comments