Skip to content

Commit f15121e

Browse files
committed
Reviewed Chapter 6
1 parent c0843bc commit f15121e

File tree

18 files changed

+93
-146
lines changed

18 files changed

+93
-146
lines changed

.vscode/Test.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
#include <iostream>
22
#include <type_traits>
3+
#include <string>
34
int main(){
4-
using namespace std;
5-
constexpr static const char *str {"cppHusky"};
6-
cout << (void*)str << endl;
7-
cout << &str; //这两个输出结果可是不一样的
5+
std::string s;
6+
s.assign("1");
87
return 0;
98
}

.vscode/launch.json

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,36 +24,4 @@
2424
}
2525
],
2626
"version": "2.0.0"
27-
}
28-
// {
29-
// "configurations": [
30-
31-
// {
32-
// "name": "C/C++: g++.exe build and debug active file",
33-
// "type": "cppdbg",
34-
// "request": "launch",
35-
// "program": "${fileDirname}\\${fileBasenameNoExtension}.exe",
36-
// "args": [],
37-
// "stopAtEntry": false,
38-
// "cwd": "D:\\msys64\\ucrt64\\bin",
39-
// "environment": [],
40-
// "externalConsole": false,
41-
// "MIMode": "gdb",
42-
// "miDebuggerPath": "D:\\msys64\\ucrt64\\bin\\gdb.exe",
43-
// "setupCommands": [
44-
// {
45-
// "description": "Enable pretty-printing for gdb",
46-
// "text": "-enable-pretty-printing",
47-
// "ignoreFailures": true
48-
// },
49-
// {
50-
// "description": "Set Disassembly Flavor to Intel",
51-
// "text": "-gdb-set disassembly-flavor intel",
52-
// "ignoreFailures": true
53-
// }
54-
// ],
55-
// "preLaunchTask": "C/C++: g++.exe build active file"
56-
// }
57-
// ],
58-
// "version": "2.0.0"
59-
// }
27+
}

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"string": "cpp",
6363
"iomanip": "cpp",
6464
"fstream": "cpp",
65-
"sstream": "cpp"
65+
"sstream": "cpp",
66+
"*.tcc": "cpp"
6667
}
6768
}

code_in_book/6.1/list.exe

132 KB
Binary file not shown.

generalized_parts/06_custom_types_and_their_use.tex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ \chapter{自定义类型及其使用}
44
int n {0x63794b37}; //这是一个16进制字面量,其10进制为1668893495
55
cout << (char*)&n; //以字符串形式输出
66
\end{lstlisting}
7-
在某个环境下\footnote{不同环境下给出的结果不尽相同},它的输出是\\\noindent\rule{\linewidth}{.2pt}\texttt{
8-
7KycT�\$��
7+
这是一个未定义行为,其运行结果因环境而异,无法预测。它的输出可能是\\\noindent\rule{\linewidth}{.2pt}\texttt{
8+
7KycTu\$\%p)
99
}\\\noindent\rule{\linewidth}{.2pt}
1010
为什么把 \lstinline@int*@ 类型转换成 \lstinline@char*@ 类型之后再输出就会得到这么奇怪的内容呢?原因就出在类型上。\par
1111
\begin{figure}[htbp]
1212
\centering
1313
\includegraphics[width=\textwidth]{../images/generalized_parts/06_0_1_string_to_int_or_char.drawio.png}
1414
\caption{对同一段比特串,不同类型会解读出不同内容}
1515
\end{figure}
16-
内存中的32个比特如图所示。如果用 \lstinline@int@ 类型去解读它,就会得到 \lstinline@1668893495@;而如果用 \lstinline@char@ 类型去解读它,就会分别得到四个字符 \lstinline@'7'@, \lstinline@'K'@, \lstinline@'y'@, \lstinline@'c'@。而当我们以 \lstinline@char*@ 类型输出时,因为这里没有结束符 \lstinline@'\0'@,所以它还会继续输出后面内存中的无意义内容,直到碰到结束符为止。\par
16+
内存中的32个比特如图所示。如果用 \lstinline@int@ 类型去解读它,就会得到 \lstinline@1668893495@;而如果用 \lstinline@char@ 类型去解读它,就会分别得到四个字符 \lstinline@'7'@, \lstinline@'K'@, \lstinline@'y'@, \lstinline@'c'@。而当我们以 \lstinline@char*@ 类型输出时,因为这里没有结束符 \lstinline@'\0'@,所以它还会继续输出后面内存中的无意义内容,直到碰到结束符为止(这也是结束符的重要性)\par
1717
所以我们可以认为,类型就是信息与二进制编码之间的一套转换方案。同样一串二进制编码,在不同类型下会解释出不同的信息——这也就是为什么我从第一章起就在强调``类型''\par
1818
那么言归正传(刚才的内容看不懂也没关系)。在本章,我将讲解如何利用基本数据类型和上一章中讲到的复合数据类型,来自创类型。我们不需要研究到比特这个层次,更不需要讲什么编码,我们拿C++现成的类型来组合就可以了。\par
1919
\import{06_custom_types_and_their_use/}{01_enum.tex}

generalized_parts/06_custom_types_and_their_use/01_enum.tex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ \section{枚举常量\texttt{enum}}
33
\begin{lstlisting}
44
enum <枚举名> {<枚举项>=<整型常量表达式>, <枚举项>=<整型常量表达式>, ...};
55
\end{lstlisting}\par
6-
枚举名不是必须的。如果不使用枚举名,那么这些枚举项的类型就是匿名枚举,这样做相当于定义了一些整型常量。如果我们规定了名字,那它们就是具名枚举,有丰富的作用\par
7-
枚举常量都是基于整型的,它的枚举项可以隐式类型转换为整型来输出
6+
枚举名不是必须的。如果不使用枚举名,那么这些枚举项的类型就是匿名枚举,这样做相当于定义了一些整型常量。如果我们规定了名字,那它们就是具名枚举,有更丰富的作用\par
7+
枚举常量都是基于整型的,它的枚举项可以隐式类型转换为整型
88
\begin{lstlisting}
99
enum {A=1, B=3, C=5}; //不具名枚举
1010
cout << B; //输出3

generalized_parts/06_custom_types_and_their_use/02_struct.tex

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ \section{结构体\texttt{struct}}
88
\begin{itemize}
99
\item 数组类型没有排他性。它就是一个 \lstinline@int[3]@ 类型,但是有很多东西都是 \lstinline@int[3]@ 类型的,比如说三维空间坐标,或者是颜色的RGB值。如果说这些东西都算是同一类型的话,那未免有点牵强。
1010
\item 各个维度的数据没有明确的含义。\lstinline@cuboid[0]@,这个数据到底是长度,还是宽度,还是高度?这会造成困惑,所以我们需要事先约定第几个数据代表什么。这样就增加了理解成本,也容易犯错。如果我们可以直接给每个数据命名呢,岂不美哉?\footnote{这点也可以通过枚举常量作下标的方式来强行实现,不过就请读者自行尝试吧。}
11-
\item 数组对数据的包装不够彻底。它们归根到底还是三个数据,我们很难把它当成真正的``整体''来对待。比如说,函数的返回值只能是单个(整体)数据,但很明显函数不能直接返回一个数组。(我们可以让它返回一个指向数组的指针,或都是数组引用,但是那会非常麻烦)这就说明它的集成度还不够,我们需要集成度更高的方案。
11+
\item 数组对数据的包装不够彻底。它们归根到底还是三个数据,我们很难把它当成真正的``整体''来对待。比如说,函数的返回值只能是单个(整体)数据,但很明显函数不能直接返回一个数组。(我们可以让它返回一个指向数组的指针,或者是数组引用,但是那会非常麻烦)这就说明它的集成度还不够,我们需要集成度更高的方案。
1212
\item 数组只能存储同一类型的数据,这是一大硬伤。试想,如果我们描述一个人的特征,我们可能需要很多类型的数据放在一起,做成大杂烩:描述名字要用字符串类型,描述性别要用 \lstinline@bool@ 类型(或者以 \lstinline@bool@ 为枚举基的 \lstinline@Sex@),描述身高体重要用 \lstinline@float@ 类型(如果对精度要求不高),描述年龄要用 \lstinline@unsigned short@ 类型(用 \lstinline@int@ 也行),这么多类型,肯定是无法放在一个数组里的。
1313
\end{itemize}
1414
结构体能很好地解决以上困难,它是一个有排他性,集成度更高,类型支持更丰富的解决方案。有了结构体之后,我们就可以自定义数据类型,并存储我们想要存储的信息了。\par
@@ -32,16 +32,17 @@ \subsection*{定义、声明、初始化和使用}
3232
\begin{lstlisting}
3333
Cuboid cub1 {1,2,3}; //length为1,width为2,height为3
3434
\end{lstlisting}
35-
也就是说,在花括号 \lstinline@{}@ 内的初始化数据会一一对应到 \lstinline@Cuboid@ 的三个成员中。那么如何使用它们呢?我们要用到成员访问运算符 \lstinline@.@。\par
35+
也就是说,在花括号 \lstinline@{}@ 内的初始化数据会一一对应到 \lstinline@Cuboid@ 的三个成员中。\par
36+
那么如何使用它们呢?我们要用到成员访问运算符``\lstinline@.@''\par
3637
\begin{lstlisting}
3738
int volume1 {cub1.length * cub1.width * cub1.height}; //计算其体积
3839
\end{lstlisting}
3940
在这里,\lstinline@cub1.length@ 就是 \lstinline@cub1@ 的 \lstinline@length@ 成员,它的值是 \lstinline@1@;同理,\lstinline@cub1.width@ 的值就是 \lstinline@2@,\lstinline@cub1.height@ 的值就是 \lstinline@3@。所以最后会算得 \lstinline@volume1@ 的值是 \lstinline@6@。\par
40-
我们知道,同一个类型的不同数据可以存储不同的值,这是因为它们在内存中有各自的存储空间,互不干扰。对于结构体的对象来说也是如此,我可以定义若干个对象,并给它们不同的值,这时它们是互不干扰的
41+
我们知道,同一个类型的不同数据可以存储不同的值,这是因为它们在内存中有各自的存储空间,互不干扰。对于结构体的对象来说也是如此,我可以定义若干个对象,并给它们不同的值。这样 \lstinline@cub1@, \lstinline@cub2@ 和 \lstinline@cub3@ 有着各自的存储空间
4142
\begin{lstlisting}
4243
Cuboid cub2 {3,5,7}, cub3 {4,6,5}; //再定义两个Cuboid类型的对象
43-
\end{lstlisting}
44-
这就意味着 \lstinline@cub1@, \lstinline@cub2@ 和 \lstinline@cub3@ 有着各自的存储空间,互不干扰。我们可以用取地址运算符 \lstinline@&@ 来返回它的地址——也就是它存储位置中第一个字节的地址。\par
44+
\end{lstlisting}\par\pagebreak
45+
我们可以用取地址运算符 \lstinline@&@ 来返回它的地址——也就是它存储位置中第一个字节的地址。\par
4546
\begin{lstlisting}
4647
Cuboid cub1 {1,2,3}, cub2 {3,5,7}, cub3 {4,6,5};
4748
cout << sizeof (Cuboid) << endl //输出Cuboid类型的内存占用
@@ -120,7 +121,7 @@ \subsection*{结构体成员的类型}
120121
刚才的例子比较简单,\lstinline@Cuboid@ 的三个成员都是同一类型的。实际上我们可以用不同类型的数据,把它们组织到同一个结构体中。\par
121122
例如,如果要表示一个人的基本信息,我们可能需要用字符串表示名字,用 \lstinline@Sex@(上一节中自定义的枚举类型)表示性别,用 \lstinline@double@ 身高、体重,用 \lstinline@unsigned@ 表示年龄。那么我们可以这样写:
122123
\begin{lstlisting}
123-
enum Sex : bool{male, female}; //枚举基为bool
124+
enum Sex : bool {male, female}; //枚举基为bool
124125
struct PersonalInfo { //一个结构体,表示个人信息
125126
char name[33]; //字符串,表示名字
126127
const Sex sex; //性别一般是不会改变的,所以设置成const
@@ -157,15 +158,15 @@ \subsection*{结构体成员的类型}
157158
这个程序的运行结果如下:\\\noindent\rule{\linewidth}{.2pt}\texttt{
158159
John Doe,男,30岁\\
159160
身高 175.5,体重 70.2\\
160-
\\
161+
\newline
161162
Jane Smith,女,25岁\\
162163
身高 162.3,体重 55.8\\
163-
\\
164+
\newline
164165
Bob Johnson,男,35岁\\
165166
身高 180,体重 80.5
166167
}\\\noindent\rule{\linewidth}{.2pt}\par
167-
读者可能注意到 \lstinline@person.sex==male?"男":"女"@ 此段中我们使用的条件表达式。如果 \lstinline@person.sex==male@ 为 \lstinline@true@,那么就会返回 \lstinline@"男"@;否则返回 \lstinline@"女"@。\par
168-
看上去无论是内置类型还是自定义类型,我们都可以把它放到 \lstinline@struct@ 当中,构成一个结构体。那么有什么是不可以放入其中构成结构体的呢?那就是这个结构体本身!在函数定义中我们见过递归定义,但是结构体是不允许递归定义的。
168+
读者可能注意到 \lstinline@person.sex==male ? "男" : "女"@,在这里我们也选择使用条件表达式。如果\linebreak\lstinline@person.sex==male@ 为 \lstinline@true@,那么就会返回 \lstinline@"男"@;否则返回 \lstinline@"女"@。\par
169+
看上去,无论是内置类型还是自定义类型,我们都可以把它放到 \lstinline@struct@ 当中,构成一个结构体。那么有什么是不可以放入其中构成结构体的呢?那就是这个结构体本身!在函数定义中我们见过递归定义,但是结构体是不允许递归定义的。
169170
\begin{lstlisting}
170171
struct Data {
171172
int num;
@@ -180,5 +181,5 @@ \subsection*{结构体成员的类型}
180181
Data *next; //可以
181182
}
182183
\end{lstlisting}
183-
\lstinline@Data*@ 与 \lstinline@Data@ 可不是同一个类型,而且 \lstinline@Data*@ 是一个指针,它占用内存空间的大小是确定的,所以程序当然知道 \lstinline@sizeof(Data)@ 是多少,所以在我们定义 \lstinline@Data@ 对象时也就知道要使用多大的内存空间了。\par
184-
基于这个用法,我们可以写一个简单的单链表,用来存储任意量的数据。我们将会在下一节中介绍相关内容。\par
184+
\lstinline@Data*@ 与 \lstinline@Data@ \textbf{不是同一个类型}\footnote{更细致地说,\lstinline@Data*@ 不是一个自定义类型,它只是一种指向自定义类型的指针。}。\lstinline@Data*@ 是一个指针,它占用内存空间的大小是确定的,所以程序当然知道 \lstinline@sizeof(Data)@ 是多少,那么在我们定义 \lstinline@Data@ 对象时也就知道要使用多大的内存空间了。\par
185+
基于这个用法,我们可以写一个简单的单链表,用来存储任意量的数据。\par

0 commit comments

Comments
 (0)