Skip to content

Commit 0341c11

Browse files
committed
Updated to Chapter 10, Section 5
1 parent e2ccb7e commit 0341c11

22 files changed

+565
-122
lines changed

code_in_book/10.1/Header.h

+28-12
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ struct Shape { //抽象基类,这个类用来表示各种几何图形
99
return rad * 180 / Pi;
1010
}
1111
virtual double perimeter()const = 0; //周长,纯虚函数
12-
virtual double area()const = 0; //周长,纯虚函数
13-
virtual ~Shape() {} //析构,虚函数
12+
virtual double area()const = 0; //面积,纯虚函数
1413
};
1514
class Triangle : public Shape { //三角形
1615
double _a;
@@ -19,8 +18,8 @@ class Triangle : public Shape { //三角形
1918
public:
2019
Triangle(double a, double b, double c)
2120
: _a {a}, _b {b}, _c {c} {} //构造函数
22-
virtual double perimeter()const { return _a + _b + _c; } //周长
23-
virtual double area()const { //面积
21+
double perimeter()const { return _a + _b + _c; } //周长
22+
double area()const { //面积
2423
double s {(_a + _b + _c) / 2};
2524
return std::sqrt(s * (s - _a) * (s - _b) * (s - _c));
2625
}
@@ -29,8 +28,8 @@ class Circle : public Shape { //圆形
2928
double _r; //圆的半径
3029
public:
3130
Circle(double r) : _r {r} {}
32-
virtual double perimeter()const { return 2 * Pi * _r; } //周长
33-
virtual double area()const { return Pi * _r * _r; } //面积
31+
double perimeter()const { return 2 * Pi * _r; } //周长
32+
double area()const { return Pi * _r * _r; } //面积
3433
};
3534
class Parallelogram_abc : public Shape { // 抽象平行四边形基类
3635
protected:
@@ -47,17 +46,34 @@ class Parallelogram : public Parallelogram_abc { //平行四边形
4746
double perimeter()const { return 2 * (_a + _b); }
4847
double area()const { return _a * _b * std::sin(Deg2Rad(_theta)); }
4948
};
50-
struct Rhombus_abc : Parallelogram_abc { //抽象菱形基类
51-
double perimeter()const { return 4 * _a; }
52-
Rhombus_abc(double a) : Parallelogram_abc {a} {} //构造函数
49+
struct Rhombus_abc : virtual public Parallelogram_abc { //抽象菱形基类
50+
Rhombus_abc(double a) : Parallelogram_abc {a} {}
51+
double perimeter()const { return 4 * _a; } //菱形、正方形通用的周长公式
52+
};
53+
struct Rectangle_abc : virtual public Parallelogram_abc { //抽象矩形基类
54+
Rectangle_abc(double a) : Parallelogram_abc {a} {}
5355
};
5456
class Rhombus : public Rhombus_abc { //菱形
5557
double _theta;
5658
public:
57-
Rhombus(double a, double theta) : Rhombus_abc {a}, _theta {theta} {}
59+
Rhombus(double a, double theta)
60+
: Parallelogram_abc {a}, Rhombus_abc {a}, _theta {theta} {}
61+
//因为Rhombus_abc虚继承自Parallelogram_abc,我们必须单独调用后者的构造函数
62+
//又因为Rhombus继承自Rhombus_abc,我们必须调用后者的构造函数
5863
double area()const { return _a * _a * std::sin(Deg2Rad(_theta)); }
5964
};
60-
struct Square : Rhombus_abc { //正方形
61-
Square(double a) : Rhombus_abc {a} {}
65+
class Rectangle : public Rectangle_abc { //矩形
66+
double _b;
67+
public:
68+
Rectangle(double a, double b)
69+
: Parallelogram_abc {a}, Rectangle_abc {a}, _b {b} {}
70+
//同上,不再赘述
71+
double perimeter()const { return 2 * (_a + _b); }
72+
double area()const { return _a * _b; }
73+
};
74+
struct Square : Rhombus_abc, Rectangle_abc { //正方形
75+
Square(double a)
76+
: Parallelogram_abc {a}, Rhombus_abc {a}, Rectangle_abc {a} {}
6277
double area()const { return _a * _a; }
78+
//perimeter函数继承自Rhombus_abc足矣,不必再写
6379
};

generalized_parts/09_class_inheritance/04_sequential_inheriance.tex

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ \subsection*{生物分类法}
6666
//...
6767
};
6868
\end{lstlisting}\par
69-
这样一来,赤狐种 \lstinline@Vulpes_vulpes@ 和家犬亚种 \lstinline@Canis_lupus_familaries@ 就在相同的特怔方面继承同一个类,而在各自的特性上又有自己的一套。就这样,面对这样错综复杂的关系,C++可以通过继承功能,把它们梳理得十分清晰。\par
69+
这样一来,赤狐种 \lstinline@Vulpes_vulpes@ 和家犬亚种 \lstinline@Canis_lupus_familaries@ 就在相同的特征方面继承同一个类,而在各自的特性上又有自己的一套。就这样,面对这样错综复杂的关系,C++可以通过继承功能,把它们梳理得十分清晰。\par
7070
\subsection*{C++流输入/输出库}
7171
C++的流输入/输出库是一个系统,包含 \lstinline@iostream@, \lstinline@fstream@, \lstinline@sstream@ 等许多库文件,\lstinline@std::istream@, \lstinline@std::ostream@ 等许多我们已经熟知的类模版或类。它们之间有一套复杂的继承关系,见图9.6。其中,\lstinline@std::istream@ 是 \lstinline@std::basic_istream@ 类模版的一个实例,定义为 \lstinline@std::basic_istream<char>@;\lstinline@std::ostream@ 是 \lstinline@std::basic_ostream@ 类模版的一个实例,定义为 \lstinline@std::basic_ostream<char>@。\par
7272
\begin{figure}[htbp]

generalized_parts/10_common_problems_in_inheritance.tex

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ \chapter{继承中的常见问题}
44
\item 继承关系中的类型转换是怎么进行的?又有什么规则?\par
55
\item 多态又是怎么一回事?虚函数到底在做什么?\par
66
\item 如果描述基类所需要的成员比描述派生类的还要多,那怎么办?\par
7-
\item 多重继承是什么?怎么理解?又有什么用\par
7+
\item 多重继承是什么?怎么理解?又要怎么用\par
88
\item 棱形继承关系中,基类的成员重复了怎么办?\par
99
\end{itemize}
1010
这些问题在实际编程中很常见,所以我们需要好好研究一下,以防将来真的用的时候你突然一拍脑门说:``哎呀,我没学过这玩意。''那就有点麻烦了。\par

generalized_parts/10_common_problems_in_inheritance/03_abstract_base_class_and_pure_virtual_function.tex

+7-11
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,14 @@ \subsection*{实操:不同的几何图形}
5757
return rad * 180 / Pi;
5858
}
5959
virtual double perimeter()const = 0; //周长,纯虚函数
60-
virtual double area()const = 0; //周长,纯虚函数
61-
virtual ~Shape() {} //析构,虚函数
60+
virtual double area()const = 0; //面积,纯虚函数
6261
};
6362
\end{lstlisting}
6463
这里的 \lstinline@Shape@ 用 \lstinline@struct@ 来定义,这样可以少写一个 \lstinline@public@ 了。\par
6564
接下来我们就用 \lstinline@Shape@ 派生其它类,如图4.5所示。\par
6665
\begin{figure}[htbp]
6766
\centering
68-
\includegraphics[width=.8\textwidth]{../images/generalized_parts/10_abstract_shape_class_300.png}
67+
\includegraphics[width=.6\textwidth]{../images/generalized_parts/10_abstract_shape_class_300.png}
6968
\caption{\lstinline@Shape@ 之下派生的各个类}
7069
\end{figure}
7170
三角形和圆形的定义很简单,没什么可说的。
@@ -77,8 +76,8 @@ \subsection*{实操:不同的几何图形}
7776
public:
7877
Triangle(double a, double b, double c)
7978
: _a {a}, _b {b}, _c {c} {} //构造函数
80-
virtual double perimeter()const { return _a + _b + _c; } //周长
81-
virtual double area()const { //面积
79+
double perimeter()const { return _a + _b + _c; } //周长
80+
double area()const { //面积
8281
double s {(_a + _b + _c) / 2};
8382
return std::sqrt(s * (s - _a) * (s - _b) * (s - _c));
8483
}
@@ -87,8 +86,8 @@ \subsection*{实操:不同的几何图形}
8786
double _r; //圆的半径
8887
public:
8988
Circle(double r) : _r {r} {}
90-
virtual double perimeter()const { return 2 * Pi * _r; } //周长
91-
virtual double area()const { return Pi * _r * _r; } //面积
89+
double perimeter()const { return 2 * Pi * _r; } //周长
90+
double area()const { return Pi * _r * _r; } //面积
9291
};
9392
\end{lstlisting}
9493
至于平行四边形这边,就要更复杂一点。我们先把抽象平行四边形基类写出来吧。
@@ -153,7 +152,4 @@ \subsection*{实操:不同的几何图形}
153152
double area()const { return _a * _a; }
154153
};
155154
\end{lstlisting}
156-
而当调用 \lstinline@Rhombus@ 或者 \lstinline@square@ 的 \lstinline@perimeter()@ 函数时,编译器自然能根据名称查找规则找到 \lstinline@Rhombus_abc::perimeter()@ 来。总之,不必为此操心。\par
157-
\subsection*{完整代码}
158-
以下是本例的完整代码。因为所有成员函数都定义在类内了,所以我只提供一个头文件。读者可以自行写源文件并验证其效果。
159-
\lstinputlisting[caption=\texttt{Header.h}]{code_in_book/10.1/Header.h}\par
155+
而当调用 \lstinline@Rhombus@ 或者 \lstinline@square@ 的 \lstinline@perimeter()@ 函数时,编译器自然能根据名称查找规则找到 \lstinline@Rhombus_abc::perimeter()@ 来。总而言之,不必为此操心。\par

generalized_parts/10_common_problems_in_inheritance/04_multiple_inheritance.tex

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
\section{多重继承}
22
在上一章中,我们看到了C++流输入/输出库中复杂的继承关系。其中的 \lstinline@std::basic_iostream@ 尤为引人注目。你没看错,这个类同时继承自 \lstinline@std::basic_ostream@ 和 \lstinline@std::basic_istream@。这种继承多个基类的操作叫做\textbf{多重继承(Multiple Inheritance)}。\par
3-
多重继承并不神秘。如果我们把继承视作是单纯的内嵌一个基类对象的话(私有继承是这样做的),那么多重继承就意味着在派生类对象中同时内嵌两个基类对象;如果我们把继承视作是共性成员的代码重用的话(公开继承是这样做的),那么多重继承就意味着派生类同时拥有这两个基类的特怔\par
3+
多重继承并不神秘。如果我们把继承视作是单纯的内嵌一个基类对象的话(私有继承是这样做的),那么多重继承就意味着在派生类对象中同时内嵌两个基类对象;如果我们把继承视作是共性成员的代码重用的话(公开继承是这样做的),那么多重继承就意味着派生类同时拥有这两个基类的特征\par
44
\lstinline@std::stringstream@ 为例,它继承自 \lstinline@std::iostream@,而 \lstinline@std::iostream@ 多重继承自 \lstinline@std::istream@ 和 \lstinline@std::ostream@,所以它同时拥有 \lstinline@>>@ 和 \lstinline@<<@ 运算符的重载。于是这个类的对象既有输入功能又有输出功能。\par
55
\begin{lstlisting}
66
std::stringstream ss;
@@ -98,7 +98,7 @@ \subsection*{菱形继承结构的重复成员}
9898
换句话说,虽然 \lstinline@Derived@ 从根本上来说继承自 \lstinline@Base@,但是因为多重继承的缘故,它实际上有了两份 \lstinline@Base::num@ 成员,一个来自基类 \lstinline@A@,另一个来自基类 \lstinline@B@。\par
9999
\begin{figure}[htbp]
100100
\centering
101-
\includegraphics[width=.5\textwidth]{../images/generalized_parts/10_diamond_inheritance_300.png}
102-
\caption{菱形继承关系下,\lstinline@Derived@ 类内嵌了两份 \lstinline@Base@ 类的对象}
101+
\includegraphics[width=.56\textwidth]{../images/generalized_parts/10_diamond_inheritance_300.png}
102+
\caption{菱形继承关系下,\lstinline@Derived@ 类内嵌了两份 \lstinline@Base@ 类对象}
103103
\end{figure}
104104
在多数情况下,这并不是我们想要的结果,而且还会引发无数的麻烦——比如说,内存空间的浪费,我们明明只需要一份数据,但多重继承为我们搞出了两份数据;再比如说,名称的歧义和冲突,我们不能单纯写成 \lstinline@d.num@,必须写成 \lstinline@d.A::num@ 或 \lstinline@d.B::num@。它们本应该相同的。而在C++中,解决这个问题的对策,就是下一节中要讲到的虚继承。\par

0 commit comments

Comments
 (0)