-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcpp-classes.html
268 lines (268 loc) · 89 KB
/
cpp-classes.html
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=2"><meta name="theme-color" content="#222"><meta http-equiv="X-UA-COMPATIBLE" content="IE=edge,chrome=1"><meta name="renderer" content="webkit"><link rel="icon" type="image/ico" sizes="32x32" href="/assets/favicon.ico"><link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon.png"><link rel="alternate" href="/rss.xml" title="Jiankychen's Blog" type="application/rss+xml"><link rel="alternate" href="/atom.xml" title="Jiankychen's Blog" type="application/atom+xml"><link rel="alternate" type="application/json" title="Jiankychen's Blog" href="https://jiankychen.github.io/feed.json"><link rel="preconnect" href="https://lf9-cdn-tos.bytecdntp.com"><link rel="preconnect" href="https://at.alicdn.com"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Mulish:400,400italic,700,700italic%7CFredericka%20the%20Great:400,400italic,700,700italic%7CNoto%20Serif%20JP:400,400italic,700,700italic%7CNoto%20Serif%20SC:400,400italic,700,700italic%7CInconsolata:400,400italic,700,700italic&display=swap&subset=latin,latin-ext" media="none" onload="this.media='all'"><link rel="stylesheet" href="/css/app.css?v=0.4.2"><link rel="modulepreload" href="/js/chunk-7IVLRIQ3.js"><link rel="modulepreload" href="/js/chunk-IXT6LZJL.js"><link rel="modulepreload" href="/js/chunk-PHSEV26P.js"><link rel="modulepreload" href="/js/chunk-XHQGHZCW.js"><link rel="modulepreload" href="/js/comments-TUWNDU5I.js"><link rel="modulepreload" href="/js/post-P6IN2S3Y.js"><link rel="modulepreload" href="/js/quicklink-HAJEHOPK.js"><link rel="modulepreload" href="/js/search-WFXK2K66.js"><link rel="modulepreload" href="/js/siteInit.js"><link rel="stylesheet" href="https://npm.webcache.cn/@waline/[email protected]/dist/waline.css" media="none" onload="this.media='all'"><link rel="preload" href="https://i.imgtg.com/2023/03/09/Y0xvg.jpg" as="image" fetchpriority="high"><link rel="preload" href="https://img.timelessq.com/images/2022/07/26/1135e8eb0ca0a462aa1c2f6ecb6a5ae2.jpg" as="image" fetchpriority="high"><link rel="preload" href="https://i.imgtg.com/2023/03/09/Y0zdB.jpg" as="image" fetchpriority="high"><link rel="preload" href="https://i.imgtg.com/2023/03/09/YSj7p.jpg" as="image" fetchpriority="high"><link rel="preload" href="https://img.timelessq.com/images/2022/07/26/8fe50780c15461b629c9aeab5a7f2acd.jpg" as="image" fetchpriority="high"><link rel="preload" href="https://i.imgtg.com/2023/03/09/YS2LU.jpg" as="image" fetchpriority="high"><link rel="canonical" href="https://jiankychen.github.io/cpp-classes"><title>C++ 类</title><meta name="generator" content="Hexo 7.0.0"></head><body itemscope="" itemtype="http://schema.org/WebPage"><div id="loading"><div class="cat"><div class="body"></div><div class="head"><div class="face"></div></div><div class="foot"><div class="tummy-end"></div><div class="bottom"></div><div class="legs left"></div><div class="legs right"></div></div><div class="paw"><div class="hands left"></div><div class="hands right"></div></div></div></div><div id="container"><header id="header" itemscope="" itemtype="http://schema.org/WPHeader"><div class="inner"><div id="brand"><div class="pjax"><h1 itemprop="name headline">C++ 类</h1><div class="meta"><span class="item" title="创建时间:2022-10-25 22:11:12"><span class="icon"><i class="ic i-calendar"></i></span><span class="text">发表于</span><time itemprop="dateCreated datePublished" datetime="2022-10-25T22:11:12+08:00">2022-10-25</time></span><span class="item" title="本文字数"><span class="icon"><i class="ic i-pen"></i></span><span class="text">本文字数</span><span>14k</span><span class="text">字</span></span><span class="item" title="阅读时长"><span class="icon"><i class="ic i-clock"></i></span><span class="text">阅读时长</span><span>13 分钟</span></span></div></div></div><nav id="nav"><div class="inner"><div class="toggle"><div class="lines" aria-label="切换导航栏"><span class="line"></span><span class="line"></span><span class="line"></span></div></div><ul class="menu"><li class="item title"><a href="/" rel="start">Jiankychen</a></li></ul><ul class="right" id="rightNav"><li class="item theme"><i class="ic i-sun"></i></li><li class="item search"><i class="ic i-search"></i></li></ul></div></nav></div><div class="pjax" id="imgs"><ul><li class="item" style="background-image: url("https://i.imgtg.com/2023/03/09/Y0xvg.jpg");"></li><li class="item" style="background-image: url("https://img.timelessq.com/images/2022/07/26/1135e8eb0ca0a462aa1c2f6ecb6a5ae2.jpg");"></li><li class="item" style="background-image: url("https://i.imgtg.com/2023/03/09/Y0zdB.jpg");"></li><li class="item" style="background-image: url("https://i.imgtg.com/2023/03/09/YSj7p.jpg");"></li><li class="item" style="background-image: url("https://img.timelessq.com/images/2022/07/26/8fe50780c15461b629c9aeab5a7f2acd.jpg");"></li><li class="item" style="background-image: url("https://i.imgtg.com/2023/03/09/YS2LU.jpg");"></li></ul></div></header><div id="waves"><svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto"><defs><path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z"></path></defs><g class="parallax"><use xlink:href="#gentle-wave" x="48" y="0"></use><use xlink:href="#gentle-wave" x="48" y="3"></use><use xlink:href="#gentle-wave" x="48" y="5"></use><use xlink:href="#gentle-wave" x="48" y="7"></use></g></svg></div><main><div class="inner"><div class="pjax" id="main"><div class="article wrap"><div class="breadcrumb" itemlistelement="" itemscope="" itemtype="https://schema.org/BreadcrumbList"><i class="ic i-home"></i><span><a href="/">首页</a></span><i class="ic i-angle-right"></i><span class="current" itemprop="itemListElement" itemscope="itemscope" itemtype="https://schema.org/ListItem"><a href="/categories/C/" itemprop="item" rel="index" title="分类于C++"><span itemprop="name">C++<meta itemprop="position" content="0"></span></a></span></div><article class="post block" itemscope="itemscope" itemtype="http://schema.org/Article" lang="zh-CN"><link itemprop="mainEntityOfPage" href="https://jiankychen.github.io/cpp-classes.html"><span hidden="hidden" itemprop="author" itemscope="itemscope" itemtype="http://schema.org/Person"><meta itemprop="image" content="/assets/avatar.jpg"><meta itemprop="name" content="Jiankychen"><meta itemprop="description" content="Never put off till tomorrow what you can do today, "></span><span hidden="hidden" itemprop="publisher" itemscope="itemscope" itemtype="http://schema.org/Organization"><meta itemprop="name" content="Jiankychen's Blog"></span><div class="body md" itemprop="articleBody"><h1 id="定义抽象数据类型"><a class="anchor" href="#定义抽象数据类型">#</a> 定义抽象数据类型</h1>
<p>类的基本思想是<strong>数据抽象</strong>(data abstraction)和<strong>封装</strong>(encapsulation)</p>
<p>数据抽象是一种依赖于<strong>接口</strong>(interface)和<strong>实现</strong>(implementation)分离的编程(以及设计)技术</p>
<ul>
<li>类的接口:包括用户所能执行的操作</li>
<li>类的实现:包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数</li>
</ul>
<p>封装实现了类的接口和实现的分离。封装后的类隐藏了它的实现细节,也就是说,类的用户只能使用接口而无法访问实现部分</p>
<p>类要想实现数据抽象和封装,需要首先定义一个<strong>抽象数据类型</strong>(abstract data type)</p>
<ul>
<li>在抽象数据类型中,由类的设计者负责考虑类的实现过程</li>
<li>使用该类的程序员则只需要抽象地思考类型做了什么,而无须了解类型的工作细节</li>
</ul>
<p>本节将以 Sales_data 类为例,介绍类的定义与使用</p>
<h2 id="设计-sales_data-类"><a class="anchor" href="#设计-sales_data-类">#</a> 设计 Sales_data 类</h2>
<p>我们希望 Sales_item 类有一个名为 isbn 的成员函数(member function),并且具有一些等效于 + 、= 、+= 、<<和>> 运算符的函数</p>
<p>于是,Sales_data 的接口应该包含以下操作:</p>
<ul>
<li>一个 isbn 成员函数,用于返回对象的 ISBN 编号</li>
<li>一个 combine 成员函数,用于将一个 Sales_data 对象加到另一个对象上(等效于 += 运算符)</li>
<li>一个名为 add 的普通函数,执行两个 Sales_data 对象的加法(等效于 + 运算符)</li>
<li>一个名为 read 的普通函数,将数据从 istream 读入到 Sales_data 对象中(等效于 >> 运算符)</li>
<li>一个名为 print 的函数,将 Sales_data 对象的值输出到 ostream (等效于 << 运算符)</li>
</ul>
<p>定义 Sales_data 类如下所示:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">struct</span> <span class="token class-name">Sales_data</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="2"></td><td><pre> <span class="token comment">// 数据成员</span></pre></td></tr><tr><td data-num="3"></td><td><pre> std<span class="token double-colon punctuation">::</span>string bookNo<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre> <span class="token keyword">unsigned</span> units_sold <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="5"></td><td><pre> <span class="token keyword">double</span> revenue <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="6"></td><td><pre> <span class="token comment">// 成员函数:关于 Sales_data 对象的操作</span></pre></td></tr><tr><td data-num="7"></td><td><pre> std<span class="token double-colon punctuation">::</span>string <span class="token function">isbn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> bookNo<span class="token punctuation">;</span> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="8"></td><td><pre> Sales_data<span class="token operator">&</span> <span class="token function">combine</span><span class="token punctuation">(</span><span class="token keyword">const</span> Sales_data<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="9"></td><td><pre> <span class="token keyword">double</span> <span class="token function">avg_price</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token punctuation">}</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="11"></td><td><pre></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token comment">// 非成员的普通函数</span></pre></td></tr><tr><td data-num="13"></td><td><pre>Sales_data <span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">const</span> Sales_data<span class="token operator">&</span><span class="token punctuation">,</span> <span class="token keyword">const</span> Sales_data<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="14"></td><td><pre>std<span class="token double-colon punctuation">::</span>ostream <span class="token operator">&</span><span class="token function">print</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>ostream<span class="token operator">&</span><span class="token punctuation">,</span> <span class="token keyword">const</span> Sales_data<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>std<span class="token double-colon punctuation">::</span>istream <span class="token operator">&</span><span class="token function">read</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>istream<span class="token operator">&</span><span class="token punctuation">,</span> Sales_data<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>其中,Sales_data 类的数据成员包括:bookNo 、units_sold 、revenue ,分别表示书本的 ISBN 编号、销量、总销售额;Sales_data 类的成员函数包括 isbn 和 combine ;add、read 和 print 则是普通函数,并未作为 Sales_data 类的成员</p>
<p>Sales_data 类的使用示例:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>Sales_data total<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">read</span><span class="token punctuation">(</span>cin<span class="token punctuation">,</span> total<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="3"></td><td><pre> Sales_data trans<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre> <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token function">read</span><span class="token punctuation">(</span>cin<span class="token punctuation">,</span> trans<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="5"></td><td><pre> <span class="token keyword">if</span> <span class="token punctuation">(</span>total<span class="token punctuation">.</span><span class="token function">isbn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> trans<span class="token punctuation">.</span><span class="token function">isbn</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre> total<span class="token punctuation">.</span><span class="token function">combine</span><span class="token punctuation">(</span>trans<span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="7"></td><td><pre> <span class="token keyword">else</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="8"></td><td><pre> <span class="token function">print</span><span class="token punctuation">(</span>cout<span class="token punctuation">,</span> total<span class="token punctuation">)</span> <span class="token operator"><<</span> endl<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="9"></td><td><pre> total <span class="token operator">=</span> trans<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="10"></td><td><pre> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="11"></td><td><pre> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="12"></td><td><pre> <span class="token function">print</span><span class="token punctuation">(</span>cout<span class="token punctuation">,</span> total<span class="token punctuation">)</span> <span class="token operator"><<</span> endl<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="13"></td><td><pre><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="14"></td><td><pre> cerr <span class="token operator"><<</span> <span class="token string">"No data?!"</span> <span class="token operator"><<</span> endl<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="15"></td><td><pre><span class="token punctuation">}</span></pre></td></tr></tbody></table></figure><h3 id="定义成员函数"><a class="anchor" href="#定义成员函数">#</a> 定义成员函数</h3>
<p><strong>成员函数的声明必须在类的内部,它的定义既可以在类的内部也可以在类的外部</strong></p>
<p>例如,上述的 isbn 函数定义在 Sales_data 类的内部,combine 和 avg_price 定义在类的外部</p>
<p>和其他函数一样,类的成员函数也由函数类型、函数名、形参列表和函数体组成。以 isbn 函数为例,isbn 函数用于返回 Sales_data 对象的 bookNo 数据成员,其定义为</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>std<span class="token double-colon punctuation">::</span>string <span class="token function">isbn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> bookNo<span class="token punctuation">;</span> <span class="token punctuation">}</span></pre></td></tr></tbody></table></figure><p>然而,isbn 函数是如何获得 bookNo 成员所依赖的对象的呢?</p>
<h4 id="this"><a class="anchor" href="#this">#</a> this</h4>
<p>不妨先观察 isbn 成员函数的调用:使用点运算符访问 trans 对象的 isbn 成员,由此来调用该函数</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>Sales_data trans<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>trans<span class="token punctuation">.</span><span class="token function">isbn</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p><strong>成员函数通过一个名为 <strong> <code>this</code> </strong> 的隐式参数来访问调用它的那个对象</strong>。具体来说,当我们调用一个成员函数时,请求该函数的对象地址 将会被用于初始化 <code>this</code></p>
<p>例如,如果调用 <code>trans.isbn()</code> ,编译器将会把 trans 的地址传递给 isbn 函数的隐式形参 <code>this</code> 。可以等价地认为,编译器将 <code>trans.isbn()</code> 这一调用重写成了如下形式:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token comment">// 伪代码,用于说明调用成员函数的实际执行过程</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token class-name">Sales_data</span><span class="token double-colon punctuation">::</span><span class="token function">isbn</span><span class="token punctuation">(</span><span class="token operator">&</span>trans<span class="token punctuation">)</span></pre></td></tr></tbody></table></figure><p>其中,调用 Sales_data 的 isbn 成员时传入了 trans 的地址</p>
<p>在成员函数内部,可以直接使用调用该函数的对象的成员,而无须通过成员访问运算符来做到这一点,因为 <code>this</code> 所指的就是当前对象。<strong>任何对类成员的直接访问都被看作 <code>this</code> 的隐式引用</strong>,也就是说,当 isbn 使用 bookNo 时,它隐式地使用 <code>this</code> 指向的成员,就像我们书写了 this->bookNo 一样</p>
<p>this 形参是隐式定义的。任何自定义名为 this 的参数或变量都是非法的</p>
<p>我们可以在成员函数体内部使用 this (尽管没有必要)</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>std<span class="token double-colon punctuation">::</span>string <span class="token function">isbn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token operator">-></span>bookNo<span class="token punctuation">;</span> <span class="token punctuation">}</span></pre></td></tr></tbody></table></figure><p>需要注意,<strong> <code>this</code> 是一个常量指针</strong>,不允许改变 <code>this</code> 中保存的地址</p>
<h4 id="const-成员函数"><a class="anchor" href="#const-成员函数">#</a> const 成员函数</h4>
<p>isbn 函数的另一个关键之处:参数列表后的 <code>const</code> 关键字,用于修改隐式 <code>this</code> 指针的类型</p>
<p>默认情况下, <code>this</code> 的类型是指向类类型非常量版本的常量指针。例如,在 Sales_data 的成员函数中,this 的类型是 Sales_data *const</p>
<p>尽管 <code>this</code> 是隐式的,也依然需要遵循初始化规则,这意味着:(在默认情况下)我们不能把 <code>this</code> 绑定到一个常量对象上,即,我们不能在一个常量对象上调用普通的成员函数(不加 <code>const</code> 的成员函数)</p>
<p>于是,我们不禁会想到:如果想要在常量对象上调用普通成员函数,就应该将 <code>this</code> 声明成指向常量的指针(即,const Sales_data *const )。然而, <code>this</code> 是隐式的,并不会出现在参数列表中。那我们应该在哪里将 <code>this</code> 声明成指向常量的指针呢?</p>
<p>C++ 语言的做法是:允许把 <code>const</code> 关键字放在成员函数的参数列表之后,此时,紧跟在参数列表后面的 <code>const</code> 表示 <code>this</code> 是一个指向常量的指针</p>
<p>像这样使用 <code>const</code> 的成员函数被称作 <strong>常量成员函数</strong>(const member function)</p>
<p>因为 <code>this</code> 是指向常量的指针,常量成员函数不能改变调用它的对象的内容</p>
<p><strong>常量对象,以及常量对象的引用或指针都只能调用常量成员函数</strong></p>
<h4 id="类作用域和成员函数"><a class="anchor" href="#类作用域和成员函数">#</a> 类作用域和成员函数</h4>
<p>类本身就是一个作用域,类成员函数的定义嵌套在类的作用域之内</p>
<p>在上例中,isbn 中用到的名字 bookNo 是定义在 Sales_data 内的数据成员,并且,即使 bookNo 定义在 isbn 之后,isbn 也还是能够使用 bookNo</p>
<p>这是因为,编译器分两步处理类:首先编译成员的声明,然后才编译成员函数体(如果有的话)</p>
<p>因此,<strong>成员函数体可以随意使用类中的其他成员,无须在意这些成员出现的次序</strong></p>
<h4 id="在类的外部定义"><a class="anchor" href="#在类的外部定义">#</a> 在类的外部定义</h4>
<p>当我们在类的外部定义成员函数时,成员函数的定义必须与它的声明匹配</p>
<ul>
<li>返回类型、参数列表和函数名都得与类内部的声明保持一致</li>
<li>如果成员被声明成常量成员函数,那么它的定义也必须在参数列表后明确指定 <code>const</code> 属性</li>
</ul>
<p>并且,类外部定义的成员名字必须包含它所属的类名</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">double</span> <span class="token class-name">Sales_data</span><span class="token double-colon punctuation">::</span><span class="token function">avg_price</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="2"></td><td><pre> <span class="token keyword">if</span> <span class="token punctuation">(</span>units_sold<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre> <span class="token keyword">return</span> revenue<span class="token operator">/</span>units_sold<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre> <span class="token keyword">else</span></pre></td></tr><tr><td data-num="5"></td><td><pre> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token punctuation">}</span></pre></td></tr></tbody></table></figure><p>其中,函数名 Sales_data::avg_price 使用作用域运算符来说明 avg_price 函数被声明在类 Sales_data 的作用域内。因此,当 avg_price 使用 revenue 和 units_sold 时,它实际上是使用了 Sales_data 的成员</p>
<h4 id="定义一个返回-this-对象的函数"><a class="anchor" href="#定义一个返回-this-对象的函数">#</a> 定义一个返回 this 对象的函数</h4>
<p>函数 combine 的设计初衷类似于复合赋值运算符 += 。调用该函数的对象代表的是赋值运算符左侧的运算对象,右侧运算对象则通过显式的实参被传入函数</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>Sales_data<span class="token operator">&</span> <span class="token class-name">Sales_data</span><span class="token double-colon punctuation">::</span><span class="token function">combine</span><span class="token punctuation">(</span><span class="token keyword">const</span> Sales_data <span class="token operator">&</span>rhs<span class="token punctuation">)</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="2"></td><td><pre> units_sold <span class="token operator">+=</span> rhs<span class="token punctuation">.</span>units_sold<span class="token punctuation">;</span> <span class="token comment">// 把 rhs 的成员加到 this 对象的成员上</span></pre></td></tr><tr><td data-num="3"></td><td><pre> revenue <span class="token operator">+=</span> rhs<span class="token punctuation">.</span>revenue<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre> <span class="token keyword">return</span> <span class="token operator">*</span><span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token comment">// 返回调用该函数的对象</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">}</span></pre></td></tr></tbody></table></figure><p>当我们的程序调用如下函数时,</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>total<span class="token punctuation">.</span><span class="token function">combine</span><span class="token punctuation">(</span>trans<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 更新变量 total 的值</span></pre></td></tr></tbody></table></figure><p>total 的地址被绑定到隐式的 this 参数上,而 rhs 绑定到了 trans 上</p>
<p>因此,当 combine 执行下面的语句时,</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>units_sold <span class="token operator">+=</span> rhs<span class="token punctuation">.</span>units_sold<span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>等效于 total.units_sold += trans.unit_sold</p>
<p>一般来说,当我们定义的函数类似于某个内置运算符时,应该令该函数的行为尽量模仿这个运算符</p>
<ul>
<li>内置的赋值运算符把它的左侧运算对象当成左值返回</li>
<li>为了与它保持一致,combine 函数必须返回引用类型。因为此时的左侧运算对象是一个 Sales_data 的对象,所以返回类型应该是 Sales_data&</li>
</ul>
<p>如前所述,我们无须使用隐式的 <code>this</code> 指针访问函数调用者的某个具体成员,但是,我们需要使用 <code>this</code> 来把调用函数的对象当成一个整体访问。因此,需要使用</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">return</span> <span class="token operator">*</span><span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token comment">// 返回调用该函数的对象</span></pre></td></tr></tbody></table></figure><p>以获得执行该函数的对象,其中,解引用 <code>this</code> 指针就是得到了 total 的地址</p>
<h3 id="定义类相关的非成员函数"><a class="anchor" href="#定义类相关的非成员函数">#</a> 定义类相关的非成员函数</h3>
<p><strong>作为接口组成部分的非成员函数,它们的定义和声明都在类的外部</strong>,例如 add、read 和 print 等</p>
<blockquote>
<p>一般来说,如果非成员函数是类接口的组成部分,则这些函数的声明应该与类在同一个头文件内。在这种方式下,用户使用接口的任何部分都只需要引入一个文件</p>
</blockquote>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token comment">// 定义 read 函数</span></pre></td></tr><tr><td data-num="2"></td><td><pre>istream <span class="token operator">&</span><span class="token function">read</span><span class="token punctuation">(</span>istream <span class="token operator">&</span>is<span class="token punctuation">,</span> Sales_data <span class="token operator">&</span>item<span class="token punctuation">)</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="3"></td><td><pre> <span class="token keyword">double</span> price <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre> is <span class="token operator">>></span> item<span class="token punctuation">.</span>bookNo <span class="token operator">>></span> item<span class="token punctuation">.</span>units_sold <span class="token operator">>></span> price<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="5"></td><td><pre> item<span class="token punctuation">.</span>revenue <span class="token operator">=</span> price <span class="token operator">*</span> item<span class="token punctuation">.</span>units_sold<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="6"></td><td><pre> <span class="token keyword">return</span> is<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token punctuation">}</span></pre></td></tr><tr><td data-num="8"></td><td><pre></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token comment">// 定义 print 函数</span></pre></td></tr><tr><td data-num="10"></td><td><pre>ostream <span class="token operator">&</span><span class="token function">print</span><span class="token punctuation">(</span>ostream <span class="token operator">&</span>os<span class="token punctuation">,</span> <span class="token keyword">const</span> Sales_data <span class="token operator">&</span>item<span class="token punctuation">)</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="11"></td><td><pre> os <span class="token operator"><<</span> item<span class="token punctuation">.</span><span class="token function">isbn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator"><<</span> <span class="token string">" "</span> <span class="token operator"><<</span> item<span class="token punctuation">.</span>units_sold <span class="token operator"><<</span> <span class="token string">" "</span></pre></td></tr><tr><td data-num="12"></td><td><pre> <span class="token operator"><<</span> item<span class="token punctuation">.</span>revenue <span class="token operator"><<</span> <span class="token string">" "</span> <span class="token operator"><<</span> item<span class="token punctuation">.</span><span class="token function">avg_price</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="13"></td><td><pre> <span class="token keyword">return</span> os<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token punctuation">}</span></pre></td></tr><tr><td data-num="15"></td><td><pre></pre></td></tr><tr><td data-num="16"></td><td><pre><span class="token comment">// 定义 add 函数</span></pre></td></tr><tr><td data-num="17"></td><td><pre>Sales_data <span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">const</span> Sales_data <span class="token operator">&</span>lhs<span class="token punctuation">,</span> <span class="token keyword">const</span> Sales_data <span class="token operator">&</span>rhs<span class="token punctuation">)</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="18"></td><td><pre> Sales_data sum <span class="token operator">=</span> lhs<span class="token punctuation">;</span> <span class="token comment">// copy data members from lhs into sum</span></pre></td></tr><tr><td data-num="19"></td><td><pre> sum<span class="token punctuation">.</span><span class="token function">combine</span><span class="token punctuation">(</span>rhs<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// add data members from rhs into sum</span></pre></td></tr><tr><td data-num="20"></td><td><pre> <span class="token keyword">return</span> sum<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="21"></td><td><pre><span class="token punctuation">}</span></pre></td></tr></tbody></table></figure><p>read 函数从给定流中将数据读到给定的对象里,print 函数则负责将给定对象的内容打印到给定的流中。其中,read 和 print 分别接受一个各自 IO 类型的引用作为其参数,这是因为 IO 类属于不能被拷贝的类型,因此我们只能通过引用来传递它们。而且,因为读取和写入的操作会改变流的内容,两个函数接受的都是普通引用,而非对常量的引用</p>
<h2 id="构造函数"><a class="anchor" href="#构造函数">#</a> 构造函数</h2>
<p>每个类都分别定义了它的对象被初始化的方式</p>
<p>类通过一个或几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做 <strong>构造函数</strong>(constructor)</p>
<ul>
<li>
<p><strong>构造函数的名字和类名相同</strong></p>
</li>
<li>
<p>类似于其他函数,构造函数有一个参数列表(可能为空)和一个函数体(可能为空)</p>
</li>
<li>
<p><strong>构造函数没有返回类型</strong></p>
</li>
</ul>
<p>构造函数的任务是初始化类对象的数据成员。无论何时,只要类的对象被创建,就会执行构造函数</p>
<p>类可以包含多个构造函数,但是,不同的构造函数之间必须在参数数量或参数类型上有所区别(类似于函数重载)</p>
<p>不同于其他成员函数,构造函数不能被声明成 <code>const</code> 。当我们创建类的一个 <code>const</code> 对象时,直到构造函数完成初始化过程,对象才能真正取得其 “常量” 属性。因此,构造函数在构造 <code>const</code> 对象的过程中可以向其写值</p>
<h3 id="合成的默认构造函数"><a class="anchor" href="#合成的默认构造函数">#</a> 合成的默认构造函数</h3>
<p>我们的 Sales_data 类并没有定义任何构造函数,但是之前使用了 Sales_data 对象的程序仍然可以正确编译和运行,因此可以说明,Sales_data 对象执行了默认初始化</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>Sales_data total<span class="token punctuation">;</span> <span class="token comment">// 没有为 total 提供初始值,执行默认初始化</span></pre></td></tr><tr><td data-num="2"></td><td><pre>Sales_data trans<span class="token punctuation">;</span> <span class="token comment">// 没有为 trans 提供初始值,执行默认初始化</span></pre></td></tr></tbody></table></figure><p>类通过一个特殊的构造函数来控制默认初始化过程,这个函数叫做 <strong>默认构造函数</strong>(default constructor)</p>
<ul>
<li>
<p>默认构造函数无须任何实参</p>
</li>
<li>
<p>如果我们的类没有显式地定义构造函数,编译器将会为我们隐式地定义一个默认构造函数</p>
</li>
</ul>
<p>编译器自动生成的构造函数被称为<strong>合成的默认构造函数</strong>(synthesized default constructor)</p>
<p>对于大多数类来说,合成的默认构造函数将按照如下规则初始化类的数据成员:</p>
<ul>
<li>如果存在类内的初始值,用类内初始值来初始化成员</li>
<li>否则,默认初始化该成员</li>
</ul>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">struct</span> <span class="token class-name">Sales_data</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="2"></td><td><pre> std<span class="token double-colon punctuation">::</span>string bookNo<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="3"></td><td><pre> <span class="token keyword">unsigned</span> units_sold <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre> <span class="token keyword">double</span> revenue <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">}</span></pre></td></tr></tbody></table></figure><p>例如,对上面的类而言,因为 Sales_data 为 units_sold 和 revenue 提供了初始值,所以合成的默认构造函数将使用这些值来初始化对应的成员,而 bookNo 并未提供初始值,所以合成的默认构造函数把 bookNo 默认初始化成一个空字符串</p>
<p>然而,合成的默认构造函数只适合非常简单的类,比如现在定义的这个 Sales_data 版本</p>
<p>通常来说,<strong>一个类必须定义它自己的默认构造函数,而不能仅依赖于合成的默认构造函数</strong> 。这是因为:</p>
<ul>
<li>
<p>只有当类没有声明任何构造函数时,编译器才会自动地生成默认构造函数。一旦我们定义了一些其他的构造函数,类将没有默认构造函数(除非我们自己定义一个默认构造函数)</p>
</li>
<li>
<p>对于某些类来说,合成的默认构造函数可能执行错误的操作。如果类包含有内置类型或者复合类型的成员,则只有当这些成员全都被赋予了类内的初始值时,这个类才适合于使用合成的默认构造函数</p>
</li>
<li>
<p>有的时候编译器不能为某些类合成默认的构造函数。例如,如果类中包含一个其他类类型的成员且这个成员的类型没有默认构造函数,那么编译器将无法初始化该成员。此外,还有其他一些情况也会导致编译器无法生成一个正确的默认构造函数,我们将在以后讨论</p>
</li>
</ul>
<h3 id="定义构造函数"><a class="anchor" href="#定义构造函数">#</a> 定义构造函数</h3>
<p>对于我们的 Sales_data 类来说,我们可根据实际需要而定义不同的构造函数,例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">struct</span> <span class="token class-name">Sales_data</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="2"></td><td><pre> <span class="token comment">// 数据成员及成员函数</span></pre></td></tr><tr><td data-num="3"></td><td><pre> std<span class="token double-colon punctuation">::</span>string bookNo<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre> <span class="token keyword">unsigned</span> units_sold <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="5"></td><td><pre> <span class="token keyword">double</span> revenue <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="6"></td><td><pre> std<span class="token double-colon punctuation">::</span>string <span class="token function">isbn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> bookNo<span class="token punctuation">;</span> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="7"></td><td><pre> Sales_data<span class="token operator">&</span> <span class="token function">combine</span><span class="token punctuation">(</span><span class="token keyword">const</span> Sales_data<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="8"></td><td><pre> <span class="token keyword">double</span> <span class="token function">avg_price</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="9"></td><td><pre> <span class="token comment">// 构造函数</span></pre></td></tr><tr><td data-num="10"></td><td><pre> <span class="token function">Sales_data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="11"></td><td><pre> <span class="token function">Sales_data</span><span class="token punctuation">(</span><span class="token keyword">const</span> std<span class="token double-colon punctuation">::</span>string <span class="token operator">&</span>s<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token function">bookNo</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="12"></td><td><pre> <span class="token function">Sales_data</span><span class="token punctuation">(</span><span class="token keyword">const</span> std<span class="token double-colon punctuation">::</span>string <span class="token operator">&</span>s<span class="token punctuation">,</span> <span class="token keyword">unsigned</span> n<span class="token punctuation">,</span> <span class="token keyword">double</span> p<span class="token punctuation">)</span><span class="token operator">:</span></pre></td></tr><tr><td data-num="13"></td><td><pre> <span class="token function">bookNo</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">units_sold</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">revenue</span><span class="token punctuation">(</span>p<span class="token operator">*</span>n<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="14"></td><td><pre> <span class="token function">Sales_data</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>istream <span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="15"></td><td><pre><span class="token punctuation">}</span><span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><h4 id="default"><a class="anchor" href="#default">#</a> = default</h4>
<p>在 C++ 11 标准中,如果我们需要默认的行为,可以通过在参数列表后面写上 <code>= default</code> 来要求编译器生成默认构造函数,其作用完全等同于之前使用的合成默认构造函数</p>
<p>其中, <code>= default</code> 既可以与声明一起出现在类的内部,也可以作为定义出现在类的外部</p>
<ul>
<li>如果 <code>= default</code> 在类的内部,则默认构造函数是内联的</li>
<li>如果它在类的外部,则该成员默认情况下不是内联的</li>
</ul>
<blockquote>
<p>If the <code>= default</code> appears inside the class body, the default constructor will be inlined; if it appears on the definition outside the class, the member will not be inlined by default.</p>
</blockquote>
<p>例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token function">Sales_data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>需注意, <code>= default</code> 生成的默认构造函数之所以对 Sales_data 类有效,是因为我们已经为内置类型的数据成员提供初始值。如果编译器不支持类内初始值,默认构造函数就应该使用构造函数初始值列表来初始化类的每个成员</p>
<h4 id="构造函数初始值列表"><a class="anchor" href="#构造函数初始值列表">#</a> 构造函数初始值列表</h4>
<p>对于以下两个构造函数,其定义中出现了新的部分,即,冒号 以及 冒号与花括号之间的代码。我们把新出现的部分称为 <strong>构造函数初始值列表</strong>(constructor initialize list)</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token function">Sales_data</span><span class="token punctuation">(</span><span class="token keyword">const</span> std<span class="token double-colon punctuation">::</span>string <span class="token operator">&</span>s<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token function">bookNo</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token function">Sales_data</span><span class="token punctuation">(</span><span class="token keyword">const</span> std<span class="token double-colon punctuation">::</span>string <span class="token operator">&</span>s<span class="token punctuation">,</span> <span class="token keyword">unsigned</span> n<span class="token punctuation">,</span> <span class="token keyword">double</span> p<span class="token punctuation">)</span><span class="token operator">:</span></pre></td></tr><tr><td data-num="3"></td><td><pre> <span class="token function">bookNo</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">units_sold</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">revenue</span><span class="token punctuation">(</span>p<span class="token operator">*</span>n<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span></pre></td></tr></tbody></table></figure><p>构造函数初始值列表负责为新创建对象的一个或几个数据成员赋初值</p>
<p>构造函数初始值列表的组成:</p>
<ul>
<li>成员名字</li>
<li>成员名字后面的、括号(或者花括号)括起来的成员初始值</li>
<li>不同成员之间的逗号分隔符</li>
</ul>
<p>以 <code>Sales_data(const std::string &s, unsigned n, double p): bookNo(s), units_sold(n), revenue(p*n) { }</code> 为例,该函数使用 <code>s</code> 和 <code>n</code> 来分别初始化成员 bookNo 和 units_sold ,并用 <code>p</code> 与 <code>n</code> 的乘积来初始化 revenue</p>
<p><code>Sales_data(const std::string &s): bookNo(s) { }</code> ,其只使用 <code>s</code> 来显式初始化 bookNo 。对于 units_sold 和 revenue 这两个成员而言,将利用类内初始值进行隐式初始化(类似于合成默认构造函数的方式)。因此,该构造函数等价于</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token function">Sales_data</span><span class="token punctuation">(</span><span class="token keyword">const</span> std<span class="token double-colon punctuation">::</span>string <span class="token operator">&</span>s<span class="token punctuation">)</span><span class="token operator">:</span></pre></td></tr><tr><td data-num="2"></td><td><pre> <span class="token function">bookNo</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">units_sold</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">revenue</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">}</span></pre></td></tr></tbody></table></figure><blockquote>
<p>当某个数据成员被构造函数初始值列表忽略时,它将以与合成默认构造函数相同的方式隐式初始化</p>
</blockquote>
<p>通常情况下,构造函数使用类内初始值不失为一种好的选择,因为只要这样的初始值存在我们就能确保为成员赋予了一个正确的值。不过,如果你的编译器不支持类内初始值,则所有构造函数都应该显式地初始化每个内置类型的成员</p>
<p>另外,我们需要注意, <code>Sales_data(const std::string &s): bookNo(s) { }</code> 中的 <code>{ }</code> 实际是构造函数的函数体。因为我们定义这些构造函数的目的是为数据成员赋初值,其并不需要通过函数体实现,所以将函数体定义成空的</p>
<h4 id="在类的外部定义构造函数"><a class="anchor" href="#在类的外部定义构造函数">#</a> 在类的外部定义构造函数</h4>
<p>与其他几个构造函数不同,以 <code>istream</code> 对象为参数的构造函数 <code>Sales_data(std::istream &)</code> 需要执行一些实际的操作,在它的函数体内调用了 read 函数来给数据成员赋以初值</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token class-name">Sales_data</span><span class="token double-colon punctuation">::</span><span class="token function">Sales_data</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>istream <span class="token operator">&</span>is<span class="token punctuation">)</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="2"></td><td><pre> <span class="token function">read</span><span class="token punctuation">(</span>is<span class="token punctuation">,</span> <span class="token operator">*</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 从 is 中读取一条信息然后存入 this 对象中</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token punctuation">}</span></pre></td></tr></tbody></table></figure><p>和其他成员函数一样,<strong>当我们在类的外部定义构造函数时,必须指明该构造函数是哪个类的成员</strong></p>
<p>因此, <code>Sales_data::Sales_data</code> 指出,我们定义了 Sales_data 类的成员 Sales_data 。因为该成员的名字和类名相同,所以它是一个构造函数(构造函数没有返回类型)</p>
<p>由于 <code>Sales_data(std::istream &)</code> 函数定义了函数体,在执行该构造函数时,对象的成员是可以被初始化的(尽管这个构造函数初始值列表是空的)</p>
<h2 id="拷贝-赋值和析构"><a class="anchor" href="#拷贝-赋值和析构">#</a> 拷贝、赋值和析构</h2>
<p>除了定义类的对象如何初始化之外,类还需要控制拷贝、赋值和销毁对象时发生的行为</p>
<p>如果我们不主动定义这些操作,编译器将替我们合成它们。一般来说,编译器生成的版本将对对象的每个成员执行拷贝、赋值和销毁操作</p>
<p>例如:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre>total <span class="token operator">=</span> trans<span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>实际上等价于</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token comment">// Sales_data 的默认赋值操作等价于</span></pre></td></tr><tr><td data-num="2"></td><td><pre>total<span class="token punctuation">.</span>bookNo <span class="token operator">=</span> trans<span class="token punctuation">.</span>bookNo<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>total<span class="token punctuation">.</span>units_sold <span class="token operator">=</span> trans<span class="token punctuation">.</span>units_sold<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre>total<span class="token punctuation">.</span>revenue <span class="token operator">=</span> trans<span class="token punctuation">.</span>revenue<span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>尽管编译器能替我们合成拷贝、赋值和销毁的操作,但是必须要清楚的一点是,对于某些类来说合成的版本无法正常工作。特别是,当类需要分配类对象之外的资源时,合成的版本常常会失效。例如,管理动态内存的类通常不能依赖于上述操作的合成版本</p>
<ul>
<li>很多需要动态内存的类能够并且应该使用 vector 对象或者 string 对象来管理必要的存储空间</li>
<li>如果类包含 vector 或者 string 成员,则其拷贝、赋值和销毁的合成版本能够正常工作</li>
</ul>
<h1 id="访问控制与封装"><a class="anchor" href="#访问控制与封装">#</a> 访问控制与封装</h1>
<p>在 C++ 语言中,我们使用 <strong>访问说明符</strong>(access specifiers)加强类的封装性:</p>
<ul>
<li>
<p>定义在 <strong> <code>public</code> </strong> 说明符之后的成员可以在整个程序内被访问。 <code>public</code> 成员定义类的接口</p>
</li>
<li>
<p>定义在 <strong> <code>private</code> </strong> 说明符之后的成员可以被类的成员函数访问,但是不能被使用该类的代码访问。 <code>private</code> 封装(即,隐藏)类的实现细节</p>
</li>
</ul>
<p>一个类可以包含 0 个或多个访问说明符,而且,某个访问说明符可以出现多次</p>
<p>每个访问说明符指定了接下来的成员的访问级别,其有效范围直到出现下一个访问说明符或者到达类的结尾处为止</p>
<p>再一次定义 Sales_data 类,其新形式如下所示:</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> <span class="token class-name">Sales_data</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">public</span><span class="token operator">:</span> <span class="token comment">// 添加了访问说明符</span></pre></td></tr><tr><td data-num="3"></td><td><pre> <span class="token function">Sales_data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre> <span class="token function">Sales_data</span><span class="token punctuation">(</span><span class="token keyword">const</span> std<span class="token double-colon punctuation">::</span>string <span class="token operator">&</span>s<span class="token punctuation">,</span> <span class="token keyword">unsigned</span> n<span class="token punctuation">,</span> <span class="token keyword">double</span> p<span class="token punctuation">)</span><span class="token operator">:</span></pre></td></tr><tr><td data-num="5"></td><td><pre> <span class="token function">bookNo</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">units_sold</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">revenue</span><span class="token punctuation">(</span>p<span class="token operator">*</span>n<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="6"></td><td><pre> <span class="token function">Sales_data</span><span class="token punctuation">(</span><span class="token keyword">const</span> std<span class="token double-colon punctuation">::</span>string <span class="token operator">&</span>s<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token function">bookNo</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="7"></td><td><pre> <span class="token function">Sales_data</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>istream<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="8"></td><td><pre> std<span class="token double-colon punctuation">::</span>string <span class="token function">isbn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> bookNo<span class="token punctuation">;</span> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="9"></td><td><pre> Sales_data <span class="token operator">&</span><span class="token function">combine</span><span class="token punctuation">(</span><span class="token keyword">const</span> Sales_data<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token keyword">private</span><span class="token operator">:</span> <span class="token comment">// 添加了访问说明符</span></pre></td></tr><tr><td data-num="11"></td><td><pre> <span class="token keyword">double</span> <span class="token function">avg_price</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span></pre></td></tr><tr><td data-num="12"></td><td><pre> <span class="token punctuation">{</span> <span class="token keyword">return</span> units_sold <span class="token operator">?</span> revenue<span class="token operator">/</span>units_sold <span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="13"></td><td><pre> std<span class="token double-colon punctuation">::</span>string bookNo<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="14"></td><td><pre> <span class="token keyword">unsigned</span> units_sold <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="15"></td><td><pre> <span class="token keyword">double</span> revenue <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="16"></td><td><pre><span class="token punctuation">}</span><span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>作为接口的一部分,构造函数和部分成员函数(即 isbn 和 combine )紧跟在 <code>public</code> 说明符之后;而数据成员和作为实现部分的函数则跟在 <code>private</code> 说明符后面</p>
<p>在上面的定义中我们还做了一个微妙的变化,我们使用了 <code>class</code> 关键字而非 <code>struct</code> 开始类的定义。这种变化仅仅是形式上有所不同,实际上我们可以使用这两个关键字中的任何一个定义类。唯一的一点区别是,<strong> <code>struct</code> 和 <code>class</code> 的默认访问权限不太一样</strong></p>
<p><strong>类可以在它的第一个访问说明符之前定义成员,这种成员的访问权限依赖于类定义的方式</strong></p>
<ul>
<li>
<p>如果我们使用 <code>struct</code> 关键字,则定义在第一个访问说明符之前的成员是 <code>public</code> 的</p>
</li>
<li>
<p>如果我们使用 <code>class</code> 关键字,则这些成员是 <code>private</code> 的</p>
</li>
</ul>
<p>出于统一编程风格的考虑,<strong>如果我们希望定义的类的所有成员是 <code>public</code> 的,使用 <code>struct</code> ;反之,如果希望成员是 <code>private</code> 的,使用 <code>class</code> </strong></p>
<h2 id="友元"><a class="anchor" href="#友元">#</a> 友元</h2>
<p>类可以将其他类或者函数声明成为它的 <strong>友元</strong>(friend),从而允许其他类或者函数访问它的非公有成员</p>
<p><strong>如果类想把一个函数作为它的友元,需要增加一条以 <code>friend</code> 关键字开始的函数声明语句</strong></p>
<p>例如,此前 <strong>定义抽象数据类型</strong> 时的 read、print 和 add 函数并不是类的成员,但却需要访问 Sales_data 类中 <code>private</code> 的数据成员,因此,我们需要将 read、print 和 add 函数作为 Sales_data 类的友元</p>
<figure class="highlight cpp"><figcaption data-lang="C++"></figcaption><table><tbody><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> <span class="token class-name">Sales_data</span> <span class="token punctuation">{</span></pre></td></tr><tr><td data-num="2"></td><td><pre> <span class="token comment">// 为 Sales_data 的非成员函数所做的友元声明</span></pre></td></tr><tr><td data-num="3"></td><td><pre> <span class="token keyword">friend</span> Sales_data <span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">const</span> Sales_data<span class="token operator">&</span><span class="token punctuation">,</span> <span class="token keyword">const</span> Sales_data<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="4"></td><td><pre> <span class="token keyword">friend</span> std<span class="token double-colon punctuation">::</span>istream <span class="token operator">&</span><span class="token function">read</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>istream<span class="token operator">&</span><span class="token punctuation">,</span> Sales_data<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="5"></td><td><pre> <span class="token keyword">friend</span> std<span class="token double-colon punctuation">::</span>ostream <span class="token operator">&</span><span class="token function">print</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>ostream<span class="token operator">&</span><span class="token punctuation">,</span> <span class="token keyword">const</span> Sales_data<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="6"></td><td><pre> <span class="token comment">// 其他成员及访问说明符与之前一致</span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token keyword">public</span><span class="token operator">:</span></pre></td></tr><tr><td data-num="8"></td><td><pre> <span class="token function">Sales_data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="9"></td><td><pre> <span class="token function">Sales_data</span><span class="token punctuation">(</span><span class="token keyword">const</span> std<span class="token double-colon punctuation">::</span>string <span class="token operator">&</span>s<span class="token punctuation">,</span> <span class="token keyword">unsigned</span> n<span class="token punctuation">,</span> <span class="token keyword">double</span> p<span class="token punctuation">)</span><span class="token operator">:</span></pre></td></tr><tr><td data-num="10"></td><td><pre> <span class="token function">bookNo</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">units_sold</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">revenue</span><span class="token punctuation">(</span>p<span class="token operator">*</span>n<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="11"></td><td><pre> <span class="token function">Sales_data</span><span class="token punctuation">(</span><span class="token keyword">const</span> std<span class="token double-colon punctuation">::</span>string <span class="token operator">&</span>s<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token function">bookNo</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="12"></td><td><pre> <span class="token function">Sales_data</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>istream<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="13"></td><td><pre> std<span class="token double-colon punctuation">::</span>string <span class="token function">isbn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> bookNo<span class="token punctuation">;</span> <span class="token punctuation">}</span></pre></td></tr><tr><td data-num="14"></td><td><pre> Sales_data <span class="token operator">&</span><span class="token function">combine</span><span class="token punctuation">(</span><span class="token keyword">const</span> Sales_data<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="15"></td><td><pre><span class="token keyword">private</span><span class="token operator">:</span></pre></td></tr><tr><td data-num="16"></td><td><pre> std<span class="token double-colon punctuation">::</span>string bookNo<span class="token punctuation">;</span></pre></td></tr><tr><td data-num="17"></td><td><pre> <span class="token keyword">unsigned</span> units_sold <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="18"></td><td><pre> <span class="token keyword">double</span> revenue <span class="token operator">=</span> <span class="token number">0.0</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="19"></td><td><pre><span class="token punctuation">}</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="20"></td><td><pre></pre></td></tr><tr><td data-num="21"></td><td><pre><span class="token comment">// Sales_data 接口的非成员组成部分的声明</span></pre></td></tr><tr><td data-num="22"></td><td><pre>Sales_data <span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">const</span> Sales_data<span class="token operator">&</span><span class="token punctuation">,</span> <span class="token keyword">const</span> Sales_data<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="23"></td><td><pre>std<span class="token double-colon punctuation">::</span>istream <span class="token operator">&</span><span class="token function">read</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>istream<span class="token operator">&</span><span class="token punctuation">,</span> Sales_data<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr><tr><td data-num="24"></td><td><pre>std<span class="token double-colon punctuation">::</span>ostream <span class="token operator">&</span><span class="token function">print</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>ostream<span class="token operator">&</span><span class="token punctuation">,</span> <span class="token keyword">const</span> Sales_data<span class="token operator">&</span><span class="token punctuation">)</span><span class="token punctuation">;</span></pre></td></tr></tbody></table></figure><p>友元声明只能出现在类定义的内部,但是在类内出现的具体位置不限。友元不是类的成员也不受它所在区域访问控制级别的约束</p>
<blockquote>
<p>一般来说,最好在类定义开始或结束前的位置集中声明友元</p>
</blockquote>
<h3 id="友元的声明"><a class="anchor" href="#友元的声明">#</a> 友元的声明</h3>
<p>友元的声明仅仅指定了访问的权限,而非一个通常意义上的函数声明</p>
<p>如果我们希望类的用户能够调用某个友元函数,那么我们就必须在友元声明之外再专门对函数进行一次声明</p>
<p>为了使友元对类的用户可见,我们通常把友元的声明与类本身放置在同一个头文件中(类的外部)</p>
<p>因此,我们的 Sales_data 头文件应该为 read 、print 和 add 提供独立的声明(除了类内部的友元声明之外)</p>
<blockquote>
<p>许多编译器并未强制限定友元函数必须在使用之前在类的外部声明</p>
</blockquote>
<p>一些编译器允许在尚无友元函数的初始声明的情况下就调用它,不过最好还是提供一个独立的函数声明</p>
<h2 id="封装"><a class="anchor" href="#封装">#</a> 封装</h2>
<p>封装有两个重要的优点:</p>
<ul>
<li>确保用户代码不会无意间破坏封装对象的状态</li>
<li>被封装的类的具体实现细节可以随时改变,而无须调整用户级别的代码</li>
</ul>
<p>一旦把数据成员定义成 <code>private</code> 的,类的作者就可以比较自由地修改数据了</p>
<ul>
<li>当实现部分改变时,我们只需要检查类的代码本身以确认这次改变有什么影响。换句话说,只要类的接口不变,用户代码就无须改变</li>
<li>如果数据是 <code>public</code> 的,所有使用了原来数据成员的代码都可能失效,这时我们必须定位并重写所有依赖于老版本实现的代码,之后才能重新使用该程序</li>
</ul>
<p>把数据成员的访问权限设成 <code>private</code> 还有另外一个好处:防止由于用户的原因造成数据被破坏。如果我们发现有程序缺陷破坏了对象的状态,则可以在有限的范围内定位缺陷</p>
<blockquote>
<p>尽管当类的定义发生改变时无须更改用户代码,但是使用了该类的源文件必须重新编译</p>
</blockquote>
<p>参考:C++ Primer 中文版(第 5 版)</p>
</div><footer><div class="meta"><span class="item"><span class="icon"><i class="ic i-calendar-check"></i></span><span class="text">更新于</span><time title="修改时间:2024-06-08 23:08:08" itemprop="dateModified" datetime="2024-06-08T23:08:08+08:00">2024-06-08</time></span></div><div id="copyright"><ul><li class="author"><strong>本文作者:</strong>Jiankychen<i class="ic i-at"><em>@</em></i>Jiankychen's Blog</li><li class="link"><strong>本文链接:</strong><a href="https://jiankychen.github.io/cpp-classes.html" title="C++ 类">https://jiankychen.github.io/cpp-classes.html</a></li><li class="license"><strong>版权声明:</strong>本站所有文章除特别声明外,均采用 <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh"><i class="ic i-creative-commons"><em>(CC)</em></i>BY-NC-SA</a> 许可协议。转载请注明出处!</li></ul></div></footer></article></div><div class="post-nav"><div class="item left"><a href="/shoka-opt.html" rel="prev" itemprop="url" data-background-image="https://i.imgtg.com/2023/03/09/Y0zdB.jpg" title="shoka 主题的若干改动"><span class="type">上一篇</span><span class="category"><i class="ic i-flag"></i>Hexo</span><h3>shoka 主题的若干改动</h3></a></div><div class="item right"><a href="/leetcode-analog.html" rel="next" itemprop="url" data-background-image="https://i.imgtg.com/2023/03/09/Y0iNK.jpg" title="LeetCode - 模拟专题"><span class="type">下一篇</span><span class="category"><i class="ic i-flag"></i>Coding</span><h3>LeetCode - 模拟专题</h3></a></div></div><div class="wrap" id="comments"></div></div><div id="sidebar"><div class="inner"><div class="panels"><div class="inner"><div class="contents panel pjax" data-title="文章目录"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89%E6%8A%BD%E8%B1%A1%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B"><span class="toc-number">1.</span> <span class="toc-text"> 定义抽象数据类型</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%AE%BE%E8%AE%A1-sales_data-%E7%B1%BB"><span class="toc-number">1.1.</span> <span class="toc-text"> 设计 Sales_data 类</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0"><span class="toc-number">1.1.1.</span> <span class="toc-text"> 定义成员函数</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#this"><span class="toc-number">1.1.1.1.</span> <span class="toc-text"> this</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#const-%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0"><span class="toc-number">1.1.1.2.</span> <span class="toc-text"> const 成员函数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%B1%BB%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0"><span class="toc-number">1.1.1.3.</span> <span class="toc-text"> 类作用域和成员函数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%9C%A8%E7%B1%BB%E7%9A%84%E5%A4%96%E9%83%A8%E5%AE%9A%E4%B9%89"><span class="toc-number">1.1.1.4.</span> <span class="toc-text"> 在类的外部定义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89%E4%B8%80%E4%B8%AA%E8%BF%94%E5%9B%9E-this-%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%87%BD%E6%95%B0"><span class="toc-number">1.1.1.5.</span> <span class="toc-text"> 定义一个返回 this 对象的函数</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89%E7%B1%BB%E7%9B%B8%E5%85%B3%E7%9A%84%E9%9D%9E%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0"><span class="toc-number">1.1.2.</span> <span class="toc-text"> 定义类相关的非成员函数</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0"><span class="toc-number">1.2.</span> <span class="toc-text"> 构造函数</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%90%88%E6%88%90%E7%9A%84%E9%BB%98%E8%AE%A4%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0"><span class="toc-number">1.2.1.</span> <span class="toc-text"> 合成的默认构造函数</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0"><span class="toc-number">1.2.2.</span> <span class="toc-text"> 定义构造函数</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#default"><span class="toc-number">1.2.2.1.</span> <span class="toc-text"> = default</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E5%88%9D%E5%A7%8B%E5%80%BC%E5%88%97%E8%A1%A8"><span class="toc-number">1.2.2.2.</span> <span class="toc-text"> 构造函数初始值列表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%9C%A8%E7%B1%BB%E7%9A%84%E5%A4%96%E9%83%A8%E5%AE%9A%E4%B9%89%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0"><span class="toc-number">1.2.2.3.</span> <span class="toc-text"> 在类的外部定义构造函数</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%8B%B7%E8%B4%9D-%E8%B5%8B%E5%80%BC%E5%92%8C%E6%9E%90%E6%9E%84"><span class="toc-number">1.3.</span> <span class="toc-text"> 拷贝、赋值和析构</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E8%AE%BF%E9%97%AE%E6%8E%A7%E5%88%B6%E4%B8%8E%E5%B0%81%E8%A3%85"><span class="toc-number">2.</span> <span class="toc-text"> 访问控制与封装</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8F%8B%E5%85%83"><span class="toc-number">2.1.</span> <span class="toc-text"> 友元</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%8B%E5%85%83%E7%9A%84%E5%A3%B0%E6%98%8E"><span class="toc-number">2.1.1.</span> <span class="toc-text"> 友元的声明</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%B0%81%E8%A3%85"><span class="toc-number">2.2.</span> <span class="toc-text"> 封装</span></a></li></ol></li></ol></div><div class="related panel pjax" data-title="系列文章"><ul><li><a href="/introduce-cpp.html" rel="bookmark" title="初识 C++">初识 C++</a></li><li><a href="/cpp-variables.html" rel="bookmark" title="C++ 变量和基本类型">C++ 变量和基本类型</a></li><li><a href="/cpp-vectors.html" rel="bookmark" title="C++ 字符串、向量和数组">C++ 字符串、向量和数组</a></li><li><a href="/cpp-experssions.html" rel="bookmark" title="C++ 表达式">C++ 表达式</a></li><li><a href="/cpp-statement.html" rel="bookmark" title="C++ 语句">C++ 语句</a></li><li><a href="/cpp-functions.html" rel="bookmark" title="C++ 函数">C++ 函数</a></li><li class="active"><a href="/cpp-classes.html" rel="bookmark" title="C++ 类">C++ 类</a></li></ul></div><div class="overview panel" data-title="站点概览"><div class="author" itemprop="author" itemscope="itemscope" itemtype="http://schema.org/Person"><img class="image" loading="lazy" decoding="async" itemprop="image" alt="Jiankychen" src="/assets/avatar.webp"><p class="name" itemprop="name">Jiankychen</p><div class="description" itemprop="description"></div></div><nav class="state"><div class="item posts"><a href="/archives/"><span class="count">51</span><span class="name">文章</span></a></div><div class="item categories"><a href="/categories/"><span class="count">8</span><span class="name">分类</span></a></div><div class="item tags"><a href="/tags/"><span class="count">20</span><span class="name">标签</span></a></div></nav><div class="social"><a target="_blank" rel="noopener" href="https://github.com/jiankychen" class="item github" title="https://github.com/jiankychen"><i class="ic i-github"></i></a><a href="mailto:[email protected]" class="item email" title="mailto:[email protected]"><i class="ic i-envelope"></i></a><a target="_blank" rel="noopener" href="https://music.163.com/#/user/home?id=447771275" class="item music" title="https://music.163.com/#/user/home?id=447771275"><i class="ic i-cloud-music"></i></a><a target="_blank" rel="noopener" href="https://www.zhihu.com/people/jiankychen" class="item zhihu" title="https://www.zhihu.com/people/jiankychen"><i class="ic i-zhihu"></i></a></div><div class="menu"><li class="item"><a href="/" rel="section"><i class="ic i-home"></i>首页</a></li><li class="item dropdown"><a href="#" onclick="return false;"><i class="ic i-feather"></i>文章</a><ul class="submenu"><li class="item"><a href="/archives/" rel="section"><i class="ic i-list-alt"></i>归档</a></li><li class="item"><a href="/categories/" rel="section"><i class="ic i-th"></i>分类</a></li><li class="item"><a href="/tags/" rel="section"><i class="ic i-tags"></i>标签</a></li></ul></li><li class="item dropdown"><a href="#" onclick="return false;"><i class="ic i-feather"></i>链接</a><ul class="submenu"><li class="item"><a href="/peers/" rel="section"><i class="ic i-magic"></i>链环</a></li><li class="item"><a href="/friends/" rel="section"><i class="ic i-heart"></i>友链</a></li></ul></li><li class="item dropdown"><a href="#" onclick="return false;"><i class="ic i-stars"></i>关于</a><ul class="submenu"><li class="item"><a href="/owner/" rel="section"><i class="ic i-user"></i>关于博主</a></li><li class="item"><a href="/site/" rel="section"><i class="ic i-paw"></i>关于本站</a></li><li class="item"><a href="/update/" rel="section"><i class="ic i-cloud"></i>更新日志</a></li></ul></li></div></div></div></div><ul id="quick"><li class="prev pjax"><a href="/leetcode-analog.html" rel="prev" title="上一篇"><i class="ic i-chevron-left"></i></a></li><li class="up"><i class="ic i-arrow-up"></i></li><li class="down"><i class="ic i-arrow-down"></i></li><li class="next pjax"><a href="/shoka-opt.html" rel="next" title="下一篇"><i class="ic i-chevron-right"></i></a></li><li class="percent"></li></ul></div></div><div class="dimmer"></div></div></main><footer id="footer"><div class="inner"><div class="widgets"><div class="rpost pjax"><h2>随机文章</h2><ul><li class="item"><div class="breadcrumb"><a href="/categories/Coding/" title="分类于Coding">Coding</a></div><span><a href="/leetcode-hashtable.html">LeetCode - 哈希表专题</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Tutorial/" title="分类于Tutorial">Tutorial</a><i class="ic i-angle-right"></i><a href="/categories/Tutorial/LaTeX/" title="分类于LaTeX">LaTeX</a></div><span><a href="/latex-to-word.html">LaTeX 转 Word</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Data-Structure/" title="分类于Data Structure">Data Structure</a></div><span><a href="/KMP.html">KMP 算法</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Coding/" title="分类于Coding">Coding</a></div><span><a href="/leetcode-stacks&queues.html">LeetCode - 栈与队列专题</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Data-Structure/" title="分类于Data Structure">Data Structure</a></div><span><a href="/binary-search.html">二分查找</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Data-Structure/" title="分类于Data Structure">Data Structure</a></div><span><a href="/hash-table.html">哈希表</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Python/" title="分类于Python">Python</a></div><span><a href="/python-container.html">Python 数据容器</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Data-Structure/" title="分类于Data Structure">Data Structure</a></div><span><a href="/recursive.html">递推与递归</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Coding/" title="分类于Coding">Coding</a></div><span><a href="/leetcode-monotonicstacks.html">LeetCode - 单调栈专题</a></span></li><li class="item"><div class="breadcrumb"><a href="/categories/Coding/" title="分类于Coding">Coding</a></div><span><a href="/leetcode-search.html">LeetCode - 搜索专题</a></span></li></ul></div><div class="rpost pjax"><h2>最新评论</h2><ul class="leancloud-recent-comment" id="new-comment"></ul></div></div><div class="status"><div class="copyright">© 2021 -<span itemprop="copyrightYear">2024</span><span class="with-love"><i class="ic i-sakura rotate"></i></span><span class="author" itemprop="copyrightHolder">Jiankychen @ Jiankychen</span></div><div class="count"><span class="post-meta-item-icon"><i class="ic i-chart-area"></i></span><span title="站点总字数">955k 字</span><span class="post-meta-divider"> | </span><span class="post-meta-item-icon"><i class="ic i-coffee"></i></span><span title="站点阅读时长">14:28</span></div><div class="powered-by">基于 <a target="_blank" rel="noopener" href="https://hexo.io/">Hexo</a> & Theme.<a target="_blank" rel="noopener" href="https://github.com/theme-shoka-x/hexo-theme-shokaX/">ShokaX</a></div></div><script src="https://unpkg.com/[email protected]/bsz.pure.mini.js"></script><div id="busuanzi-wrap"><span class="ic i-eye"></span><span id="busuanzi_container_site_pv">本站总访问量 <span id="busuanzi_value_site_pv"></span> 次</span> | <span class="ic i-user"></span><span id="busuanzi_container_site_uv">本站总访客量 <span id="busuanzi_value_site_uv"></span> 次</span></div></div></footer></div><script data-config="" type="text/javascript">var LOCAL = {
ispost: true,
path: `/cpp-classes`,
favicon: {
show: `Jiankychen`,
hide: `Jiankychen`
},
search: {
placeholder: "文章搜索",
empty: "关于 「 ${query} 」,什么也没搜到",
stats: "${time} ms 内找到 ${hits} 条结果"
},
copy_tex: true,
katex: true,
mermaid: false,
audio: undefined,
fancybox: true,
nocopy: false,
outime: true,
template: `<div class="note warning"><p><span class="label warning">文章时效性提示</span><br>这是一篇发布于 {{publish}} 天前,最后一次更新在 {{updated}} 天前的文章,部分信息可能已经发生改变,请注意甄别。</p></div>`,
quiz: {
choice: `单选题`,
multiple: `多选题`,
true_false: `判断题`,
essay: `问答题`,
gap_fill: `填空题`,
mistake: `错题备注`
},
ignores: [
(uri) => uri.includes('#'),
(uri) => new RegExp(LOCAL.path + '$').test(uri),
[]
]
};
</script><script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-6-M/pace/1.2.4/pace.min.js" async=""></script><script src="https://polyfill.io/v3/polyfill.min.js?features=default,fetch" defer=""></script><script src="/js/siteInit.js?v=0.4.2" type="module" fetchpriority="high" defer=""></script></body></html>