-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
137 lines (63 loc) · 427 KB
/
search.xml
File metadata and controls
137 lines (63 loc) · 427 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>考完还得记的数学知识(</title>
<link href="/2025/11/06/%E8%80%83%E5%AE%8C%E8%BF%98%E5%BE%97%E8%AE%B0%E7%9A%84%E6%95%B0%E5%AD%A6%E7%9F%A5%E8%AF%86/"/>
<url>/2025/11/06/%E8%80%83%E5%AE%8C%E8%BF%98%E5%BE%97%E8%AE%B0%E7%9A%84%E6%95%B0%E5%AD%A6%E7%9F%A5%E8%AF%86/</url>
<content type="html"><,,来源于 武汉大学数学与统计学院信息与计算科学系黄正华 老师个人网页,分享仅供学习参考交流,相关课程更多内容通过黄老师个人网站获取,网址: http%3A%2F%2Faff.whu.edu.cn%2Fhuangzh)</p><p>求逆矩阵:<a href="https://blog.csdn.net/u010551600/article/details/81504909">矩阵逆的三种求法-CSDN博客</a></p><h1 id="矩阵论:矩阵函数和矩阵求导"><a href="#矩阵论:矩阵函数和矩阵求导" class="headerlink" title="矩阵论:矩阵函数和矩阵求导"></a>矩阵论:矩阵函数和矩阵求导</h1><h2 id="矩阵函数介绍"><a href="#矩阵函数介绍" class="headerlink" title="矩阵函数介绍"></a>矩阵函数介绍</h2><p>矩阵函数与通常的函数类似,但是**因变量和自变量都为 $n$ 阶矩阵。**定义如下:</p><h3 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h3><p>设一元函数 $f(x)$ 能展开为 $x$ 的幂级数:</p><p>$$<br>f(x) = \sum_{k=0}^{\infty} a_k x^k<br>$$</p><p>其中 <strong>$R$ 表示该幂级数的收敛半径</strong>。当 $n$ 阶矩阵 $A$ 的<strong>谱半径 $\rho(A) < R$ 时</strong>,把<strong>收敛矩阵幂级数 $\sum_{k=0}^{\infty} a_k A^k$ 的和称为矩阵函数</strong>,记为 $f(A)$,即:</p><p>$$<br>f(A) = \sum_{k=0}^{\infty} a_k A^k<br>$$</p><p>通过以上定义和一些基本函数,可以导出<strong>矩阵指数函数</strong>和<strong>矩阵三角函数</strong>,并推得一系列等式。</p><hr><h2 id="函数矩阵对矩阵的导数"><a href="#函数矩阵对矩阵的导数" class="headerlink" title="函数矩阵对矩阵的导数"></a>函数矩阵对矩阵的导数</h2><p>我们已经在上一节中引入了矩阵函数的概念,类似地,也有微分和导数的定义。相较于标量导数,对矩阵函数的求导和微分较为复杂。在具体应用中,如梯度下降等算法,这部分内容具有重要意义,因此需要深入理解。</p><p>首先,我们将标量求导拓展到向量,对于 $\frac{\partial y}{\partial x}$,存在以下几种情况:</p><ol><li>$y$ 是标量:<ul><li>$x$ 是标量</li><li>$x$ 是向量(<strong>默认为列向量,即 $n \times 1$)</strong></li><li>$x$ 是矩阵</li></ul></li><li>$y$ 是向量:<ul><li>标量变元</li><li>向量变元</li><li>矩阵变元</li></ul></li><li>$y$ 是矩阵,同样有三种变元。</li></ol><p>简单理解:</p><ul><li>分子布局:<strong>分子是列向量形式,分母是行向量形式</strong></li><li>分母布局:<strong>分子是行向量形式,分母是列向量形式</strong></li></ul><hr><h3 id="向量化操作"><a href="#向量化操作" class="headerlink" title="向量化操作"></a>向量化操作</h3><p>在具体分析之前,先介绍矩阵的向量化操作。例如,对矩阵 $A$ 进行<strong>列优先展开:</strong></p><p>$$<br>\operatorname{vec}(A) = [a_{11}, a_{21}, …, a_{n1}, a_{12}, …, a_{nn}]^T<br>$$</p><hr><h3 id="向量变元的实值标量函数"><a href="#向量变元的实值标量函数" class="headerlink" title="向量变元的实值标量函数"></a>向量变元的实值标量函数</h3><ol><li><strong>行向量偏导(分子布局)</strong></li></ol><p>$$<br>\mathbf{D}_x f = \frac{\partial f}{\partial \mathbf{x}^\top} = \left[ \frac{\partial f}{\partial x_1} \quad \frac{\partial f}{\partial x_2} \quad \cdots \quad \frac{\partial f}{\partial x_n} \right]<br>$$</p><p> 2.<strong>梯度向量偏导(分母布局)</strong><br>$$<br>\nabla_x f = \frac{\partial f}{\partial \mathbf{x}} =<br>\begin{bmatrix}<br>\frac{\partial f}{\partial x_1} \<br>\frac{\partial f}{\partial x_2} \<br>\vdots \<br>\frac{\partial f}{\partial x_n}<br>\end{bmatrix}<br>$$</p><hr><h3 id="矩阵变元的实值标量函数"><a href="#矩阵变元的实值标量函数" class="headerlink" title="矩阵变元的实值标量函数"></a>矩阵变元的实值标量函数</h3><p>从标量求导到矩阵求导,就是<strong>分子的转置、向量化和分母的转置、向量化的各种组合。</strong></p><p>对于分子布局:</p><ul><li>分子:标量、列向量、矩阵<strong>向量化后的列向量</strong></li><li>分母:标量、<strong>列向量转置后的行向量</strong>、矩阵的转置矩阵、<strong>矩阵向量化后的列向量转置后的行向量</strong></li></ul><p>对于分母布局:</p><ul><li>分子:标量、列向量转置后的行向量、矩阵向量化后的列向量转置后的行向量</li><li>分母:标量、列向量、矩阵<strong>自身</strong>、矩阵向量化后的列向量</li></ul><p>在机器学习领域,常见的主要有以下三种形式:</p><ol><li><p><strong>梯度</strong></p></li><li><p><strong>Hesse矩阵</strong><br> $$<br> H(f) =<br> \begin{bmatrix}<br> \frac{\partial^2 f}{\partial x_1^2} & \frac{\partial^2 f}{\partial x_1 \partial x_2} & \cdots & \frac{\partial^2 f}{\partial x_1 \partial x_n} \<br> \frac{\partial^2 f}{\partial x_2 \partial x_1} & \frac{\partial^2 f}{\partial x_2^2} & \cdots & \frac{\partial^2 f}{\partial x_2 \partial x_n} \<br> \vdots & \vdots & \ddots & \vdots \<br> \frac{\partial^2 f}{\partial x_n \partial x_1} & \frac{\partial^2 f}{\partial x_n \partial x_2} & \cdots & \frac{\partial^2 f}{\partial x_n^2}<br> \end{bmatrix}<br> $$</p></li><li><p><strong>Jacobi矩阵</strong><br> $$<br> \begin{bmatrix}<br> \frac{\partial y_1}{\partial x_1} & \cdots & \frac{\partial y_1}{\partial x_n} \<br> \vdots & \ddots & \vdots \<br> \frac{\partial y_m}{\partial x_1} & \cdots & \frac{\partial y_m}{\partial x_n}<br> \end{bmatrix}<br> $$</p></li></ol><p>不难发现:</p><ul><li>若 $f(x)$ 是一个标量函数,则 <strong>Jacobi矩阵是一个向量</strong>,<strong>等于 $f(x)$ 的梯度,Hesse矩阵是一个二维矩阵</strong></li><li>若 $f(x)$ 是一个向量值函数,则 <strong>Jacobi矩阵是一个二维矩阵,Hesse矩阵是一个三维矩阵</strong></li><li><strong>梯度是 Jacobi 矩阵的特例,梯度的 Jacobi 矩阵就是 Hesse 矩阵</strong>,这其实就是<strong>一阶偏导与二阶偏导</strong>的关系</li></ul><hr><h2 id="自动求导与链式法则"><a href="#自动求导与链式法则" class="headerlink" title="自动求导与链式法则"></a>自动求导与链式法则</h2><p>在实际应用问题中,标量求导中的链式法则也可以拓展到向量乃至矩阵。在使用程序计算时,库函数一般采用<strong>自动求导</strong>方式,而不是一般的符号求导或数值求导。自动求导应用了链式法则,并将整个计算过程表示成一个<strong>无环图</strong>,其中包括两种模式:</p><ol><li>正向积累</li><li>反向传递</li></ol><p><strong>反向传递过程需要存储中间变量</strong>,导致内存复杂度比正向积累高。</p><p>其在实际场景中的主要应用就是神经网络,在神经网络的正向传播中,我们将输入数据通过网络层<strong>逐层传递,计算出最终的输出值</strong>。我们的目标是<strong>计算神经网络的输出 $y$ 和损失函数 $L$ 的值</strong>。而反向传播用于计算<strong>损失函数对所有网络参数的梯度</strong>。</p><p>在实际应用问题中,由于反向传播的链路过长,涉及到多次激活函数关于净输入的偏导数矩阵、当前层输入关于净输入的偏导数矩阵的连乘,如果这个矩阵的<strong>谱半径小于一</strong>,那么随着反向传播的进行,<strong>回传的梯度信号衰减</strong>地越厉害,这使得<strong>越是网络浅层的参数地梯度越微弱</strong>,那么<strong>其越难得到很好地更新</strong>。这就是<strong>梯度消失</strong>问题。</p><h1 id="概论"><a href="#概论" class="headerlink" title="概论"></a>概论</h1><h1 id="离散"><a href="#离散" class="headerlink" title="离散"></a>离散</h1>]]></content>
</entry>
<entry>
<title>计科相关自感知识点的一个一个补充</title>
<link href="/2025/07/25/%E8%AE%A1%E7%A7%91%E7%9B%B8%E5%85%B3%E8%87%AA%E6%84%9F%E7%9F%A5%E8%AF%86%E7%82%B9%E7%9A%84%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%A1%A5%E5%85%85/"/>
<url>/2025/07/25/%E8%AE%A1%E7%A7%91%E7%9B%B8%E5%85%B3%E8%87%AA%E6%84%9F%E7%9F%A5%E8%AF%86%E7%82%B9%E7%9A%84%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%A1%A5%E5%85%85/</url>
<content type="html"><![CDATA[<p>[toc]</p><h1 id="小知识点"><a href="#小知识点" class="headerlink" title="小知识点"></a>小知识点</h1><h2 id="绘图工具"><a href="#绘图工具" class="headerlink" title="绘图工具"></a>绘图工具</h2><p><strong>让ai生成</strong>uml(统一建模语言)和mermaid(类似Markdown的语法来创建和修改图表)</p><h2 id="文件"><a href="#文件" class="headerlink" title="文件"></a>文件</h2><ul><li><p>.csv :逗号分隔的⽂件,⽤于存储<strong>表格数据</strong>,可以⽤Excel打开</p></li><li><p>/ .avi :视频⽂件。</p></li><li><p>.tar / .gz :Linux下常⻅的压缩⽂件。</p></li></ul><h2 id="如何查看⽂件编码?"><a href="#如何查看⽂件编码?" class="headerlink" title="如何查看⽂件编码?"></a>如何查看⽂件编码?</h2><p>⽤VS Code或记事本打开⽂件,底部会显⽰⽂件编码(如UTF-8、GBK)。打开⽂件后,左上⻆ 文件 选择 <strong>另存为 就可以改变编码</strong></p><h3 id="x64与arm64处理器安装包"><a href="#x64与arm64处理器安装包" class="headerlink" title="x64与arm64处理器安装包"></a>x64与arm64处理器安装包</h3><p><code>x64</code> 架构长期以来一直是<strong>桌面计算机和服务器</strong>的主导架构,得益于其与旧 <code>x86</code> 应用程序的兼容性,以及其在处理高性能计算任务方面的能力。这意味着,对于运行复杂的桌面操作系统、大型数据库和高端游戏等,<code>x64</code> 提供了强大的支持。</p><p>而 <code>arm64</code>,由于其出色的能效比和对低功耗的优化,主要用于<strong>智能手机、平板电脑、嵌入式系统和轻薄笔记本电脑</strong>。近年来,随着 <code>Apple</code> 推出<strong>基于 <code>arm64</code> 的 <code>M1</code> 芯片</strong>,以及微软和 <code>Qualcomm</code> 合作开发的 <code>Windows</code> on <code>ARM</code> 项目,<code>arm64</code> 架构开始进入高性能计算和桌面计算领域,挑战 <code>x64</code> 的主导地位。</p><h2 id="dll文件"><a href="#dll文件" class="headerlink" title="dll文件"></a>dll文件</h2><p>.dll文件是<strong>Dynamic Link Library(<a href="https://zhida.zhihu.com/search?content_id=229567508&content_type=Article&match_order=1&q=%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5%E5%BA%93&zhida_source=entity">动态链接库</a>)文件的缩写</strong>,它是一种共享库文件,包含了程序所需的代码和数据。与静态链接库不同,动态链接库可以在<strong>程序运行时动态加载</strong>,使得程序的内存占用更小,同时也方便了程序的更新和维护。</p><h2 id="鲁棒性"><a href="#鲁棒性" class="headerlink" title="鲁棒性"></a>鲁棒性</h2><p>鲁棒性是 robustness 的音译,在中文中常常也被表达为健壮性和强壮性,总体来说其可以用于反映<strong>一个系统在面临着内部结构或外部环境的改变时也能够维持其功能稳定运行的能力</strong> 。</p><p>计算机系统:鲁棒性是一个系统或组件在出现不正确的或矛盾的输入时能够正确运行的程度。</p><p>人类语言技术:语言的鲁棒性(识别和分解等)是指人类即使在信息不完全、意思模糊或不断的变化情况下,仍然能够实现沟通的能力。</p><p>实用非线性控制:鲁棒性是一个系统在遇到了设计中所没有考虑过的情况时不受到影响的程度。</p><p>生物系统:鲁棒性是那些具有恢复、自动修复、自控制、自组装、自复制能力的系统所具有的特性。</p><p>面向对象的软件构造:鲁棒性是软件在非正常环境下(也就是在规范外的环境下,包括新的平台、网络超载、内存故障等)做出适当反应的能力。</p><h2 id="句柄(HANDLE)"><a href="#句柄(HANDLE)" class="headerlink" title="句柄(HANDLE)"></a>句柄(HANDLE)</h2><p>句柄是一个<strong>整数</strong>,单独的看它只是数字。</p><p>但这个整数是<strong>进程句柄表数组的下标</strong>,有了这个下标,操作系统就可以找到其索引的数据结构,并能找到数据结构里面的指针,然后根据这个指针获取内核里的某个对象。</p><h2 id="proxy(代理)"><a href="#proxy(代理)" class="headerlink" title="proxy(代理)"></a><strong>proxy(代理)</strong></h2><p>一种充当 “中间媒介” 的服务或服务器,它位于客户端和目标服务器之间,帮助客户端与目标服务器间接交互。</p><h3 id="常见作用:"><a href="#常见作用:" class="headerlink" title="常见作用:"></a>常见作用:</h3><ol><li><strong>突破访问限制</strong>:比如访问某些地区受限的网站(如学术资源、特定地区服务)。</li><li><strong>隐藏真实地址</strong>:保护客户端的 IP 地址不被目标服务器直接获取,增强隐私。</li><li><strong>缓存加速</strong>:代理服务器可<strong>缓存常用数据,再次请求时直接返回</strong>,提高访问速度。</li><li><strong>过滤内容</strong>:企业或学校可能通过代理限制某些网站访问(如屏蔽不良内容)。</li><li><strong>负载均衡</strong>:在服务器集群中,代理可分配请求到不同服务器,避免单台服务器压力过大。</li></ol><h3 id="前端开发中的常见场景:"><a href="#前端开发中的常见场景:" class="headerlink" title="前端开发中的常见场景:"></a>前端开发中的常见场景:</h3><p>在前端项目(如 Vue、React)开发时,常遇到 <strong>“跨域请求” 问题(浏览器限制不同域名间的直接数据交互)</strong>。此时可配置<strong>开发环境代理</strong>(如 Vue 的<code>vue.config.js</code>、Vite 的<code>vite.config.js</code>中设置<code>proxy</code>),让前端请求<strong>先经过本地代理服务器,再转发到后端接口</strong>,从而<strong>绕过跨域限制</strong>。</p><p>例如 Vue 项目配置:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// vue.config.js</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> <span class="attr">devServer</span>: {</span><br><span class="line"> <span class="attr">proxy</span>: {</span><br><span class="line"> <span class="string">'/api'</span>: { <span class="comment">// 匹配以/api开头的请求</span></span><br><span class="line"> <span class="attr">target</span>: <span class="string">'http://localhost:3000'</span>, <span class="comment">// 目标后端服务器地址</span></span><br><span class="line"> <span class="attr">changeOrigin</span>: <span class="literal">true</span> <span class="comment">// 让代理服务器假装是目标服务器的域名,解决跨域</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://zhida.zhihu.com/search?content_id=249803514&content_type=Article&match_order=1&q=TUN%E6%A8%A1%E5%BC%8F&zhida_source=entity">TUN模式</a>是一种网络隧道技术,主要用于VPN(<a href="https://zhida.zhihu.com/search?content_id=249803514&content_type=Article&match_order=1&q=%E8%99%9A%E6%8B%9F%E4%B8%93%E7%94%A8%E7%BD%91%E7%BB%9C&zhida_source=entity">虚拟专用网络</a>)中的数据包传输。TUN(网络层隧道)和TAP(<a href="https://zhida.zhihu.com/search?content_id=249803514&content_type=Article&match_order=1&q=%E6%95%B0%E6%8D%AE%E9%93%BE%E8%B7%AF%E5%B1%82&zhida_source=entity">数据链路层</a>隧道)是两种主要的隧道模式,它们在数据包的传输层次上有所不同。TUN模式专注于<a href="https://zhida.zhihu.com/search?content_id=249803514&content_type=Article&match_order=1&q=IP%E6%95%B0%E6%8D%AE%E5%8C%85&zhida_source=entity">IP数据包</a>(第三层)的隧道化,而<a href="https://zhida.zhihu.com/search?content_id=249803514&content_type=Article&match_order=1&q=TAP%E6%A8%A1%E5%BC%8F&zhida_source=entity">TAP模式</a>则是处理二层的数据帧。了解TUN模式的优缺点可以帮助更好地配置VPN和网络隧道策略。</p><h2 id="TUN模式"><a href="#TUN模式" class="headerlink" title="TUN模式"></a>TUN模式</h2><h3 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h3><ul><li><p><strong>效率较高</strong></p></li><li><p><strong>更强的跨平台兼容性</strong></p></li><li><p><strong>适合路由流量</strong></p></li><li><p>**适合在不同网络之间建立隧道,并基于IP地址路由特定流量。**因为TUN模式处理IP层数据包,适合通过配置路由表来控制哪些流量通过VPN隧道传输。这非常适用于那些只需要对特定IP地址或网段进行隧道化的情况,如远程访问特定内网资源。</p></li><li><p><strong>节省网络资源</strong>,<strong>不涉及数据链路层</strong>TUN模式传输的数据包比TAP模式小,因为它省略了二层的头信息。对于需要频繁传输大量数据的场景,减少了网络带宽的占用,提高了整体数据传输效率。</p></li></ul><h3 id="TUN模式的缺点"><a href="#TUN模式的缺点" class="headerlink" title="TUN模式的缺点"></a>TUN模式的缺点</h3><ul><li><p><strong>不支持非IP协议</strong></p></li><li><p><strong>无法支持<a href="https://zhida.zhihu.com/search?content_id=249803514&content_type=Article&match_order=1&q=%E4%BA%8C%E5%B1%82%E5%B9%BF%E6%92%AD&zhida_source=entity">二层广播</a>和组播</strong></p></li><li><p>TUN模式因在三层工作,不能支持二层广播和<a href="https://zhida.zhihu.com/search?content_id=249803514&content_type=Article&match_order=1&q=%E7%BB%84%E6%92%AD%E5%8C%85&zhida_source=entity">组播包</a>。</p></li><li><p><strong>不适用于模拟完整的局域网环境</strong>。它主要用于<strong>远程访问而非二层网络的桥接</strong>。</p></li></ul><h3 id="TUN模式的应用场景"><a href="#TUN模式的应用场景" class="headerlink" title="TUN模式的应用场景"></a>TUN模式的应用场景</h3><p>综上所述,TUN模式非常适合用于以下场景:</p><ul><li><strong>远程访问指定IP或网段资源</strong>(例如远程办公,连接公司内网)。</li><li>对带宽要求高的场景,因为TUN模式的<strong>数据包更小,传输效率更高</strong>。</li><li>**需要对特定流量进行VPN保护,**而无需模拟完整局域网的场景。</li></ul><h1 id="大知识点"><a href="#大知识点" class="headerlink" title="大知识点"></a>大知识点</h1><h2 id="缓冲(buffer)"><a href="#缓冲(buffer)" class="headerlink" title="缓冲(buffer)"></a>缓冲(buffer)</h2><p>缓冲区是<strong>内存空间的一部分</strong>。也就是说,在内存空间中<strong>预留</strong>了一定的存储空间,这些存储空间用来<strong>缓冲输入或输出的数据</strong>,这部分预留的空间就叫做缓冲区。缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。</p><h4 id="为什么要引入缓冲区"><a href="#为什么要引入缓冲区" class="headerlink" title="为什么要引入缓冲区"></a>为什么要引入缓冲区</h4><p>比如我们从磁盘里取信息,我们先把读出的数据放在缓冲区,<strong>计算机再直接从缓冲区中取数据</strong>,等缓冲区的数据取完后再去磁盘中读取,这样就可以<strong>减少磁盘的读写次数</strong>,再加上计算机对缓冲区的操作<strong>速度大大快于</strong>对磁盘的操作,故应用缓冲区可大大<strong>提高计算机的运行速度。</strong></p><p>又比如,我们使用打印机打印文档,由于打印机的打印速度相对较慢,我们先把文档输出到打印机相应的缓冲区,打印机再自行逐步打印,这时我们的<strong>CPU可以处理别的事情</strong>。缓冲区就是一块内存区,它<strong>用在输入输出设备和CPU之间</strong>,用来缓存数据。它使得低速的输入输出设备和高速的CPU能够<strong>协调工作</strong>,避免低速的输入输出设备占用CPU,<strong>解放出CPU,使其能够高效率工作</strong>。</p><h2 id="缓存(cache)"><a href="#缓存(cache)" class="headerlink" title="缓存(cache)"></a>缓存(cache)</h2><p>cache是一个非常大的概念。</p><h3 id="一、CPU的Cache"><a href="#一、CPU的Cache" class="headerlink" title="一、CPU的Cache"></a><strong>一、CPU的Cache</strong></h3><p>CPU的Cache,它中文名称是高速缓冲存储器,读写速度很快,几乎与CPU一样。由于CPU的运算速度太快,<strong>内存的数据存取速度无法跟上CPU的速度</strong>,所以在cpu与内存间设置了cache为cpu的数据快取区。当计算机执行程序时,数据与地址管理部件会<strong>预测</strong>可能要用到的数据和指令,并将这些数据和指令<strong>预先从内存中读出送到Cache</strong>。一旦需要时,<strong>先检查Cache</strong>,若有就从Cache中读取,若无再访问内存,现在的CPU还有一级cache,二级cache。简单来说,Cache就是用来<strong>解决CPU与内存之间速度不匹配的问题</strong>,避免内存与辅助内存频繁存取数据,这样就提高了系统的执行效率。</p><h3 id="二、磁盘的Cache"><a href="#二、磁盘的Cache" class="headerlink" title="二、磁盘的Cache"></a><strong>二、磁盘的Cache</strong></h3><p>磁盘也有cache,硬盘的cache作用就类似于CPU的cache,它解决了总线接口的高速需求和读写硬盘的矛盾以及对某些扇区的反复读取。</p><h3 id="三、浏览器的Cache"><a href="#三、浏览器的Cache" class="headerlink" title="三、浏览器的Cache"></a><strong>三、浏览器的Cache</strong></h3><p><a href="https://so.csdn.net/so/search?q=%E6%B5%8F%E8%A7%88%E5%99%A8%E7%BC%93%E5%AD%98&spm=1001.2101.3001.7020">浏览器缓存</a>(Browser Caching)是为了节约网络的资源加速浏览,浏览器在用户磁盘上对最近请求过的文档进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览,并且可以减少服务器的压力。游览器的缓存的数据只是短时间保存,可以人为的清空</p><h4 id="缓存(cache)与缓冲-buffer-的主要区别"><a href="#缓存(cache)与缓冲-buffer-的主要区别" class="headerlink" title="缓存(cache)与缓冲(buffer)的主要区别"></a>缓存(cache)与缓冲(buffer)的主要区别</h4><p>Buffer的核心作用是用来缓冲,缓和冲击(对输出设备的冲击,包括磁盘、打印机、显示器)。比如你每秒要写100次硬盘,对系统冲击很大,<strong>浪费了大量时间</strong>在忙着<strong>处理</strong>开始写和结束写这两件事嘛。用个buffer暂存起来,变成每10秒写一次硬盘,对系统的冲<strong>击就很小</strong>,写入效率高了,日子过得爽了。极大缓和了冲击。</p><p>Cache的核心作用是加快取用的速度(加快读取速度,包括CPU读内存、内存读磁盘、用户通过浏览器请求资源)。比如你一个很复杂的计算做完了,下次还要用结果,就把结果放手边一个<strong>好拿</strong>的地方存着,下次不用再算了。加快了数据取用的速度。</p><p>简单来说就是buffer偏重于写,而cache偏重于读。</p><h2 id="JSON"><a href="#JSON" class="headerlink" title="JSON"></a>JSON</h2><p>JSON: JavaScript Object Notation JS对象简谱 , 是一种轻量级的数据交换格式.</p><p>2.JSON对象格式<br>我们通过java,js,xml和json这几种不同的语言来描述一个对象<br>对象是book,它有两个属性,分别是name和info</p><p>java格式</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">> <span class="keyword">class</span> <span class="title class_">Book</span>{ </span><br><span class="line">> <span class="keyword">private</span> String name; </span><br><span class="line">> <span class="keyword">private</span> String info;</span><br><span class="line">> get/set... </span><br><span class="line">> }</span><br><span class="line">> <span class="type">Book</span> <span class="variable">b</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Book</span>();</span><br><span class="line">> b.setName(“金苹果”); </span><br><span class="line">> b.setInfo(“种苹果”); </span><br><span class="line">> ...</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">> <span class="keyword">var</span> b = <span class="keyword">new</span> <span class="title class_">Object</span>(); </span><br><span class="line">> b.<span class="property">name</span> = <span class="string">"金苹果"</span>; </span><br><span class="line">> b.<span class="property">info</span> = <span class="string">"种苹果"</span>;</span><br></pre></td></tr></table></figure><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">book</span>></span></span><br><span class="line"><span class="tag"><<span class="name">name</span>></span>金苹果<span class="tag"></<span class="name">name</span>></span> </span><br><span class="line"><span class="tag"><<span class="name">info</span>></span>种苹果<span class="tag"></<span class="name">info</span>></span> </span><br><span class="line"><span class="tag"></<span class="name">book</span>></span></span><br></pre></td></tr></table></figure><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span> </span><br><span class="line"><span class="attr">"name"</span><span class="punctuation">:</span><span class="string">"金苹果"</span><span class="punctuation">,</span> </span><br><span class="line"><span class="attr">"info"</span><span class="punctuation">:</span><span class="string">"种苹果"</span> </span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><p><strong>一个对象, 由一个大括号表示.</strong><br> 括号中 描述对象的属性 .<br> 通过<strong>键值对来描述对象的属性</strong> (可以理解为, 大括号中, 包含的是一个个的键值对.)<br> 格式<br> 键与值之间使用冒号连接, 多个键值对之间使用<strong>逗号分隔.</strong><br> 键值对的键 应使用<strong>引号引住</strong> (通常Java解析时, 键不使用引号会报错. 而JS能正确解 析.) 键值对的值, 可以是JS中的<strong>任意类型</strong>的数据</p><p>数组格式</p><blockquote><p>在JSON格式中可以与对象<strong>互相嵌套</strong><br>[元素1,元素2…]</p></blockquote><p><strong>案例</strong></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span> </span><br><span class="line"><span class="attr">"name"</span><span class="punctuation">:</span><span class="string">"小陈"</span><span class="punctuation">,</span> </span><br><span class="line"><span class="attr">"age"</span><span class="punctuation">:</span><span class="number">20</span><span class="punctuation">,</span> </span><br><span class="line"><span class="attr">"pengyou"</span><span class="punctuation">:</span><span class="punctuation">[</span><span class="string">"张三"</span><span class="punctuation">,</span><span class="string">"李四"</span><span class="punctuation">,</span><span class="string">"王二"</span><span class="punctuation">,</span><span class="string">"麻子"</span><span class="punctuation">,</span><span class="punctuation">{</span> </span><br><span class="line"><span class="attr">"name"</span><span class="punctuation">:</span><span class="string">"小明"</span><span class="punctuation">,</span> </span><br><span class="line"><span class="attr">"info"</span><span class="punctuation">:</span><span class="string">"像彭于晏一样帅气的男人"</span> </span><br><span class="line"><span class="punctuation">}</span><span class="punctuation">]</span><span class="punctuation">,</span> </span><br><span class="line"><span class="attr">"heihei"</span><span class="punctuation">:</span><span class="punctuation">{</span> </span><br><span class="line"><span class="attr">"name"</span><span class="punctuation">:</span><span class="string">"大长刀"</span><span class="punctuation">,</span> </span><br><span class="line"><span class="attr">"length"</span><span class="punctuation">:</span><span class="string">"40m"</span> </span><br><span class="line"><span class="punctuation">}</span> </span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><p>3.JSON数据解析<br>下面开始讲解如何<strong>使用IDEA将Java对象快速转换成JSON数据</strong>,和如何将JSON数据转换成Java对象</p><p>将Java中的对象 快速的<strong>转换为 JSON格式的字符串.</strong><br>将JSON格式的字符串, 转换为Java的对象.</p><p>注意:</p><p>1.以下的导jar包操作如果不会,请去参考一些导jar包操作<br>2.在文章的底部提供了有关的jar包,需要自取<br><strong>GSON解析</strong></p><ul><li><p>将对象转换为JSON字符串</p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">转换JSON字符串的步骤: </span><br><span class="line"><span class="number">1.</span> 引入JAR包 </span><br><span class="line"><span class="number">2.</span> 在需要转换JSON字符串的位置编写如下代码即可: </span><br><span class="line"><span class="type">String</span> <span class="variable">json</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Gson</span>().toJSON(要转换的对象); </span><br><span class="line">案例:</span><br><span class="line"><span class="comment">//1. 创建Gson类型的对象</span></span><br><span class="line"> <span class="type">Gson</span> <span class="variable">g</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Gson</span>();</span><br><span class="line"> <span class="comment">//2. 转换</span></span><br><span class="line"> <span class="comment">//优化 可以使用匿名对象</span></span><br><span class="line"> <span class="type">book</span> <span class="variable">b</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">book</span>(<span class="string">"100"</span>,<span class="string">"金苹果"</span>,<span class="string">"种植苹果的故事"</span>);</span><br><span class="line"> <span class="type">String</span> <span class="variable">s</span> <span class="operator">=</span> g.toJson(b);</span><br><span class="line"> System.out.println(s);</span><br></pre></td></tr></table></figure><p> 将JSON字符串转换为对象</p> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1.</span> 引入JAR包 </span><br><span class="line"><span class="number">2.</span> 在需要转换Java对象的位置, 编写如下代码:</span><br><span class="line"><span class="comment">//1. 创建Gson类型的对象</span></span><br><span class="line"> <span class="type">Gson</span> <span class="variable">g</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Gson</span>();</span><br><span class="line"> <span class="comment">//2. 转换 {"id":"100","name":"金苹果","info":"种植苹果的故事","page":["远赴人间惊鸿晏","一度人间盛世颜","致hdd"]}</span></span><br><span class="line"> <span class="comment">//2.1 返回book类型</span></span><br><span class="line"> <span class="type">book</span> <span class="variable">b</span> <span class="operator">=</span> g.fromJson(<span class="string">"{\"id\":\"100\",\"name\":\"金苹果\",\"info\":\"种植苹果的故事\"}"</span>, book.class);</span><br><span class="line"> System.out.println(b.getId());</span><br><span class="line"> <span class="comment">//2.2 返回MAP类型,键值对形式</span></span><br><span class="line"> <span class="type">HashMap</span> <span class="variable">hm</span> <span class="operator">=</span> g.fromJson(<span class="string">"{\"id\":\"100\",\"name\":\"金苹果\",\"info\":\"种植苹果的故事\"}"</span>, HashMap.class);</span><br><span class="line"> System.out.println(hm.get(<span class="string">"id"</span>));</span><br><span class="line"> <span class="comment">//2.3 MAP类型中值是一个数组形式</span></span><br><span class="line"> <span class="type">HashMap</span> <span class="variable">data</span> <span class="operator">=</span> g.fromJson(<span class="string">"{\"id\":\"100\",\"name\":\"金苹果\",\"info\":\"种植苹果的故事\",\"page\":[\"远赴人间惊鸿晏\",\"一度人间盛世颜\",\"致hdd\"]}"</span>, HashMap.class);</span><br><span class="line"> <span class="type">List</span> <span class="variable">list</span> <span class="operator">=</span> (List) data.get(<span class="string">"page"</span>);</span><br><span class="line"> System.out.println(list.get(<span class="number">1</span>));</span><br></pre></td></tr></table></figure></li></ul><h2 id="MCP"><a href="#MCP" class="headerlink" title="MCP"></a>MCP</h2><p>MCP (Model Context Protocol,模型上下文协议)定义了应用程序和 AI 模型之间交换上下文信息的方式。这使得开发者能够<strong>以一致的方式将各种数据源、工具和功能连接到 AI 模型</strong>(一个中间协议层),就像 USB-C 让不同设备能够通过相同的接口连接一样。MCP 的目标是创建一个通用标准,使 AI 应用程序的开发和集成变得更加简单和统一。<img src="/2025/07/25/%E8%AE%A1%E7%A7%91%E7%9B%B8%E5%85%B3%E8%87%AA%E6%84%9F%E7%9F%A5%E8%AF%86%E7%82%B9%E7%9A%84%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%A1%A5%E5%85%85/v2-3a242914e1f4958e631dd158e043b7c3_1440w.jpg" alt="img"></p><p>MCP 就是以更标准的方式让 LLM Chat 使用不同工具<img src="/2025/07/25/%E8%AE%A1%E7%A7%91%E7%9B%B8%E5%85%B3%E8%87%AA%E6%84%9F%E7%9F%A5%E8%AF%86%E7%82%B9%E7%9A%84%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%A1%A5%E5%85%85/v2-9fe7fb51f264338a079a444eefa041b1_1440w.jpg" alt="img"></p><p> MCP 的出现是 prompt engineering 发展的产物。更结构化的上下文信息对模型的 performance 提升是显著的。我们在构造 prompt 时,希望能提供一些<strong>更 specific 的信息</strong>(比如本地文件,数据库,一些网络实时信息等)给模型,这样模型更容易理解真实场景中的问题。</p><p><strong>想象一下没有 MCP 之前我们会怎么做</strong>?我们可能会人工从数据库中筛选或者使用工具检索可能需要的信息,手动的粘贴到 prompt 中。随着我们要解决的问题越来越复杂,<strong>手工</strong>把信息引入到 prompt 中会变得越来越困难。</p><p>为了克服手工 prompt 的局限性,许多 LLM 平台(如 OpenAI、Google)引入了 <code>function call</code> 功能。这一机制允许模型在需要时<strong>调用预定义的函数来获取数据或执行操作</strong>,显著提升了自动化水平。</p><p>但是 function call 也有其局限性(我对于 function call vs MCP 的理解不一定成熟,欢迎大家补充),我认为重点在于 <strong>function call 平台依赖性强</strong>,不同 LLM 平台的 function call API 实现差异较大。例如,OpenAI 的函数调用方式<strong>与 Google 的不兼容</strong>,开发者在切换模型时需要<strong>重写代码</strong>,增加了适配成本。除此之外,还有安全性,交互性等问题。</p><p>数据与工具本身是<strong>客观存在的</strong>,只不过我们希望将数据连接到模型的这个环节可以<strong>更智能更统一</strong>。Anthropic 基于这样的痛点设计了 MCP,充当 AI 模型的**”万能转接头”**,让 LLM 能轻松的获取数据或者调用工具。更具体的说 MCP 的优势在于:</p><p>生态 - MCP 提供很多<strong>现成的插件</strong>,你的 AI 可以直接使用。</p><p>统一性 - 不限制于特定的 AI 模型,<strong>任何支持 MCP 的模型</strong>都可以灵活切换。</p><p>数据安全 - 你的敏感数据留在自己的电脑上,不必全部上传。(因为我们可以自行设计接口确定传输哪些数据)</p><h3 id="用户如何使用-MCP?"><a href="#用户如何使用-MCP?" class="headerlink" title="用户如何使用 MCP?"></a>用户如何使用 MCP?</h3><p>对于用户来说,我们并不关心 MCP 是如何实现的,通常我们只考虑如何更简单的用上这一特性。</p><p>具体的使用方式参考官方文档:For Claude Desktop Users。这里不再赘述,配置成功后可以在 Claude 中测试:Can you write a poem and save it to my desktop? Claude 会请求你的权限后在本地新建一个文件。</p><p>并且官方也提供了非常多现成的 MCP Servers,你只需要选择你希望接入的工具,然后接入即可。</p><ul><li><a href="https://link.zhihu.com/?target=https://github.com/punkpeye/awesome-mcp-servers">Awesome MCP Servers</a></li><li><a href="https://link.zhihu.com/?target=https://mcpservers.org/">MCP Servers Website</a></li><li><a href="https://link.zhihu.com/?target=https://github.com/modelcontextprotocol/servers">Official MCP Servers</a></li></ul><p>比如官方介绍的 <code>filesystem</code> 工具,它允许 Claude 读取和写入文件,就像在本地文件系统中一样。</p><h3 id="MCP-Architecture-解构"><a href="#MCP-Architecture-解构" class="headerlink" title="MCP Architecture 解构"></a>MCP Architecture 解构</h3><p>这里首先引用官方给出的架构图。</p><p><img src="/2025/07/25/%E8%AE%A1%E7%A7%91%E7%9B%B8%E5%85%B3%E8%87%AA%E6%84%9F%E7%9F%A5%E8%AF%86%E7%82%B9%E7%9A%84%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%A1%A5%E5%85%85/v2-9d3681630ed930a8dc74d3b452c0cc94_1440w.jpg" alt="img"></p><p>MCP 由三个核心组件构成:Host、Client 和 Server。让我们通过一个实际场景来理解这些组件如何协同工作:</p><p>假设你正在使用 Claude Desktop (Host) 询问:”我桌面上有哪些文档?”</p><ol><li><strong>Host</strong>:Claude Desktop 作为 Host,负责接收你的提问<strong>并与 Claude 模型交互。</strong></li><li><strong>Client</strong>:当 Claude 模型决定需要访问你的文件系统时,Host 中内置的 <strong>MCP Client</strong> 会被激活。这个 Client 负责<strong>与适当的 MCP Server</strong> 建立连接。</li><li><strong>Server</strong>:在这个例子中,文件系统 MCP Server 会被调用。它负责执行实际的文件扫描操作,访问你的桌面目录,并返回找到的文档列表。</li></ol><p>整个流程是这样的:你的问题 → Claude Desktop(Host) → Claude 模型 → 需要文件信息 → MCP Client 连接 → 文件系统 MCP Server → 执行操作 → 返回结果 → Claude 生成回答 → 显示在 Claude Desktop 上。</p><p>这种架构设计使得 Claude 可以在不同场景下灵活调用各种工具和数据源,而开发<strong>者只需专注于开发对应的 MCP Server</strong>,无需关心 Host 和 Client 的实现细节。</p><p><img src="/2025/07/25/%E8%AE%A1%E7%A7%91%E7%9B%B8%E5%85%B3%E8%87%AA%E6%84%9F%E7%9F%A5%E8%AF%86%E7%82%B9%E7%9A%84%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%A1%A5%E5%85%85/v2-3f7ceba80b16ef134b27119308a04472_1440w.jpg" alt="img"></p><h2 id="原理:模型是如何确定工具的选用的?"><a href="#原理:模型是如何确定工具的选用的?" class="headerlink" title="原理:模型是如何确定工具的选用的?"></a>原理:模型是如何确定工具的选用的?</h2><p>在学习的过程中,我一直好奇一个问题:<strong>Claude(模型)是在什么时候确定使用哪些工具的呢</strong>?好在 Anthropic 为我们提供了详细的<a href="https://link.zhihu.com/?target=https://modelcontextprotocol.io/quickstart/server%23what%E2%80%99s-happening-under-the-hood">解释</a>:</p><p>当用户提出一个问题时:</p><ol><li>客户端(Claude Desktop / Cursor)将你的<strong>问题发送</strong>给 Claude。</li><li>Claude <strong>分析可用的工具,并决定使用</strong>哪一个(或多个)。</li><li>客户端<strong>通过 MCP Server 执行所选</strong>的工具。</li><li>工具的执行结果被送回给 Claude。</li><li>Claude 结合执行结果构造最终的 prompt 并生成自然语言的回应。</li><li>回应最终展示给用户!</li></ol><blockquote><p>MCP Server 是由 Claude 主动选择并调用的。有意思的是 Claude 具体是如何确定该使用哪些工具呢?以及是否会使用一些不存在的工具呢(幻觉)?</p></blockquote><p>**(原谅我之前解释的过于简单)**为了探索这个问题让我们深入<a href="https://link.zhihu.com/?target=https://github.com/modelcontextprotocol/python-sdk/tree/main/examples/clients/simple-chatbot/mcp_simple_chatbot">源码</a>。显然这个调用过程可以分为两个步骤:</p><ol><li>由 <strong>LLM(Claude)确定使用哪些 MCP Server</strong>。</li><li><strong>执行对应的 MCP Server</strong> 并对执行结果进行重新处理。</li></ol><p>先给出一个简单可视化帮助理解:<img src="/2025/07/25/%E8%AE%A1%E7%A7%91%E7%9B%B8%E5%85%B3%E8%87%AA%E6%84%9F%E7%9F%A5%E8%AF%86%E7%82%B9%E7%9A%84%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%A1%A5%E5%85%85/v2-2bcd98f6541da0b6f14dc9082ee2dcda_1440w.jpg" alt="img"></p><h3 id="模型如何智能选择工具?"><a href="#模型如何智能选择工具?" class="headerlink" title="模型如何智能选择工具?"></a>模型如何智能选择工具?</h3><p>先理解第一步<strong>模型如何确定该使用哪些工具?<strong>这里以 MCP 官方提供的 <a href="https://link.zhihu.com/?target=https://github.com/modelcontextprotocol/python-sdk/tree/main/examples/clients/simple-chatbot/mcp_simple_chatbot">client example</a> 为讲解示例,并简化了对应的代码(删除了一些不影响阅读逻辑的异常控制代码)。通过阅读代码,可以发现模型是</strong>通过 prompt 来确定当前有哪些工具</strong>。我们通过<strong>将工具的具体使用描述以文本的形式传递给模型</strong>,供模型了解有哪些工具以及结合实时情况进行选择。参考代码中的注释:</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">... </span><span class="comment"># 省略了无关的代码</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">start</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="comment"># 初始化所有的 mcp server</span></span><br><span class="line"> <span class="keyword">for</span> server <span class="keyword">in</span> <span class="variable language_">self</span>.servers:</span><br><span class="line"> <span class="keyword">await</span> server.initialize()</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 获取所有的 tools 命名为 all_tools</span></span><br><span class="line"> all_tools = []</span><br><span class="line"> <span class="keyword">for</span> server <span class="keyword">in</span> <span class="variable language_">self</span>.servers:</span><br><span class="line"> tools = <span class="keyword">await</span> server.list_tools()</span><br><span class="line"> all_tools.extend(tools)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 将所有的 tools 的功能描述格式化成字符串供 LLM 使用</span></span><br><span class="line"> <span class="comment"># tool.format_for_llm() 我放到了这段代码最后,方便阅读。</span></span><br><span class="line"> tools_description = <span class="string">"\n"</span>.join(</span><br><span class="line"> [tool.format_for_llm() <span class="keyword">for</span> tool <span class="keyword">in</span> all_tools]</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 这里就不简化了,以供参考,实际上就是基于 prompt 和当前所有工具的信息</span></span><br><span class="line"> <span class="comment"># 询问 LLM(Claude) 应该使用哪些工具。</span></span><br><span class="line"> system_message = (</span><br><span class="line"> <span class="string">"You are a helpful assistant with access to these tools:\n\n"</span></span><br><span class="line"> <span class="string">f"<span class="subst">{tools_description}</span>\n"</span></span><br><span class="line"> <span class="string">"Choose the appropriate tool based on the user's question. "</span></span><br><span class="line"> <span class="string">"If no tool is needed, reply directly.\n\n"</span></span><br><span class="line"> <span class="string">"IMPORTANT: When you need to use a tool, you must ONLY respond with "</span></span><br><span class="line"> <span class="string">"the exact JSON object format below, nothing else:\n"</span></span><br><span class="line"> <span class="string">"{\n"</span></span><br><span class="line"> <span class="string">' "tool": "tool-name",\n'</span></span><br><span class="line"> <span class="string">' "arguments": {\n'</span></span><br><span class="line"> <span class="string">' "argument-name": "value"\n'</span></span><br><span class="line"> <span class="string">" }\n"</span></span><br><span class="line"> <span class="string">"}\n\n"</span></span><br><span class="line"> <span class="string">"After receiving a tool's response:\n"</span></span><br><span class="line"> <span class="string">"1. Transform the raw data into a natural, conversational response\n"</span></span><br><span class="line"> <span class="string">"2. Keep responses concise but informative\n"</span></span><br><span class="line"> <span class="string">"3. Focus on the most relevant information\n"</span></span><br><span class="line"> <span class="string">"4. Use appropriate context from the user's question\n"</span></span><br><span class="line"> <span class="string">"5. Avoid simply repeating the raw data\n\n"</span></span><br><span class="line"> <span class="string">"Please use only the tools that are explicitly defined above."</span></span><br><span class="line"> )</span><br><span class="line"> messages = [{<span class="string">"role"</span>: <span class="string">"system"</span>, <span class="string">"content"</span>: system_message}]</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"> <span class="comment"># Final... 假设这里已经处理了用户消息输入.</span></span><br><span class="line"> messages.append({<span class="string">"role"</span>: <span class="string">"user"</span>, <span class="string">"content"</span>: user_input})</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 将 system_message 和用户消息输入一起发送给 LLM</span></span><br><span class="line"> llm_response = <span class="variable language_">self</span>.llm_client.get_response(messages)</span><br><span class="line"></span><br><span class="line"> ... <span class="comment"># 后面和确定使用哪些工具无关</span></span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Tool</span>:</span><br><span class="line"> <span class="string">"""Represents a tool with its properties and formatting."""</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params"></span></span><br><span class="line"><span class="params"> self, name: <span class="built_in">str</span>, description: <span class="built_in">str</span>, input_schema: <span class="built_in">dict</span>[<span class="built_in">str</span>, <span class="type">Any</span>]</span></span><br><span class="line"><span class="params"> </span>) -> <span class="literal">None</span>:</span><br><span class="line"> <span class="variable language_">self</span>.name: <span class="built_in">str</span> = name</span><br><span class="line"> <span class="variable language_">self</span>.description: <span class="built_in">str</span> = description</span><br><span class="line"> <span class="variable language_">self</span>.input_schema: <span class="built_in">dict</span>[<span class="built_in">str</span>, <span class="type">Any</span>] = input_schema</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 把工具的名字 / 工具的用途(description)和工具所需要的参数(args_desc)转化为文本</span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">format_for_llm</span>(<span class="params">self</span>) -> <span class="built_in">str</span>:</span><br><span class="line"> <span class="string">"""Format tool information for LLM.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> Returns:</span></span><br><span class="line"><span class="string"> A formatted string describing the tool.</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> args_desc = []</span><br><span class="line"> <span class="keyword">if</span> <span class="string">"properties"</span> <span class="keyword">in</span> <span class="variable language_">self</span>.input_schema:</span><br><span class="line"> <span class="keyword">for</span> param_name, param_info <span class="keyword">in</span> <span class="variable language_">self</span>.input_schema[<span class="string">"properties"</span>].items():</span><br><span class="line"> arg_desc = (</span><br><span class="line"> <span class="string">f"- <span class="subst">{param_name}</span>: <span class="subst">{param_info.get(<span class="string">'description'</span>, <span class="string">'No description'</span>)}</span>"</span></span><br><span class="line"> )</span><br><span class="line"> <span class="keyword">if</span> param_name <span class="keyword">in</span> <span class="variable language_">self</span>.input_schema.get(<span class="string">"required"</span>, []):</span><br><span class="line"> arg_desc += <span class="string">" (required)"</span></span><br><span class="line"> args_desc.append(arg_desc)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="string">f"""</span></span><br><span class="line"><span class="string">Tool: <span class="subst">{self.name}</span></span></span><br><span class="line"><span class="string">Description: <span class="subst">{self.description}</span></span></span><br><span class="line"><span class="string">Arguments:</span></span><br><span class="line"><span class="string"><span class="subst">{<span class="built_in">chr</span>(<span class="number">10</span>).join(args_desc)}</span></span></span><br><span class="line"><span class="string">"""</span></span><br></pre></td></tr></table></figure><p>那 tool 的描述和代码中的 <code>input_schema</code> 是从哪里来的呢?通过进一步分析 MCP 的 Python SDK 源代码可以发现:大部分情况下,当<strong>使用装饰器 <code>@mcp.tool()</code> 来装饰函数</strong>时,对应的 <code>name</code> 和 <code>description</code> 等其实直接源自<strong>用户定义函数的函数名</strong>以及<strong>函数的 <code>docstring</code> 等</strong>。</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@classmethod</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">from_function</span>(<span class="params"></span></span><br><span class="line"><span class="params"> cls,</span></span><br><span class="line"><span class="params"> fn: <span class="type">Callable</span>,</span></span><br><span class="line"><span class="params"> name: <span class="built_in">str</span> | <span class="literal">None</span> = <span class="literal">None</span>,</span></span><br><span class="line"><span class="params"> description: <span class="built_in">str</span> | <span class="literal">None</span> = <span class="literal">None</span>,</span></span><br><span class="line"><span class="params"> context_kwarg: <span class="built_in">str</span> | <span class="literal">None</span> = <span class="literal">None</span>,</span></span><br><span class="line"><span class="params"></span>) -> <span class="string">"Tool"</span>:</span><br><span class="line"> <span class="string">"""Create a Tool from a function."""</span></span><br><span class="line"> func_name = name <span class="keyword">or</span> fn.__name__ <span class="comment"># 获取函数名</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> func_name == <span class="string">"<lambda>"</span>:</span><br><span class="line"> <span class="keyword">raise</span> ValueError(<span class="string">"You must provide a name for lambda functions"</span>)</span><br><span class="line"></span><br><span class="line"> func_doc = description <span class="keyword">or</span> fn.__doc__ <span class="keyword">or</span> <span class="string">""</span> <span class="comment"># 获取函数 docstring</span></span><br><span class="line"> is_async = inspect.iscoroutinefunction(fn)</span><br><span class="line"> </span><br><span class="line"> ... <span class="comment"># 更多请参考原始代码...</span></span><br></pre></td></tr></table></figure><p>总结:模型是通过 prompt engineering,即<strong>提供所有工具的结构化描述和 few-shot 的 example</strong> 来确定该使用哪些工具。另一方面,Anthropic 肯定对 Claude 做了专门的训练(毕竟是自家协议,Claude 更能理解工具的 prompt 以及输出结构化的 tool call json 代码)</p><h3 id="工具执行与结果反馈机制"><a href="#工具执行与结果反馈机制" class="headerlink" title="工具执行与结果反馈机制"></a>工具执行与结果反馈机制</h3><p>其实工具的执行就比较简单和直接了。承接上一步,我们把 system prompt(指令与工具调用描述)和用户消息一起发送给模型,然后接收模型的回复。当模型分析用户请求后,它会决定是否需要调用工具:</p><ul><li><strong>无需工具时</strong>:模型<strong>直接生成自然语言</strong>回复。</li><li><strong>需要工具时</strong>:模型<strong>输出结构化 JSON 格式的工具调用请求</strong>。</li></ul><p>如果回复中包含结构化 JSON 格式的工具调用请求,则客户端会根据这个 json 代码执行对应的工具。具体的实现逻辑都在 <code>process_llm_response</code> 中,<a href="https://link.zhihu.com/?target=https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/clients/simple-chatbot/mcp_simple_chatbot/main.py%23L295-L338">代码</a>,逻辑非常简单。</p><p>如果模型执行了 tool call,则工具执行的结果 <code>result</code> 会和 system prompt 和用户消息一起<strong>重新发送</strong>给模型,请求模型生成最终回复。</p><p>如果 tool call 的 json 代码存在问题或者模型产生了幻觉怎么办呢?通过阅读<a href="https://link.zhihu.com/?target=https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/clients/simple-chatbot/mcp_simple_chatbot/main.py%23L295-L338">代码</a> 发现,我们会 <strong>skip 掉无效的调用请求</strong>。</p><p>执行相关的代码与注释如下:</p><figure class="highlight py"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">... </span><span class="comment"># 省略无关的代码</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">def</span> <span class="title function_">start</span>(<span class="params">self</span>):</span><br><span class="line"> ... <span class="comment"># 上面已经介绍过了,模型如何选择工具</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"> <span class="comment"># 假设这里已经处理了用户消息输入.</span></span><br><span class="line"> messages.append({<span class="string">"role"</span>: <span class="string">"user"</span>, <span class="string">"content"</span>: user_input})</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 获取 LLM 的输出</span></span><br><span class="line"> llm_response = <span class="variable language_">self</span>.llm_client.get_response(messages)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 处理 LLM 的输出(如果有 tool call 则执行对应的工具)</span></span><br><span class="line"> result = <span class="keyword">await</span> <span class="variable language_">self</span>.process_llm_response(llm_response)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 如果 result 与 llm_response 不同,说明执行了 tool call (有额外信息了)</span></span><br><span class="line"> <span class="comment"># 则将 tool call 的结果重新发送给 LLM 进行处理。</span></span><br><span class="line"> <span class="keyword">if</span> result != llm_response:</span><br><span class="line"> messages.append({<span class="string">"role"</span>: <span class="string">"assistant"</span>, <span class="string">"content"</span>: llm_response})</span><br><span class="line"> messages.append({<span class="string">"role"</span>: <span class="string">"system"</span>, <span class="string">"content"</span>: result})</span><br><span class="line"></span><br><span class="line"> final_response = <span class="variable language_">self</span>.llm_client.get_response(messages)</span><br><span class="line"> logging.info(<span class="string">"\nFinal response: %s"</span>, final_response)</span><br><span class="line"> messages.append(</span><br><span class="line"> {<span class="string">"role"</span>: <span class="string">"assistant"</span>, <span class="string">"content"</span>: final_response}</span><br><span class="line"> )</span><br><span class="line"> <span class="comment"># 否则代表没有执行 tool call,则直接将 LLM 的输出返回给用户。</span></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> messages.append({<span class="string">"role"</span>: <span class="string">"assistant"</span>, <span class="string">"content"</span>: llm_response}) </span><br></pre></td></tr></table></figure><p>结合这部分原理分析:</p><ul><li>工具文档至关重要 - 模型通过工具描述文本来理解和选择工具,因此精心编写工具的名称、docstring 和参数说明至关重要。</li><li>由于 MCP 的选择是<strong>基于 prompt 的</strong>,所以<strong>任何模型其实都适配 MCP</strong>,只要你能提供对应的工具描述。但是当你使用非 Claude 模型时,MCP 使用的效果和体验难以保证(没有做专门的训练)。</li></ul><h2 id="6-总结"><a href="#6-总结" class="headerlink" title="6. 总结"></a>6. 总结</h2><p>MCP (Model Context Protocol) 代表了 <strong>AI 与外部工具和数据交互的标准建立</strong>。通过本文,我们可以了解到:</p><ol><li><strong>MCP 的本质</strong>:它是一个<strong>统一的协议标准</strong>,使 AI 模型能够以一致的方式连接各种数据源和工具,类似于 AI 世界的”USB-C”接口。</li><li><strong>MCP 的价值</strong>:它解决了传统 function call 的平台依赖问题,提供了更统一、开放、安全、灵活的工具调用机制,让用户和开发者都能从中受益。</li><li><strong>使用与开发</strong>:对于普通用户,MCP 提供了丰富的现成工具,<strong>用户可以在不了解任何技术细节的情况下使用</strong>;对于开发者,MCP 提供了清晰的架构和 SDK,使工具开发变得相对简单。</li></ol><h3 id="一个简单的-MCP-Server-开发实践"><a href="#一个简单的-MCP-Server-开发实践" class="headerlink" title="一个简单的 MCP Server 开发实践"></a><strong>一个简单的 MCP Server 开发实践</strong></h3><p>对绝大部分 AI 开发者来说,我们只需要关心 <strong>Server 的实现</strong></p><p>MCP servers 可以提供三种主要类型的功能:</p><ul><li>Resources(资源):类似文件的数据,可以被客户端读取(如 <strong>API 响应或文件内容</strong>)</li><li>Tools(工具):可以<strong>被 LLM 调用的函数(需要用户批准)</strong></li><li>Prompts(提示):<strong>预先编写的模板</strong>,帮助用户完成特定任务</li></ul><h3 id="A-I-使用-LLM-构建-MCP-的最佳实践"><a href="#A-I-使用-LLM-构建-MCP-的最佳实践" class="headerlink" title="A.I 使用 LLM 构建 MCP 的最佳实践"></a>A.I 使用 LLM 构建 MCP 的最佳实践</h3><p>在开始之前,Anthropic 为我们提供了一个基于 LLM 的 MCP Server 的<a href="https://link.zhihu.com/?target=https://modelcontextprotocol.io/tutorials/building-mcp-with-llms">最佳开发实践</a>,总结如下:</p><ul><li>引入 <strong>domain knowledge</strong> (说人话就是,告诉他<strong>一些 MCP Server 开发的范例和资料</strong>)<ul><li>访问 <a href="https://link.zhihu.com/?target=https://modelcontextprotocol.io/llms-full.txt">https://modelcontextprotocol.io/llms-full.txt</a> 并复制完整的文档文本。(实测这个太长了,可以忽略)</li><li>导航到 MCP <a href="https://link.zhihu.com/?target=https://github.com/modelcontextprotocol/typescript-sdk">TypeScript SDK</a> 或 <a href="https://link.zhihu.com/?target=https://github.com/modelcontextprotocol/python-sdk">Python SDK</a> Github 项目中并复制相关内容。</li><li>把这些<strong>作为 prompt 输入到你的 chat 对话中(作为 context)</strong>。</li></ul></li><li>描述你的需求<ul><li>你的服务器<strong>会开放哪些资源</strong></li><li>它<strong>会提供哪些工具</strong></li><li><strong>它应该给出哪些引导或建议</strong></li><li>它需要<strong>跟哪些外部系统互动</strong></li></ul></li></ul><p>给出一个 example prompt:</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">... (这里是已经引入的 domain knowledge)</span><br><span class="line"></span><br><span class="line">打造一个 MCP 服务器,它能够:</span><br><span class="line"></span><br><span class="line">- 连接到我公司的 PostgreSQL 数据库</span><br><span class="line">- 将表格结构作为资源开放出来</span><br><span class="line">- 提供运行只读 SQL 查询的工具</span><br><span class="line">- 包含常见数据分析任务的引导</span><br></pre></td></tr></table></figure><p>剩下的部分也很重要,但是偏重于方法论,实践性较弱,我这里就不展开了,推荐大家直接看<a href="https://link.zhihu.com/?target=https://modelcontextprotocol.io/tutorials/building-mcp-with-llms">官方文档</a>。</p><h3 id="A-II-手动实践"><a href="#A-II-手动实践" class="headerlink" title="A.II 手动实践"></a>A.II 手动实践</h3><p>使用 Python 实现一个 MCP Server,用来<strong>统计当前桌面上的 txt 文件数量和获取对应文件的名字</strong>(你可以理解为一点用都没有,但是它足够简单,主要是为了难以配置环境的读者提供一个足够短的实践记录)。</p><p><strong>构造一个 prompt</strong></p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">"""</span><br><span class="line">... (这里是已经引入的 domain knowledge)</span><br><span class="line">"""</span><br><span class="line"></span><br><span class="line">打造一个 MCP 服务器,它能够:</span><br><span class="line">- 功能:</span><br><span class="line"> - 统计当前桌面上的 txt 文件数量</span><br><span class="line"> - 获取对应文件的名字</span><br><span class="line"></span><br><span class="line">要求:</span><br><span class="line">- 不需要给出 prompt 和 resource 相关代码。</span><br><span class="line">- 你可以假设我的桌面路径为 /Users/{username}/Desktop</span><br></pre></td></tr></table></figure><p><strong>实现 MCP Server</strong></p><p>以下代码由 Claude 3.7 直接生成。当然,这主要是因为我的需求足够简单,当你需要实现一个复杂的 MCP Server 时,你可能需要<strong>多步的引导和 Debug</strong> 才能得到最终的代码。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> pathlib <span class="keyword">import</span> Path</span><br><span class="line"><span class="keyword">from</span> mcp.server.fastmcp <span class="keyword">import</span> FastMCP</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建 MCP Server</span></span><br><span class="line">mcp = FastMCP(<span class="string">"桌面 TXT 文件统计器"</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@mcp.tool()</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">count_desktop_txt_files</span>() -> <span class="built_in">int</span>:</span><br><span class="line"> <span class="string">"""Count the number of .txt files on the desktop."""</span></span><br><span class="line"> <span class="comment"># Get the desktop path</span></span><br><span class="line"> username = os.getenv(<span class="string">"USER"</span>) <span class="keyword">or</span> os.getenv(<span class="string">"USERNAME"</span>)</span><br><span class="line"> desktop_path = Path(<span class="string">f"/Users/<span class="subst">{username}</span>/Desktop"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Count .txt files</span></span><br><span class="line"> txt_files = <span class="built_in">list</span>(desktop_path.glob(<span class="string">"*.txt"</span>))</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">len</span>(txt_files)</span><br><span class="line"></span><br><span class="line"><span class="meta">@mcp.tool()</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">list_desktop_txt_files</span>() -> <span class="built_in">str</span>:</span><br><span class="line"> <span class="string">"""Get a list of all .txt filenames on the desktop."""</span></span><br><span class="line"> <span class="comment"># Get the desktop path</span></span><br><span class="line"> username = os.getenv(<span class="string">"USER"</span>) <span class="keyword">or</span> os.getenv(<span class="string">"USERNAME"</span>)</span><br><span class="line"> desktop_path = Path(<span class="string">f"/Users/<span class="subst">{username}</span>/Desktop"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Get all .txt files</span></span><br><span class="line"> txt_files = <span class="built_in">list</span>(desktop_path.glob(<span class="string">"*.txt"</span>))</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Return the filenames</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> txt_files:</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"No .txt files found on desktop."</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># Format the list of filenames</span></span><br><span class="line"> file_list = <span class="string">"\n"</span>.join([<span class="string">f"- <span class="subst">{file.name}</span>"</span> <span class="keyword">for</span> file <span class="keyword">in</span> txt_files])</span><br><span class="line"> <span class="keyword">return</span> <span class="string">f"Found <span class="subst">{<span class="built_in">len</span>(txt_files)}</span> .txt files on desktop:\n<span class="subst">{file_list}</span>"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> <span class="comment"># Initialize and run the server</span></span><br><span class="line"> mcp.run()</span><br></pre></td></tr></table></figure><h3 id="理解-name-变量"><a href="#理解-name-变量" class="headerlink" title="理解 __name__ 变量"></a>理解 <code>__name__</code> 变量</h3><ul><li><p><code>__name__</code> 是 Python 内置的特殊变量,用于标识当前模块的名称。</p></li><li><p>当一个模块(<code>.py</code> 文件)被<strong>直接运行</strong>时(例如通过 <code>python 文件名.py</code> 命令),Python 会<strong>将该模块的 <code>__name__</code> 赋值为 <code>"__main__"</code>。</strong></p></li><li><p>当一个模块被<strong>导入到其他模块</strong>中时(例如 <code>import 模块名</code>),该模块的 <code>__name__</code> 会<strong>被赋值为其模块名(即文件名,不含 <code>.py</code>)。</strong></p></li><li><p><strong>避免不必要的执行</strong>:<strong>如果没有这个判断,当脚本被导入到其他模块时,<code>mcp.run()</code> 会被自动执行</strong>,可能导致意外启动服务器、重复执行代码等问题,<strong>符合 Python 的模块化设计理念</strong>。</p></li></ul><p>假设这个脚本是一个服务器模块 <code>server.py</code>:</p><ul><li>直接运行 <code>python server.py</code> 时,<code>__name__ == "__main__"</code> 成立,服务器启动(<code>mcp.run()</code> 执行)。</li><li>其他脚本 <code>import server</code> 时,<code>__name__</code> 为 <code>"server"</code>,条件不成立,服务器不会启动,仅能调用脚本中的其他函数 / 类。</li></ul><p> <strong>测试 MCP Server</strong></p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ mcp dev txt_counter.py</span><br><span class="line">Starting MCP inspector...</span><br><span class="line">Proxy server listening on port 3000</span><br><span class="line"></span><br><span class="line">MCP Inspector is up and running at http://localhost:5173</span><br></pre></td></tr></table></figure><p><img src="/2025/07/25/%E8%AE%A1%E7%A7%91%E7%9B%B8%E5%85%B3%E8%87%AA%E6%84%9F%E7%9F%A5%E8%AF%86%E7%82%B9%E7%9A%84%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%A1%A5%E5%85%85/v2-a5e671c689907229a1d86162597e2da4_1440w.jpg" alt="img"></p><p><strong>接入 Claude</strong></p><p>最后一步就是把我们<strong>写好的 MCP 接入到 Claude Desktop</strong> 中。</p><p>在配置文件中添加以下内容,记得替换 <code>/Users/{username}</code> 为你的实际用户名,以及其他路径为你的实际路径。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"mcpServers"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"txt_counter"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"command"</span><span class="punctuation">:</span> <span class="string">"/Users/{username}/.local/bin/uv"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"args"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="string">"--directory"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="string">"/Users/{username}/work/mcp-learn/code-example-txt"</span><span class="punctuation">,</span> <span class="comment">// 你的项目路径(这里是我的)</span></span><br><span class="line"> <span class="string">"run"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="string">"txt_counter.py"</span> <span class="comment">// 你的 MCP Server 文件名</span></span><br><span class="line"> <span class="punctuation">]</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><ul><li><code>uv</code> 最好是绝对路径,推荐使用 <code>which uv</code> 获取。</li></ul><p>配置好后重启,如果没问题就能看到对应的 MCP Server 了。</p><p><img src="/2025/07/25/%E8%AE%A1%E7%A7%91%E7%9B%B8%E5%85%B3%E8%87%AA%E6%84%9F%E7%9F%A5%E8%AF%86%E7%82%B9%E7%9A%84%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%A1%A5%E5%85%85/v2-cb51fd06ef7663f05a5dd3da1aedeba2_1440w.jpg" alt="img"></p><p><strong>Step7. 实际使用</strong></p><p>接下来,我们通过一个简单的 prompt 进行实际测试:</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">能推测我当前桌面上 txt 文件名的含义吗?</span><br></pre></td></tr></table></figure><p>它可能会请求你的使用权限,如图一所示,你可以点击 <code>Allow for This Chat</code></p><p><img src="/2025/07/25/%E8%AE%A1%E7%A7%91%E7%9B%B8%E5%85%B3%E8%87%AA%E6%84%9F%E7%9F%A5%E8%AF%86%E7%82%B9%E7%9A%84%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%A1%A5%E5%85%85/v2-44e6397dc33c38875198e62d6fcd4317_1440w.jpg" alt="img"></p><p><img src="/2025/07/25/%E8%AE%A1%E7%A7%91%E7%9B%B8%E5%85%B3%E8%87%AA%E6%84%9F%E7%9F%A5%E8%AF%86%E7%82%B9%E7%9A%84%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%A1%A5%E5%85%85/v2-d99e12160a8ae3af75df8ddf7eddda24_1440w.jpg" alt="img"></p><p>看起来我们 MCP Server 已经正常工作了!</p>]]></content>
</entry>
<entry>
<title>面向对象(c艹)一个一个记</title>
<link href="/2025/07/03/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88c%E8%89%B9%EF%BC%89%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/"/>
<url>/2025/07/03/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88c%E8%89%B9%EF%BC%89%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/</url>
<content type="html"><![CDATA[<h2 id="面向过程程序设计OPP-Oriented-Procedural-Programming"><a href="#面向过程程序设计OPP-Oriented-Procedural-Programming" class="headerlink" title="面向过程程序设计OPP(Oriented Procedural Programming)"></a>面向过程程序设计OPP(Oriented Procedural Programming)</h2><p>将复杂过程简单的按功能分层从而解决问题<br>编程是<strong>面向操作</strong>的,编程的<strong>单位是函数</strong><br>规范的过程化程序: 过程的功能划分 / 编写</p><h4 id="功能与数据分离"><a href="#功能与数据分离" class="headerlink" title="功能与数据分离"></a>功能与数据分离</h4><p>不符合人们对现实世界的认识<br>要保持功能与数据的相容困难</p><h4 id="自顶向下的设计方法"><a href="#自顶向下的设计方法" class="headerlink" title="自顶向下的设计方法"></a>自顶向下的设计方法</h4><p>限制了软件的可重用性,<br>降低开发效率,<br>软件系统难以维护。</p><p><img src="/2025/07/03/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88c%E8%89%B9%EF%BC%89%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/4e9f2fabd86809a8bfd9e32c2ea93a51.png" alt="4e9f2fabd86809a8bfd9e32c2ea93a51"></p><p>结合在对象中,按对象组织</p><h2 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h2><p>子类自动共享父类数据和方法的机制,它由<strong>类的派生</strong>体现。一个子类直接继承父类的全部描述,同时<strong>可修改和扩充</strong>,继承是对父类的<strong>重用</strong>机制。</p><p>E.G</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 派生类:圆锥体</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Cone</span> : <span class="keyword">public</span> Circle</span><br><span class="line">{</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"><span class="type">double</span> height; <span class="comment">// 圆锥高度</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"><span class="built_in">Cone</span>(<span class="type">double</span> X , <span class="type">double</span> Y , <span class="type">double</span> r , <span class="type">double</span> h = <span class="number">1</span>)</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setHeight</span><span class="params">(<span class="type">double</span> h)</span> </span>;</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">getHeight</span><span class="params">()</span> <span class="type">const</span> </span>;</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">calculateArea</span><span class="params">()</span> <span class="type">const</span> </span>;</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">calculateVolume</span><span class="params">()</span> <span class="type">const</span></span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">printInfo</span><span class="params">()</span> <span class="type">const</span> </span>;</span><br><span class="line">……</span><br><span class="line">};</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这段代码定义了一个名为<code>Cone</code>(圆锥体)的派生类,它继承自<code>Circle</code>(圆形)基类。这种结构体现了面向对象编程中的<strong>继承特性</strong>,下面详细解释其各部分含义:</p><h3 id="1-类的继承关系"><a href="#1-类的继承关系" class="headerlink" title="1. 类的继承关系"></a>1. 类的继承关系</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Cone</span> : <span class="keyword">public</span> Circle</span><br></pre></td></tr></table></figure><ul><li><code>class Cone</code>:声明一个名为<code>Cone</code>的类(圆锥体)。</li><li><code>: public Circle</code>:表示<code>Cone</code>是<code>Circle</code>的<strong>公有派生类</strong>(<code>public</code>为<strong>继承方式</strong>)。<br> 这意味着:<code>Cone</code>会继承<code>Circle</code>中<strong>所有的非私有成员</strong>(包括<strong>成员变量和成员函数</strong>),可以直接使用基类的功能,同时扩展自己的特性。<br> (例如:圆形的圆心坐标<code>X,Y</code>、半径<code>r</code>等属性,圆锥体也需要,因此无需重复定义,直接继承即可。)</li></ul><h3 id="2-私有成员变量"><a href="#2-私有成员变量" class="headerlink" title="2. 私有成员变量"></a>2. 私有成员变量</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">double</span> height; <span class="comment">// 圆锥高度</span></span><br></pre></td></tr></table></figure><ul><li><code>private</code>:访问权限修饰符,标识该部分成员<strong>仅能在<code>Cone</code>类内部</strong>使用,外部无法直接访问。</li><li><code>double height</code>:定义了圆锥体特有的成员变量<code>height</code>(高度),这是<code>Cone</code>在基类<code>Circle</code>基础上扩展的属性(圆形没有高度,圆锥有)。</li></ul><h3 id="3-公有成员函数(接口)"><a href="#3-公有成员函数(接口)" class="headerlink" title="3. 公有成员函数(接口)"></a>3. 公有成员函数(接口)</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Cone</span>(<span class="type">double</span> X , <span class="type">double</span> Y , <span class="type">double</span> r , <span class="type">double</span> h = <span class="number">1</span>); <span class="comment">// 构造函数</span></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setHeight</span><span class="params">(<span class="type">double</span> h)</span></span>; <span class="comment">// 设置高度</span></span><br><span class="line"> <span class="function"><span class="type">double</span> <span class="title">getHeight</span><span class="params">()</span> <span class="type">const</span></span>; <span class="comment">// 获取高度</span></span><br><span class="line"> <span class="function"><span class="type">double</span> <span class="title">calculateArea</span><span class="params">()</span> <span class="type">const</span></span>; <span class="comment">// 计算表面积</span></span><br><span class="line"> <span class="function"><span class="type">double</span> <span class="title">calculateVolume</span><span class="params">()</span> <span class="type">const</span></span>; <span class="comment">// 计算体积</span></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">printInfo</span><span class="params">()</span> <span class="type">const</span></span>; <span class="comment">// 打印信息</span></span><br><span class="line"> ……</span><br></pre></td></tr></table></figure><p>这些是<code>Cone</code>类<strong>对外提供的</strong>接口,用于操作和访问类的成员,具体功能如下:</p><ul><li><p><strong>构造函数</strong> <code>Cone(...)</code>(<strong>名称与类名相同</strong>,<strong>无返回类型</strong>,<strong>可重载</strong>,):<br> 用于初始化圆锥体对象,参数包括:<code>X,Y</code>(圆心 / 顶点坐标,继承自<code>Circle</code>)、<code>r</code>(底面半径,继承自<code>Circle</code>)、<code>h</code>(高度,默认值为 1)。<br> 构造函数会<strong>先调用基类<code>Circle</code>的构造函数初始化继承的属性</strong>(如<code>X,Y,r</code>),<strong>再初始化自己的</strong><code>height</code>。</p><h5 id="可初始化成员变量"><a href="#可初始化成员变量" class="headerlink" title="可初始化成员变量"></a><strong>可初始化成员变量</strong></h5> <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyClass</span> {</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">int</span> value;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">MyClass</span>(<span class="type">int</span> val) : <span class="built_in">value</span>(val) {} <span class="comment">// 初始化列表(推荐,最直观显式)</span></span><br><span class="line"> <span class="comment">// 或在函数体中赋值:</span></span><br><span class="line"> <span class="comment">// MyClass(int val) { value = val; }</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><h5 id="可调用父类构造函数(继承场景)"><a href="#可调用父类构造函数(继承场景)" class="headerlink" title="可调用父类构造函数(继承场景)"></a><strong>可调用父类构造函数(继承场景)</strong></h5><ul><li><p>在派生类的构造函数中,<strong>必须显式调用</strong>父类的构造函数(除非父类有<strong>默认</strong>构造函数(如果类中<strong>未定义任何构造函数</strong>,编译器会自动生成一个<strong>隐式默认构造函数</strong>(无参数)。</p> <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Base</span>(<span class="type">int</span> x) {}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Derived</span> : <span class="keyword">public</span> Base {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Derived</span>(<span class="type">int</span> x, <span class="type">int</span> y) : <span class="built_in">Base</span>(x) {} <span class="comment">// 调用父类构造函数</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure></li></ul><h5 id="不能被声明为const、virtual或static"><a href="#不能被声明为const、virtual或static" class="headerlink" title="不能被声明为const、virtual或static"></a><strong>不能被声明为<code>const</code>、<code>virtual</code>或<code>static</code></strong></h5><ul><li>构造函数不能是<code>const</code>(因为它<strong>会修改对象状态</strong>)。</li><li>构造函数不能是<code>virtual</code>(虚函数<strong>依赖于对象</strong>的存在,而构造函数<strong>正在创建对象</strong>)。</li><li>构造函数不能是<code>static</code>(<strong>静态</strong>函数<strong>属于类</strong>,而<strong>构造</strong>函数<strong>属于对象</strong>)。</li></ul><h5 id="委托构造函数(C-11-)"><a href="#委托构造函数(C-11-)" class="headerlink" title="委托构造函数(C++11+)"></a><strong>委托构造函数(C++11+)</strong></h5><ul><li><p>构造函数可以<strong>调用同一个类的其他构造函数</strong>,避免代码重复。</p> <figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyClass</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">MyClass</span>(<span class="type">int</span> x) : <span class="built_in">value</span>(x) {}</span><br><span class="line"> <span class="built_in">MyClass</span>() : <span class="built_in">MyClass</span>(<span class="number">0</span>) {} <span class="comment">// 委托给另一个构造函数</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">int</span> value;</span><br><span class="line">};</span><br></pre></td></tr></table></figure></li></ul></li><li><p><strong>setter 和 getter 函数</strong>:</p><ul><li><code>setHeight(double h)</code>:设置圆锥的高度(修改<code>height</code>的值)。</li><li><code>getHeight() const</code>:返回圆锥的高度(读取<code>height</code>的值)。<br> 这是封装特性的体现:<strong>通过函数间接访问私有</strong>变量**(在内部)**,<strong>避免外部直接修改</strong>,保证数据安全性。</li></ul></li><li><p><strong>功能计算函数</strong>:</p><ul><li><code>calculateArea() const</code>:计算圆锥的表面积(基类<code>Circle</code>若有计算圆面积的函数,<strong>则此处重写</strong>为圆锥的表面积)。</li><li><code>calculateVolume() const</code>:计算圆锥的体积(圆锥特有的功能,基类<code>Circle</code>没有)。</li></ul></li><li><p><strong>信息打印函数</strong>:</p><ul><li><code>printInfo() const</code>:打印圆锥的所有信息(如圆心坐标、半径、高度、表面积、体积等),通常会结合继承自基类的信息和自身的信息。</li></ul></li></ul><h3 id="总结:结构的核心意义"><a href="#总结:结构的核心意义" class="headerlink" title="总结:结构的核心意义"></a>总结:结构的核心意义</h3><ul><li><strong>继承复用</strong>:<code>Cone</code>通过继承<code>Circle</code>,直接复用了圆形的属性(如圆心、半径),<strong>无需重复定义,减少代码冗余</strong>。</li><li><strong>扩展功能</strong>:在继承的基础上,<code>Cone</code><strong>增加</strong>了自身特有的属性(高度)和方法(体积计算、表面积计算等),实现了 “圆锥是一种特殊的圆形(带高度)” 的逻辑关系。</li><li><strong>封装接口</strong>:通过公有成员函数对外提供访问接口,<strong>隐藏内部实现细节</strong>(如<code>height</code>的存储方式),符合面向对象的封装原则。</li></ul><p>这种结构使得代码更具扩展性和维护性,例如未来若需要修改圆形的属性(如增加颜色),圆锥体也能自动继承该特性。这段代码定义了一个名为<code>Cone</code>的类,它是从<code>Circle</code>类派生而来的,这意味着<code>Cone</code>继承了<code>Circle</code>的属性和方法。这种继承关系形成了面向对象编程中的<strong>父子类结构</strong>。</p><h3 id="代码结构解析:"><a href="#代码结构解析:" class="headerlink" title="代码结构解析:"></a>代码结构解析:</h3><ol><li><strong>类定义</strong>:<ul><li><code>class Cone : public Circle</code>:<code>Cone</code>类公开继承自<code>Circle</code>类,因此<code>Cone</code>可以访问<code>Circle</code>的公有成员。</li></ul></li><li><strong>私有成员变量</strong>:<ul><li><code>double height;</code>:圆锥的高度,这是<code>Cone</code>类特有的属性。</li></ul></li><li><strong>构造函数</strong>:<ul><li><code>Cone(double X, double Y, double r, double h = 1)</code>:初始化圆锥的位置(继承自<code>Circle</code>的<code>X</code>和<code>Y</code>)、底面半径(继承自<code>Circle</code>的<code>r</code>)和高度<code>h</code>(默认值为 1)。</li></ul></li><li><strong>成员函数</strong>:<ul><li><code>setHeight(double h)</code>:设置圆锥的高度。</li><li><code>getHeight() const</code>:返回圆锥的高度。</li><li><code>calculateArea() const</code>:计算圆锥的表面积(可能包括底面积和侧面积)。</li><li><code>calculateVolume() const</code>:计算圆锥的体积。</li><li><code>printInfo() const</code>:打印圆锥的信息,可能包括位置、半径、高度、表面积和体积。</li></ul></li></ol><p><code>const</code> 关键字放在函数声明的后面,其作用是表明这个函数属于<strong>常量成员函数</strong>。如果写在<strong>前面</strong>则表示的是其<strong>返回值</strong>是常量</p><h3 id="1-常量成员函数的功能"><a href="#1-常量成员函数的功能" class="headerlink" title="1. 常量成员函数的功能"></a>1. 常量成员函数的功能</h3><ul><li><strong>保护对象状态</strong>:常量成员函数不可以对调用它的对象的非静态数据成员进行修改。</li><li><strong>适配常量对象</strong>:<strong>只有</strong>常量成员函数才能够被常量对象调用。</li></ul><h3 id="2-代码示例与说明"><a href="#2-代码示例与说明" class="headerlink" title="2. 代码示例与说明"></a>2. 代码示例与说明</h3><p>下面是一个包含常量成员函数的类:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Circle</span> {</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">double</span> radius;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Circle</span>(<span class="type">double</span> r) : <span class="built_in">radius</span>(r) {}</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 常量成员函数:不能修改对象状态</span></span><br><span class="line"> <span class="function"><span class="type">double</span> <span class="title">getRadius</span><span class="params">()</span> <span class="type">const</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> radius; <span class="comment">// 允许,因为没有修改成员变量</span></span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 常量成员函数:计算圆的面积</span></span><br><span class="line"> <span class="function"><span class="type">double</span> <span class="title">calculateArea</span><span class="params">()</span> <span class="type">const</span> </span>{</span><br><span class="line"> <span class="comment">// radius = 10.0; // 错误!不可以修改成员变量</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">3.14</span> * radius * radius;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 非常量成员函数:可以修改对象状态</span></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setRadius</span><span class="params">(<span class="type">double</span> r)</span> </span>{</span><br><span class="line"> radius = r; <span class="comment">// 允许,因为这不是常量成员函数</span></span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h3 id="3-常量对象与函数调用规则"><a href="#3-常量对象与函数调用规则" class="headerlink" title="3. 常量对象与函数调用规则"></a>3. 常量对象与函数调用规则</h3><ul><li><strong>常量对象</strong>:<strong>只能调用常量成员</strong>函数。</li><li><strong>非常量对象</strong>:<strong>既能调用常量成员函数,也能调用非常量成员函数</strong>。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">const</span> Circle <span class="title">c1</span><span class="params">(<span class="number">5.0</span>)</span></span>; <span class="comment">// 常量对象,同时进行了初始化</span></span><br><span class="line"><span class="type">double</span> area = c<span class="number">1.</span><span class="built_in">calculateArea</span>(); <span class="comment">// 正确,calculateArea是常量成员函数</span></span><br><span class="line"><span class="comment">// c1.setRadius(10.0); // 错误,常量对象不能调用非常量成员函数</span></span><br><span class="line"></span><br><span class="line"><span class="function">Circle <span class="title">c2</span><span class="params">(<span class="number">3.0</span>)</span></span>; <span class="comment">// 非常量对象</span></span><br><span class="line">c<span class="number">2.</span><span class="built_in">setRadius</span>(<span class="number">10.0</span>); <span class="comment">// 正确</span></span><br><span class="line">area = c<span class="number">2.</span><span class="built_in">calculateArea</span>(); <span class="comment">// 正确</span></span><br></pre></td></tr></table></figure><h3 id="4-技术原理"><a href="#4-技术原理" class="headerlink" title="4. 技术原理"></a>4. 技术原理</h3><ul><li><p>隐式<code>this</code>指针的类型</p><p> :</p><ul><li>在常量成员函数里,<code>this</code> 指针的类型是 <code>const ClassName*</code>。</li><li>在非常量成员函数中,<code>this</code> 指针的类型是 <code>ClassName*</code>。</li></ul></li></ul><h3 id="5-实际应用场景"><a href="#5-实际应用场景" class="headerlink" title="5. 实际应用场景"></a>5. 实际应用场景</h3><ul><li><strong>访问器(Getter)函数</strong>:通常会被声明为常量成员函数,比如 <code>getRadius()</code>。</li><li><strong>不修改对象的计算函数</strong>:像 <code>calculateArea()</code> 就属于这类函数。</li><li><strong>操作符重载</strong>:例如 <code>operator==</code> 通常也会被声明为常量成员函数。</li></ul><h3 id="6-注意要点"><a href="#6-注意要点" class="headerlink" title="6. 注意要点"></a>6. 注意要点</h3><ul><li><strong>函数重载</strong>:常量版本和非常量版本的同一函数可以同时存在。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyClass</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">const</span> <span class="type">char</span>* <span class="title">getData</span><span class="params">()</span> <span class="type">const</span> </span>{ <span class="keyword">return</span> constData; } <span class="comment">// 常量版本</span></span><br><span class="line"> <span class="function"><span class="type">char</span>* <span class="title">getData</span><span class="params">()</span> </span>{ <span class="keyword">return</span> data; } <span class="comment">// 非常量版本</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">char</span>* data;</span><br><span class="line"> <span class="type">const</span> <span class="type">char</span>* constData;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><ul><li><strong>可变数据成员(mutable)</strong>:被 <code>mutable</code> 修饰的数据成员,能够在常量成员函数中被修改。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Counter</span> {</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="keyword">mutable</span> <span class="type">int</span> accessCount; <span class="comment">// 可变数据成员</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">doSomething</span><span class="params">()</span> <span class="type">const</span> </span>{</span><br><span class="line"> accessCount++; <span class="comment">// 允许,因为accessCount是mutable的</span></span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h3 id="继承关系说明:"><a href="#继承关系说明:" class="headerlink" title="继承关系说明:"></a>继承关系说明:</h3><ul><li><strong>父类(基类)</strong>:<code>Circle</code>类(假设包含圆心坐标<code>X</code>、<code>Y</code>和半径<code>r</code>)。</li><li><strong>子类(派生类)</strong>:<code>Cone</code>类通过继承获得了<code>Circle</code>的属性,并添加了自己的属性<code>height</code>。</li></ul><h2 id="多态"><a href="#多态" class="headerlink" title="多态"></a>多态</h2><p>在<strong>继承体系结构</strong>中,同一消息<strong>为不同的对象接受时可产生完全不同的行动</strong></p><p>利用多态性用户可发送一个通用的信息,而将所有的实现细节都留给接受消<br>息的对象自行决定</p><h2 id="template"><a href="#template" class="headerlink" title="template"></a>template<typename t></typename></h2><p>template:声明这是一个模板。<br>typename T:声明一个<strong>类型参数T</strong>,T<strong>可以是任何类型</strong>(如int、double、string等)。<br>typename 也可以用 class 替代(如 template<class t>),两者在模板中<strong>含义相同</strong>。</class></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span><<span class="keyword">typename</span> T></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Stack</span> {</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> T buffer[<span class="number">100</span>]; <span class="comment">// 假设栈大小为100</span></span><br><span class="line"> <span class="type">int</span> top; <span class="comment">// 栈顶位置</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Stack</span>() : <span class="built_in">top</span>(<span class="number">-1</span>) {} <span class="comment">// 初始化栈顶为-1,表示空栈</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">bool</span> <span class="title">push</span><span class="params">(<span class="type">const</span> T& x)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (top >= <span class="number">99</span>) <span class="keyword">return</span> <span class="literal">false</span>; <span class="comment">// 栈满</span></span><br><span class="line"> buffer[++top] = x;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function">T <span class="title">pop</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (top < <span class="number">0</span>) <span class="keyword">throw</span> <span class="string">"Stack is empty!"</span>; <span class="comment">// 栈空</span></span><br><span class="line"> <span class="keyword">return</span> buffer[top--];</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line">Stack<<span class="type">int</span>> intStack; <span class="comment">// 存储int的栈</span></span><br><span class="line">Stack<<span class="type">double</span>> doubleStack; <span class="comment">// 存储double的栈</span></span><br><span class="line"></span><br><span class="line">intStack.<span class="built_in">push</span>(<span class="number">10</span>);</span><br><span class="line">doubleStack.<span class="built_in">push</span>(<span class="number">3.14</span>);</span><br></pre></td></tr></table></figure><h2 id="数据结构与数据访问"><a href="#数据结构与数据访问" class="headerlink" title="数据结构与数据访问"></a><strong>数据结构</strong>与数据访问</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> *p1 = (<span class="type">int</span> *)<span class="built_in">malloc</span>(<span class="built_in">sizeof</span>(<span class="type">int</span>) * length); </span><br><span class="line"><span class="built_in">free</span>(p1);</span><br><span class="line"><span class="type">int</span> *p2 = <span class="keyword">new</span> <span class="type">int</span> [length] ;</span><br><span class="line"><span class="keyword">delete</span> [ ]p2; <span class="comment">// 释放数组用法</span></span><br><span class="line"><span class="keyword">delete</span> p2; <span class="comment">// 释放单个元素</span></span><br></pre></td></tr></table></figure><h2 id="C-函数的新特性"><a href="#C-函数的新特性" class="headerlink" title="C++函数的新特性"></a>C++函数的新特性</h2><h4 id="引用"><a href="#引用" class="headerlink" title="引用"></a><strong>引用</strong></h4><p>对一个数据可以使用引用(reference)的方式声明,引用的作用是<strong>为一个变量起一个别名</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">int a ; </span><br><span class="line">int &b = a; // 声明b是int a的引用</span><br><span class="line">b = 20; // a = 20</span><br></pre></td></tr></table></figure><p>在一条语句中声明多个引用时<strong>应逐一声明</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span>& x=a, y = b,z = c ; <span class="comment">//error</span></span><br><span class="line"><span class="type">int</span> &x=a, &y=b, &z=c; </span><br></pre></td></tr></table></figure><p>声明引用变量<strong>必须进行初始化</strong>,引用未定义变量称悬挂引用。<br>将前面声明的引用重新变为另一变量的别名是个<strong>逻辑错误</strong>.</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> y =<span class="number">5</span>, z = <span class="number">3</span>; </span><br><span class="line"><span class="type">int</span> &x = z, &x = y</span><br></pre></td></tr></table></figure><p>&在此<strong>不是求地址运算</strong>,而是起<strong>标识</strong>作用。</p><p>引用声明完毕后,相当于目标变量名有<strong>两个名称</strong>,</p><p>声明一个引用,<strong>不是新定义了一个变量</strong>,引用本身<strong>不占存储单元</strong>,系统也不给引用分配存储单元。</p><p>引用即用别名引用这个变量,目的是为了消除指针</p><h4 id="引用传递的特点"><a href="#引用传递的特点" class="headerlink" title="引用传递的特点"></a>引用传递的特点</h4><p><strong>消除了复制大量数据的开销</strong>,有利提高执行效率;<br>在被调用函数中直接使用形参变量,提高可读性;<br>安全性较差,被调用函数能直接访问和修改调用者的数据。<br>fun( const T& value);<br>若要传递较大的对象,用常量引用参数模拟按值调用.<br>要指定引用常量,在参数声明的类型说明符前面加上const</p><h4 id="内联函数inline-以相应代码代替"><a href="#内联函数inline-以相应代码代替" class="headerlink" title="内联函数inline(以相应代码代替)"></a><strong>内联函数inline(以相应代码代替)</strong></h4><p>C++为<strong>降低小程序调用开销</strong>的一种机制。<br><strong>默认参数值 default parameter value</strong><br>函数参数的默认值使得在函数调用时<strong>可不指定参数。</strong></p><p><strong>建议性声明:不能含有复杂结构控制语句和递归调用</strong></p><h4 id="函数重载"><a href="#函数重载" class="headerlink" title="函数重载"></a><strong>函数重载</strong></h4><p>常用于处理<strong>不同数据类型</strong>而<strong>功能类似</strong>的<strong>同名</strong>函数;</p><h4 id="函数默认参数"><a href="#函数默认参数" class="headerlink" title="函数默认参数"></a>函数默认参数</h4><p>经常需要用<strong>相同的参数调用同一函数时</strong>,简化函数调用。<br>当函数调用时,若实参数个数少于形参数的总数时,<br>则所缺参数自动取函数参数表中设置的缺省值。:<br>当函数声明时,由<strong>右至左指定默认参数</strong>的值</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">volumn</span><span class="params">( <span class="type">int</span> length, <span class="type">int</span> width = <span class="number">1</span>, <span class="type">int</span> highth =<span class="number">1</span>)</span>;</span></span><br><span class="line"><span class="function"><span class="title">volumn</span><span class="params">(<span class="number">2</span>)</span></span>;</span><br><span class="line"><span class="built_in">volumn</span>(<span class="number">2</span>,<span class="number">2</span>); </span><br><span class="line"><span class="built_in">volumn</span>(<span class="number">2</span>, ,<span class="number">2</span>);<span class="comment">//中间参数默认</span></span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">volumn</span><span class="params">( <span class="type">int</span> length, <span class="type">int</span> width = <span class="number">1</span>, <span class="type">int</span> highth =<span class="number">1</span>)</span>;</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">volumn</span><span class="params">( <span class="type">int</span> length, <span class="type">int</span> width )</span>;</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">volumn</span><span class="params">( <span class="type">int</span> length)</span>;</span></span><br><span class="line"><span class="function"><span class="title">volumn</span><span class="params">(<span class="number">1</span>)</span></span>;<span class="comment">//有二义性,回出错</span></span><br><span class="line"><span class="built_in">volumn</span>(<span class="number">2</span>,<span class="number">3</span>); </span><br><span class="line"><span class="built_in">volumn</span>(<span class="number">1</span>,<span class="number">2</span> ,<span class="number">3</span>);</span><br></pre></td></tr></table></figure><h2 id="初识类"><a href="#初识类" class="headerlink" title="初识类"></a>初识类</h2><h2 id="封装-encapsulate"><a href="#封装-encapsulate" class="headerlink" title="封装(encapsulate)"></a>封装(encapsulate)</h2><ul><li>把全部属性和全部行为封装在一起,<br> 形成一个不可分割的独立单位(即对象)。</li><li>信息隐蔽(information hiding)<br> 对象的外部不能直接地存取对象属性,只能通过几个允许外部使用的服务与对象发生联系。</li><li><strong>对象间通发送消息</strong>进行交互.</li></ul><p>类是面向对象编程的<strong>程序基本单位</strong><br>程序<strong>模块</strong>是各种<strong>由类构成的</strong><br>类是<strong>逻辑上相关</strong>数据和函数的<strong>封装</strong><br>类是对问题的<strong>抽象描述</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> 类名 {</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"><span class="comment">//私有数据成员和成员函数;</span></span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line"><span class="comment">//保护数据成员和成员函数;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"><span class="comment">//公有数据成员和成员函数;</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><h4 id="成员函数"><a href="#成员函数" class="headerlink" title="成员函数"></a>成员函数</h4><p>在<strong>类的外部</strong>定义成员函数</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">返回类型 类名::成员函数名(参数列表)</span><br><span class="line">{</span><br><span class="line">函数定义体</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">A::show</span><span class="params">( )</span></span>{ cout << m_a << m_b <<endl;}</span><br></pre></td></tr></table></figure><p>在<strong>类内</strong>直接定义成员函数, <strong>默认创建为内联</strong>函数<br>如果成员函数在类体外定义,要<strong>用inline声明为内联</strong>函数</p><p><strong>域运算符“∷”</strong>,<strong>成员运算符“.”</strong></p><p>在类外定义函数时,应指明成员函数的<strong>作用域</strong></p><p>在成员函数引用本对象的数据成员时,只需<strong>直接写数据成员名,</strong><br>这时C++系统会把它默认为本对象的数据成员。</p><h4 id="保护-protected"><a href="#保护-protected" class="headerlink" title="保护 protected"></a><strong>保护</strong> protected</h4><p><strong>除了类本身的成员函数和说明为友元函数或友元类的成员函数可以访问保</strong></p><p><strong>护成员外,该类的派生类的成员也可以访问</strong><strong>。</strong></p><p><strong>private 在首次出现时可以忽略</strong></p><h2 id="对象的使用"><a href="#对象的使用" class="headerlink" title="对象的使用"></a><strong>对象的使用</strong></h2><p><strong>同类对象之间可以相互赋值</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Time tA, tB;</span><br><span class="line">tA.<span class="built_in">set</span> (<span class="number">15</span>,<span class="number">6</span>,<span class="number">0</span>);</span><br><span class="line">tB = tA;</span><br></pre></td></tr></table></figure><p>成员访问运算符“.” 和**“->”(对象指针名->成员名)**</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cout << t.hour << pTime -><span class="built_in">min</span>(相当于访问所指对象的成员min) << (*pTime).sec;</span><br></pre></td></tr></table></figure><p>软件工程的一个最基本的原则就是<strong>将接口与实现分离</strong>,信息隐蔽是软件工程中一个非常重要的概念。</p><p><strong>自定义类库头文件.h</strong><br>文件中有用户自行设计的类的定义,包括类的外部接口(公有成员函数的原型)。任何需要使用这些类的源程序,只要在文件中包含这些头文件即可。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//point.h</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Point</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">distance</span><span class="params">(Point & p)</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setX</span><span class="params">(<span class="type">double</span> i)</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setY</span><span class="params">(<span class="type">double</span> j)</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"><span class="type">double</span> x;</span><br><span class="line"><span class="type">double</span> y;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//point.cpp</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><cmath></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">"point.h"</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">Point::distance</span><span class="params">(Point & p)</span></span>{<span class="comment">//定义要指明作用域</span></span><br><span class="line"><span class="keyword">return</span> <span class="built_in">sqrt</span>((p.x-x)*(p.x-x)+(p.y-y)*(p.y-y)); }</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Point::setX</span><span class="params">(<span class="type">double</span> i)</span></span>{x=i;}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Point::setY</span><span class="params">(<span class="type">double</span> j)</span></span>{y=j;}</span><br></pre></td></tr></table></figure><p>在面向对象的程序开发中,一般做法是将<strong>类的声明</strong>放在指定的头文件中,用户如果想用该类,只要把有关的头文件包含进来即可,不必在程序中重复书写类的声明,在<strong>程序中</strong>就可以用该类来<strong>定义</strong>对象.为了实现信息隐蔽,对类成员函数的定义一般不放在头文件中,而另外放在一个文件中。</p><h4 id="构造函数与析构函数"><a href="#构造函数与析构函数" class="headerlink" title="构造函数与析构函数"></a>构造函数与析构函数</h4><p>(自定义)<strong>默认构造函数</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><类名>::<默认构造函数名>()</span><br><span class="line">{ }</span><br><span class="line">Time::<span class="built_in">Time</span>( )</span><br><span class="line">{ hour=min=sec = <span class="number">0</span>; }</span><br></pre></td></tr></table></figure><p><strong>析构函数</strong></p><p>构造函数的反函数,析构函数是用于<strong>取消对象成员</strong>函数,<br>当一个对象生命期结束时,系统自动调用析构函数。</p><ul><li>析构函数名字为**符号“~”**加类名;<ul><li>析构函数<strong>没有参数和返回值</strong>。<ul><li>一个类中只可能定义一个析构函数,<br> 析构函数<strong>不能重载</strong>。<ul><li>析构函数的作用<br> 进行清除对象,释放内存等;</li></ul></li></ul></li></ul></li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">类名::</span><br><span class="line">~默认析构函数名()</span><br><span class="line">{ }<span class="comment">//空函数</span></span><br></pre></td></tr></table></figure><p>自动调用<br>(1) 一个对象当其结束生命周期时 ;<br>(2) 使用<strong>new运算符创建的对象</strong>,<br>在<strong>使用delete运算符释放</strong>该对象时;<br>一般析构函数的<strong>调用顺序</strong>与构造函数<strong>相反</strong>。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">class Rational{</span><br><span class="line">public:</span><br><span class="line">Rational(int nn=1,int mm=1); //构造函数</span><br><span class="line">void print() ; // 输出化简的 分子/分母</span><br><span class="line">void simple(); // 约分</span><br><span class="line">double getValue(); // 返回分数值</span><br><span class="line">Rational add(Rational & A); // r = r1.add(r2); </span><br><span class="line">void sub(Rational & A , Rational &B); // r.sub(r1,r2);</span><br><span class="line">Rational mul(Rational & A);</span><br><span class="line">void _div(Rational & A, Rational &B); </span><br><span class="line">private:</span><br><span class="line">int m; // 分母</span><br><span class="line">int n; // 分子</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>一般情况下,如果类中的数据<strong>都在栈里</strong>,程序员不需要开发<strong>自定义的拷贝构造函数</strong></p><p><strong>默认拷贝构造函数</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">A b ( a )</span><br></pre></td></tr></table></figure><p>对象复制与对象赋值是<strong>不同的</strong></p><p>静态数据成员的初始化与一般数据成员不同,<strong>外部</strong>静态数据成员初始化的格式如下:</p><p><类型> <类名>::<静态数据成员> = <值>;</p><p>3)在引用静态数据成员时采用格式: </p><p><strong><类名>::<静态数据成员></strong> <对象名>. <静态数据成员></p><p>静态数据成员 vs 全局变量 </p><p>有了静态数据成员,<strong>各对象之间(即不依赖于对象使用)<strong>的数据有了沟通的渠道,实现数据共享 。 全局变量</strong>破坏了封装的原则</strong>,不符合面向对象程序的要求。 </p><p>公用静态数据成员与全局变量的作用域不同 </p><p>静态数据成员的作用域<strong>只限于定义该类的作用域内</strong> </p><p>静态成员函数<strong>只能访问静态数据成员、静态成员函数和类以外的函数和数据,不能访问类中的非静态数据成员(因为非静态数据成员只有对象存在时才有意义)</strong>。但静态数据成员和静态成员函数可由任意访问权限许可的函数访问。和一般成员函数类似,静态成员函数也有访问限制,私有静态成员函数不能由外界访问。静态成员函数<strong>没有this指针</strong>,因此,静态成员函数<strong>只能直接访问类中的静态成员</strong>,若要访问类中的非静态成员时,<strong>必须借助对象名或指向对象的指针</strong>。 </p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><iostream.h></span></span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Counter</span></span><br><span class="line">{ <span class="type">static</span> <span class="type">int</span> num ;</span><br><span class="line"> <span class="keyword">public</span> :</span><br><span class="line"> <span class="built_in">Counter</span>( ){ num++; }</span><br><span class="line"> ~<span class="built_in">Counter</span>(){ num--; }</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setnum</span> <span class="params">( <span class="type">int</span> i )</span> </span>{ num = i ; }</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">shownum</span><span class="params">()</span> </span>{ cout << num << <span class="string">'\t'</span> ; }</span><br><span class="line"> <span class="function"><span class="type">static</span> <span class="type">int</span> <span class="title">get</span><span class="params">()</span></span>{ <span class="keyword">return</span> num ; }</span><br><span class="line">} ;</span><br><span class="line"><span class="type">int</span> Counter :: num = <span class="number">0</span> ;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{ Counter a ; a.<span class="built_in">shownum</span>() ;</span><br><span class="line">Counter b ; b.<span class="built_in">shownum</span>() ;</span><br><span class="line">cout<<Counter::<span class="built_in">get</span>()<<endl;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>static 数据是<strong>类共有</strong>的,static 函数可以类名调用,也可以对象调用,<strong>普通成员函数能访问static数据</strong></p><p>const成员变量<strong>只能由构造函数通过初始化列表对该数据成员进行初始化</strong></p><p>若<strong>成员函数不修改对象</strong>,则声明为const.</p><p>const关键词可以<strong>参与区分重载函数</strong>。</p><p>const 对象<strong>只能调用它的const 成员函数</strong>,而不能调用其他成员函数。</p><p>直接初始化</p><p><strong>分配空间的同时进行初始化</strong>. 一般数组成员较少.</p><p>Box b[3] = {Box(1),Box(1,1),Box(1,1,1)}; </p><p>间接初始化</p><p><strong>先分配空间</strong>,<strong>之后完成初始化</strong>. </p><p>Box a[50];//先调用默认 </p><p>for( int i = 0; i<50;i++){ a[i].set(i, i, i); }</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Box box [<span class="number">3</span>] ;</span><br><span class="line">Box box [<span class="number">3</span>] = {<span class="built_in">Box</span>(<span class="number">1</span>),<span class="built_in">Box</span>(<span class="number">1</span>,<span class="number">1</span>),<span class="built_in">Box</span>(<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>)}; </span><br><span class="line">Box box [<span class="number">3</span>] = {<span class="built_in">Box</span>(),<span class="built_in">Box</span>(),<span class="built_in">Box</span>()};</span><br><span class="line">Box box [<span class="number">3</span>] = {Box, Box ,Box };</span><br><span class="line">Box box [<span class="number">3</span>] = {<span class="built_in">Box</span>(<span class="number">1</span>),<span class="built_in">Box</span>(<span class="number">2</span>), };</span><br><span class="line">Box box [<span class="number">3</span>] = {<span class="built_in">Box</span>(<span class="number">1</span>),<span class="built_in">Box</span>(<span class="number">1</span>) };</span><br><span class="line">Box box [<span class="number">3</span>] = {<span class="built_in">Box</span>(<span class="number">1</span>) };</span><br><span class="line">Box box [<span class="number">3</span>] = { };</span><br><span class="line">Box box [<span class="number">3</span>] = { <span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>};</span><br><span class="line">Box *p =<span class="keyword">new</span> Box[<span class="number">3</span>];</span><br><span class="line">Box *p =<span class="keyword">new</span> Box[<span class="number">3</span>]{<span class="built_in">Box</span>(<span class="number">1</span>),<span class="built_in">Box</span>(<span class="number">1</span>,<span class="number">1</span>),<span class="built_in">Box</span>(<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>) };</span><br></pre></td></tr></table></figure><h2 id="类的组合"><a href="#类的组合" class="headerlink" title="类的组合"></a>类的组合</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Point</span></span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"> Point(<span class="type">int</span>=<span class="number">0</span>,<span class="type">int</span>=<span class="number">0</span>);</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">print</span><span class="params">( )</span> </span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setX</span><span class="params">(<span class="type">int</span> x)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setY</span><span class="params">(<span class="type">int</span> y)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">getX</span><span class="params">()</span></span>;</span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">getY</span><span class="params">()</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">int</span> x; </span><br><span class="line"> <span class="type">int</span> y;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Circle</span> </span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Circle</span>(<span class="type">double</span> r, Point p);</span><br><span class="line"> <span class="built_in">Circle</span>(<span class="type">double</span> r, <span class="type">int</span> x, <span class="type">int</span> y);</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">setCenter</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y)</span></span>;</span><br><span class="line"> <span class="function">Point <span class="title">getCenter</span> <span class="params">()</span></span>;</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> <span class="type">double</span> radius;</span><br><span class="line"> Point center;<span class="comment">//类的嵌套 </span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><h4 id="构造函数-必须首先初始化内嵌对象的数据"><a href="#构造函数-必须首先初始化内嵌对象的数据" class="headerlink" title="构造函数: 必须首先初始化内嵌对象的数据"></a>构造函数: 必须首先初始化内嵌对象的数据</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Circle ::<span class="built_in">Circle</span>(<span class="type">double</span> r, <span class="type">int</span> x, <span class="type">int</span> y):<span class="built_in">radius</span>(r), <span class="built_in">center</span>(x,y){ }</span><br><span class="line">Circle ::<span class="built_in">Circle</span>(<span class="type">double</span> r, Point p):<span class="built_in">radius</span>(r), <span class="built_in">center</span>(p){ }</span><br></pre></td></tr></table></figure><h4 id="成员函数-可以使用内嵌对象调用其函数-注意访问权限控制!"><a href="#成员函数-可以使用内嵌对象调用其函数-注意访问权限控制!" class="headerlink" title="成员函数: 可以使用内嵌对象调用其函数. 注意访问权限控制!"></a>成员函数: 可以使用内嵌对象调用其函数. 注意访问权限控制!</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">setCenter</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y)</span></span>{</span><br><span class="line"> center.<span class="built_in">setX</span>(x); <span class="comment">//center.x = x; compiler error!</span></span><br><span class="line"> center.<span class="built_in">setY</span>(y); <span class="comment">//center.y = y; compiler error!</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="成员对象的初始化"><a href="#成员对象的初始化" class="headerlink" title="成员对象的初始化"></a>成员对象的初始化</h4><p>一个对象如果有**“成员对象”<strong>(即它的成员数据不是普通类型,而是“类”类型的),那么在</strong>实现构造函数时应对“成员对象”进行初始化**)</p><p>方式是在构造函数中<strong>增加构造参数,指明“成员对象”构造的方式</strong></p><p>若没有“成员对象”构造方式的声明,系统<strong>默认调用“成员对象”的无参的构造函数。</strong></p><h4 id="组合关系"><a href="#组合关系" class="headerlink" title="组合关系"></a>组合关系</h4><p>一个类包含另一个类的对象</p><p>描述<strong>整体拥有部分</strong>的关系,即<strong>has-a关系</strong></p><p>该类不与其他类共享对象的引用。即**“整体”端重数只能是1**</p><p>如果这种类的对象生命周期结束,<strong>被包含的对象的生命周期也会结束。</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Textfield</span>;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Botton</span>;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DialogueWindow</span>{</span><br><span class="line">Textfield textfield; <span class="comment">//data member</span></span><br><span class="line">Botton botton; <span class="comment">//data member</span></span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DialogueWindow</span>{</span><br><span class="line">Textfield textfield1; <span class="comment">//data member</span></span><br><span class="line"> Textfield textfield2; <span class="comment">//data member</span></span><br><span class="line">Botton botton1; <span class="comment">//data member</span></span><br><span class="line"> Botton botton2; <span class="comment">//data member</span></span><br><span class="line">};</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="依赖关系"><a href="#依赖关系" class="headerlink" title="依赖关系"></a>依赖关系</h4><p>描述两个类对象之间<strong>短暂的相互作用</strong></p><p>依赖关系表示一个类的对象<strong>短暂使用了另一个类对象</strong>,代表类之间**“uses-a”关系**</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Time</span>{</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"><span class="type">int</span> hour;</span><br><span class="line"><span class="type">int</span> minute;</span><br><span class="line"><span class="type">int</span> second;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">print</span><span class="params">()</span></span>{ cout<<hour<<“ ”<<minute<<“ ”<<second<<endl;}</span><br><span class="line">};</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Printer</span> { <span class="comment">// 打印机类</span></span><br><span class="line"><span class="keyword">public</span>: <span class="function"><span class="type">void</span> <span class="title">print</span><span class="params">()</span></span>{...} <span class="comment">// 打印</span></span><br><span class="line">}; </span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span> <span class="comment">// 学生类</span></span><br><span class="line">{ <span class="comment">// 使用打印机</span></span><br><span class="line"><span class="keyword">public</span>: <span class="function"><span class="type">void</span> <span class="title">usePrinter</span><span class="params">(Printer &p)</span></span>{ p.<span class="built_in">print</span>(); }</span><br><span class="line"> ……</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line">Printer printer; Student studnet;</span><br><span class="line">student.<span class="built_in">usePrinter</span>(printer); </span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Student类的成员<strong>不包含打印机Printer的对象或者指针</strong>,即二者<strong>不具有“拥有has-a’关系</strong>。<strong>只有学生对象调用usePrinter( )函数时,学生对象与打印机对象才建立关系</strong>,并且在该<strong>函数执行完毕后,二者关系就结束了</strong>。一种<strong>短暂的”使用关系”</strong>,即“use-a”关系。依赖关系除了<strong>被依赖方作为依赖方的函数参数,还可能作为依赖方的函数中的临时对象</strong>。</p><h2 id="友元"><a href="#友元" class="headerlink" title="友元"></a>友元</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><iostream></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span>{</span><br><span class="line"><span class="keyword">private</span>: <span class="type">int</span> x;</span><br><span class="line">Public: <span class="built_in">A</span>(){x=<span class="number">1</span>;}</span><br><span class="line"> <span class="keyword">friend</span> <span class="keyword">class</span> <span class="title class_">B</span>;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">B</span>{</span><br><span class="line">Private: <span class="type">char</span> c;</span><br><span class="line">Public: <span class="built_in">B</span>() {c=<span class="string">'c'</span>;}</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">something</span><span class="params">()</span></span>{</span><br><span class="line"> A instance;</span><br><span class="line">cout<<instance.x<<endl;</span><br><span class="line">}</span><br><span class="line">};</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line">B b_instance;</span><br><span class="line">b_instance.<span class="built_in">something</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="友元-函数-与友元类"><a href="#友元-函数-与友元类" class="headerlink" title="友元 函数 与友元类"></a>友元 函数 与友元类</h2><h4 id="友元函数和友元类"><a href="#友元函数和友元类" class="headerlink" title="友元函数和友元类"></a>友元函数和友元类</h4><p>可以访问另一个类的<strong>私有和保护(稍后更多)成员(区别于组合)</strong></p><p>友元函数不是类的成员函数</p><p>友元函数<strong>在类范围之外定义</strong></p><h4 id="友元的特性"><a href="#友元的特性" class="headerlink" title="友元的特性"></a>友元的特性</h4><p>友元是**“给予”的,而不是“索要”的**</p><p><strong>非对称</strong>性(如果 B 是 A 的友元,A 不一定是 B 的友元)</p><p><strong>非传递</strong>性(如果 A 是 B 的友元,B 是 C 的友元,A 不一定是 C 的友元) </p><h4 id="友元的主要用途"><a href="#友元的主要用途" class="headerlink" title="友元的主要用途"></a>友元的主要用途</h4><p>提供了一种访问类成员的更方便快捷的途径</p><p>为运算符重载的实现提供了更方便的途径</p><p>友元<strong>可以访问类的任何成员(可不通过成员函数)</strong>,这破环了类的封装性,因此要谨慎使用友元</p><p><strong>有权从类外部更改类的内部状态</strong>。 因此推荐使用成员函数而不是友元来改变状态 </p><h4 id="friend-声明"><a href="#friend-声明" class="headerlink" title="friend 声明"></a>friend 声明</h4><p><strong>friend 函数</strong> </p><p> Keyword friend</p><p>friend int myFunction( int x );</p><p> <strong>声明在类内</strong>,<strong>保证这个函数可以在类外访问类成员</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Accumulator</span>{</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="type">int</span> m_value;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Accumulator</span>() { m_value = <span class="number">0</span>; } </span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> value)</span> </span>{ m_value += value; }</span><br><span class="line"> <span class="comment">// 声明reset() 函数是本类的友元函数</span></span><br><span class="line"> <span class="function"><span class="keyword">friend</span> <span class="type">void</span> <span class="title">reset</span><span class="params">(Accumulator &accumulator)</span></span>;</span><br><span class="line">};</span><br><span class="line"><span class="comment">// reset() 现在是 Accumulator 的友元</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">reset</span><span class="params">(Accumulator &accumulator)</span></span>{</span><br><span class="line"> <span class="comment">// 可以直接访问Accumulator 对象的任何数据</span></span><br><span class="line"> accumulator.m_value = <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="friend-类"><a href="#friend-类" class="headerlink" title="friend 类"></a>friend 类</h4><p>在类名前加 friend <strong>保证该类可以访问类成员</strong>,可以让整个类成为另一个类的友元。 这使<strong>友元类的所有成员函数都可以访问其他类的私有成员</strong>。友元类的<strong>所有函数都是友元函数</strong> </p><p>也可以不把整个类声明为友元, 仅仅只声明一个或多个函数为另一个类的友元函数. 这类似于声明普通函数成为友元,<strong>除了使用包含 className:: 前缀</strong></p><p>友元常用于<strong>定义重载运算符时</strong>。当两个或多个类需要以一种亲密的方式一起工作时,<strong>不常使用友元</strong>。使一个<strong>类</strong>成为友元<strong>只需要作为前向声明</strong>该类存在。 但是,使特定的类的<strong>成员函数</strong>成为友元则需要<strong>首先看到成员函数类的完整声明</strong>.</p><h2 id="继承-不允许继承循环"><a href="#继承-不允许继承循环" class="headerlink" title="继承(不允许继承循环)"></a><strong>继承</strong>(<strong>不允许继承循环</strong>)</h2><h4 id="继承的概念"><a href="#继承的概念" class="headerlink" title="继承的概念"></a><strong>继承的概念</strong></h4><p><strong>派生类具有基类的特性</strong></p><ul><li><p><strong>共享</strong>基类的<strong>成员函数</strong></p></li><li><p>使用基类的数据成员</p></li></ul><p><strong>派生类新增成员(拓展)</strong></p><ul><li><p>定义自己的数据成员</p></li><li><p>定义独特的成员函数</p></li></ul><p><strong>派生类改造基类</strong></p><ul><li><strong>重写基类某些成员函数</strong></li></ul><p>C++中单继承派生类的定义形式如下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> 派生类名 : [继承方式] 基类名</span><br><span class="line">{</span><br><span class="line">派生类成员声明;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>继承方式包括:</p><p>public(公有继承)<br>private(私有继承,<strong>默认</strong>)<br>protected(保护继承)(<strong>保护成员在本类与派生类中能直接访问</strong>)</p><p>C++中多重继承派生类的定义形式如下</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> 派生类名: [继承方式] 基类名<span class="number">1</span>, [继承方式] 基类名<span class="number">2</span>,…, </span><br><span class="line">[继承方式] 基类名n</span><br><span class="line">{</span><br><span class="line">派生类成员声明;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Assistant</span> : <span class="keyword">protected</span> Student, Teacher<span class="comment">//对Teacher默认是私有继承</span></span><br><span class="line">{ …… };</span><br></pre></td></tr></table></figure><p><img src="/2025/07/03/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88c%E8%89%B9%EF%BC%89%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/fa7f516ffbe998538989d54213bb5837.png" alt="fa7f516ffbe998538989d54213bb5837"></p><p><strong>继承方式决定了基类成员在派生类中的访问权,</strong></p><p><strong>这种访问来自两个方面</strong>:</p><ul><li><strong>派生类中</strong></li></ul><p>新增函数成员访问从基类继承来的成员</p><ul><li><strong>派生类外部</strong></li></ul><p>通过派生类的对象访问从基类继承的成员</p><p><img src="/2025/07/03/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88c%E8%89%B9%EF%BC%89%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/94de02253d18c73529beb0753b328c5d.png" alt="94de02253d18c73529beb0753b328c5d"></p><p><img src="/2025/07/03/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88c%E8%89%B9%EF%BC%89%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/98f37468b772cd29ba63bb8ba5dfef81.png" alt="98f37468b772cd29ba63bb8ba5dfef81"></p><h4 id="访问私有继承的成员"><a href="#访问私有继承的成员" class="headerlink" title="访问私有继承的成员"></a>访问私有继承的成员</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span> { <span class="type">int</span> number; <span class="type">char</span> school[<span class="number">10</span>]; </span><br><span class="line"><span class="keyword">protected</span>: <span class="type">char</span> name[<span class="number">10</span>]; <span class="type">char</span> sex; </span><br><span class="line"><span class="keyword">public</span>: <span class="function"><span class="type">void</span> <span class="title">input_data</span><span class="params">( )</span></span>; <span class="function"><span class="type">void</span> <span class="title">print</span><span class="params">()</span></span>; };</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CollegeStudent</span> : <span class="keyword">private</span> Student</span><br><span class="line">{ <span class="type">char</span> major[<span class="number">10</span>]; <span class="comment">//新增成员:专业</span></span><br><span class="line"><span class="keyword">public</span>: </span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">input_major</span><span class="params">( )</span></span>{cin>>major ; } </span><br><span class="line"><span class="comment">// 输入专业</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">print</span><span class="params">( )</span></span>{ Student::<span class="built_in">print</span>(); </span><br><span class="line"><span class="comment">// 输出信息</span></span><br><span class="line">cout<<“name:”<<name <<“ sex:”<<sex <<endl; </span><br><span class="line">cout<<<span class="string">"major:"</span><<major<<endl; } </span><br><span class="line">};</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span> { …… };</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CollegeStudent</span> : <span class="keyword">private</span> Student{...};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line">Student s; s.<span class="built_in">input_data</span>(); s.<span class="built_in">print</span>();</span><br><span class="line">CollegeStudent cs;</span><br><span class="line">cs.<span class="built_in">input_data</span>(); <span class="comment">// 错误</span></span><br><span class="line">cs.<span class="built_in">input_major</span>();</span><br><span class="line">cs.<span class="built_in">print</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>; }</span><br></pre></td></tr></table></figure><p>CollegeStudentl类访问基类,Student的能力没有变化,</p><p>在私有继承的情况下,通过派生类**对象(并非类内)**无法访问基类的任何成员</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">A</span></span><br><span class="line">{ <span class="keyword">public</span> :</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">get_XY</span><span class="params">( )</span> </span>; </span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">put_XY</span><span class="params">( )</span> </span>; </span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line"><span class="type">int</span> x, y;</span><br><span class="line">};</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">A::get_XY</span><span class="params">( )</span></span></span><br><span class="line"><span class="function"></span>{ cin >> x >> y ; }</span><br><span class="line"><span class="type">void</span> A:: <span class="built_in">put_XY</span>( )</span><br><span class="line">{ cout << <span class="string">"x = "</span><< x << <span class="string">", y = "</span> << y << <span class="string">'\n'</span> ; }</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">B</span> : <span class="keyword">public</span> A</span><br><span class="line">{ <span class="keyword">public</span> :</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">get_S</span><span class="params">()</span> </span>{ s = x * y ; <span class="keyword">return</span> s ; } </span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line"><span class="type">int</span> s;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">C</span> : <span class="keyword">public</span> B</span><br><span class="line">{ <span class="keyword">public</span> : </span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">get_H</span><span class="params">()</span> </span>{ cin >> h ; } </span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">get_V</span><span class="params">()</span> </span>{v = <span class="built_in">get_S</span>() * h ; <span class="keyword">return</span> v ; }</span><br><span class="line"><span class="keyword">protected</span>: </span><br><span class="line"><span class="type">int</span> h, v;</span><br><span class="line">};</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{ A objA ;</span><br><span class="line">B objB ;</span><br><span class="line">C objC ;</span><br><span class="line">objA.<span class="built_in">get_XY</span>() ;</span><br><span class="line">objA.<span class="built_in">put_XY</span>() ;</span><br><span class="line">objB.<span class="built_in">get_XY</span>() ;</span><br><span class="line">cout << <span class="string">"S = "</span> << objB.<span class="built_in">get_S</span>() << endl ;</span><br><span class="line">objC.<span class="built_in">get_XY</span>() ;</span><br><span class="line">objC.<span class="built_in">get_H</span>();</span><br><span class="line">cout << <span class="string">"V = "</span> << objC.<span class="built_in">get_V</span>() << endl ;}</span><br></pre></td></tr></table></figure><p><img src="/2025/07/03/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88c%E8%89%B9%EF%BC%89%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/a87a6f027be2e1b66c1c2497d61e59f3.png" alt="a87a6f027be2e1b66c1c2497d61e59f3"></p><p><img src="/2025/07/03/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88c%E8%89%B9%EF%BC%89%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/4d2fe503e36e73613dfe44ad25b9ed9e.png" alt="4d2fe503e36e73613dfe44ad25b9ed9e"></p><p><strong>三种继承方式的对比</strong></p><p>一般采用不会改变基类成员访问权限的<strong>公有继承</strong>。</p><p><strong>私有继承:</strong></p><p>基类的可被继承的成员都成了其直接派生类的私有成员,</p><p>无法再进一步派生,</p><p>实际上私有继承相当于终止了基类成员的继续派生。</p><p><strong>保护继承</strong>:</p><p>基类的可被继承的成员都成了直接派生类的保护成员,</p><p>保护继承保证了最上层基类的成员依然能被继承树中的</p><p>次级子类所继承。</p><p><strong>访问级别不能升只能降,一层一层来看即可</strong></p><p><img src="/2025/07/03/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88c%E8%89%B9%EF%BC%89%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/42f4358cb52c4ca97743131bb9022ee9.png" alt="42f4358cb52c4ca97743131bb9022ee9"></p><h2 id="派生类的构造函数"><a href="#派生类的构造函数" class="headerlink" title="派生类的构造函数"></a><strong>派生类的构造函数</strong></h2><p><strong>派生类的构造与析构函数</strong></p><p>• <strong>创建派生类对象时调用基类的构造函数来初始化基类数据。</strong></p><p>• <strong>执行派生类的析构函数时,基类的析构函数也将被调用。</strong></p><p><img src="/2025/07/03/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88c%E8%89%B9%EF%BC%89%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/dcef7a843dbe532605e80a1b95c20b54.png" alt="dcef7a843dbe532605e80a1b95c20b54"></p><p><strong>派生类构造函数的定义方式:</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">派生类名(参数总表):基类名(基类构造函数参数表<span class="number">1</span>), 对象成员(参数表<span class="number">2</span>)</span><br><span class="line">{</span><br><span class="line">派生类成员初始化;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">Student</span>(<span class="type">int</span> no, <span class="type">char</span> name[],<span class="type">char</span> sex):<span class="built_in">Person</span>(name,sex)</span><br><span class="line">{ id = no; }</span><br><span class="line"><span class="built_in">Student</span>(<span class="type">int</span> no, <span class="type">char</span> name[],<span class="type">char</span> sex):</span><br><span class="line"><span class="built_in">Person</span>(name,sex), <span class="built_in">id</span>( no ){ }</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Base1</span> { <span class="keyword">public</span>: <span class="built_in">Base1</span>() { cout << <span class="string">"Base1"</span> << endl; } };</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Base2</span> { <span class="keyword">public</span>: <span class="built_in">Base2</span>() { cout << <span class="string">"Base2"</span> << endl; } };</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Derived</span> : <span class="keyword">public</span> Base2, <span class="keyword">public</span> Base1 { <span class="comment">// 基类声明顺序:Base2 → Base1</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Derived</span>() : <span class="built_in">Base1</span>(), <span class="built_in">Base2</span>() { <span class="comment">// 初始化列表顺序:Base1 → Base2(与声明顺序相反)</span></span><br><span class="line"> cout << <span class="string">"Derived"</span> << endl;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h3 id="1-初始化顺序的决定因素"><a href="#1-初始化顺序的决定因素" class="headerlink" title="1. 初始化顺序的决定因素"></a>1. <strong>初始化顺序的决定因素</strong></h3><ul><li><strong>基类</strong>:按照<strong>派生类定义时基类的声明顺序</strong>初始化(无论初始化列表中如何排列)。</li><li><strong>成员变量</strong>:按照成员在类中<strong>声明的顺序</strong>初始化(<strong>与初始化列表顺序无关</strong>)。</li><li><strong>派生类自身</strong>:<strong>最后执行派生类构造函数</strong>的<strong>函数体</strong>。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Base1</span> { <span class="keyword">public</span>: <span class="built_in">Base1</span>() { cout << <span class="string">"Base1"</span> << endl; } };</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Base2</span> { <span class="keyword">public</span>: <span class="built_in">Base2</span>() { cout << <span class="string">"Base2"</span> << endl; } };</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Derived</span> : <span class="keyword">public</span> Base2, <span class="keyword">public</span> Base1 { <span class="comment">// 基类声明顺序:Base2 → Base1</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Derived</span>() : <span class="built_in">Base1</span>(), <span class="built_in">Base2</span>() { <span class="comment">// 初始化列表顺序:Base1 → Base2(与声明顺序相反)</span></span><br><span class="line"> cout << <span class="string">"Derived"</span> << endl;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Base2</span><br><span class="line">Base1</span><br><span class="line">Derived</span><br></pre></td></tr></table></figure><h4 id="为什么初始化顺序固定?"><a href="#为什么初始化顺序固定?" class="headerlink" title="为什么初始化顺序固定?"></a><strong>为什么初始化顺序固定?</strong></h4><ul><li><strong>成员变量依赖</strong>:若成员变量的初始化依赖于其他成员的顺序,固定顺序可避免潜在错误。</li><li><strong>基类依赖</strong>:若基类的初始化顺序被用户随意调整,可能导致基类未完全初始化就被使用。</li></ul><h5 id="最佳实践"><a href="#最佳实践" class="headerlink" title="最佳实践"></a><strong>最佳实践</strong></h5><ul><li><strong>保持初始化列表顺序与声明顺序一致</strong>:提高代码可读性,避免混淆。</li><li><strong>避免成员间的初始化依赖</strong>:若必须依赖,通过构造函数体或成员函数处理。</li></ul><p>**构造函数执行顺序:**基类->派生类中对象成员->派生类</p><h4 id="派生类构造函数的几点说明"><a href="#派生类构造函数的几点说明" class="headerlink" title="派生类构造函数的几点说明"></a>派生类构造函数的几点说明</h4><p>1)派生类构造函数的定义中可<strong>省略对基类构造函数的调用</strong>其条件是<strong>在基类中必须有默认的构造函数或者根本没有定义构造函数。</strong><br>2)当基类的构造函数使用<strong>一个或多个参数</strong>时,则<strong>派生类必须定义构造函数</strong>,提供<strong>将参数传递</strong>给基类构造函数途径。</p><h4 id="继承中的同名成员访问"><a href="#继承中的同名成员访问" class="headerlink" title="继承中的同名成员访问"></a><strong>继承中的同名成员访问</strong></h4><p>多重继承时不同基类成员同名也可以用**类名限定符“::”**来解决。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line"><span class="type">int</span> id;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Teacher</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">protected</span> :</span><br><span class="line"><span class="type">int</span> id;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Assistant</span>: <span class="keyword">public</span> Student, <span class="keyword">public</span> Teacher</span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">print</span><span class="params">()</span></span>{</span><br><span class="line">cout<<id<<endl; <span class="comment">//error!访问是二义的</span></span><br><span class="line">cout<<Student::id<<endl; <span class="comment">//访问Student的id</span></span><br><span class="line">cout<<Teacher::id<<endl; <span class="comment">//访问Teacher的id</span></span><br><span class="line">}</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p><strong>继承时的同名成员隐藏规则</strong><br>派生类定义了与基类相同的成员,此时<strong>基类的同名成员在派生类内不可见</strong>,派生类成员隐藏了同名的基类成员.</p><p>基类成员与派生类成员同名,可以通过类名限定符“::”来解决。其语法为:<strong>类名</strong> <strong>: :</strong> <strong>成员</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span> : <span class="keyword">public</span> Person{ <span class="comment">// 派生类</span></span><br><span class="line"><span class="keyword">private</span>: <span class="type">int</span> id; <span class="comment">// 学号</span></span><br><span class="line">……</span><br><span class="line"><span class="keyword">public</span>: <span class="function"><span class="type">void</span> <span class="title">test</span><span class="params">()</span></span>{ <span class="comment">//测试函数</span></span><br><span class="line">id=<span class="number">123</span>; <span class="comment">// 访问派生类成员</span></span><br><span class="line">Person::id =<span class="number">456</span>; <span class="comment">// 访问基类成员</span></span><br><span class="line">}</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>继承中成员同名有两种情况:<br>1.<strong>基类</strong>成员与<strong>派生类</strong>成员同名<br>2.多重继承时<strong>不同基类</strong>成员同名</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span></span><br><span class="line">{ <span class="keyword">protected</span>: <span class="type">int</span> id; }; <span class="comment">// 身份号码</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span> : <span class="keyword">public</span> Person</span><br><span class="line">{ <span class="keyword">protected</span>: <span class="type">int</span> id; }; <span class="comment">// 学号</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Teacher</span> : <span class="keyword">public</span> Person</span><br><span class="line">{ <span class="keyword">protected</span>: <span class="type">int</span> id;}; <span class="comment">// 职工号</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Assistant</span>: <span class="keyword">public</span> Student, <span class="keyword">public</span> Teacher{</span><br><span class="line"><span class="keyword">public</span>: <span class="function"><span class="type">void</span> <span class="title">test</span><span class="params">()</span></span>{ <span class="comment">// 测试</span></span><br><span class="line">Student::id = <span class="number">1001</span>; <span class="comment">// 正确:访问Student类的id</span></span><br><span class="line">Teacher::id = <span class="number">101</span>; <span class="comment">// 正确:访问Teacher类的id</span></span><br><span class="line">Person::id =<span class="number">230</span>×××<span class="number">0001</span>; <span class="comment">// 错误}</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p><img src="/2025/07/03/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88c%E8%89%B9%EF%BC%89%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/1e35cab60282a49190c05943c754245c.png" alt="1e35cab60282a49190c05943c754245c"></p><h3 id="类族中的赋值兼容"><a href="#类族中的赋值兼容" class="headerlink" title="类族中的赋值兼容"></a>类族中的<strong>赋值兼容</strong></h3><p><strong>公有继承</strong>时,一个<strong>派生类的对象</strong>可用于<strong>基类对象</strong>适用的地方,需要基类对象的任何地方都可以使用派生类对象<strong>替代</strong>。</p><p>赋值兼容规则有三种情况:<br>(1)派生类的对象可以<strong>赋值给基类的对象</strong>。<br>base <em>Obj = derived</em> Obj;<br>(2)派生类的对象可以<strong>初始化基类的引用</strong>。<br>base& base_Obj = derived_obj;<br>(3)派生类的对象的<strong>地址可以赋给指向基类的指针</strong>。<br>base *pBase = &derived_obj;</p><p><img src="/2025/07/03/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88c%E8%89%B9%EF%BC%89%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/8b80309614a6abf7fa39b9b15d0abf52.png" alt="8b80309614a6abf7fa39b9b15d0abf52"></p><h2 id="多态-1"><a href="#多态-1" class="headerlink" title="多态"></a><strong>多态</strong></h2><p>指<strong>同样的消息</strong>被<strong>不同类型的对象接收</strong>时,产生不同行为的<strong>现象</strong>。(<strong>同一名字,多种语义</strong>;<strong>同个接口,多种方法</strong>)</p><h4 id="静态多态的概念"><a href="#静态多态的概念" class="headerlink" title="静态多态的概念"></a><strong>静态多态的概念</strong></h4><p>在程序<strong>编译时</strong>系统就能够<strong>确定要调用的是哪个函数</strong>,也被称为<strong>编译时多态</strong>。</p><ul><li><p>函数重载</p></li><li><p>运算符重载</p></li></ul><h4 id="函数重载注意事项"><a href="#函数重载注意事项" class="headerlink" title="函数重载注意事项"></a>函数重载注意事项</h4><p><strong>不能仅靠函数的返回值</strong>来区别重载函数,必须从<strong>形式参数上</strong>区别开来。</p><p><strong>派生类中的同名成员函数</strong></p><p>• <strong>使用</strong> <strong>::</strong> <strong>加以区分</strong></p><p>• <strong>使用对象加以区分</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">MonkyKong sun; </span><br><span class="line">sun. <span class="built_in">fly</span> ( );</span><br><span class="line">sun . Follower :: <span class="built_in">fly</span> ( );</span><br><span class="line">Pig pigsy;</span><br><span class="line">pigsy.<span class="built_in">fly</span>();</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">B</span> :<span class="keyword">public</span> A</span><br><span class="line"><span class="type">char</span> name[<span class="number">10</span>];</span><br><span class="line">A::<span class="built_in">Show</span> ( )</span><br><span class="line"><span class="built_in">Show</span> ( )</span><br><span class="line">Aobj . <span class="built_in">Show</span> ( );</span><br><span class="line">Bobj . <span class="built_in">Show</span> ( );</span><br><span class="line">Bobj . A :: <span class="built_in">Show</span> ( );</span><br></pre></td></tr></table></figure><h4 id="动态多态性"><a href="#动态多态性" class="headerlink" title="动态多态性"></a><strong>动态多态性</strong></h4><p>指程序在编译时并不能确定要调用的函数,<strong>直到运行时</strong>系统才能动态地确定操作所针对的具体对象,它又被称为<strong>运行时多态</strong></p><p>动态多态是通过<strong>虚函数</strong>(virtual function)实现。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span></span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">show</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{ cout << <span class="string">"Base"</span><<endl ; }</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Derv1</span>: <span class="keyword">public</span> Base</span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">show</span><span class="params">()</span></span>{ cout << <span class="string">"Derv1"</span><<endl ; }</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Derv2</span>: <span class="keyword">public</span> Base</span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">show</span><span class="params">()</span></span>{ cout << <span class="string">"Derv2"</span><<endl ; }</span><br><span class="line">};</span><br><span class="line">Derv1 dv1; </span><br><span class="line">Derv2 dv2; </span><br><span class="line">dv<span class="number">1.</span><span class="built_in">show</span>() ;</span><br><span class="line">dv<span class="number">2.</span><span class="built_in">show</span>() ;</span><br><span class="line">Base* pBase; </span><br><span class="line">pBase = &dv1;</span><br><span class="line">pBase-><span class="built_in">show</span>();</span><br><span class="line">pBase = <span class="keyword">new</span> <span class="built_in">Derv2</span>(); </span><br><span class="line">pBase-><span class="built_in">show</span>();</span><br><span class="line"><span class="comment">//通过基类指针只能访问从基类继承的成员</span></span><br></pre></td></tr></table></figure><h4 id="虚函数"><a href="#虚函数" class="headerlink" title="虚函数"></a>虚函数</h4><p>C++中的虚函数的作用是<strong>允许在派生类中重新定义与基类同名的函数</strong>,并且可以<strong>通过基类指针或者基类引用来访问这个同名函数(最重要区别)</strong>。虚函数成员声明的语法为:</p><p>1.virtual只能使用<strong>在类定义</strong>的函数<strong>原型声明</strong>中,</p><p>不能在成员函数实现的时候使用,也<strong>不能用来限定类外</strong>的普通函数。</p><p>2.用virtual声明类的<strong>非静态</strong>的成员函数,<strong>只用于类的继承层次结构</strong>中。</p><p>不能将类外的普通函数(友员)和静态成员函数声明成虚函数。</p><p>virtual具有<strong>继承性</strong></p><p>在派生类中<strong>重新定义虚函数</strong>,要求函数名、函数类型、函数参数个数和类型全部与基类的虚函数<strong>完全相同</strong>。<br>否则不能实现多态性, 为<strong>函数重载</strong>.</p><p>虚函数是在基类中冠以关键字 virtual 的非静态成员函数。<br>继承体系:判断成员函数所在的类是否会作为基类;虚函数为类族提供了一种公共接口。<br>重写函数:该函数在类被继承后有无可能被更改功能;允许在派生类中对基类的虚函数重新定义<br>调用形式:是否通过基类指针或引用调用该虚函数;赋值兼容性原则</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Follower</span> <span class="comment">// 徒弟类</span></span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">fly</span><span class="params">( )</span></span>; </span><br><span class="line"><span class="function"><span class="keyword">virtual</span> <span class="type">bool</span> <span class="title">fight</span><span class="params">( Ghost&)</span></span>;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">WuKong</span></span><br><span class="line">: <span class="keyword">public</span> Follower</span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">fly</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">fight</span><span class="params">( Ghost* )</span></span>; </span><br><span class="line">};</span><br></pre></td></tr></table></figure><h4 id="虚析构函数"><a href="#虚析构函数" class="headerlink" title="虚析构函数"></a>虚析构函数</h4><p><strong>构造函数不能是虚函数</strong></p><p>建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数</p><p><strong>析构函数可以是虚函数</strong></p><p>虚析构函数用于<strong>指引 delete 运算符正确析构动态对象</strong></p><p>当<strong>基类的析构函数为虚函数</strong>时,无论指针指的是<strong>同一类族的哪一个类对象</strong>,对象撤销时,系统会采用动态关联,调用<strong>相应的析构函数</strong>,完成该对象的清理工作。</p><p>习惯把析构函数声明为虚函数,即使基类并不需要析构函数,以<strong>确保撤销动态存储空间时能够得到正确的处理</strong>。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span></span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"><span class="built_in">Base</span>( ){ cout << <span class="string">"Base"</span> << endl; }</span><br><span class="line">~<span class="built_in">Base</span>( ){ cout << <span class="string">"Desconstruct Base"</span> << endl; }</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Derv1</span>: <span class="keyword">public</span> Base</span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"><span class="built_in">Derv1</span>( ){ cout << <span class="string">"Derv1"</span> << endl; }</span><br><span class="line">~<span class="built_in">Derv1</span>( ){ cout << <span class="string">"Desconstruct Derv1"</span> << endl; }</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Derv2</span>: <span class="keyword">public</span> Derv1</span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"><span class="built_in">Derv2</span>( ){ cout << <span class="string">"Derv2"</span> << endl; }</span><br><span class="line">~<span class="built_in">Derv2</span>( ){ cout << <span class="string">"Desconstruct Derv2"</span> << endl; }</span><br><span class="line">};</span><br><span class="line">Base* pBase = <span class="keyword">new</span> <span class="built_in">Base</span>();</span><br><span class="line"><span class="keyword">delete</span> pBase;</span><br><span class="line">Derv1* pDerv1 = <span class="keyword">new</span> <span class="built_in">Derv1</span>();</span><br><span class="line"><span class="keyword">delete</span> pDerv1;</span><br><span class="line">pBase = <span class="keyword">new</span> <span class="built_in">Derv2</span>();</span><br><span class="line"><span class="keyword">delete</span> pBase;<span class="comment">//析构由基类指针建立的派生类对象,没有调用派生类析构函数</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span></span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"><span class="built_in">Base</span>( ){ cout << <span class="string">"Base"</span> << endl; }</span><br><span class="line"><span class="keyword">virtual</span> ~<span class="built_in">Base</span>( ){ cout << <span class="string">"Desconstruct Base"</span> << endl; }<span class="comment">//后面默认都虚,基类指针也可做到连删</span></span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Derv1</span>: <span class="keyword">public</span> Base</span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"><span class="built_in">Derv1</span>( ){ cout << <span class="string">"Derv1"</span> << endl; }</span><br><span class="line">~<span class="built_in">Derv1</span>( ){ cout << <span class="string">"Desconstruct Derv1"</span> << endl; }</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Derv2</span>: <span class="keyword">public</span> Derv1</span><br><span class="line">{ <span class="keyword">public</span>:</span><br><span class="line"><span class="built_in">Derv2</span>( ){ cout << <span class="string">"Derv2"</span> << endl; }</span><br><span class="line">~<span class="built_in">Derv2</span>( ){ cout << <span class="string">"Desconstruct Derv2"</span> << endl; }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p><strong>实现动态多态</strong></p><p>• 基类<strong>声明</strong>虚函数</p><p>• 派生类重写虚函数</p><p>• 基类指针或引用调用</p><p>在许多情况下,在基类中<strong>不能给出有意义的虚函数定义</strong>,这时可把它说明成纯虚函数,把它的<strong>定义留给派生类来做</strong>。<br>定义纯虚函数的一般形式为:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> 类名{</span><br><span class="line"><span class="keyword">virtual</span> 返回值类型 函数名(参数表) = <span class="number">0</span>;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>①纯虚函数<strong>没有函数体</strong>;<br>②最后面的“=0” 不表示函数返回值为0<br>③这是一个<strong>声明语句</strong>。</p><p>纯虚函数的作用<br>在基类中<strong>为其派生类保留一个函数的名字</strong>,<strong>以便派生类根据需要对它进行定义</strong>, 否则无法实现多态性。</p><h2 id="抽象类的概念"><a href="#抽象类的概念" class="headerlink" title="抽象类的概念"></a><strong>抽象类的概念</strong></h2><p>如果一个类中<strong>至少有一个纯虚函数</strong>,那么这个类被成为<strong>抽象类</strong>(abstract class**)**。</p><p>抽象类必须用作派生其他类的基类,不能作为返回或参数类型,可使用指向抽象类的指针<strong>支持运行时多态性</strong>。而<strong>不能用于直接创建对象实例</strong>。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">p-><span class="built_in">getArea</span>(); (*p).<span class="built_in">draw</span>( );<span class="comment">//作指针做对象时的不同写法以区分</span></span><br></pre></td></tr></table></figure><p><strong>派</strong>生类中应<strong>重写</strong>基类中的纯虚函数,否则派生类<strong>仍将被看作</strong>一个<strong>抽象类</strong>。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">void test1(TwoDimensionalShape & t){</span><br><span class="line">t.show( ); t.draw( ); cout<<t.getArea()<<endl;</span><br><span class="line">}</span><br><span class="line">void test2(TwoDimensionalShape * p){</span><br><span class="line">p->show( ); p->draw( ); </span><br><span class="line">cout<< (*p).getArea()<<endl; //???</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果二维图形又派生出了椭圆形,用于测试的test函数需要需改吗?这有何意义?</p><ol><li><strong>test 函数是否需要修改?</strong><br> <strong>不需要修改</strong>。<br> 若<code>椭圆形(Ellipse)</code>是<code>TwoDimensionalShape</code>的派生类,且正确重写了基类中的<code>show()</code>、<code>draw()</code>、<code>getArea()</code>虚函数(假设这三个函数在<code>TwoDimensionalShape</code>中是<strong>虚函数</strong>),则<code>test1</code>和<code>test2</code>函数可以<strong>直接接收<code>Ellipse</code>对象(或指针 / 引用)<strong>并</strong>正确调用派生类</strong>的实现。</li><li><strong>意义:</strong><br> 这体现了<strong>面向对象的多态性</strong>,具体意义如下:<ul><li><strong>代码复用性</strong>:新增派生类(如椭圆形)时,无需修改已有的<code>test1</code>、<code>test2</code>等通用函数,只需<strong>保证派生类遵循基类的接口规范(重写虚函数)</strong>,即可直接使用这些函数进行测试。</li><li><strong>扩展性</strong>:系统可以轻松扩展新的二维图形类型(如椭圆形、三角形等),而不影响原有代码的逻辑,符合 “开闭原则”(对扩展开放,对修改关闭)。</li><li><strong>接口统一性</strong>:通过基类的引用或指针调用派生类的方法,屏蔽了不同派生类的实现差异,使代码更简洁、通用,降低了模块间的耦合度。</li></ul></li></ol><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">TwoDimensionalShape</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">show</span><span class="params">()</span> </span>= <span class="number">0</span>; <span class="comment">// 纯虚函数:显示图形信息</span></span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">draw</span><span class="params">()</span> </span>= <span class="number">0</span>; <span class="comment">// 纯虚函数:绘制图形</span></span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">double</span> <span class="title">getArea</span><span class="params">()</span> </span>= <span class="number">0</span>; <span class="comment">// 纯虚函数:计算面积</span></span><br><span class="line"> <span class="keyword">virtual</span> ~<span class="built_in">TwoDimensionalShape</span>() {} <span class="comment">// 虚析构函数</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 已有的派生类:圆形</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Circle</span> : <span class="keyword">public</span> TwoDimensionalShape {</span><br><span class="line"> <span class="comment">// 实现show()、draw()、getArea()...</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新增派生类:椭圆形</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Ellipse</span> : <span class="keyword">public</span> TwoDimensionalShape {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">show</span><span class="params">()</span> <span class="keyword">override</span> </span>{ <span class="comment">/* 实现 */</span> }</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">draw</span><span class="params">()</span> <span class="keyword">override</span> </span>{ <span class="comment">/* 实现 */</span> }</span><br><span class="line"> <span class="function"><span class="type">double</span> <span class="title">getArea</span><span class="params">()</span> <span class="keyword">override</span> </span>{ <span class="comment">/* 计算椭圆面积 */</span> }</span><br><span class="line">};</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> Ellipse e;</span><br><span class="line"> <span class="built_in">test1</span>(e); <span class="comment">// 传入Ellipse对象的引用,正确调用Ellipse的成员函数</span></span><br><span class="line"> <span class="built_in">test2</span>(&e); <span class="comment">// 传入Ellipse对象的指针,正确调用Ellipse的成员函数</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>override</code>是一个<strong>关键字</strong>,用于显式声明派生类中的成员函数<strong>重写(覆盖)</strong> 了基类中的虚函数(<code>virtual</code>函数)。它的主要作用是<strong>增强代码的可读性和安全性</strong>。</p><p><strong>(3)</strong> <strong>在类的层次结构中,顶层或最上面的几层可以是抽象基类。</strong></p><p>抽象基类体现了<strong>本类族中各类的共性</strong>,把各类中共有的成员函数集中在抽象基类中<strong>声明</strong>。</p><p>为什么引⼊多态<br>利⽤多态性可以设计和实现⼀个易于扩展的系统。增强代码的通⽤性</p><h3 id="区别"><a href="#区别" class="headerlink" title="区别"></a><strong>区别</strong></h3><table><thead><tr><th><strong>对比维度</strong></th><th><strong>重载(Overload)</strong></th><th><strong>多态(Polymorphism)</strong></th></tr></thead><tbody><tr><td><strong>定义</strong></td><td><strong>同一作用域</strong>内,多个函数名相同但<strong>参数列表(参数类型、个数、顺序)不同</strong>的函数。</td><td><strong>基类与派生类中</strong>,派生类<strong>重写(<code>override</code>)基类的虚函数</strong>,通过<strong>基类指针 / 引用调用</strong>时,**根据对象实际类型执行(多种对象对应执行)**对应函数。</td></tr><tr><td><strong>实现阶段</strong></td><td><strong>编译时确定(静态多态)</strong>。编译器根据函数参数列表匹配对应的函数。</td><td><strong>运行时确定(动态多态)</strong>。程序运行时根据对象实际类型调用对应的函数。</td></tr><tr><td><strong>作用范围</strong></td><td><strong>同一类中(或全局函数)</strong>,函数名相同但参数<strong>不同(不完全等同于重写,属于新建,仅名字相同)</strong>。</td><td><strong>继承关系中,基类与派生类之间,函数名、参数列表、返回值完全相同(相同信息重写虚函数带来的不同处理响应)。</strong></td></tr><tr><td><strong>核心依赖</strong></td><td>函数<strong>参数列表的差异</strong>(与返回值无关)。</td><td><strong>基类虚函数、派生类重写、基类指针 / 引用能指向相应派生类对象并执行相应的函数。</strong></td></tr><tr><td></td><td></td><td></td></tr></tbody></table><h3 id="二、联系"><a href="#二、联系" class="headerlink" title="二、联系"></a><strong>二、联系</strong></h3><ol><li><strong>都是代码复用的手段</strong><ul><li>重载允许<strong>同一功能(函数名)适配不同参数</strong>,避免为相似功能起不同名字(如<code>printInt</code>、<code>printDouble</code>)。</li><li>多态允许通过<strong>统一接口(基类函数)操作不同派生类对象</strong>,简化代码逻辑(如<strong>用<code>Shape*</code>统一管理圆形、方形</strong>等)。</li></ul></li><li><strong>都体现 “一个接口,多种实现” 的思想</strong><ul><li>重载:同一函数名对应<strong>多种参数组合</strong>的实现。</li><li>多态:同一虚函数接口(基类)对应派生类的<strong>多种重写实现</strong>。</li></ul></li><li><strong>都依赖编译器的处理</strong><ul><li>重载依赖编译器在编译时<strong>根据参数匹配函数(静态绑定)</strong>。</li><li>多态依赖编译器对<strong>虚函数表的处理</strong>,实现<strong>运行时动态绑定</strong>。</li></ul></li></ol><h3 id="三、总结"><a href="#三、总结" class="headerlink" title="三、总结"></a><strong>三、总结</strong></h3><ul><li><strong>重载</strong>是 “横向” 的函数扩展(同一类内,同名不同参),解决同一功能的不同参数适配问题,属于<strong>静态多态</strong>。</li><li><strong>多态</strong>是 “纵向” 的函数扩展(继承体系中,重写虚函数),解决不同派生类对象的统一接口调用问题,属于<strong>动态多态</strong>。</li></ul><h2 id="模板"><a href="#模板" class="headerlink" title="模板"></a><strong>模板</strong></h2><p><strong>模板</strong>可以实现<strong>类型参数化(包括新类型)</strong>,C++模板包括 函数模板和类模板两种类型**。**</p><p><strong>函数模板</strong>就解决函数<strong>重载中</strong>多次定义函数的问题。</p><p><strong>类模板</strong>就是对一批<strong>仅仅成员数据类型不同</strong>的类的<strong>抽象</strong>。</p><p><strong>泛型编程(generic programming)</strong><br>模板用于<strong>表达逻辑结构相同</strong>,但<strong>具体数据元素类型不同</strong>的数据对象的通用行为。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">square</span> <span class="params">( <span class="type">int</span> x )</span></span></span><br><span class="line"><span class="function"></span>{ <span class="keyword">return</span> x*x ; }</span><br><span class="line"><span class="function">Complex <span class="title">square</span> <span class="params">(Complex x )</span></span></span><br><span class="line"><span class="function"></span>{ <span class="keyword">return</span> x*x ; }</span><br><span class="line"><span class="keyword">template</span> < <span class="keyword">typename</span> T ></span><br><span class="line"><span class="function">T <span class="title">square</span> <span class="params">(T x )</span></span></span><br><span class="line"><span class="function"></span>{ <span class="keyword">return</span> x*x; }</span><br></pre></td></tr></table></figure><p>通过模板可以产生类或函数的<strong>集合</strong>,使它们操作不同的数据类型,避免需要为每种数据类型产生一个单独的类或函数。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> <<span class="keyword">class</span> 类型参数名<span class="number">1</span> ,<span class="keyword">class</span> 类型参数 <span class="number">2</span>,…></span><br><span class="line">函数返回值类型 函数名(形式参数表)</span><br><span class="line">{ 函数体 }</span><br></pre></td></tr></table></figure><p>关键字class也可以使用<strong>关键字typename</strong>;</p><p>在template语句与函数模板定义语句<返回类型>之间<strong>不允许有别的语句</strong></p><p>函数模板允许使用多个类型参数,但在template定义部分的<strong>每个形参</strong>前必须有<strong>关键字typename或class</strong>,</p><p>函数形式参数表中可以使用模板类型参数,也可以使用一般类型参数.</p><p>模板参数说明的每个类型参数必须在函数定义形参表中至<strong>少出现一次</strong>;</p><p>类模板主要用于<strong>数据存储(容器)类</strong>。<strong>表示和算法</strong>不受所包含的元素类型的影响。</p><p>一个类模板在类层次结构中<br>既可以是基类也可以是派生类:<br>Ø 类模板可以从模板类派生<br>Ø 类模板可以从非模板类派生<br>Ø 模板类可以从类模板派生<br>Ø 非模板类可以从类模板派生</p><h1 id="EasyX-基础"><a href="#EasyX-基础" class="headerlink" title="EasyX 基础"></a>EasyX 基础</h1><ol><li>在项目中创建一个.cpp源文件(右键源文件 -> 添加 -> 新建项 -> 设置文件名 first.cpp)</li></ol><p><img src="/2025/07/03/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88c%E8%89%B9%EF%BC%89%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/image-20250707160940619.png" alt="image-20250707160940619"></p><h2 id="EasyX-基本概念"><a href="#EasyX-基本概念" class="headerlink" title="EasyX 基本概念"></a>EasyX 基本概念</h2><h3 id="绘图窗口与设备"><a href="#绘图窗口与设备" class="headerlink" title="绘图窗口与设备"></a>绘图窗口与设备</h3><p>initgraph 函数用于<strong>初始化绘图窗口</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">HWND <span class="title">initgraph</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"><span class="type">int</span> width,</span></span></span><br><span class="line"><span class="params"><span class="function"><span class="type">int</span> height,</span></span></span><br><span class="line"><span class="params"><span class="function"><span class="type">int</span> flag = <span class="literal">NULL</span></span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br></pre></td></tr></table></figure><p>示例1:创建禁用最小化和关闭按钮的绘图窗口</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">initgraph(800, 600, EX_NOMINIMIZE | EX_NOCLOSE);</span><br></pre></td></tr></table></figure><p>示例2:窗口开启 EX_SHOWCONSOLE 模式,可以进行代码调试</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">initgraph(800, 600, EX_SHOWCONSOLE);// 带控制台的图形窗口</span><br></pre></td></tr></table></figure><p>在 EasyX 中,<strong>设备</strong>分两种,一种是默认的<strong>绘图窗口</strong>,另一种是 <strong>IMAGE 对象</strong>。</p><p>通过 SetWorkingImage 函数可以设置当前用于绘图的设备。设置当前用于绘图的设备后,所有的绘图函数都会绘制在该设备上。</p><h3 id="坐标"><a href="#坐标" class="headerlink" title="坐标"></a>坐标</h3><p>在 EasyX 中,坐标分两种:<strong>物理坐标和逻辑坐标</strong>。</p><ul><li><strong>物理坐标</strong></li></ul><p>物理坐标是描述设备的坐标体系。</p><p><strong>坐标原点在设备的左上角,X 轴向右为正,Y 轴向下为正(特点),度量单位是像素(Pixel)。</strong></p><p>坐标原点、坐标轴方向、缩放比例都不能改变。</p><ul><li><strong>逻辑坐标</strong></li></ul><p>逻辑坐标是在程序中<strong>用于绘图的</strong>坐标体系。</p><p><strong>坐标默认的原点在窗口的左上角,X 轴向右为正,Y 轴向下为正,度量单位是点。</strong></p><p>默认情况下,逻辑坐标<strong>与物理坐标是一一对应</strong>的,<strong>一个逻辑点等于一个物理像素</strong>。</p><p><strong>在 EasyX 中,凡是没有特殊注明的坐标,均指逻辑坐标。</strong></p><p><strong>坐标相关函数</strong></p><table><thead><tr><th>函数用法</th><th>函数说明</th></tr></thead><tbody><tr><td>void <strong>setorigin</strong> ( int x, int y )</td><td>用于设置坐标原点。</td></tr><tr><td>void <strong>setaspectratio</strong> ( float xasp, float yasp )</td><td>通过设置 x 和 y 方向上的<strong>缩放因子</strong>,从而<strong>修改绘图的缩放比例或坐标轴方向</strong>。</td></tr></tbody></table><p>范例:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">#include <graphics.h></span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line">initgraph(600, 600);</span><br><span class="line">setorigin(300, 300);// 将绘图窗口的中心点作为坐标原点 </span><br><span class="line">circle(0, 0, 100);</span><br><span class="line">setorigin(0, 0);// 将绘图窗口的左上角作为坐标原点</span><br><span class="line">setaspectratio(2, 1);// x轴方向的缩放因子为2,y轴方向的缩放因子为1(默认值)</span><br><span class="line">circle(100, 100, 100);</span><br><span class="line">setorigin(0, 600);// 将绘图窗口的左下角作为坐标原点</span><br><span class="line">setaspectratio(1, -1);// 缩放因子为负数,可以实现坐标轴的翻转,此行可使y轴向上为正</span><br><span class="line">circle(100, 100, 100);</span><br><span class="line">system("pause");</span><br><span class="line">closegraph();</span><br><span class="line">return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="颜色"><a href="#颜色" class="headerlink" title="颜色"></a>颜色</h3><p>EasyX 使用 24bit 真彩色,有四种表示颜色的方法:<a href="https://docs.easyx.cn/zh-cn/color%EF%BC%8C%E9%80%9A%E8%BF%87">https://docs.easyx.cn/zh-cn/color,通过</a> <strong>setlinecolor</strong> 函数可以设置线条颜色</p><ol><li><p>用<strong>预定义常量</strong>表示颜色( 常量名要大写 )</p></li><li><p>用<strong>16进制数字表示颜色( 0xBBGGRR )</strong>,注意<strong>颜色的顺序与RGB宏相反</strong></p></li><li><p>用 <strong>RGB 宏合成颜色( RGB(RRGGBB) )</strong></p></li><li><p>用 HSLtoRGB、HSVtoRGB 转换其他色彩模型到 RGB 颜色</p></li></ol><p>范例:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">#include <graphics.h></span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line">initgraph(800, 600);</span><br><span class="line">setfillcolor(BLUE);// 用预定义常量表示颜色</span><br><span class="line">solidcircle(100, 200, 100);</span><br><span class="line">setfillcolor(0xaa0000);// 用16进制数字表示颜色</span><br><span class="line">solidcircle(300, 200, 100);</span><br><span class="line">setfillcolor(RGB(0, 0, 170));// 用RGB宏合成颜色</span><br><span class="line">solidcircle(500, 200, 100);</span><br><span class="line">setfillcolor(HSLtoRGB(240, 1, 0.33));// 用 HSLtoRGB、HSVtoRGB 转换其他色彩模型到 RGB 颜色</span><br><span class="line">solidcircle(700, 200, 100);</span><br><span class="line">system("pause");</span><br><span class="line">closegraph();</span><br><span class="line">return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="EasyX-图形绘制函数(33个)"><a href="#EasyX-图形绘制函数(33个)" class="headerlink" title="EasyX 图形绘制函数(33个)"></a>EasyX 图形绘制函数(33个)</h2><p><a href="https://docs.easyx.cn/zh-cn/drawing-func">https://docs.easyx.cn/zh-cn/drawing-func</a></p><table><thead><tr><th align="left">函数用法</th><th>函数说明</th></tr></thead><tbody><tr><td align="left">void <strong>circle</strong> ( int x, int y, int radius )</td><td>画无填充的圆</td></tr><tr><td align="left">fillcircle</td><td>画有边框的填充圆</td></tr><tr><td align="left">solidcircle</td><td>画无边框的填充圆</td></tr><tr><td align="left">clearcircle</td><td>用当前背景色清空圆形区域</td></tr><tr><td align="left"></td><td></td></tr><tr><td align="left">void <strong>ellipse</strong> ( int left, int top, int right, int bottom )</td><td>画无填充的椭圆</td></tr><tr><td align="left">fillellipse</td><td>画有边框的填充椭圆</td></tr><tr><td align="left">solidellipse</td><td>画无边框的填充椭圆</td></tr><tr><td align="left">clearellipse</td><td>用当前背景色清空椭圆区域</td></tr><tr><td align="left"></td><td></td></tr><tr><td align="left">void <strong>pie</strong> ( int left, int top, int right, int bottom, double stangle, double endangle );</td><td>画无填充的扇形</td></tr><tr><td align="left">fillpie</td><td>画有边框的填充扇形</td></tr><tr><td align="left">solidpie</td><td>画无边框的填充扇形</td></tr><tr><td align="left">clearpie</td><td>用当前背景色清空扇形区域</td></tr><tr><td align="left"></td><td></td></tr><tr><td align="left">void <strong>rectangle</strong> ( int left, int top, int right, int bottom )</td><td>画无填充的矩形</td></tr><tr><td align="left">fillrectangle</td><td>画有边框的填充矩形</td></tr><tr><td align="left">solidrectangle</td><td>画无边框的填充矩形</td></tr><tr><td align="left">clearrectangle</td><td>用当前背景色清空矩形区域</td></tr><tr><td align="left"></td><td></td></tr><tr><td align="left">void <strong>roundrect</strong> ( int left, int top, int right, int bottom, int ellipsewidth, int ellipseheight )</td><td>画无填充的圆角矩形</td></tr><tr><td align="left">fillroundrect</td><td>画有边框的填充圆角矩形</td></tr><tr><td align="left">solidroundrect</td><td>画无边框的填充圆角矩形</td></tr><tr><td align="left">clearroundrect</td><td>用当前背景色清空圆角矩形区域</td></tr><tr><td align="left"></td><td></td></tr><tr><td align="left">void <strong>polygon</strong> ( const POINT *points, int num );</td><td>画无填充的多边形</td></tr><tr><td align="left">fillpolygon</td><td>画有边框的填充多边形</td></tr><tr><td align="left">solidpolygon</td><td>画无边框的填充多边形</td></tr><tr><td align="left">clearpolygon</td><td>用当前背景色清空多边形区域</td></tr><tr><td align="left"></td><td></td></tr><tr><td align="left">void <strong>putpixel</strong> ( int x, int y, COLORREF color )</td><td>画点</td></tr><tr><td align="left">void <strong>line</strong> ( int x1, int y1, int x2, int y2 )</td><td>画直线</td></tr><tr><td align="left">void <strong>arc</strong> ( int left, int top, int right, int bottom, double stangle, double endangle )</td><td>画椭圆弧</td></tr><tr><td align="left">void <strong>polyline</strong> ( const POINT *points, int num )</td><td>画多条连续的直线</td></tr><tr><td align="left">void <strong>polybezier</strong> ( const POINT *points, int num )</td><td>画三次方贝塞尔曲线</td></tr><tr><td align="left"></td><td></td></tr><tr><td align="left">void <strong>floodfill</strong> ( int x, int y, COLORREF color, int filltype = FLOODFILLBORDER )</td><td>填充区域</td></tr><tr><td align="left"></td><td></td></tr><tr><td align="left">COLORREF <strong>getpixel</strong> ( int x, int y )</td><td>获取坐标点的颜色</td></tr><tr><td align="left">int <strong>getwidth</strong> ( )</td><td>获取绘图区的宽度</td></tr><tr><td align="left">int <strong>getheight</strong> ( )</td><td>获取绘图区的高度</td></tr></tbody></table><h2 id="双缓冲绘图"><a href="#双缓冲绘图" class="headerlink" title="双缓冲绘图"></a>双缓冲绘图</h2><p>双缓冲绘图通过在内存中创建一个与屏幕绘图区域<strong>一致的对象</strong>,先将图形绘制到<strong>内存中的这个对象上</strong>,再<strong>一次性将这个对象上的图形拷贝</strong>到屏幕上,从而<strong>减少对屏幕的直接绘图操作</strong>,<strong>提高绘图效率、消除屏幕闪烁</strong>,广泛应用于游戏开发、图形界面等领域。</p><table><thead><tr><th>函数用法</th><th>函数说明</th></tr></thead><tbody><tr><td>void <strong>BeginBatchDraw</strong> ()</td><td>开始批量绘图</td></tr><tr><td>void <strong>EndBatchDraw</strong> ()<br>void <strong>EndBatchDraw</strong> ( int left, int top, int right, int bottom ) // 指定区域</td><td>结束批量绘制,并执行(指定区域内)未完成的绘制任务</td></tr><tr><td>void <strong>FlushBatchDraw</strong> ()<br>void <strong>FlushBatchDraw</strong> ( int left, int top, int right, int bottom ) // 指定区域</td><td>执行(指定区域内)未完成的绘制任务</td></tr></tbody></table><p><a href="https://docs.easyx.cn/zh-cn/other-func">https://docs.easyx.cn/zh-cn/other-func</a></p><p><strong>自动移动的圆(帧数控制)</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">//include <windows.h></span><br><span class="line">#include <graphics.h></span><br><span class="line"></span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line">initgraph(640, 480);</span><br><span class="line">BeginBatchDraw();</span><br><span class="line"></span><br><span class="line">setlinecolor(WHITE);</span><br><span class="line">setfillcolor(RED);</span><br><span class="line">for (int i = 50; i < 600; i++)</span><br><span class="line">{</span><br><span class="line">DWORD beginTime = GetTickCount();// 记录循环开始时间</span><br><span class="line"></span><br><span class="line">cleardevice();</span><br><span class="line">circle(i, 100, 40);</span><br><span class="line">floodfill(i, 100, WHITE);</span><br><span class="line">FlushBatchDraw();</span><br><span class="line"></span><br><span class="line">DWORD endTime = GetTickCount();// 记录循环结束时间</span><br><span class="line">DWORD elapsedTime = endTime - beginTime;// 计算循环耗时</span><br><span class="line">if (elapsedTime < 1000 / 60)// 按每秒60帧进行补时</span><br><span class="line">Sleep(1000 / 60 - elapsedTime);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">EndBatchDraw();</span><br><span class="line">closegraph();</span><br><span class="line">return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>GetTickCount</strong> 是一个 Windows 系统函数,用于<strong>获取从操作系统启动以来所经过的毫秒数</strong>,通过在代码中的不同位置调用该函数,并计算两次调用之间的差值,可以得知某段代码或某个操作的执行时间。</p><p>注:GetTickCount 的值会在系统启动后约49.7天((2^32-1) ms)后回绕到0,这是因为其返回值是一个32位无符号整数,<strong>可以使用 GetTickCount64 代替,需添加 windows.h 头文件。</strong></p><h3 id><a href="#" class="headerlink" title></a></h3><h1 id="EasyX-进阶"><a href="#EasyX-进阶" class="headerlink" title="EasyX 进阶"></a>EasyX 进阶</h1><h2 id="图像处理"><a href="#图像处理" class="headerlink" title="图像处理"></a>图像处理</h2><p><a href="https://docs.easyx.cn/zh-cn/image-func">https://docs.easyx.cn/zh-cn/image-func</a></p><table><thead><tr><th>函数用法</th><th>函数说明</th></tr></thead><tbody><tr><td>void <strong>loadimage</strong> (<br>IMAGE* pDstImg,// 保存图像的 IMAGE 对象指针<br>LPCTSTR pImgFile, // 图片文件名<br>int nWidth = 0, // 图片的拉伸宽度<br>int nHeight = 0, // 图片的拉伸高度<br>bool bResize = false //是否调整IMAGE的大小以适应图片<br>)</td><td>从文件中读取图像。如果pDstImg为NULL,则读取到绘图窗口</td></tr><tr><td>void <strong>putimage</strong> (<br> int dstX, // 绘制位置的 x 坐标<br> int dstY, // 绘制位置的 y 坐标<br> IMAGE *pSrcImg,// 要绘制的 IMAGE 对象指针<br> DWORD dwRop = SRCCOPY// 三元光栅操作码<br>);</td><td>在当前设备上绘制指定图像</td></tr><tr><td>void <strong>putimage</strong> (<br> int dstX, // 绘制位置的 x 坐标<br> int dstY, // 绘制位置的 y 坐标<br> int dstWidth,// 绘制的宽度 <br> int dstHeight, // 绘制的高度<br> IMAGE *pSrcImg,// 要绘制的 IMAGE 对象指针<br> int srcX, // 绘制内容在 IMAGE 对象中的左上角 x 坐标<br> int srcY, // 绘制内容在 IMAGE 对象中的左上角 y 坐标 <br> DWORD dwRop = SRCCOPY// 三元光栅操作码<br>)</td><td>在当前设备上绘制指定图像(指定宽高和起始位置)</td></tr><tr><td>void <strong>Resize</strong> ( IMAGE* pImg, int width, int height )</td><td>调整指定绘图设备的尺寸,pImg 如果为 NULL 表示默认绘图窗口</td></tr><tr><td>void <strong>rotateimage</strong> (<br>IMAGE *dstimg,<br>IMAGE *srcimg,<br>double radian,<br>COLORREF bkcolor = BLACK,<br>bool autosize = false,<br>bool highquality = true<br>)</td><td>旋转 IMAGE 中的绘图内容</td></tr><tr><td>void <strong>saveimage</strong> (<br>LPCTSTR strFileName,<br>IMAGE* pImg = NULL<br>)</td><td>保存绘图内容至图片文件,支持 bmp / gif / jpg / png / tif 格式</td></tr><tr><td>void <strong>SetWorkingImage</strong> ( IMAGE* pImg = NULL )</td><td>设定当前的绘图设备,如果参数为 NULL,表示绘图设备为默认绘图窗口</td></tr><tr><td>IMAGE* **GetWorkingImage **()</td><td>获取当前的绘图设备,如果返回值为 NULL,表示当前绘图设备为绘图窗口</td></tr><tr><td>void <strong>getimage</strong> (<br>IMAGE* pDstImg, // 保存图像的 IMAGE 对象指针<br>int srcX, // 要获取图像区域左上角 x 坐标<br>int srcY, // 要获取图像区域的左上角 y 坐标<br>int srcWidth, // 要获取图像区域的宽度<br>int srcHeight // 要获取图像区域的高度<br>)</td><td>从当前绘图设备中获取图像</td></tr><tr><td>DWORD* <strong>GetImageBuffer</strong> ( IMAGE* pImg = NULL )</td><td>获取绘图设备的显示缓冲区指针,pImg 如果为 NULL,表示默认的绘图窗口</td></tr><tr><td>HDC <strong>GetImageHDC</strong> ( IMAGE* pImg = NULL )</td><td>获取绘图设备句柄(HDC)</td></tr></tbody></table><h3 id="IMAGE-类"><a href="#IMAGE-类" class="headerlink" title="IMAGE 类"></a><strong>IMAGE 类</strong></h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">class</span> <span class="title">IMAGE</span><span class="params">(<span class="type">int</span> width = <span class="number">0</span>, <span class="type">int</span> height = <span class="number">0</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">公有成员</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">getwidth</span><span class="params">()</span></span>;</span><br><span class="line">返回 IMAGE 对象的宽度,以像素为单位。</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">getheight</span><span class="params">()</span></span>;</span><br><span class="line">返回 IMAGE 对象的高度,以像素为单位。</span><br><span class="line"></span><br><span class="line"><span class="keyword">operator</span> =</span><br><span class="line">实现IMAGE对象的直接赋值。该操作仅拷贝源图像的内容,不拷贝源图像的绘图环境。</span><br></pre></td></tr></table></figure><p>在内存中保存图像信息。</p><h3 id="loadimage-函数"><a href="#loadimage-函数" class="headerlink" title="loadimage 函数"></a>loadimage 函数</h3><p>范例1:loadimage <strong>直接读取图片至绘图窗口</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">1400</span>, <span class="number">600</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">loadimage</span>(<span class="literal">NULL</span>, _T(<span class="string">"image\\background.jpg"</span>));<span class="comment">// 第一个参数为NULL时,直接读取图片至绘图窗口</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>注:修改窗口大小,可以显示图片部分内容,但<strong>只能从绘图窗口的坐标原点(左上角)开始显示</strong>图片</p><p>范例2:loadimage 直接读取图片至绘图窗口并进行图片或窗口缩放</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">700</span>, <span class="number">300</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">loadimage</span>(<span class="literal">NULL</span>, _T(<span class="string">"image\\background.jpg"</span>), <span class="number">700</span>, <span class="number">300</span>, <span class="literal">false</span>);<span class="comment">// 将图像缩放为700*300在绘图窗口显示</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>注1:图片缩放后的尺寸<strong>小于</strong>窗口尺寸,则窗口会有<strong>黑边</strong>;若<strong>大于</strong>窗口尺寸,则图片<strong>显示不全</strong></p><p>注2:<strong>第五个参数若为 true,则会调整窗口以适应图片的大小</strong></p><p>注3:从磁盘中<strong>读取大量图片显示</strong>的情况下,使用 loadimage 直接读取图片至绘图窗口<strong>性能较差</strong></p><p>范例3:loadimage 读取本地图片文件,输出图片宽度和高度</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">#include <graphics.h></span><br><span class="line">#include <stdio.h></span><br><span class="line"></span><br><span class="line">int main()</span><br><span class="line">{</span><br><span class="line">initgraph(1000, 600, SHOWCONSOLE);// 初始化绘图窗口并开启终端</span><br><span class="line"></span><br><span class="line">IMAGE img;// 定义图像对象</span><br><span class="line">loadimage(&img, _T("image\\background.jpg"));// 读取本地图片文件,存入图像对象</span><br><span class="line">printf("width=%d, height=%d \n", img.getwidth(), img.getheight());// 输出图像宽度和高度</span><br><span class="line"></span><br><span class="line">system("pause");</span><br><span class="line">closegraph();</span><br><span class="line">return 0;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>注:本例中的图片内容<strong>不会在窗口内显示</strong></p><h3 id="putimage-函数"><a href="#putimage-函数" class="headerlink" title="putimage 函数"></a>putimage 函数</h3><p>范例1:putimage <strong>在绘图窗口显示图像</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">1000</span>, <span class="number">600</span>);</span><br><span class="line"></span><br><span class="line">IMAGE img;</span><br><span class="line"><span class="built_in">loadimage</span>(&img, _T(<span class="string">"image\\background.jpg"</span>));</span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">0</span>, &img);<span class="comment">// 将IMAGE对象显示在绘图窗口的坐标(0,0)处</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>范例2:putimage 截取图像部分内容进行显示</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">900</span>, <span class="number">600</span>);</span><br><span class="line"></span><br><span class="line">IMAGE img;</span><br><span class="line"><span class="built_in">loadimage</span>(&img, _T(<span class="string">"image\\background.jpg"</span>));</span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">900</span>, <span class="number">600</span>, &img, <span class="number">115</span>, <span class="number">0</span>);<span class="comment">// 从图像的(115,0)坐标处截取宽900、高600的部分内容显示在窗口(0,0)处</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>范例3:putimage 三元光栅操作码</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">1000</span>, <span class="number">600</span>);</span><br><span class="line"></span><br><span class="line">IMAGE img;</span><br><span class="line"><span class="built_in">loadimage</span>(&img, _T(<span class="string">"image\\background.jpg"</span>));</span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">0</span>, &img, NOTSRCCOPY);<span class="comment">// 第四个参数是三元光栅操作码</span></span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>注:putimage 第四个参数是 <strong>三元光栅操作码</strong> ,它定义了<strong>源图像与目标图像的位合并形式</strong>,默认值为 <strong>SRCCOPY</strong> 详见</p><p><a href="https://docs.easyx.cn/zh-cn/putimage">https://docs.easyx.cn/zh-cn/putimage</a></p><h4 id="透明贴图"><a href="#透明贴图" class="headerlink" title="透明贴图"></a>透明贴图</h4><p>范例1:通过PS制作<strong>原图</strong>的<strong>掩码图</strong>和<strong>前景图</strong>,再进行三元光栅操作叠加而成</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">IMAGE imgGuoqi, imgGuohui, imgGuohuiMask, imgGuohuiFg;</span><br><span class="line"><span class="built_in">loadimage</span>(&imgGuoqi, _T(<span class="string">"image\\guoqi.jpg"</span>), <span class="number">1000</span>, <span class="number">600</span>);<span class="comment">// 加载国旗(背景图)</span></span><br><span class="line"><span class="built_in">loadimage</span>(&imgGuohui, _T(<span class="string">"image\\guohui.jpg"</span>), <span class="number">200</span>, <span class="number">200</span>);<span class="comment">// 加载国徽原图(白色周边)</span></span><br><span class="line"><span class="built_in">loadimage</span>(&imgGuohuiMask, _T(<span class="string">"image\\guohui_mask.jpg"</span>), <span class="number">200</span>, <span class="number">200</span>);<span class="comment">// 加载国徽掩码图(白色周边+黑色内容)</span></span><br><span class="line"><span class="built_in">loadimage</span>(&imgGuohuiFg, _T(<span class="string">"image\\guohui_fg.jpg"</span>), <span class="number">200</span>, <span class="number">200</span>);<span class="comment">// 加载国徽前景图(黑色周边+待显示内容)</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">1000</span>, <span class="number">600</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">0</span>, &imgGuoqi);<span class="comment">// 显示国旗</span></span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">0</span>, &imgGuohui);<span class="comment">// 显示国徽原图</span></span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">200</span>, &imgGuohuiMask);<span class="comment">// 显示国徽掩码图</span></span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">400</span>, &imgGuohuiFg);<span class="comment">// 显示国徽前景图</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 透明贴图</span></span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">200</span>, <span class="number">0</span>, &imgGuohuiMask, SRCAND);<span class="comment">// 显示掩码图(SRCAND:按位与)</span></span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">200</span>, <span class="number">0</span>, &imgGuohuiFg, SRCPAINT);<span class="comment">// 显示前景图(SRCPAINT:按位或)</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>范例2:TransparentBlt 函数实现</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">pragma</span> comment(lib, <span class="string">"MSIMG32.LIB"</span>)<span class="comment">// 链接器在链接过程中包含指定的库文件</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">putimage_alpha</span><span class="params">(IMAGE* dstImg, <span class="type">int</span> x, <span class="type">int</span> y, IMAGE* srcImg, UINT transparentColor)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">HDC dstDC = <span class="built_in">GetImageHDC</span>(dstImg);</span><br><span class="line">HDC srcDC = <span class="built_in">GetImageHDC</span>(srcImg);</span><br><span class="line"><span class="type">int</span> w = srcImg-><span class="built_in">getwidth</span>();</span><br><span class="line"><span class="type">int</span> h = srcImg-><span class="built_in">getheight</span>();</span><br><span class="line"><span class="built_in">TransparentBlt</span>(dstDC, x, y, w, h, srcDC, <span class="number">0</span>, <span class="number">0</span>, w, h, transparentColor);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">1000</span>, <span class="number">600</span>);</span><br><span class="line">IMAGE imgGuoqi, imgBaidu;</span><br><span class="line"><span class="built_in">loadimage</span>(&imgGuoqi, _T(<span class="string">"image\\guoqi.jpg"</span>), <span class="number">1000</span>, <span class="number">600</span>);<span class="comment">// 加载国旗(背景图)</span></span><br><span class="line"><span class="built_in">loadimage</span>(&imgBaidu, _T(<span class="string">"image\\baidu.png"</span>));<span class="comment">// 加载百度LOGO(PNG格式)</span></span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">0</span>, &imgGuoqi);<span class="comment">// 显示国旗</span></span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">0</span>, &imgBaidu);<span class="comment">// 显示百度LOGO</span></span><br><span class="line"><span class="built_in">putimage_alpha</span>(<span class="literal">NULL</span>, <span class="number">0</span>, <span class="number">300</span>, &imgBaidu, BLACK);<span class="comment">// 显示百度LOGO(透明贴图)</span></span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>TransparentBlt 是 Windows GDI(Graphics Device Interface)中的一个函数,用于在绘制位图时支持透明效果。</p><p>函数说明:第1个参数为目标设备,第2、3个参数是输出目标矩形左上角坐标,第4、5个参数是目标矩形的宽和高,参数6-10与1-5类似,第11个参数是<strong>透明底色</strong>(若图片是透明图片,默认为BLACK)</p><p><strong>注:此方法只支持 PNG 格式的图片</strong></p><p>范例3:AlphaBlend 函数实现(<strong>推荐</strong>)</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">pragma</span> comment(lib, <span class="string">"MSIMG32.LIB"</span>)<span class="comment">// 链接器在链接过程中包含指定的库文件</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">putimage_alpha</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y, IMAGE* img)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="type">int</span> w = img-><span class="built_in">getwidth</span>();</span><br><span class="line"><span class="type">int</span> h = img-><span class="built_in">getheight</span>();</span><br><span class="line"><span class="built_in">AlphaBlend</span>(<span class="built_in">GetImageHDC</span>(<span class="literal">NULL</span>), x, y, w, h, <span class="built_in">GetImageHDC</span>(img), <span class="number">0</span>, <span class="number">0</span>, w, h, { AC_SRC_OVER, <span class="number">0</span>, <span class="number">255</span>, AC_SRC_ALPHA });</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">1000</span>, <span class="number">600</span>);</span><br><span class="line"></span><br><span class="line">IMAGE imgGuoqi, imgBaidu;</span><br><span class="line"><span class="built_in">loadimage</span>(&imgGuoqi, _T(<span class="string">"image\\guoqi.jpg"</span>), <span class="number">1000</span>, <span class="number">600</span>);<span class="comment">// 加载国旗(背景图)</span></span><br><span class="line"><span class="built_in">loadimage</span>(&imgBaidu, _T(<span class="string">"image\\baidu.png"</span>));<span class="comment">// 加载百度LOGO(PNG格式)</span></span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">0</span>, &imgGuoqi);<span class="comment">// 显示国旗</span></span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">0</span>, &imgBaidu);<span class="comment">// 显示百度LOGO</span></span><br><span class="line"><span class="built_in">putimage_alpha</span>(<span class="number">0</span>, <span class="number">300</span>, &imgBaidu);<span class="comment">// 显示百度LOGO(透明贴图)</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>AlphaBlend 是 Windows GDI 中用于实现 <strong>Alpha 混合(透明/半透明)</strong> 绘制的函数,比 TransparentBlt 更强大,支持 <strong>逐像素透明度(Alpha 通道)</strong> 和 <strong>整体透明度(全局 Alpha)</strong>。</p><p><strong>注:此方法只支持 PNG 格式的图片</strong></p><h4 id="图片动画"><a href="#图片动画" class="headerlink" title="图片动画"></a>图片动画</h4><p>图片动画的核心是<strong>一系列静态的图像(动画帧)</strong>。每一帧都是一张静态的图片,但它们之间略有不同,通常表现为物体的位置、形状或颜色的微小变化。这些帧按照特定的顺序排列,并以一定的速度连续播放,使得观者感受到运动的效果。</p><p>范例:角色动画</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">pragma</span> comment(lib, <span class="string">"MSIMG32.LIB"</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> WINDOW_WIDTH = <span class="number">1000</span>;<span class="comment">//窗口宽度</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> WINDOW_HEIGHT = <span class="number">600</span>;<span class="comment">//窗口高度</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> FRAME = <span class="number">60</span>;<span class="comment">//帧数</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> INTERVAL_MS = <span class="number">15</span>;<span class="comment">//动画帧间隔</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> IMAGE_NUM = <span class="number">13</span>;<span class="comment">//动画图片数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//显示透明图片</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">putimage_alpha</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y, IMAGE* img)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="type">int</span> w = img-><span class="built_in">getwidth</span>();</span><br><span class="line"><span class="type">int</span> h = img-><span class="built_in">getheight</span>();</span><br><span class="line"><span class="built_in">AlphaBlend</span>(<span class="built_in">GetImageHDC</span>(<span class="literal">NULL</span>), x, y, w, h, <span class="built_in">GetImageHDC</span>(img), <span class="number">0</span>, <span class="number">0</span>, w, h, { AC_SRC_OVER, <span class="number">0</span>, <span class="number">255</span>, AC_SRC_ALPHA });</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="type">bool</span> running = <span class="literal">true</span>;<span class="comment">//主循环控制</span></span><br><span class="line">ExMessage msg;<span class="comment">//键鼠消息</span></span><br><span class="line">IMAGE imgBackground;<span class="comment">//背景图片对象</span></span><br><span class="line">IMAGE imgPEA[<span class="number">13</span>];<span class="comment">//玩家动画图片</span></span><br><span class="line">TCHAR imgPath[<span class="number">256</span>];<span class="comment">//动画图片文件路径</span></span><br><span class="line"><span class="type">int</span> imgIndex = <span class="number">0</span>;<span class="comment">//动画帧索引</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> timer = <span class="number">0</span>;<span class="comment">//动画计时器</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">loadimage</span>(&imgBackground, _T(<span class="string">"image\\background.jpg"</span>));<span class="comment">//加载背景图片</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < IMAGE_NUM; i++)<span class="comment">//加载动画图片</span></span><br><span class="line">{</span><br><span class="line">_stprintf_s(imgPath, _T(<span class="string">"image\\pea\\%d.png"</span>), i + <span class="number">1</span>);<span class="comment">//动画图片路径(格式转换)</span></span><br><span class="line"><span class="built_in">loadimage</span>(&imgPEA[i], imgPath);<span class="comment">//加载动画图片</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">initgraph</span>(WINDOW_WIDTH, WINDOW_HEIGHT);</span><br><span class="line"><span class="built_in">BeginBatchDraw</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">//主循环</span></span><br><span class="line"><span class="keyword">while</span> (running)</span><br><span class="line">{</span><br><span class="line">DWORD beginTime = <span class="built_in">GetTickCount</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">//消息处理</span></span><br><span class="line"><span class="keyword">while</span> (<span class="built_in">peekmessage</span>(&msg))</span><br><span class="line">{</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//数据处理</span></span><br><span class="line">timer += <span class="number">5</span>;</span><br><span class="line"><span class="keyword">if</span> (timer > INTERVAL_MS)<span class="comment">//定时器超过预定的时间间隔时切换下一张图片</span></span><br><span class="line">{</span><br><span class="line">imgIndex = (imgIndex + <span class="number">1</span>) % IMAGE_NUM;<span class="comment">//循环切换图片:索引值0-12</span></span><br><span class="line">timer = <span class="number">0</span>;<span class="comment">//重置计时器</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//绘图</span></span><br><span class="line"><span class="built_in">cleardevice</span>();</span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">0</span>, &imgBackground);<span class="comment">//绘制背景图片</span></span><br><span class="line"><span class="built_in">putimage_alpha</span>(<span class="number">500</span>, <span class="number">300</span>, &imgPEA[imgIndex]);<span class="comment">//绘制豌豆图片</span></span><br><span class="line"><span class="built_in">FlushBatchDraw</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">//帧延时处理</span></span><br><span class="line">DWORD endTime = <span class="built_in">GetTickCount</span>();</span><br><span class="line">DWORD elapsedTime = endTime - beginTime;</span><br><span class="line"><span class="keyword">if</span> (elapsedTime < <span class="number">1000</span> / FRAME)</span><br><span class="line"><span class="built_in">Sleep</span>(<span class="number">1000</span> / FRAME - elapsedTime);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">EndBatchDraw</span>();</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="Resize-函数"><a href="#Resize-函数" class="headerlink" title="Resize 函数"></a>Resize 函数</h3><h3 id="GetImageBuffer-函数"><a href="#GetImageBuffer-函数" class="headerlink" title="GetImageBuffer 函数"></a>GetImageBuffer 函数</h3><p>范例1:GetImageBuffer 通过<strong>直接操作显示缓冲区绘制渐变的蓝色</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">600</span>, <span class="number">400</span>);</span><br><span class="line"></span><br><span class="line">DWORD* pMem = <span class="built_in">GetImageBuffer</span>();<span class="comment">// 获取当前窗口所指图像缓冲区的指针</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">600</span> * <span class="number">400</span>; i++)</span><br><span class="line">pMem[i] = <span class="built_in">BGR</span>(<span class="built_in">RGB</span>(<span class="number">0</span>, <span class="number">0</span>, i * <span class="number">256</span> / (<span class="number">600</span> * <span class="number">400</span>)));<span class="comment">// 直接对图像缓冲区每个坐标像素赋值(颜色)</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>范例2:图像翻转</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 图像翻转</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">flip_image</span><span class="params">(IMAGE* srcImg, IMAGE* dstImg)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="type">int</span> w = srcImg-><span class="built_in">getwidth</span>();<span class="comment">// 获取源图像宽度</span></span><br><span class="line"><span class="type">int</span> h = srcImg-><span class="built_in">getheight</span>();<span class="comment">// 获取源图像高度</span></span><br><span class="line"><span class="built_in">Resize</span>(dstImg, w, h);<span class="comment">// 设置目标图像与源图像宽高一致</span></span><br><span class="line">DWORD* src_buffer = <span class="built_in">GetImageBuffer</span>(srcImg);<span class="comment">// 获取源图像缓冲区指针</span></span><br><span class="line">DWORD* dst_buffer = <span class="built_in">GetImageBuffer</span>(dstImg);<span class="comment">// 获取目标图像缓冲区指针</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> y = <span class="number">0</span>; y < h; y++)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> x = <span class="number">0</span>; x < w; x++)</span><br><span class="line">{</span><br><span class="line"><span class="type">int</span> idx_src = y * w + x;</span><br><span class="line"><span class="type">int</span> idx_dst = y * w + (w - x - <span class="number">1</span>);</span><br><span class="line">dst_buffer[idx_dst] = src_buffer[idx_src];<span class="comment">// 交换对应坐标像素的颜色值</span></span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">1400</span>, <span class="number">600</span>);</span><br><span class="line"></span><br><span class="line">IMAGE img1, img2;</span><br><span class="line"><span class="built_in">loadimage</span>(&img1, _T(<span class="string">"image\\background.jpg"</span>));</span><br><span class="line"><span class="built_in">flip_image</span>(&img1, &img2);</span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">0</span>, &img2);</span><br><span class="line"></span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="消息处理"><a href="#消息处理" class="headerlink" title="消息处理"></a>消息处理</h2><p><a href="https://docs.easyx.cn/zh-cn/msg-func">https://docs.easyx.cn/zh-cn/msg-func</a></p><p><strong>消息缓冲区</strong>可以<strong>缓冲 63 个未处理的消息</strong>。每次获取消息时,将从消息缓冲区<strong>取出一个最早发生的</strong>消息。</p><table><thead><tr><th>函数用法</th><th>函数说明</th></tr></thead><tbody><tr><td>ExMessage <strong>getmessage</strong> ( BYTE filter = -1 )<br>void <strong>getmessage</strong> ( ExMessage *msg, BYTE filter = -1 )</td><td>从消息缓冲区获取一个消息。如果缓冲区中没有消息,则程序会一直等待(阻塞式)</td></tr><tr><td>bool <strong>peekmessage</strong> ( ExMessage *msg, BYTE filter = -1, bool removemsg = true)</td><td>从消息缓冲区获取一个消息,并立即返回</td></tr><tr><td>void <strong>flushmessage</strong> ( BYTE filter = -1 )</td><td>清空消息缓冲区</td></tr></tbody></table><p><strong>参数说明:</strong></p><ul><li><strong>msg</strong>:指向消息结构体 ExMessage 的指针,用来保存获取到的消息。</li><li><strong>filter</strong>:指定要获取的消息范围,默认 -1 获取所有类别的消息。可以用以下值或值的组合获取指定类别的消息</li></ul><table><thead><tr><th>标志</th><th>描述</th></tr></thead><tbody><tr><td>EX_MOUSE</td><td><strong>鼠标</strong>消息。</td></tr><tr><td>EX_KEY</td><td><strong>按键</strong>消息。</td></tr><tr><td>EX_CHAR</td><td>字符消息。</td></tr><tr><td>EX_WINDOW</td><td><strong>窗口</strong>消息。</td></tr></tbody></table><ul><li><strong>removemsg</strong>:在 peekmessage 处理完消息后,<strong>是否将其从消息队列中移除。</strong></li></ul><h3 id="ExMessage-结构体"><a href="#ExMessage-结构体" class="headerlink" title="ExMessage 结构体"></a><strong>ExMessage 结构体</strong></h3><p><a href="https://docs.easyx.cn/zh-cn/exmessage">https://docs.easyx.cn/zh-cn/exmessage</a></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">struct ExMessage</span><br><span class="line">{</span><br><span class="line">USHORT message;// 消息标识</span><br><span class="line">union</span><br><span class="line">{</span><br><span class="line">// 鼠标消息的数据</span><br><span class="line">struct</span><br><span class="line">{</span><br><span class="line">bool ctrl:1;// Ctrl 键是否按下</span><br><span class="line">bool shift:1;// Shift 键是否按下</span><br><span class="line">bool lbutton:1;// 鼠标左键是否按下</span><br><span class="line">bool mbutton:1;// 鼠标中键是否按下</span><br><span class="line">bool rbutton:1;// 鼠标右键</span><br><span class="line">short x;// 鼠标的 x 坐标</span><br><span class="line">short y;// 鼠标的 y 坐标</span><br><span class="line">short wheel;// 鼠标滚轮滚动值,为 120 的倍数</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">// 按键消息的数据</span><br><span class="line">struct</span><br><span class="line">{</span><br><span class="line">BYTE vkcode;// 按键的虚拟键码</span><br><span class="line">BYTE scancode;// 按键的扫描码(依赖于 OEM)</span><br><span class="line">bool extended:1;// 按键是否是扩展键</span><br><span class="line">bool prevdown:1;// 按键的前一个状态是否按下</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">// 字符消息的数据</span><br><span class="line">TCHAR ch;</span><br><span class="line"></span><br><span class="line">// 窗口消息的数据</span><br><span class="line">struct</span><br><span class="line">{</span><br><span class="line">WPARAM wParam;</span><br><span class="line">LPARAM lParam;</span><br><span class="line">};</span><br><span class="line">};</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p><strong>message</strong> :可以分为四大类:<strong>EX_MOUSE</strong>(鼠标11项)、<strong>EX_KEY</strong>(键盘2项)、<strong>EX_CHAR</strong>(字符1项)、<strong>EX_WINDOW</strong>(窗口3项)</p><p><strong>union</strong> :共用体中存储具体消息的数据</p><h3 id="鼠标消息"><a href="#鼠标消息" class="headerlink" title="鼠标消息"></a>鼠标消息</h3><p>范例:跟随鼠标移动的圆</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Ball</span> {</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"><span class="type">int</span> posX;</span><br><span class="line"><span class="type">int</span> posY;</span><br><span class="line"><span class="type">int</span> radius;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"><span class="built_in">Ball</span>(<span class="type">int</span> x = <span class="number">100</span>, <span class="type">int</span> y=<span class="number">100</span>, <span class="type">int</span> r=<span class="number">50</span>) : <span class="built_in">posX</span>(x), <span class="built_in">posY</span>(y), <span class="built_in">radius</span>(r) {}<span class="comment">// 构造方法</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">getX</span><span class="params">()</span> <span class="type">const</span> </span>{ <span class="keyword">return</span> posX; }</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">getY</span><span class="params">()</span> <span class="type">const</span> </span>{ <span class="keyword">return</span> posY; }</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">getRadius</span><span class="params">()</span> <span class="type">const</span> </span>{ <span class="keyword">return</span> radius; }</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setX</span><span class="params">(<span class="type">int</span> x)</span> </span>{ posX = x; }</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setY</span><span class="params">(<span class="type">int</span> y)</span> </span>{ posY = y; }</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">setRadius</span><span class="params">(<span class="type">int</span> r)</span> </span>{ radius = r; }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"><span class="type">bool</span> running = <span class="literal">true</span>;<span class="comment">// 主循环控制参数</span></span><br><span class="line">ExMessage msg;<span class="comment">// 消息对象</span></span><br><span class="line"><span class="function">Ball <span class="title">ball</span><span class="params">(<span class="number">400</span>, <span class="number">300</span>, <span class="number">50</span>)</span></span>;<span class="comment">// 待绘制对象</span></span><br><span class="line"><span class="comment">//Ball ball;</span></span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">800</span>, <span class="number">600</span>);<span class="comment">// 初始化绘图窗口</span></span><br><span class="line"><span class="built_in">BeginBatchDraw</span>();<span class="comment">// 开启批量绘图</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 主循环</span></span><br><span class="line"><span class="keyword">while</span> (running) {</span><br><span class="line"><span class="comment">// 消息处理</span></span><br><span class="line"><span class="keyword">while</span> (<span class="built_in">peekmessage</span>(&msg)) {</span><br><span class="line"><span class="keyword">if</span> (msg.message == WM_MOUSEMOVE) {<span class="comment">// 圆的位置随鼠标位置变化</span></span><br><span class="line">ball.<span class="built_in">setX</span>(msg.x);</span><br><span class="line">ball.<span class="built_in">setY</span>(msg.y);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (msg.message == WM_LBUTTONDOWN) {<span class="comment">// 左键按下圆变红色</span></span><br><span class="line"><span class="built_in">setfillcolor</span>(RED);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (msg.message == WM_LBUTTONUP) {<span class="comment">// 左键松开圆变白色</span></span><br><span class="line"><span class="built_in">setfillcolor</span>(WHITE);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (msg.message == WM_RBUTTONDOWN) {<span class="comment">// 右键按下结束主循环</span></span><br><span class="line">running = <span class="literal">false</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 绘图</span></span><br><span class="line"><span class="built_in">cleardevice</span>();<span class="comment">// 清除屏幕</span></span><br><span class="line"><span class="built_in">solidcircle</span>(ball.<span class="built_in">getX</span>(), ball.<span class="built_in">getY</span>(), ball.<span class="built_in">getRadius</span>());<span class="comment">// 绘制当前帧内容</span></span><br><span class="line"><span class="built_in">FlushBatchDraw</span>();<span class="comment">// 刷新批量绘图</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">EndBatchDraw</span>();<span class="comment">// 关闭批量绘图</span></span><br><span class="line"><span class="built_in">closegraph</span>();<span class="comment">// 关闭绘图窗口</span></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="键盘消息"><a href="#键盘消息" class="headerlink" title="键盘消息"></a>键盘消息</h3><p>范例1:用键盘控制小球</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 用结构体封装小球属性</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">Ball</span></span><br><span class="line">{</span><br><span class="line"><span class="type">int</span> x;<span class="comment">// 小球圆心坐标x</span></span><br><span class="line"><span class="type">int</span> y;<span class="comment">// 小球圆心坐标y</span></span><br><span class="line"><span class="type">int</span> r;<span class="comment">// 小球半径</span></span><br><span class="line"><span class="type">int</span> dx;<span class="comment">// 小球在x轴方向移动的增量</span></span><br><span class="line"><span class="type">int</span> dy;<span class="comment">// 小球在y轴方向移动的增量</span></span><br><span class="line">COLORREF color;<span class="comment">// 小球颜色</span></span><br><span class="line">} Ball;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="type">bool</span> running = <span class="literal">true</span>;</span><br><span class="line">ExMessage msg;</span><br><span class="line">Ball ball = { <span class="number">300</span>, <span class="number">300</span>, <span class="number">20</span>, <span class="number">5</span>, <span class="number">5</span>, YELLOW };<span class="comment">// 创建小球并初始化</span></span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">600</span>, <span class="number">600</span>);</span><br><span class="line"><span class="built_in">BeginBatchDraw</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (running)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">while</span> (<span class="built_in">peekmessage</span>(&msg))</span><br><span class="line">{</span><br><span class="line"><span class="keyword">if</span> (msg.message == WM_KEYDOWN)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">switch</span> (msg.vkcode)<span class="comment">// 判断虚拟键代码</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">case</span> <span class="string">'w'</span>:<span class="comment">// 上键:小球Y坐标减少</span></span><br><span class="line"><span class="keyword">case</span> <span class="string">'W'</span>:</span><br><span class="line"><span class="keyword">case</span> VK_UP:</span><br><span class="line">ball.y -= ball.dy;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> <span class="string">'s'</span>:</span><br><span class="line"><span class="keyword">case</span> <span class="string">'S'</span>:</span><br><span class="line"><span class="keyword">case</span> VK_DOWN:<span class="comment">// 下键:小球Y坐标增加</span></span><br><span class="line">ball.y += ball.dy;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> <span class="string">'a'</span>:</span><br><span class="line"><span class="keyword">case</span> <span class="string">'A'</span>:</span><br><span class="line"><span class="keyword">case</span> VK_LEFT:<span class="comment">// 右键:小球X坐标减少</span></span><br><span class="line">ball.x -= ball.dx;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> <span class="string">'d'</span>:</span><br><span class="line"><span class="keyword">case</span> <span class="string">'D'</span>:</span><br><span class="line"><span class="keyword">case</span> VK_RIGHT:<span class="comment">// 右键:小球X坐标增加</span></span><br><span class="line">ball.x += ball.dx;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> VK_ESCAPE:<span class="comment">// ESC键:结束主循环</span></span><br><span class="line">running = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">cleardevice</span>();<span class="comment">// 清除屏幕</span></span><br><span class="line"><span class="built_in">setfillcolor</span>(ball.color);<span class="comment">// 设置填充颜色</span></span><br><span class="line"><span class="built_in">solidcircle</span>(ball.x, ball.y, ball.r);<span class="comment">// 绘制无边框填充圆;</span></span><br><span class="line"><span class="built_in">FlushBatchDraw</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">EndBatchDraw</span>();</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>虚拟键代码 <a href="https://learn.microsoft.com/zh-cn/windows/win32/inputdev/virtual-key-codes">https://learn.microsoft.com/zh-cn/windows/win32/inputdev/virtual-key-codes</a></p><p><strong>优化后</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><math.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> WIN_WIDTH 600</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> WIN_HEIGHT 600</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 用结构体封装小球属性</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">Ball</span></span><br><span class="line">{</span><br><span class="line"><span class="type">int</span> x;<span class="comment">// 小球圆心坐标x</span></span><br><span class="line"><span class="type">int</span> y;<span class="comment">// 小球圆心坐标y</span></span><br><span class="line"><span class="type">int</span> r;<span class="comment">// 小球半径</span></span><br><span class="line"><span class="type">int</span> dx;<span class="comment">// 小球在x轴方向移动的增量</span></span><br><span class="line"><span class="type">int</span> dy;<span class="comment">// 小球在y轴方向移动的增量</span></span><br><span class="line">COLORREF color;<span class="comment">// 小球颜色</span></span><br><span class="line"><span class="type">bool</span> isMoveUp = <span class="literal">false</span>;<span class="comment">// 小球是否向四个方向移动</span></span><br><span class="line"><span class="type">bool</span> isMoveDown = <span class="literal">false</span>;</span><br><span class="line"><span class="type">bool</span> isMoveLeft = <span class="literal">false</span>;</span><br><span class="line"><span class="type">bool</span> isMoveRight = <span class="literal">false</span>;</span><br><span class="line">} Ball;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="type">bool</span> running = <span class="literal">true</span>;</span><br><span class="line">ExMessage msg;</span><br><span class="line">Ball ball = { <span class="number">300</span>, <span class="number">300</span>, <span class="number">20</span>, <span class="number">5</span>, <span class="number">5</span>, YELLOW };<span class="comment">// 创建小球并初始化</span></span><br><span class="line"><span class="built_in">initgraph</span>(WIN_WIDTH, WIN_HEIGHT);</span><br><span class="line"><span class="built_in">BeginBatchDraw</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (running)</span><br><span class="line">{</span><br><span class="line">DWORD beginTime = <span class="built_in">GetTickCount</span>();<span class="comment">// 记录循环开始时间</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 消息处理</span></span><br><span class="line"><span class="keyword">while</span> (<span class="built_in">peekmessage</span>(&msg))</span><br><span class="line">{</span><br><span class="line"><span class="keyword">if</span> (msg.message == WM_KEYDOWN)<span class="comment">// 按下按键处理</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">switch</span> (msg.vkcode)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">case</span> <span class="string">'w'</span>:</span><br><span class="line"><span class="keyword">case</span> <span class="string">'W'</span>:</span><br><span class="line"><span class="keyword">case</span> VK_UP:</span><br><span class="line">ball.isMoveUp = <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> <span class="string">'s'</span>:</span><br><span class="line"><span class="keyword">case</span> <span class="string">'S'</span>:</span><br><span class="line"><span class="keyword">case</span> VK_DOWN:</span><br><span class="line">ball.isMoveDown = <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> <span class="string">'a'</span>:</span><br><span class="line"><span class="keyword">case</span> <span class="string">'A'</span>:</span><br><span class="line"><span class="keyword">case</span> VK_LEFT:</span><br><span class="line">ball.isMoveLeft = <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> <span class="string">'d'</span>:</span><br><span class="line"><span class="keyword">case</span> <span class="string">'D'</span>:</span><br><span class="line"><span class="keyword">case</span> VK_RIGHT:</span><br><span class="line">ball.isMoveRight = <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> VK_ESCAPE:</span><br><span class="line">running = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (msg.message == WM_KEYUP)<span class="comment">// 松开按键处理</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">switch</span> (msg.vkcode)</span><br><span class="line">{</span><br><span class="line"><span class="keyword">case</span> <span class="string">'w'</span>:</span><br><span class="line"><span class="keyword">case</span> <span class="string">'W'</span>:</span><br><span class="line"><span class="keyword">case</span> VK_UP:</span><br><span class="line">ball.isMoveUp = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> <span class="string">'s'</span>:</span><br><span class="line"><span class="keyword">case</span> <span class="string">'S'</span>:</span><br><span class="line"><span class="keyword">case</span> VK_DOWN:</span><br><span class="line">ball.isMoveDown = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> <span class="string">'a'</span>:</span><br><span class="line"><span class="keyword">case</span> <span class="string">'A'</span>:</span><br><span class="line"><span class="keyword">case</span> VK_LEFT:</span><br><span class="line">ball.isMoveLeft = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> <span class="string">'d'</span>:</span><br><span class="line"><span class="keyword">case</span> <span class="string">'D'</span>:</span><br><span class="line"><span class="keyword">case</span> VK_RIGHT:</span><br><span class="line">ball.isMoveRight = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 斜向移动:计算不同方向(包括同时)按下时的速度增量</span></span><br><span class="line"><span class="type">int</span> directX = ball.isMoveRight - ball.isMoveLeft;</span><br><span class="line"><span class="type">int</span> directY = ball.isMoveDown - ball.isMoveUp;</span><br><span class="line"><span class="type">double</span> directXY = <span class="built_in">sqrt</span>(directX * directX + directY * directY);</span><br><span class="line"><span class="keyword">if</span> (directXY != <span class="number">0</span>)</span><br><span class="line">{</span><br><span class="line"><span class="type">double</span> factorX = directX / directXY;<span class="comment">//计算X、Y方向的标准化分量</span></span><br><span class="line"><span class="type">double</span> factorY = directY / directXY;</span><br><span class="line">ball.x += (<span class="type">int</span>)ball.dx * factorX;<span class="comment">//小球坐标 = 方向增速 * 方向的标准化分量</span></span><br><span class="line">ball.y += (<span class="type">int</span>)ball.dy * factorY;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 边缘检测</span></span><br><span class="line"><span class="keyword">if</span> (ball.y - ball.r <= <span class="number">0</span>)<span class="comment">// 上</span></span><br><span class="line">ball.y = ball.r;</span><br><span class="line"><span class="keyword">if</span> (ball.y + ball.r >= WIN_HEIGHT)<span class="comment">// 下</span></span><br><span class="line">ball.y = WIN_HEIGHT - ball.r - <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span> (ball.x - ball.r <= <span class="number">0</span>)<span class="comment">// 左</span></span><br><span class="line">ball.x = ball.r;</span><br><span class="line"><span class="keyword">if</span> (ball.x + ball.r >= WIN_WIDTH)<span class="comment">// 右</span></span><br><span class="line">ball.x = WIN_WIDTH - ball.r - <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绘图</span></span><br><span class="line"><span class="built_in">cleardevice</span>();<span class="comment">// 清除屏幕</span></span><br><span class="line"><span class="built_in">setfillcolor</span>(ball.color);<span class="comment">// 设置填充颜色</span></span><br><span class="line"><span class="built_in">solidcircle</span>(ball.x, ball.y, ball.r);<span class="comment">// 绘制无边框填充圆;</span></span><br><span class="line"><span class="built_in">FlushBatchDraw</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 帧延时</span></span><br><span class="line">DWORD endTime = <span class="built_in">GetTickCount</span>();<span class="comment">// 记录循环结束时间</span></span><br><span class="line">DWORD elapsedTime = endTime - beginTime;<span class="comment">// 计算循环耗时</span></span><br><span class="line"><span class="keyword">if</span> (elapsedTime < <span class="number">1000</span> / <span class="number">60</span>)<span class="comment">// 按每秒60帧进行补时</span></span><br><span class="line"><span class="built_in">Sleep</span>(<span class="number">1000</span> / <span class="number">60</span> - elapsedTime);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">EndBatchDraw</span>();</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="其它函数"><a href="#其它函数" class="headerlink" title="其它函数"></a>其它函数</h2><h3 id="设置窗口标题"><a href="#设置窗口标题" class="headerlink" title="设置窗口标题"></a>设置窗口标题</h3><p>范例:使用 <strong>GetHWnd</strong> 和 <strong>SetWindowText</strong> 函数设置窗口标题</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">600</span>, <span class="number">600</span>);</span><br><span class="line"></span><br><span class="line">HWND hWnd = <span class="built_in">GetHWnd</span>();<span class="comment">// 获得窗口句柄</span></span><br><span class="line"><span class="built_in">SetWindowText</span>(hWnd, _T(<span class="string">"植物大战僵尸"</span>));<span class="comment">// 使用 Windows API 修改窗口名称</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="弹窗消息"><a href="#弹窗消息" class="headerlink" title="弹窗消息"></a>弹窗消息</h3><p>在Visual C++(VC)中,MessageBox 函数是一个常用的 Windows API 函数,用于显示一个模态对话框,其中包含文本、标题、图标和按钮等。以下是函数的详细用法:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">MessageBox</span><span class="params">( </span></span></span><br><span class="line"><span class="params"><span class="function"> HWND hWnd, <span class="comment">// 父窗口句柄。如果为NULL,则消息框没有父窗口 </span></span></span></span><br><span class="line"><span class="params"><span class="function"> LPCTSTR lpText, <span class="comment">// 要显示的消息文本 </span></span></span></span><br><span class="line"><span class="params"><span class="function"> LPCTSTR lpCaption, <span class="comment">// 消息框的标题 </span></span></span></span><br><span class="line"><span class="params"><span class="function"> UINT uType <span class="comment">// 指定消息框的内容和行为的标志 </span></span></span></span><br><span class="line"><span class="params"><span class="function">)</span></span>;</span><br></pre></td></tr></table></figure><p><strong>参数说明</strong></p><ol><li><strong>hWnd</strong>:指定消息框的父窗口句柄。如果此参数为NULL,则消息框没有父窗口,且作为顶级窗口显示。</li><li><strong>lpText</strong>:要在消息框中显示的文本。</li><li><strong>lpCaption</strong>:消息框的标题。如果此参数为NULL,则默认标题为“Error”。</li><li><strong>uType</strong>:用于指定消息框的内容和行为的标志。这可以是一个或多个以下常量的组合:<ul><li>MB_OK:消息框包含一个“确定”按钮。</li><li>MB_OKCANCEL:消息框包含“确定”和“取消”按钮。</li><li>MB_YESNO:消息框包含“是”和“否”按钮。</li><li>MB_YESNOCANCEL:消息框包含“是”、“否”和“取消”按钮。</li><li>MB_ICONEXCLAMATION、MB_ICONWARNING、MB_ICONINFORMATION、MB_ICONQUESTION、MB_ICONERROR等:用于指定消息框中显示的图标。</li></ul></li></ol><p><strong>返回值</strong></p><p>函数返回一个整数值,表示用户点击的按钮。例如:</p><ul><li><strong>IDOK</strong>:用户点击了“确定”按钮。</li><li><strong>IDCANCEL</strong>:用户点击了“取消”按钮。</li><li><strong>IDYES</strong>:用户点击了“是”按钮。</li><li><strong>IDNO</strong>:用户点击了“否”按钮。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">1000</span>, <span class="number">600</span>);</span><br><span class="line"></span><br><span class="line">HWND hWnd = <span class="built_in">GetHWnd</span>();</span><br><span class="line"><span class="built_in">MessageBox</span>(hWnd, _T(<span class="string">"你被僵尸吃掉了!"</span>), _T(<span class="string">"游戏结束"</span>), MB_OK | MB_ICONERROR);</span><br><span class="line"></span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>注:在使用 MessageBox 函数之前,需要包含 <strong>windows.h</strong> 头文件(<strong>如果已经包含了 graphics.h 头文件则可以省略</strong>)</p><h3 id="播放音频"><a href="#播放音频" class="headerlink" title="播放音频"></a>播放音频</h3><p><strong>mciSendString</strong> 是 Windows API 中的一个函数,用于向媒体控制接口(Media Control Interface,MCI)设备发送命令字符串。这个函数常用于控制多媒体设备,如音频和视频播放,支持 MPEG, AVI, WAV, MP3 等多种格式。</p><p>范例:播放背景音乐</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><graphics.h></span></span></span><br><span class="line"><span class="comment">//#include <windows.h>// 此项在导入graphics.h头文件后可以省略</span></span><br><span class="line"><span class="meta">#<span class="keyword">pragma</span> comment(lib, <span class="string">"winmm.lib"</span>)<span class="comment">// 加载多媒体静态库</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">initgraph</span>(<span class="number">1000</span>, <span class="number">600</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">mciSendString</span>(_T(<span class="string">"open audio\\bg.mp3 alias BGM"</span>), <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);<span class="comment">// 打开音乐文件,alias指定别名</span></span><br><span class="line"><span class="built_in">mciSendString</span>(_T(<span class="string">"play BGM repeat"</span>), <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);<span class="comment">// 使用别名播放音乐,repeat重复播放</span></span><br><span class="line"></span><br><span class="line">IMAGE img;</span><br><span class="line"><span class="built_in">loadimage</span>(&img, _T(<span class="string">"image\\background.jpg"</span>));</span><br><span class="line"><span class="built_in">putimage</span>(<span class="number">0</span>, <span class="number">0</span>, &img);</span><br><span class="line"></span><br><span class="line"><span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line"><span class="built_in">closegraph</span>();</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
</entry>
<entry>
<title>比赛相关内容的一个一个记</title>
<link href="/2025/04/10/%E6%AF%94%E8%B5%9B%E7%9B%B8%E5%85%B3%E5%86%85%E5%AE%B9%E7%9A%84%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/"/>
<url>/2025/04/10/%E6%AF%94%E8%B5%9B%E7%9B%B8%E5%85%B3%E5%86%85%E5%AE%B9%E7%9A%84%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E8%AE%B0/</url>
<content type="html"><![CDATA[<p>本篇对各个比赛的一个一个印象记录</p><h1 id="蓝桥杯"><a href="#蓝桥杯" class="headerlink" title="蓝桥杯"></a>蓝桥杯</h1><h4 id="题型"><a href="#题型" class="headerlink" title="题型"></a>题型</h4><ul><li><p>填空 (2-4) 手动计算(日期,如闰年;几何,数学) <strong>保证正确率</strong></p></li><li><p>编程 (省6国8) (搜索,dp,贪心) 有部分分 <strong>先暴力,后优化</strong></p></li></ul><p><strong>模板为王</strong></p><p>必须在代码结尾 <strong>return 0</strong>,否则一定为零分</p><p>检查好自己提交的代码是否为**去除多余东西,(只含必要输出成果)**有必要东西的代码</p><h1 id="全国大学生嵌入式芯片与系统设计竞赛-芯片应用赛道选题指南"><a href="#全国大学生嵌入式芯片与系统设计竞赛-芯片应用赛道选题指南" class="headerlink" title="全国大学生嵌入式芯片与系统设计竞赛(芯片应用赛道选题指南)"></a><strong>全国大学生嵌入式芯片与系统设计竞赛(<strong>芯片应用赛道选题指南</strong>)</strong></h1><p><strong>星闪物联网应用平台</strong></p><p>以下仅介绍WS63相关</p><p><strong>关键特性</strong>:高性能 32bit 微处理器、2.4GHz WiFi 6、SLE、BLE三模、丰富的外设接口,其中增强款WS63E 支持2.4GHz 的雷达人体活动检测功能</p><p><strong>应用场景</strong>:智慧家居、雷达感知、星闪网关、星闪中控屏、星闪手柄等</p><p>**注:**星闪(NearLink),是中国原生的新一代无线短距通信技术。与传统短距传输技术方案相比,星闪在功耗、速度、覆盖范围和连接性能全面领先,可以在智能终端、智能家居、智能汽车、智能制造等各类细分场景下实现更极致的用户体验。</p><p><strong>选题方向一:星闪物联网应用方向(参赛组别:本科生)</strong></p><p>1、本选题参赛作品的主控要求优先使用星闪 WS63 或 WS63E 或 BS21,</p><p>Hi3861V100 也可使用,操作系统可使用 LiteOS 或 OpenHarmony 版本。</p><p>2、如采用星闪 WS63 或 WS63E 或 BS21,并发挥星闪技术特性可酌情加分。</p><p>3、本选题重点考察参赛选手的嵌入式系统开发能力, SLE/BLE/WiFi 多端互联能力。</p><p>4、参赛选手须具备基础的 C 语言编码能力,了解物联网技术及应用相关知识。</p><p>5、本选题学习资料可参考《2025 年嵌入式竞赛海思赛道学习入口》,并知晓开发环境要求及套件功能限制。</p>]]></content>
</entry>
<entry>
<title>搭建环境做一个一个备忘</title>
<link href="/2025/04/05/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/"/>
<url>/2025/04/05/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/</url>
<content type="html"><![CDATA[<p>为临时需要再搭建环境时做一个一个备忘</p><p>记得先切换电脑自带的快捷键,如切换<strong>语言标点</strong></p><h1 id="c-类型"><a href="#c-类型" class="headerlink" title="c++类型"></a>c++类型</h1><h2 id="关于GCC"><a href="#关于GCC" class="headerlink" title="关于GCC"></a>关于GCC</h2><h2 id="C-11"><a href="#C-11" class="headerlink" title="C++11"></a>C++11</h2><p>完全支持</p><p>从GCC4.8.1版本完全支持</p><p>-std=c++11 or std=gnu++11</p><h2 id="C-14"><a href="#C-14" class="headerlink" title="C++14"></a>C++14</h2><p>完全支持</p><p>从GCC6.1版本开始完全支持,从6.1-10(包括)的默认模式</p><p>-std=c++14 or std=gnu++14</p><h2 id="C-17"><a href="#C-17" class="headerlink" title="C++17"></a>C++17</h2><p>完全支持</p><p>从GCC 5版本开始支持,到GCC 7版本已完全支持,是GCC 11到13版本的默认模式</p><p>-std=c++17 or std=gnu++17</p><h2 id="C-17-1"><a href="#C-17-1" class="headerlink" title="C++17"></a>C++17</h2><p>完全支持</p><p>从GCC 5版本开始支持,到GCC 7版本已完全支持,是GCC 11到13版本的默认模式</p><p>-std=c++17 or std=gnu++17</p><p>C++20</p><p>未完全支持</p><p>从GCC 8版本开始支持</p><p>-std=c++20 or</p><p>std=gnu++20(GCC 9以及之前版本使用**-std=c++2a**)</p><h1 id="CPH"><a href="#CPH" class="headerlink" title="CPH"></a>CPH</h1><p>抓取洛谷题目需要<strong>右键选取LuoguProblemParser</strong></p><h1 id="Devc"><a href="#Devc" class="headerlink" title="Devc"></a>Devc</h1><p>最高支持c++11标准</p><p>安装时按默认(包括语言),可改安装到那个盘</p><h3 id="选择语言"><a href="#选择语言" class="headerlink" title="选择语言"></a><strong>选择语言</strong></h3><p>在这里选择简体中文。</p><p><img src="/2025/04/05/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/903788e3cc733503d1b25dd3be391ac9.png" alt="图片"></p><h2 id="关于调试"><a href="#关于调试" class="headerlink" title="关于调试"></a>关于调试</h2><p>工具->编译选项->代码生成优化->代码生成->语言标准(根据需要修改)</p><p> -> 代码性能->生成代码性能信息</p><p> ->连接器->产生调试信息</p><p>工具->环境选项->鼠标查看变量值</p><p><strong>有改动,重启都一定要先编译再调试</strong></p><p>快捷键 F5 <strong>开始</strong>调试</p><p>快捷键 F6 <strong>停止</strong>调试</p><p>快捷键 F7 单步调试(运行下一步)</p><p>快捷键 F8 单步<strong>进入函数</strong>调试</p><p>如果你调试前设置查看的话,变量会**“Execute to evaluate”**</p><p>遇见 <code>endl</code> 会卡死!调试查看 STL 里面参数会卡死!</p><p><strong>看stl去调试的话一定会卡死,只能打印出来,或者(赋值给一个变量,查看变量)(注意取消鼠标看变量)</strong></p><p><img src="/2025/04/05/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/600d17661c6b3c7b5f944514539744ae.png" alt="查看"></p><h2 id="自动整理代码"><a href="#自动整理代码" class="headerlink" title="自动整理代码"></a>自动整理代码</h2><p>按下Ctrl+<strong>Shift</strong>+A整理</p><h4 id="调整界面风格"><a href="#调整界面风格" class="headerlink" title="调整界面风格"></a><strong>调整界面风格</strong></h4><p>工具 -> 编辑器选项 -> 基本 ->去掉高亮显示当前行的√</p><p>-> 语法->预设->obsidian</p><h2 id="手动补全-自己用过的变量"><a href="#手动补全-自己用过的变量" class="headerlink" title="手动补全(自己用过的变量)"></a>手动补全(自己用过的变量)</h2><p>打开工具 -> 快捷键选项,找到最下面的Show code completion,把快捷键改成别的 (我选择的是Ctrl+Enter,就是选中你要更改的那项,然后直接按下你想要的组合键,自动就改了),然后确定后退出。<br><img src="/2025/04/05/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/3eac3cf5dc10ab53b736de9936e7d5d6.png" alt="只要鼠标点击到上面就可以按下要更改的快捷键"></p><p>它只能提示出它<strong>已经缓存好了的头文件的内容</strong>,(不如<strong>去查api文档</strong>)如果你在安装的时候只缓存了常用头文件的话, 你包含<br>以下为收集的一些可以提示和不可以提示的总结:</p><p>已经写上的头文件里面的函数<br>函数形参列表<br>你定义的变量、函数名<br>对象实例的方法、构造方法<br>宏定义</p><p>看上去可以,实际上不能提示的有一些,但不限于:</p><p>语言保留字(一些关键字),比如break、return、continue、sizeof、malloc……<br>头文件(就是#include后不能像visual studio一样出现一些头文件的提示)<br>预处理语句</p><p>如果你想让它提示保留字出来的话,你可以这样解决,那就是写这么一个宏定义(以continue为例):</p><p>#define continue continue<br>然后continue就能被作为一个宏而提示出来了,但是完全没必要</p><h1 id="小熊猫devc"><a href="#小熊猫devc" class="headerlink" title="小熊猫devc"></a>小熊猫devc</h1><h2 id="配置及使用"><a href="#配置及使用" class="headerlink" title="配置及使用"></a>配置及使用</h2><p>**ctrl+R(非H) **替换(<strong>蓝块为当前</strong>)</p><p>可以<strong>自行调整各栏间距</strong>以方便观察</p><p>F10无功能</p><p>F11为编译并运行(如果有改动会编译,挺智能的)</p><p>F12全部重编译(<strong>改动编译器设置时必须用</strong>)</p><h4 id="数据集"><a href="#数据集" class="headerlink" title="数据集"></a>数据集</h4><p>自带类似cph插件,在下栏试题处,添加即可</p><p>编译完<strong>确认好是报错还是警告</strong></p><h2 id="调试-可能需要多按几次换行-所以最好输入时就自带"><a href="#调试-可能需要多按几次换行-所以最好输入时就自带" class="headerlink" title="调试(可能需要多按几次换行,所以最好输入时就自带)"></a>调试(可能需要多按几次换行,所以最好输入时就自带)</h2><p>如果<strong>要调试必须把输入输出优化先注释掉,不然不会提前出结果</strong>.</p><p><strong>调试窗口</strong>会<strong>吞复制内容</strong>,<strong>必须再次复制</strong>才行</p><p>ctrl+F5 执行到光标处(中间<strong>有断点就暂停</strong>)</p><p>F6 停止调试</p><h4 id="单步执行"><a href="#单步执行" class="headerlink" title="单步执行"></a>单步执行</h4><p>顾名思义,单步执行就是让程序执行一步后暂停。问题在于,到底多少程序算”一步“?在小熊猫C++的运行菜单中,有这么几种单步执行:</p><ul><li>单步跳过F8:一行程序算一步。执行完当前行后暂停。</li><li>单步进入F7:如果当前行不包含函数调用,则一行程序算一步;如果这行程序中包含对函数的调用,会在进入函数后暂停;如果找不到该函数的符号信息,则在执行完该函数后暂停。</li><li>单步跳出ctrl+F8:退出当前函数后暂停。</li></ul><h4 id="继续执行F4"><a href="#继续执行F4" class="headerlink" title="继续执行F4"></a>继续执行F4</h4><p>程序暂停后,通过”运行“菜单或者调试工具栏选择”继续执行”,程序就<strong>会继续以调试方式运行</strong>,<strong>直到遇到下一个断点(包括在循环内再次遇到自己这行)</strong>,或者程序运行结束为止。</p><h4 id="监视变量"><a href="#监视变量" class="headerlink" title="监视变量"></a>监视变量</h4><p>如果变量在<strong>当前作用域</strong>无效会直接无,尽量开全局看全局,但若同名也只优先显示当前作用域的值</p><p>添加完之后改不了名字,但是<strong>双击可以修改其值</strong></p><p>*(a+3)@10监视数组部分(注意下标也会随+3变化)</p><p>下方栏调试-局部变量可<strong>自动</strong>看当前函数作用域中的所有局部变量(包括函数参数)</p><p>gdb(小熊猫C++使用的调试器)支持监视<strong>任何合法的C、C++表达式(包括函数,这样递归函数时方便直接看返回值了)</strong>。但请保证在<strong>表达式中不会出现无穷递归、无限循环等错误</strong>,否则gdb调试器<strong>会卡死</strong>无法正常使用。</p><h4 id="调用栈视图"><a href="#调用栈视图" class="headerlink" title="调用栈视图"></a>调用栈视图</h4><p>我们按照<strong>自顶向下或者模块化</strong>的思路设计程序时,会<strong>以函数为单位</strong>来组织和实现的程序的功能。在调试程序时,我们经常需要知道,函数现在<strong>正被谁调用</strong>?调用者的状态是怎样的?</p><p>调试面板中的调用栈视图为我们<strong>提供了程序调用栈(Call Stack)的信息</strong>。从下图中我们可以看出,当前程序执行到isPrime函数中,它是在main函数的第30行被调用的。</p><p><img src="/2025/04/05/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/v2-cd058dfc523f80464f665e4296eb09eb_1440w.jpg" alt="img"></p><p>在调用栈视图中双击某一行,小熊猫C++就会自动跳转到对应的程序位置。</p><p><img src="/2025/04/05/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/v2-6cf7f15962e80b2d0f4b6c63f5505a22_1440w.jpg" alt="img"></p><h4 id="求值工具"><a href="#求值工具" class="headerlink" title="求值工具"></a>求值工具</h4><p>除了监视和局部变量之外,我们还可以使用求值工具<strong>来快速计算某个表达式</strong>。</p><p><img src="/2025/04/05/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/v2-2e2ab8da54241be35d7b47a496861217_1440w.jpg" alt="img"></p><p>注意,求值工具可以执行任意合法的C/C++表达式,包括赋值表达式!其<strong>效果和在程序中执行该表达式的作用是相同的(别冲突了)</strong>。例如,我们在求值输入框中输入n=500,就可以将变量n的值<strong>改为500</strong>。</p><h4 id="输入重定向"><a href="#输入重定向" class="headerlink" title="输入重定向"></a>输入重定向</h4><p><strong>打开gdb server调试模式</strong></p><p>调试的数据重定向功能需要打开gdb server调试模式。从“工具”菜单“选项”打开“选项”对话框,在“<a href="https://zhida.zhihu.com/search?content_id=189947528&content_type=Article&match_order=1&q=%E8%B0%83%E8%AF%95%E5%99%A8&zhida_source=entity">调试器</a>”->“通用”选项页中可以找到“使用gdb server调试“选项,将其勾选上,然后确定即可。</p><p><img src="/2025/04/05/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/v2-385e5ac602415c34da1cc9ba0bc15bd4_1440w.jpg" alt="img"></p><h4 id="指定数据文件-从而可以直接只看输出"><a href="#指定数据文件-从而可以直接只看输出" class="headerlink" title="指定数据文件(从而可以直接只看输出)"></a>指定数据文件(从而可以直接只看输出)</h4><p>通过工具栏或者**”运行“菜单中的”运行参数…“按钮,打开”运行参数“选项页**</p><p>在选项页中,勾选”将程序的标准输入重定向到下面的文件“,并选择<strong>数据所在的文件</strong>(本例中使用F盘根目录下的test.txt文件,即F:\test.txt作为输入文件),然后确定即可。</p><p><img src="/2025/04/05/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/v2-96d0c56a476cc963e80c828d9394fb00_1440w.jpg" alt="img"></p>]]></content>
</entry>
<entry>
<title>顽强拼搏记录</title>
<link href="/2025/03/23/%E9%A1%BD%E5%BC%BA%E6%8B%BC%E6%90%8F%E8%AE%B0%E5%BD%95/"/>
<url>/2025/03/23/%E9%A1%BD%E5%BC%BA%E6%8B%BC%E6%90%8F%E8%AE%B0%E5%BD%95/</url>
<content type="html"><![CDATA[<p>本篇记录博主自己顽强拼搏的时刻以供回念,以激励博主继续又菜又爱玩.<strong>(没有含金量,全是刺激性)</strong></p><p>2025.3.23</p><h1 id="大工之星第二场"><a href="#大工之星第二场" class="headerlink" title="大工之星第二场"></a>大工之星第二场</h1><p>开头ev录屏忘开了,中间又花了近1h去做了个志愿活动(雾</p><p><img src="/2025/03/23/%E9%A1%BD%E5%BC%BA%E6%8B%BC%E6%90%8F%E8%AE%B0%E5%BD%95/ab0a68dd90d0f528e83e053c6fc546fb.png" alt="ab0a68dd90d0f528e83e053c6fc546fb"></p><p>2025.4.14</p><h1 id="大工之星第五场"><a href="#大工之星第五场" class="headerlink" title="大工之星第五场"></a>大工之星第五场</h1><p>最后发现数据<strong>没取模连lld都爆了</strong>(INT128好像爆不了),急救了一波.</p><p><img src="/2025/03/23/%E9%A1%BD%E5%BC%BA%E6%8B%BC%E6%90%8F%E8%AE%B0%E5%BD%95/2d0d43ec394364fe67b27d4ab4ec41a7.png" alt="2d0d43ec394364fe67b27d4ab4ec41a7"></p>]]></content>
</entry>
<entry>
<title>唐b杂事</title>
<link href="/2025/03/17/%E5%94%90b%E6%9D%82%E4%BA%8B/"/>
<url>/2025/03/17/%E5%94%90b%E6%9D%82%E4%BA%8B/</url>
<content type="html"><![CDATA[<p>[toc]</p><h1 id="算法学习方面"><a href="#算法学习方面" class="headerlink" title="算法学习方面"></a>算法学习方面</h1><p>1.vscode调试跟coderunner插件配置文件是分开来的,要想用vscode自带调试设置为c++20,必须在tasks.json文件中的args里面添加一个”-std=c++2<strong>a”,</strong>(注意逗号,双引号和a).</p><p>2.玛德用<strong>宏定义</strong>(纯文本替换卧槽忘了<strong>运算顺序</strong>)被干死了,再用宏定义我就是煞笔.</p>]]></content>
</entry>
<entry>
<title>麦麦--qq聊天机器人部署备忘</title>
<link href="/2025/03/17/%E9%BA%A6%E9%BA%A6-qq%E8%81%8A%E5%A4%A9%E6%9C%BA%E5%99%A8%E4%BA%BA%E9%83%A8%E7%BD%B2%E5%A4%87%E5%BF%98/"/>
<url>/2025/03/17/%E9%BA%A6%E9%BA%A6-qq%E8%81%8A%E5%A4%A9%E6%9C%BA%E5%99%A8%E4%BA%BA%E9%83%A8%E7%BD%B2%E5%A4%87%E5%BF%98/</url>
<content type="html"><![CDATA[<h1 id="重启电脑后如何启动"><a href="#重启电脑后如何启动" class="headerlink" title="重启电脑后如何启动"></a>重启电脑后如何启动</h1><h2 id="启动QQ"><a href="#启动QQ" class="headerlink" title="启动QQ"></a>启动QQ</h2><h2 id="打开compass,启动数据库"><a href="#打开compass,启动数据库" class="headerlink" title="打开compass,启动数据库"></a>打开compass,启动数据库</h2><h2 id="打开bot文件夹,启动终端"><a href="#打开bot文件夹,启动终端" class="headerlink" title="打开bot文件夹,启动终端"></a>打开bot文件夹,启动终端</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">bot\\Scripts\\activate</span><br><span class="line">nb run</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><strong>身为一个小白,有了这次经历后,感觉真不该嗯看个几把教程,身为新手,无法分辨教程是不是全对,又搜不到,该问的时候就正确的去发问,真几把别不好意思.</strong></p><p>多看看新的教程,评价好的,完整的教程,甚至是视频教程(虽然慢点但是最稳,能看见所有步)</p>]]></content>
</entry>
<entry>
<title>进行一个一个前端的雪</title>
<link href="/2025/01/23/%E8%BF%9B%E8%A1%8C%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E5%89%8D%E7%AB%AF%E9%80%9F%E5%BA%A6%E7%9A%84%E9%9B%AA/"/>
<url>/2025/01/23/%E8%BF%9B%E8%A1%8C%E4%B8%80%E4%B8%AA%E4%B8%80%E4%B8%AA%E5%89%8D%E7%AB%AF%E9%80%9F%E5%BA%A6%E7%9A%84%E9%9B%AA/</url>
<content type="html"><![CDATA[<h2 id="Web-开发概况"><a href="#Web-开发概况" class="headerlink" title="Web 开发概况"></a>Web 开发概况</h2><p>Web 开发是指创建和维护<strong>网站</strong>、<strong>客户端程序</strong>、<strong>服务器</strong>与<strong>其他 Web 应用程序</strong>的过程。它包括使用不同的编程语言和技术来编写、测试和部署 Web 应用程序,以满足特定的业务需求和用户需求</p><p>通过 Web 开发技术,开发者能够设计实现诸多满足不同需求场景的<strong>应用程序</strong>,包括但不限于:网站开发、Android/IOS/Harmony OS NEXT 移动端应用程序、微信小程序、桌面应用、群聊机器人、游戏、浏览器插件、3D 建模、高性能服务器、分布式应用、虚拟现实应用、区块链、物联网设备……</p><h2 id="前端和后端"><a href="#前端和后端" class="headerlink" title="前端和后端"></a>前端和后端</h2><p>在软件架构和程序设计领域,前端是软件系统中<strong>直接</strong>和用户交互的部分,而后端控制着<strong>软件的输出</strong>。将软件分为前端和后端是一种将<strong>软件不同功能</strong>的部分相互分离的抽象</p><p>在 Web 开发中,前端在绝大多数情况下指能够被<strong>用户直接访问与交互的模块</strong>,如<strong>网页、手机 App、桌面应用、小程序等</strong>。后端包括<strong>程序运行的后台服务器</strong>、<strong>存储数据的数据库</strong>以及<strong>其他数据中间件</strong>。大部分软件都<strong>概念性地</strong>分成了前端和后端,在大多数情况下,软件的后端经常是隐藏着而不被用户看到</p><p><strong>狭义</strong>的前端通常是指网站或应用程序中与用户直接交互的部分。它是一种用于<strong>构建用户界面的技术和工具的集合</strong>,这些界面可以在 <strong>Web 浏览器</strong>中运行</p><p>后端开发主要负责编写<strong>运行在服务端上的</strong>代码,通常来说,这部分的工作需要和<strong>数据库</strong>与 <strong>Web API</strong> 打交道,比如读写数据、读写文件、实现业务逻辑等。有些时候,业务逻辑存储在<strong>客户端</strong>,这时后台就是用来<strong>以 Web 服务的形式</strong>提供数据库中的数据</p><p>开发者<strong>可以同时掌握</strong>前端和后端的技术,但大多数 Web 开发者都还是有<strong>一定的专精</strong>方向,甚至只在某一方面深入研究。尽管前后端是有天然的区别,但并没有规定它们各自的具体任务。有时前端只是完成数据的显示,而其他主要工作都在后端完成。但也有时,后端只是提供数据,而所有的计算和具体功能都在前端完成。前后端工作的分配,通常都是<strong>由项目的设计和架构来决定</strong>的</p><h2 id="浏览器"><a href="#浏览器" class="headerlink" title="浏览器"></a>浏览器</h2><p>浏览器是用来<strong>检索、展示以及传递</strong> <strong>Web 信息资源的应用程序</strong>。Web 信息资源由<strong>统一资源标识符 (Uniform Resource Identifier,URI)</strong> 所标记,它可以是一张网页、一张图片、一段视频或者<strong>任何在 Web 上所呈现的内容</strong>。使用者可以<strong>借助超链接</strong>,<strong>通过浏览器浏览</strong>互相关联的信息</p><p>浏览器<strong>内核</strong> (Rendering Engine),是指浏览器最核心的部分,负责<strong>对网页语法的解释(如标准通用标记语言下的一个应用 HTML、CSS、JavaScript)并渲染网页</strong>。通常所谓的浏览器内核也就是浏览器所采用的<strong>渲染引擎</strong>,渲染引擎决定了浏览器<strong>如何显示网页的内容</strong>以及<strong>页面的格式信息</strong>。不同的浏览器内核对网页编写语法的解释也有不同</p><h2 id="C-S-与-B-S-架构"><a href="#C-S-与-B-S-架构" class="headerlink" title="C/S 与 B/S 架构"></a><strong>C/S</strong> <strong>与</strong> <strong>B/S</strong> 架构</h2><p>C/S 架构是一种典型的<strong>两层架构</strong>,其全称是 <strong>Client/Server</strong>,即<strong>客户</strong>端<strong>服务器</strong>端架构,其客户端包含一个或多个在用户的电脑上运行的<strong>程序</strong>,而服务器端有两种,一种是<strong>数据库</strong>服务器端,客户端<strong>通过数据库连接访问服务器端的数据</strong>;另一种是 <strong>Socket</strong> 服务器端,<strong>服务器端的程序</strong>通过 Socket 与<strong>客户端的程序</strong>通信</p><p>B/S 架构的全称为 <strong>Browser/Server</strong>,即<strong>浏览器</strong>/<strong>服务器</strong>结构。Browser 指的是 <strong>Web 浏览器</strong>,极少数事务逻辑在前端实现,但<strong>主要</strong>事务逻辑在<strong>服务器端</strong>实现。B/S 架构的系统无须特别安装,<strong>只要有 Web 浏览器</strong>即可</p><h2 id="HTML"><a href="#HTML" class="headerlink" title="HTML"></a>HTML</h2><p>超文本标记语言(Hyper Text Markup Language,简称:HTML)是一种<strong>用于创建网页</strong>的标准标记语言。HTML 是一种基础技术,常与 CSS、JavaScript 一起被众多网站用于<strong>设计网页</strong>、<strong>网页应用程序</strong>以及<strong>移动应用程序</strong>的<strong>用户界面</strong>。网页<strong>浏览器</strong>可以读取 HTML 文件,并将其<strong>渲染成可视化网页</strong>。HTML 描述了<strong>一个网站的结构语义随着线索的呈现</strong>,使之成为一种<strong>标记</strong>语言而非编程语言</p><h2 id="CSS"><a href="#CSS" class="headerlink" title="CSS"></a>CSS</h2><p><strong>层叠样式表(Cascading Style Sheets)<strong>是一种用来</strong>为结构化文档(如 HTML 文档或 XML 应用)添加样式(字体、间距和颜色等)<strong>的计算机语言。CSS3 现在已被大部分现代浏览器支持,而下一版的 CSS4 仍在开发中。CSS 不仅可以</strong>静态地修饰</strong>网页,还可以<strong>配合各种脚本语言动态地</strong>对网页各元素进行<strong>格式</strong>化。CSS 能够对网页中元素位置的<strong>排版进行像素级精确控制</strong>,支持几乎所有的字体字号样式,拥有对网页对象和模型样式编辑的能力</p><h2 id="JavaScript"><a href="#JavaScript" class="headerlink" title="JavaScript"></a>JavaScript</h2><p>JavaScript 是一种高级的、<strong>解释型</strong>的<strong>编程</strong>语言</p><p>JavaScript 是一门基于原型、头等函数的语言,是一门多范式的语言,它支持面向对象程序设计,指令式编程,以及函数式编程。它由 ECMA(欧洲电脑制造商协会)通过 ECMAScript 实现语言的标准化</p><p>ECMAScript 6.0(简称 ES6)是 JavaScript 语言的下一代<strong>标准</strong>,于 2015 年 6 月正式发布。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言</p><p>ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准</p><h2 id="计算机网络基础知识"><a href="#计算机网络基础知识" class="headerlink" title="计算机网络基础知识"></a>计算机网络基础知识</h2><h4 id="HTTP-协议"><a href="#HTTP-协议" class="headerlink" title="HTTP 协议"></a>HTTP 协议</h4><p><strong>HTTP 是 <em>Hyper Text Transfer Protocol</em>(超文本传输协议)<strong>的缩写。HTTP 协议用于</strong>从 WWW 服务器传输超文本到本地浏览器</strong>的传送协议。它可以使浏览器更加高效,使<strong>网络传输减少</strong>。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容<strong>首先</strong>显示 (如文本先于图形) 等。HTTP 是一个<strong>应用层协议</strong>,由<strong>请求</strong>和<strong>响应</strong>构成,是一个标准的<strong>客户端服务器模型</strong>。</p><h4 id="URL"><a href="#URL" class="headerlink" title="URL"></a>URL</h4><p>在互联网上,<strong>每一</strong>信息资源都有<strong>统一的且在网上唯一的地址</strong>,该地址就叫 URL(Uniform Resource Locator, 统一资源定位符)。</p><p>URL 由三部分组成:资源<strong>类型</strong>、存放资源的<strong>主机域名</strong>、资源<strong>文件名</strong>。</p><p>也可认为由 4 部分组成:<strong>协议</strong>、<strong>主机</strong>、<strong>端口</strong>、<strong>路径</strong>。</p><p>URL 的一般格式为:<em>protocol :// hostname [:port] / path / [:parameters] [?query] #fragment</em></p><h4 id="IP-地址和-DNS"><a href="#IP-地址和-DNS" class="headerlink" title="IP 地址和 DNS"></a>IP 地址和 DNS</h4><p><strong>IP 地址</strong>(类似 192.168.1.1 内网网关)是<strong>互联网协议地址</strong>,它给因特网上的每<strong>台计算机和其它设备</strong>都规定了一个唯一的地址。由于有这种唯一的地址,才保证了用户在连网的计算机上操作时,能够高效而且方便地从千千万万台计算机中选出自己所需的对象来</p><p>但是 IP 地址毕竟是一串<strong>毫无规律</strong>的数字,并不方便人类的记忆和书写。因此在 IP 地址的基础上又发展出一种<strong>符号化</strong>的地址方案,来代替数字型的 IP 地址,每一个符号化的地址都与特定的 IP 地址对应。这个与网络上的数字型 IP 地址相对应的字符型地址,就是<strong>域名</strong></p><p>类似 <a href="https://www.google.com/">http://www.google.com</a> 这样的字符串就是“域名”,当访问 <a href="http://www.google.com/">www.google.com</a> 时,首先由 DNS(Domain Name System, DNS)域名系统<strong>解析为 IP 地址,随后再访问 IP</strong></p><h4 id="HTTP-请求"><a href="#HTTP-请求" class="headerlink" title="HTTP 请求"></a>HTTP 请求</h4><p>HTTP 请求是指从<strong>客户端到服务器端</strong>的请求消息,请求报文由请求行 (Request line)、请求头 (Header),空行、请求正文 4 部分组成</p><h4 id="HTTP-响应"><a href="#HTTP-响应" class="headerlink" title="HTTP 响应"></a>HTTP 响应</h4><p>在接收和解释请求消息后,服务器会<strong>返回一个 HTTP 响应消息</strong>。HTTP 响应报文也由四个部分组成,分别是:状态行、消息报头、空行和响应正文</p><h4 id="HTTP-方法"><a href="#HTTP-方法" class="headerlink" title="HTTP 方法"></a>HTTP 方法</h4><p>根据 HTTP 标准,HTTP 请求可以使用多种<strong>请求方法</strong>。HTTP 方法描述了对给定资源的期望动作,每一种请求方法都抽象出了一种不同给定语义。</p><p>HTTP1.0 定义了三种请求方法:GET、POST 和 HEAD 方法。</p><p>HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。</p><p>在实际开发中 <strong>GET、POST、PUT、DELETE</strong> 四类 HTTP 方法的使用率最高,能够用一套统一的语法规范对资源的 CRUD (增删改查) 逻辑进行抽象</p><p>GET 方法请求一个指定资源的<strong>表示形式</strong>,使用 GET 的请求应该<strong>只被用于获取数据</strong></p><p>POST 方法用于<strong>将实体提交到指定的资源</strong>,通常导致在服务器上的<strong>状态变化</strong>或副作用</p><p>PUT 方法用<strong>请求有效载荷替换</strong>目标资源的所有当前表示</p><p>DELETE 方法<strong>删除指定的资源</strong></p><h4 id="HTTP-状态码"><a href="#HTTP-状态码" class="headerlink" title="HTTP 状态码"></a>HTTP 状态码</h4><p>当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一<strong>个包含 HTTP 状态码的信息头</strong>(server header)<strong>用以响应</strong>浏览器的请求</p><p>状态码类型:</p><table><thead><tr><th>状态码</th><th>类别</th><th>原因</th></tr></thead><tbody><tr><td>1xx</td><td><strong>信息性</strong>状态码</td><td>接收的请求<strong>正在处理</strong></td></tr><tr><td>2xx</td><td><strong>成功</strong>状态码</td><td>请求正常处理<strong>完毕</strong></td></tr><tr><td>3xx</td><td><strong>重定向</strong>状态码</td><td>需要<strong>进行附加操作</strong>以完成请求</td></tr><tr><td>4xx</td><td><strong>客户端错误</strong>状态码</td><td>服务器无法处理请求</td></tr><tr><td>5xx</td><td><strong>服务端错误</strong>状态码</td><td>服务器处理请求出错</td></tr></tbody></table><p>常见的状态码:</p><p><strong>100 Continue</strong></p><p>客户端应继续其请求</p><p><strong>200 OK</strong></p><p>请求成功,一般用于 <strong>GET 与 POST</strong> 请求</p><p><strong>201 Created</strong></p><p>已创建,<strong>成功请求并创建</strong>了新的资源</p><p><strong>401 Unauthorized</strong></p><p>请求要求用户的<strong>身份认证</strong></p><p><strong>403 Forbidden</strong></p><p>服务器理解请求客户端的请求,但是<strong>拒绝执行</strong>此请求</p><p><strong>404 Not Found</strong></p><p>服务器<strong>无法</strong>根据客户端的请求<strong>找到</strong>资源(网页)。</p><p><strong>500 Internal Server Error</strong></p><p>服务器<strong>内部错误</strong>,无法完成请求</p><h4 id="RESTful-API"><a href="#RESTful-API" class="headerlink" title="RESTful API"></a><strong>RESTful</strong> <strong>API</strong></h4><p><strong>REST 全称是 Representational State Transfer</strong>,中文意思是<strong>表述性状态转移</strong>。</p><p>RESTful 架构应该遵循<strong>统一接口原则</strong>,统一接口包含了一组<strong>受限的预定义的操作</strong>,不论什么样的资源,都是<strong>通过使用相同的接口</strong>进行资源的访问。</p><p>接口应该使用<strong>标准的 HTTP 方法</strong>如 GET,PUT 和 POST,并遵循这些方法的语义。</p><p>REST 所谓的表述指的是<strong>对资源的表述</strong>。要让一个资源可以被<strong>识别</strong>,需要有个<strong>唯一标识</strong>,在 Web 中这个唯一标识就是 <strong>URI</strong></p><h2 id="版本控制工具"><a href="#版本控制工具" class="headerlink" title="版本控制工具"></a>版本控制工具</h2><h4 id="Git-版本控制工具"><a href="#Git-版本控制工具" class="headerlink" title="Git 版本控制工具"></a>Git 版本控制工具</h4><p>Git 是一个开源的<strong>分布式版本控制系统</strong>,可以有效、高速地处理从很小到非常大的项目版本管理。也是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件</p><h4 id="GitHub-代码托管仓库"><a href="#GitHub-代码托管仓库" class="headerlink" title="GitHub 代码托管仓库"></a>GitHub 代码托管仓库</h4><p>GitHub 是一个面向开源及私有软件项目的托管平台,因为<strong>只支持 Git</strong> 作为<strong>唯一的版本库格式</strong>进行托管</p><h1 id="基础知识"><a href="#基础知识" class="headerlink" title="基础知识"></a>基础知识</h1><h4 id="库"><a href="#库" class="headerlink" title="库"></a>库</h4><p>库是一系列预先定义好的数据结构和函数或类的集合,程序员可以通过调用这些代码实现功能。简单来说就是库为我们提供了很多封装好的函数,看起来比较零散,但使用起来更灵活</p><p>使用库可以简化开发流程,提高开发效率。例如,jQuery 提供了简化 DOM 操作的语法,减少了编写繁琐代码的需要。React 通过虚拟 DOM 和声明式 UI ,便于<strong>快速构建用户界面</strong></p><p>如果需要<strong>在网页中使用 JavaScript 库</strong>,可以去网上<strong>下载库文件</strong>,<strong>放在网页的同一目录</strong>下,再到**<code>script</code>标签中引入**。或者不下载通过<strong>通过链接在<code><script></code>标签中引用该库</strong>即可:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><script src=<span class="string">"https://cdn.staticfile.org/jquery/3.4.0/jquery.min.js"</span>></script></span><br></pre></td></tr></table></figure><p>或者在代码中通过 <code>require</code> 或者 <code>import</code> 中引入库。在现代的前端开发中,通常推荐使用 <code>import</code> 来进行模块导入,特别是在使用现代 JavaScript 特性的项目中。这主要与现代 JavaScript 的发展趋势和语言特性有关</p><p><code>import</code> 是 ES6 新引入的关键字,支持<strong>按需导入</strong>,而不需要导入整个模块。同时<code>import</code> 的语法也比 <code>require</code> 更直观清晰,更符合现代变成风格</p><p>随着 JavaScript 生态的发展,越来越多的库和工具采用了 ES6 模块系统,使用 <code>import</code> 能够更好地与这些现代化的工具和库进行集成。</p><h4 id="框架"><a href="#框架" class="headerlink" title="框架"></a>框架</h4><p>框架是提供<strong>如何构建应用程序的意见的库</strong>,是<strong>一整套的工具</strong>,所有东西已经准备齐全了,可以按照它的规定就可以很简单的完成一些事情,但我们<strong>不能去改变它,只能按照要求</strong>使用,并且其他人拿到这套工具也是一样的,如 Vue、Angular 等等。</p><p>注意是<strong>一套而不是单个</strong>,比如 React 就是一个库,它本身只是一个前端渲染的库,纯粹地写 UI 组件,没有什么异步处理机制、模块化等,但是当它结合 Redux 和 React-router 的时候,就是一个框架了。</p><p>框架和库的联系紧密,都是为了提高我们的开发效率而存在,库的使用上会<strong>简单</strong>一些,更加<strong>灵活</strong>,但<strong>功能不全</strong>。而框架的功能很<strong>全面</strong>,但需要我们<strong>按规定</strong>去使用。也就是说库是一种工具,我提供了,你可以不用,即使你用了,也没影响你自己的代码结构,控制权在使用者手中。框架则是面向一个领域,提供了<strong>一套解决方案</strong></p><h4 id="区别"><a href="#区别" class="headerlink" title="区别"></a>区别</h4><p>库是一组已经实现的<strong>代码集合</strong>,提供了特定功能的函数和方法,开发者可以根据需要选择性地使用。库不控制应用程序的整体架构,而是为开发者提供了可调用的工具,以便在应用程序中实现特定功能</p><p>框架是一种提供了<strong>一整套解决方案的软件结构</strong>,它规定了整个应用程序的架构,定义了组织代码的方式,并提供了一系列工具和库,以便开发者可以在框架的基础上构建应用。框架通常有一个完整的生命周期,控制着应用程序的流程,开发者需要按照框架的规则来编写代码。</p><h2 id="Node-js"><a href="#Node-js" class="headerlink" title="Node.js"></a>Node.js</h2><h4 id="什么是-Node-js"><a href="#什么是-Node-js" class="headerlink" title="什么是 Node.js"></a>什么是 Node.js</h4><p>JavaScript 是一个<strong>脚本语言</strong>,最初用来处理网页中的一些动态功能和一些用户行为。它一般运行于浏览器</p><p>但是这门语言后续不断更新,越来越多的人开始使用 JavaScript 。为了把它迁移到了服务端,但服务端上又不能跑浏览器,那我们就需要一种新的运行环境。就这样,这个基于 Chrome V8 引擎的 JavaScript 运行时 Node.js 诞生了</p><h2 id="模块化编程"><a href="#模块化编程" class="headerlink" title="模块化编程"></a>模块化编程</h2><p>在计算机编程中,模块是指一个<strong>相对独立的程序文件或代码库</strong>,通常包含一组相关的函数、变量、类或其他可重用的代码构件,每个模块在内部执行某个功能。并向外部公开一定的接口以供其他模块使用。在编程语言中,通常有一些标准库或第三方库,这些库都是由多个模块组成的,可以在程序中被引用和使用。模块化主要是为了帮助程序员组织和管理大型代码库,可以将大型的程序有逻辑地拆分成一个个相对较小的部分,实现代码复用,让程序设计更加灵活,使其更易于维护和扩展。这是优点之一。并且还可以避免变量名和函数名命名冲突的问题以及解决不同模块之间的依赖关系。</p><p>比如,我要写一个 Wordle 小游戏,普通代码编写就把所有代码像画布渲染,键盘的输入,逻辑判断等都写到一个 HTML 文件里,如果使用模块化概念,我们可以简单分块,分成主文件,键盘输入,逻辑判断以及读取 json 等多个模块,然后在各个文件里实现相应的逻辑,这样假如你发现 json 的读取有问题,你就可以直接去找读 json 那个文件有没有问题,这样会让代码的后续维护更简单,目的更明确。</p><p><code>import</code> 和 <code>export</code> 是 ES6 引入的模块系统的关键字,用于在 JavaScript 中进行模块化编程。模块化使得代码更结构化、可维护,并允许开发者将代码分割为<strong>小的可重用部分</strong></p><h4 id="export-的使用:"><a href="#export-的使用:" class="headerlink" title="export 的使用:"></a><code>export</code> 的使用:</h4><p><code>export</code> 用于将变量、函数、类或其他声明<strong>导出为模块的公共接口</strong>,以便其他模块可以使用。有三种常见的 <code>export</code> 的方式</p><h5 id="命名导出"><a href="#命名导出" class="headerlink" title="命名导出"></a>命名导出</h5><p>可以通过 <code>export</code> 关键字<strong>单独导出</strong>多个成员</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// module.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> myVariable = <span class="number">42</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">myFunction</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="默认导出"><a href="#默认导出" class="headerlink" title="默认导出"></a>默认导出</h5><p>通过 <code>export default</code> 关键字导出一个<strong>默认</strong>成员,每个模块只能有<strong>一个</strong>默认导出</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// module.js</span></span><br><span class="line"><span class="keyword">const</span> myVariable = <span class="number">42</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> myVariable;</span><br></pre></td></tr></table></figure><h4 id="import-的使用:"><a href="#import-的使用:" class="headerlink" title="import 的使用:"></a><code>import</code> 的使用:</h4><p><code>import</code> 用于在一个模块中引入其他模块导出的成员,以便在当前模块中使用。有三种常见的 <code>import</code> 的方式:</p><h5 id="命名导入"><a href="#命名导入" class="headerlink" title="命名导入"></a>命名导入</h5><p>导入其他模块中的命名导出</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="keyword">import</span> { myVariable, myFunction } <span class="keyword">from</span> <span class="string">"./module"</span>;</span><br></pre></td></tr></table></figure><h5 id="默认导入"><a href="#默认导入" class="headerlink" title="默认导入"></a>默认导入</h5><p>导入其他模块中的默认导出</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="keyword">import</span> myVariable <span class="keyword">from</span> <span class="string">"./module"</span>;</span><br></pre></td></tr></table></figure><h5 id="导入所有"><a href="#导入所有" class="headerlink" title="导入所有"></a>导入所有</h5><p>导入其他模块的所有导出,形成一个<strong>命名空间对象</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> myModule <span class="keyword">from</span> <span class="string">"./module"</span>;</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// module.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> myVariable = <span class="number">42</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">myFunction</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> internalVariable = <span class="string">"internal"</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> internalVariable;</span><br><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="keyword">import</span> { myVariable, myFunction } <span class="keyword">from</span> <span class="string">"./module"</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(myVariable); <span class="comment">// 42</span></span><br><span class="line"><span class="title function_">myFunction</span>();</span><br><span class="line"><span class="keyword">import</span> internalVariable <span class="keyword">from</span> <span class="string">"./module"</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(internalVariable); <span class="comment">// 'internal'</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> myModule <span class="keyword">from</span> <span class="string">"./module"</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(myModule.<span class="property">myVariable</span>); <span class="comment">// 42</span></span><br></pre></td></tr></table></figure><h2 id="npm"><a href="#npm" class="headerlink" title="npm"></a>npm</h2><p>JavaScript 包是一种封装了代码、资源的组织形式,能够方便共享、安装和管理代码。这些包可以包含 JavaScript 库、框架、工具或应用程序等。而 <strong><code>npm</code> 就是管理这些包的工具</strong>(当然除了 <code>npm</code> 也有其他工具,比如 <code>yarn</code>、<code>yum</code>等),专门用于在服务器端和命令行工具中管理 JavaScript 包</p><p>为什么我们需要包管理工具呢?我们一次性把包都下载到电脑里,像 C 语言的头文件一样,需要用什么拿什么不就好了吗?首先,JavaScript 的包<strong>多达 90 万个</strong>,将所有这些包完全下载到本地会占用大量存储空间。这对于开发者的计算机来说可能是不切实际的,特别是在多个项目中共享相同的依赖项时。其次,软件包和库经<strong>常会更新</strong>,手动下载所有包可能导致更新不及时,使得项目失去了最新的功能和安全性修复。最后,有的项目需要使用某个包特定的版本,使用其他版本会导致项目无法运行或出现其他 bug,而<strong>包管理工具允许开发者指定项目所使用的依赖项的特定版本</strong>,以确保项目的稳定性和一致性。手动下载所有包可能会导致版本冲突和不同环境之间的不一致。因此我们需要使用包管理工具</p><p><code>npm</code> 是<strong>随同 Node.js 安装的包管理工具</strong>,安装好 node 之后就会默认安装好 <code>npm</code> 了</p><p>我们可以在命令行中输入 <code>npm -v</code> 判断是否安装了 <code>npm</code></p><h5 id="npm-的常见命令"><a href="#npm-的常见命令" class="headerlink" title="npm 的常见命令"></a><code>npm</code> 的常见命令</h5><p><code>npm install <Module Name></code> 使用 <code>npm</code> 命令<strong>本地安装</strong>模块</p><p><code>npm install -g <Module Name></code> <strong>全局</strong>安装</p><p>两个的区别就是本地安装将安装包<strong>放在当前文件夹的 <code>node_modules</code></strong> (如果没有则会自动生成)文件夹下,<strong>通过 <code>import</code></strong> 来引入本地安装的包;全局安装包则通常放<strong>在 <code>node</code> 的安装目录下</strong>,可以<strong>直接在命令行里</strong>使用</p><p><code>npm uninstall <Name></code> 卸载模块</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g npm@<版本号>//更新 npm</span><br></pre></td></tr></table></figure><p><code>npm publish</code> 将自己的代码发布到 <strong><code>npm</code> 上的全球开源库</strong>中</p><h5 id="package-json"><a href="#package-json" class="headerlink" title="package.json"></a><code>package.json</code></h5><p><code>package.json</code> 是 Node.js 项目中的一个重要文件,它用于<strong>存储项目的配置信息</strong>。包含了项目的元数据(metadata),如项目名称、版本、作者、依赖库等信息。通过描述项目上下文、所需依赖和开发脚本,使项目具备可重复性和可移植性</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"learn_react"</span>, <span class="comment">// 项目的名称</span></span><br><span class="line"> <span class="string">"version"</span>: <span class="string">"0.1.0"</span>, <span class="comment">// 项目的版本号</span></span><br><span class="line"> <span class="string">"private"</span>: <span class="literal">true</span>, <span class="comment">// 用于指示是否将该项目发布到公共的包注册表的标志</span></span><br><span class="line"> <span class="string">"dependencies"</span>: {</span><br><span class="line"> <span class="comment">// 项目运行时所依赖的第三方包</span></span><br><span class="line"> <span class="string">"@testing-library/jest-dom"</span>: <span class="string">"^5.14.1"</span>,</span><br><span class="line"> <span class="string">"@testing-library/react"</span>: <span class="string">"^13.0.0"</span>,</span><br><span class="line"> <span class="string">"@testing-library/user-event"</span>: <span class="string">"^13.2.1"</span>,</span><br><span class="line"> <span class="string">"@types/jest"</span>: <span class="string">"^27.0.1"</span>,</span><br><span class="line"> <span class="string">"@types/node"</span>: <span class="string">"^16.7.13"</span>,</span><br><span class="line"> <span class="string">"@types/react"</span>: <span class="string">"^18.0.0"</span>,</span><br><span class="line"> <span class="string">"@types/react-dom"</span>: <span class="string">"^18.0.0"</span>,</span><br><span class="line"> <span class="string">"react"</span>: <span class="string">"^18.2.0"</span>,</span><br><span class="line"> <span class="string">"react-dom"</span>: <span class="string">"^18.2.0"</span>,</span><br><span class="line"> <span class="string">"react-scripts"</span>: <span class="string">"5.0.1"</span>,</span><br><span class="line"> <span class="string">"typescript"</span>: <span class="string">"^4.4.2"</span>,</span><br><span class="line"> <span class="string">"web-vitals"</span>: <span class="string">"^2.1.0"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"scripts"</span>: {</span><br><span class="line"> <span class="comment">// 定义一组自定义的命令脚本</span></span><br><span class="line"> <span class="string">"start"</span>: <span class="string">"react-scripts start"</span>,</span><br><span class="line"> <span class="string">"build"</span>: <span class="string">"react-scripts build"</span>,</span><br><span class="line"> <span class="string">"test"</span>: <span class="string">"react-scripts test"</span>,</span><br><span class="line"> <span class="string">"eject"</span>: <span class="string">"react-scripts eject"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"browserslist"</span>: {</span><br><span class="line"> <span class="comment">// 用于指定项目所支持的目标浏览器范围的配置文件,通常用于前端开发</span></span><br><span class="line"> <span class="string">"production"</span>: [</span><br><span class="line"> <span class="string">">0.2%"</span>, <span class="comment">// 支持全球使用率超过0.2%的浏览器</span></span><br><span class="line"> <span class="string">"not dead"</span>, <span class="comment">// 排除已经被官方宣布为不再更新的浏览器</span></span><br><span class="line"> <span class="string">"not op_mini all"</span> <span class="comment">// 用于排除 Opera Mini 浏览器,Opera Mini 具有一些独特的行为或限制,需要在项目中进行特殊处理</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="string">"development"</span>: [</span><br><span class="line"> <span class="string">"last 1 chrome version"</span>, <span class="comment">// 支持每个浏览器的最后一个版本</span></span><br><span class="line"> <span class="string">"last 1 firefox version"</span>,</span><br><span class="line"> <span class="string">"last 1 safari version"</span></span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果项目有 <code>package.json</code> 文件,则通过命令 <code>npm install</code> <strong>可以根据 <code>"dependencies"</code></strong> 自动在 <code>node_modules</code> 文件夹中安装项目所需的所有包</p><p>注:上述 <code>package.json</code> 的注释是粘贴到 md 后再加的,目的是讲解<strong>键值对的意义</strong>,而 json 文件中是<strong>不允许添加注释的</strong>:</p><h2 id="打包"><a href="#打包" class="headerlink" title="打包"></a>打包</h2><p>打包是指<strong>将多个模块( JavaScript、CSS、图片等)打包成为一个文件</strong>,这有助于代码管理、发布和使用。在前端开发中,通常需要使用打包工具将代码打包成<strong>浏览器可识别</strong>的格式,并优化加载速度和性能。</p><p>为什么前端需要打包?以前的前端开发存在三个大问题:没有模块化、第三方包的引入繁琐困难、代码以明文形式展示出来</p><p>我们利用打包工具就可以实现:支持模块化、自动打包第三方包、代码混淆,使得其他人无法阅读</p><p>下面介绍两个常使用的与打包有关的工具.</p><h4 id="Babel"><a href="#Babel" class="headerlink" title="Babel"></a>Babel</h4><p>Babel 是一个 JavaScript 编译器,它能够将 ECMAScript 2015+ 的新特性转换为向后兼容的 JavaScript 代码,例如将 ES6 的箭头函数转换为普通函数、将模板字符串转换为常规字符串等等,使得我们可以在现代浏览器中使用最新的 JavaScript 特性,从而<strong>解决浏览器兼容性问题</strong></p><p>执行 <code>npm install -g babel-cli</code> 安装 Babel</p><p>在项目根目录创建 <code>.babelrc</code> 文件,这是 Babel 的配置文件,并编写:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="string">"presets"</span>: [<span class="string">"es2015"</span>],</span><br><span class="line"> <span class="string">"plugins"</span>: []</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>执行 <code>npm install babel-preset-es2015</code> 安装转码器,就是从源码转到老版本的代码中间的语法映射表</p><p>在根目录创建 <code>src</code> 文件夹,新建 <code>index.js</code> 并编写如下代码</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ./src/index.js</span></span><br><span class="line"><span class="keyword">let</span> [a, b, c] = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line">[a, b, c] = [b, c, a + <span class="number">1</span>];</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a, b, c);</span><br></pre></td></tr></table></figure><p>这里用到了 ES6 的新特性<strong>解构赋值</strong>,执行 <code>babel src -d dist</code> Babel 就能够将它转换为旧的 ES2015 代码:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ./dist/index.js</span></span><br><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>,</span><br><span class="line"> b = <span class="number">2</span>,</span><br><span class="line"> c = <span class="number">3</span>;</span><br><span class="line"><span class="keyword">var</span> _ref = [b, c, a + <span class="number">1</span>];</span><br><span class="line">a = _ref[<span class="number">0</span>];</span><br><span class="line">b = _ref[<span class="number">1</span>];</span><br><span class="line">c = _ref[<span class="number">2</span>];</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a, b, c);</span><br></pre></td></tr></table></figure><h4 id="Webpack"><a href="#Webpack" class="headerlink" title="Webpack"></a>Webpack</h4><p>Webpack 是一个模块打包工具,它可以将<strong>多个模块打包</strong>成<strong>一个或多个 JavaScript 文件</strong>,而这些 JavaScript 文件可以被浏览器正确加载执行。Webpack 可以处理各种类型的资源文件,如 JS、CSS、图片等,并提供了各种插件和 loader 用于对不同类型的资源进行处理和优化,同时还支持热更新功能,方便开发人员进行调试和开发</p><p>Webpack 会<strong>隐藏源码的细节</strong>,把多个 JavaScript 合并成一个 JavaScript,提高浏览器的<strong>访问速度</strong>,使源码<strong>更加安全</strong></p><p>执行 <code>npm install -g webpack webpack-cli</code> 安装 Webpack</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//导入path模块,nodejs的内置模块</span></span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">"path"</span>);</span><br><span class="line"><span class="comment">//定义JS打包的规则</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> <span class="comment">//指定构建的模式</span></span><br><span class="line"> <span class="attr">mode</span>: <span class="string">"development"</span>,</span><br><span class="line"> <span class="comment">//入口函数从哪里开始进行编译打包</span></span><br><span class="line"> <span class="attr">entry</span>: <span class="string">"./src/main.js"</span>,</span><br><span class="line"> <span class="comment">//编译成功以后要把内容输出到那里去</span></span><br><span class="line"> <span class="attr">output</span>: {</span><br><span class="line"> <span class="comment">//定义输出的指定的目录__dirname 当前项目根目录,将生成一个dist文件夹</span></span><br><span class="line"> <span class="attr">path</span>: path.<span class="title function_">resolve</span>(__dirname, <span class="string">"./dist"</span>),</span><br><span class="line"> <span class="comment">//合并的js文件存储在dist/bundle.js文件中</span></span><br><span class="line"> <span class="attr">filename</span>: <span class="string">"res.js"</span>,</span><br><span class="line"> },</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>终端执行 <code>webpack</code> 即可在 <strong><code>dist</code> 文件夹中</strong>看到生成的 <code>res.js</code>,这就是合并后的 JavaScript 代码</p><p>通常在前端项目中,我们会<strong>将 Babel 和 Webpack 结合</strong>使用,使用 Babel 将最新版本的语法<strong>转换成向后兼容的代码</strong>,再由 Webpack 将这些代码<strong>打包并优化</strong>,最终生成浏览器可以解析的文件。</p>]]></content>
</entry>
<entry>
<title>博客搭建备忘</title>
<link href="/2025/01/18/hexo%E4%BD%BF%E7%94%A8/"/>
<url>/2025/01/18/hexo%E4%BD%BF%E7%94%A8/</url>
<content type="html"><![CDATA[<p>[toc]</p><p>切路径</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> E:blog</span><br></pre></td></tr></table></figure><h2 id="hexo博客最常用的套路"><a href="#hexo博客最常用的套路" class="headerlink" title="hexo博客最常用的套路"></a>hexo博客最常用的套路</h2><ol><li><code>hexo g; hexo s</code>本地预览,再更改,直到满意为止</li><li><code>hexo d</code>或者<code>hexo g; hexo d</code>远程部署。</li><li>hexo new “title” # 默认是post,如果博客名称有空格需要用双引号包裹起来</li></ol><h4 id="Front-matter"><a href="#Front-matter" class="headerlink" title="Front-matter"></a>Front-matter</h4><p>Front-matter 是 markdown 文件<strong>最上方</strong>以 — 分隔的區域,用於指定個別檔案的變數。</p><p>Page Front-matter 用於 頁面 配置<br>Post Front-matter 用於 文章頁 配置<br>如果標注可選的參數,可根據自己需要添加,不用全部都寫在 markdown 裏</p><h4 id="Page-Front-matter-md渲染好像放在开头才有效果"><a href="#Page-Front-matter-md渲染好像放在开头才有效果" class="headerlink" title="Page Front-matter(md渲染好像放在开头才有效果)"></a>Page Front-matter(md渲染好像放在开头才有效果)</h4><p><img src="/2025/01/18/hexo%E4%BD%BF%E7%94%A8/5b3a26a09ff1908363efb29e78f6c2fc.png" alt="5b3a26a09ff1908363efb29e78f6c2fc"></p><p><img src="/2025/01/18/hexo%E4%BD%BF%E7%94%A8/2f0c8e78306edad277656d22ef1c220f.png" alt="2f0c8e78306edad277656d22ef1c220f"></p><h4 id="Post-Front-matter"><a href="#Post-Front-matter" class="headerlink" title="Post Front-matter"></a>Post Front-matter</h4><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title:</span><br><span class="line">date:</span><br><span class="line">updated:</span><br><span class="line">tags:</span><br><span class="line">categories:</span><br><span class="line">keywords:</span><br><span class="line">description:</span><br><span class="line">top<span class="emphasis">_img:</span></span><br><span class="line"><span class="emphasis">comments:</span></span><br><span class="line"><span class="emphasis">cover:</span></span><br><span class="line"><span class="emphasis">toc:</span></span><br><span class="line"><span class="emphasis">toc_</span>number:</span><br><span class="line">toc<span class="emphasis">_style_</span>simple:</span><br><span class="line">copyright:</span><br><span class="line">copyright<span class="emphasis">_author:</span></span><br><span class="line"><span class="emphasis">copyright_</span>author<span class="emphasis">_href:</span></span><br><span class="line"><span class="emphasis">copyright_</span>url:</span><br><span class="line">copyright<span class="emphasis">_info:</span></span><br><span class="line"><span class="emphasis">mathjax:</span></span><br><span class="line"><span class="emphasis">katex:</span></span><br><span class="line"><span class="emphasis">aplayer:</span></span><br><span class="line"><span class="emphasis">highlight_</span>shrink:</span><br><span class="line">aside:</span><br><span class="line">abcjs:</span><br><span class="line"><span class="section">noticeOutdate:</span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></table></figure><p><img src="/2025/01/18/hexo%E4%BD%BF%E7%94%A8/c702dece8b2fa2b5e5d8eab28dc60763.png" alt="c702dece8b2fa2b5e5d8eab28dc60763"></p><h2 id="标签页"><a href="#标签页" class="headerlink" title="标签页"></a>标签页</h2><p>標籤頁<strong>文件名</strong>不一定是 tags, 例子中的 tags 只是一個示例.記得添加 type: “tags”</p><p>1.前往你的 Hexo 的根目錄</p><p>2.輸入 <strong>hexo new page tags</strong></p><p>3.你會找到 source/tags/index.md 這個文件</p><p>修改這個文件:</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: 標籤</span><br><span class="line">date: 2018-01-05 00:00:00</span><br><span class="line">type: 'tags'</span><br><span class="line">orderby: random</span><br><span class="line"><span class="section">order: 1</span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></table></figure><p><img src="/2025/01/18/hexo%E4%BD%BF%E7%94%A8/e416034ea913cfc279af6527b4174a3d.png" alt="e416034ea913cfc279af6527b4174a3d"></p><h2 id="分类页"><a href="#分类页" class="headerlink" title="分类页"></a>分类页</h2><p><strong>hexo new page categories</strong></p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">---</span><br><span class="line">title: 分類</span><br><span class="line">date: 2018-01-05 00:00:00</span><br><span class="line"><span class="section">type: 'categories'</span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></table></figure><h2 id="友链"><a href="#友链" class="headerlink" title="友链"></a>友链</h2><p><strong>hexo new page link</strong></p><p>在 Hexo 根目錄中的 source/_data(如果沒有 _data 文件夾,請自行創建),創建一個文件 link.yml</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="attr">class_name:</span> <span class="string">友情鏈接</span></span><br><span class="line"> <span class="attr">class_desc:</span> <span class="string">那些人,那些事</span></span><br><span class="line"> <span class="attr">link_list:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Hexo</span></span><br><span class="line"> <span class="attr">link:</span> <span class="string">https://hexo.io/zh-tw/</span></span><br><span class="line"> <span class="attr">avatar:</span> <span class="string">https://d33wubrfki0l68.cloudfront.net/6657ba50e702d84afb32fe846bed54fba1a77add/827ae/logo.svg</span></span><br><span class="line"> <span class="attr">descr:</span> <span class="string">快速、簡單且強大的網誌框架</span></span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> <span class="attr">class_name:</span> <span class="string">網站</span></span><br><span class="line"> <span class="attr">class_desc:</span> <span class="string">值得推薦的網站</span></span><br><span class="line"> <span class="attr">link_list:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Youtube</span></span><br><span class="line"> <span class="attr">link:</span> <span class="string">https://www.youtube.com/</span></span><br><span class="line"> <span class="attr">avatar:</span> <span class="string">https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png</span></span><br><span class="line"> <span class="attr">descr:</span> <span class="string">視頻網站</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Weibo</span></span><br><span class="line"> <span class="attr">link:</span> <span class="string">https://www.weibo.com/</span></span><br><span class="line"> <span class="attr">avatar:</span> <span class="string">https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png</span></span><br><span class="line"> <span class="attr">descr:</span> <span class="string">中國最大社交分享平台</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Twitter</span></span><br><span class="line"> <span class="attr">link:</span> <span class="string">https://twitter.com/</span></span><br><span class="line"> <span class="attr">avatar:</span> <span class="string">https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png</span></span><br><span class="line"> <span class="attr">descr:</span> <span class="string">社交分享平台</span></span><br></pre></td></tr></table></figure><p>從 4.0.0 開始,支持從遠程加載友情鏈接,遠程拉取只支持 json。</p><p><strong>注意: 選擇遠程加載後,本地生成的方法會無效。</strong></p><p>在 source/link/index.md 這個文件的 front-matter 添加遠程鏈接</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flink_url: xxxxx</span><br></pre></td></tr></table></figure><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"class_name"</span><span class="punctuation">:</span> <span class="string">"友情鏈接"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"class_desc"</span><span class="punctuation">:</span> <span class="string">"那些人,那些事"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"link_list"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"Hexo"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"link"</span><span class="punctuation">:</span> <span class="string">"https://hexo.io/zh-tw/"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"avatar"</span><span class="punctuation">:</span> <span class="string">"https://d33wubrfki0l68.cloudfront.net/6657ba50e702d84afb32fe846bed54fba1a77add/827ae/logo.svg"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"descr"</span><span class="punctuation">:</span> <span class="string">"快速、簡單且強大的網誌框架"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"class_name"</span><span class="punctuation">:</span> <span class="string">"網站"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"class_desc"</span><span class="punctuation">:</span> <span class="string">"值得推薦的網站"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"link_list"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"Youtube"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"link"</span><span class="punctuation">:</span> <span class="string">"https://www.youtube.com/"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"avatar"</span><span class="punctuation">:</span> <span class="string">"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"descr"</span><span class="punctuation">:</span> <span class="string">"視頻網站"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"Weibo"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"link"</span><span class="punctuation">:</span> <span class="string">"https://www.weibo.com/"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"avatar"</span><span class="punctuation">:</span> <span class="string">"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"descr"</span><span class="punctuation">:</span> <span class="string">"中國最大社交分享平台"</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"Twitter"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"link"</span><span class="punctuation">:</span> <span class="string">"https://twitter.com/"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"avatar"</span><span class="punctuation">:</span> <span class="string">"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"descr"</span><span class="punctuation">:</span> <span class="string">"社交分享平台"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">]</span></span><br></pre></td></tr></table></figure><h2 id="子页面"><a href="#子页面" class="headerlink" title="子页面"></a>子页面</h2><p>子頁面也是普通的頁面,你只需要 hexo n page xxxxx 創建你的頁面就行</p><p>然後使用標簽外掛 gallery,具體用法請查看對應的內容。</p><p><strong>如果你想要使用 /photo/ohmygirl 這樣的鏈接顯示你的圖片內容.你可以把創建好的 ohmygirl 整個文件夾移到 photo 文件夾裏去</strong></p><h2 id="404-頁面"><a href="#404-頁面" class="headerlink" title="404 頁面"></a>404 頁面</h2><p>主題內置了一個簡單的 404 頁面,可在設置中開啟</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># A simple 404 page</span></span><br><span class="line"><span class="attr">error_404:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">subtitle:</span> <span class="string">'頁面沒有找到'</span></span><br><span class="line"> <span class="attr">background:</span></span><br></pre></td></tr></table></figure><h2 id="ps"><a href="#ps" class="headerlink" title="ps:"></a>ps:</h2><p>关于图片插入的问题可以看这篇大佬的博客:<a href="https://luoynothing.github.io/2023/08/31/%F0%9F%93%A3-%E5%A4%84%E7%90%86hexo%E5%8D%9A%E5%AE%A2%E4%B8%AD%E5%9B%BE%E7%89%87%E4%B8%8D%E6%98%BE%E7%A4%BA%E9%97%AE%E9%A2%98/">📣 处理hexo博客中图片不显示问题 - 归故里</a>(<strong>我推荐用第一阶段的)</strong></p>]]></content>
</entry>
</search>