Skip to content

Commit 606afcb

Browse files
author
DashJay
committed
init commit
1 parent 108a43f commit 606afcb

35 files changed

+6724
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
docs/.DS_Store

.travis.yml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
dist: xenial
2+
language: python
3+
python: 3.7
4+
5+
before_install:
6+
- pip install mkdocs mkdocs-material
7+
8+
before_script:
9+
- echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
10+
- git config --global user.email [email protected]
11+
- git config --global user.name dashjay
12+
13+
script:
14+
- mkdocs build -c -d ./public
15+
16+
after_success:
17+
- DATE=$(date)
18+
- cd public
19+
- git init
20+
- git remote add origin https://dashjay:${pass}@github.com/19-20-21/19-20-21.github.io
21+
- git add .
22+
- git commit -m "${DATE}"
23+
- git push origin master -f

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# learncpp-CN
2+
23
www.learncpp.com 的中文翻译
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# 第0章 介绍并开始学习
2+
3+
## 0-1
4+
5+
还没开始写,先把架子搭建出来了。
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
# 12.7 虚基类 (Virtual base classes)
2+
3+
上一章,在[第11.7课——多重继承中](),我们留下了一个“钻石问题”。在本节中,我们将继续讨论。
4+
5+
**注意:** 本节是一个高级主题,如果需要可以跳过或略过。
6+
7+
## 钻石问题 (The diamond problem)
8+
9+
以下是上一课中的示例(使用一些构造函数)来说明菱形问题:
10+
11+
```c++
12+
class PoweredDevice
13+
{
14+
public:
15+
PoweredDevice(int power)
16+
{
17+
cout << "PoweredDevice: " << power << '\n';
18+
}
19+
};
20+
21+
class Scanner: public PoweredDevice
22+
{
23+
public:
24+
Scanner(int scanner, int power)
25+
: PoweredDevice(power)
26+
{
27+
cout << "Scanner: " << scanner << '\n';
28+
}
29+
};
30+
31+
class Printer: public PoweredDevice
32+
{
33+
public:
34+
Printer(int printer, int power)
35+
: PoweredDevice(power)
36+
{
37+
cout << "Printer: " << printer << '\n';
38+
}
39+
};
40+
41+
class Copier: public Scanner, public Printer
42+
{
43+
public:
44+
Copier(int scanner, int printer, int power)
45+
: Scanner(scanner, power), Printer(printer, power)
46+
{
47+
}
48+
};
49+
```
50+
51+
尽管你可能觉得我们得到了这样的图形:
52+
53+
![PoweredDevice](./PoweredDevice.gif)
54+
55+
如果要创建一个Copier类对象,默认情况下,您将得到PoweredDevice类的两个副本,一个来自Printer,一个来自Scanner。其结构如下:
56+
57+
![PoweredDevice2](./PoweredDevice2.gif)
58+
59+
我们可以创建一个简短的测试来说明这一点:
60+
61+
```c++
62+
int main()
63+
{
64+
Copier copier(1, 2, 3);
65+
66+
return 0;
67+
}
68+
```
69+
70+
这将产生结果
71+
72+
```bash
73+
PoweredDevice: 3
74+
Scanner: 1
75+
PoweredDevice: 3
76+
Printer: 2
77+
```
78+
79+
如你所见,PoweredDevice被构造了两次。
80+
81+
虽然这是经常需要的,但有时您可能只希望扫描仪和打印机共享PoweredDevice的一个副本。
82+
83+
## 虚基类 (Virtual base classes)
84+
85+
要共享基类,只需在派生类的继承列表中插入“virtual”关键字。这将创建所谓的虚拟基类,这意味着只有一个基类对象。基对象在继承树中的所有对象之间共享,并且只构造一次。下面是一个示例(为简单起见,不使用构造函数)演示如何使用virtual关键字创建共享基类:
86+
87+
```c++
88+
class PoweredDevice
89+
{
90+
};
91+
92+
class Scanner: virtual public PoweredDevice
93+
{
94+
};
95+
96+
class Printer: virtual public PoweredDevice
97+
{
98+
};
99+
100+
class Copier: public Scanner, public Printer
101+
{
102+
};
103+
```
104+
105+
现在,当您创建一个Copier类对象时,每个 Copier 只会得到一个 PoweredDevice 副本,该副本将由 Scanner 和 Printer 共享。
106+
107+
然而,这又引出了一个问题:如果 Scanner 和 Printer 共享PoweredDevice基类,谁负责创建它?事实证明,答案是 Copier。Copier 构造函数负责创建 PoweredDevice。因此,这允许 Copier 直接调用 **非直接 (non-immediate-parent)** 构造函数:
108+
109+
```c++
110+
#include <iostream>
111+
112+
class PoweredDevice
113+
{
114+
public:
115+
PoweredDevice(int power)
116+
{
117+
std::cout << "PoweredDevice: " << power << '\n';
118+
}
119+
};
120+
121+
class Scanner: virtual public PoweredDevice // note: PoweredDevice 现在是一个虚基类
122+
{
123+
public:
124+
Scanner(int scanner, int power)
125+
: PoweredDevice(power) // 这行是创建 Scanner 必要的,在这个例子里我们可以忽略。
126+
{
127+
std::cout << "Scanner: " << scanner << '\n';
128+
}
129+
};
130+
131+
class Printer: virtual public PoweredDevice // note: PoweredDevice 现在是一个虚基类
132+
{
133+
public:
134+
Printer(int printer, int power)
135+
: PoweredDevice(power) // 这行是创建 Printer 必要的,在这个例子里我们可以忽略。
136+
{
137+
std::cout << "Printer: " << printer << '\n';
138+
}
139+
};
140+
141+
class Copier: public Scanner, public Printer
142+
{
143+
public:
144+
Copier(int scanner, int printer, int power)
145+
: PoweredDevice(power), // PoweredDevice 在这里进行构造
146+
Scanner(scanner, power), Printer(printer, power)
147+
{
148+
}
149+
};
150+
```
151+
152+
还是用前面的例子
153+
154+
```c++
155+
int main()
156+
{
157+
Copier copier(1, 2, 3);
158+
159+
return 0;
160+
}
161+
```
162+
163+
产生结果
164+
165+
```bash
166+
PoweredDevice: 3
167+
Scanner: 1
168+
Printer: 2
169+
```
170+
171+
如您所见,PoweredDevice只构建一次。
172+
173+
有一些细节,如果我们我们必须要说明
174+
175+
**首先:** 虚基类总是在非虚基类之前创建,这确保所有基类在派生类之前创建。
176+
177+
**其次:** 请注意 Scanner 和 Printer 构造函数仍有调用 PoweredDevice 的构造函数。创建 Copier 实例时,这些构造函数调用被忽略,因为 Copier 负责创建 PoweredDevice ,而不是 Scanner 或 Printer 。但是,如果我们要创建 Scanner 或 Printer 的实例,那么调用那些构造函数,并应用常规继承规则。
178+
179+
**第三:** 如果一个类继承了一个或多个具有虚基类的类,则 **末端派生(most derived)** 的类负责构造虚基类。在本例中,Copier 继承 Printer 和 Scanner ,它们都有一个 PoweredDevice 虚拟基类。Copier 是最派生的类,负责创建 PoweredDevice 。请注意,即使在单一继承情况下也是如此:如果 Copier 是从 Printer 单独继承的,而 Printer 实际上是从 PoweredDevice 继承的,Copier 仍然负责创建 PoweredDevice 。
180+
181+
**第四:** 继承一个虚拟基类的所有类都将有一个虚拟表,即使它们通常不会有,实例化的对象会因为这个指针增加一些尺寸。
182+
183+
因为 Scanner 和 Printer 是从 PoweredDevice 虚继承出来的,所以 只有一个 PoweredDevice 子对象。Scanner 和 Printer 都需要知道如何找到这个 PoweredDevice 子对象,以便访问它的成员(因为毕竟,它们是从它派生的)。这通常是通过一些虚表来实现的(它实际上存储了从每个子类到 PoweredDevice 子对象的偏移量)。

0 commit comments

Comments
 (0)