Skip to content

Commit 5031d52

Browse files
committed
book: cpp11: add inherited constructors
1. add cpp11 - inherited constructors 2. add online book
1 parent ea21ff7 commit 5031d52

File tree

4 files changed

+237
-12
lines changed

4 files changed

+237
-12
lines changed

.github/workflows/online-ebook.yml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Sample workflow for building and deploying a mdBook site to GitHub Pages
2+
#
3+
# To get started with mdBook see: https://rust-lang.github.io/mdBook/index.html
4+
#
5+
name: Deploy Online EBook site to Pages
6+
7+
on:
8+
# Runs on pushes targeting the default branch
9+
push:
10+
branches: ["main"]
11+
paths:
12+
- 'book/src/**'
13+
14+
# Allows you to run this workflow manually from the Actions tab
15+
workflow_dispatch:
16+
17+
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
18+
permissions:
19+
contents: read
20+
pages: write
21+
id-token: write
22+
23+
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
24+
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
25+
concurrency:
26+
group: "pages"
27+
cancel-in-progress: false
28+
29+
jobs:
30+
# Build job
31+
build:
32+
runs-on: ubuntu-latest
33+
34+
defaults:
35+
run:
36+
working-directory: book
37+
38+
env:
39+
MDBOOK_VERSION: 0.4.43
40+
41+
steps:
42+
- uses: actions/checkout@v4
43+
- name: Install mdBook by Xlings
44+
run: |
45+
curl -fsSL https://d2learn.org/xlings-install.sh | bash
46+
xlings install mdbook@${MDBOOK_VERSION} --global -y
47+
- name: Setup Pages
48+
id: pages
49+
uses: actions/configure-pages@v4
50+
- name: Build with mdBook
51+
run: |
52+
export PATH=/home/xlings/.xlings_data/bin:$PATH
53+
mdbook build
54+
- name: Upload artifact
55+
uses: actions/upload-pages-artifact@v3
56+
with:
57+
path: ./book/book
58+
59+
# Deployment job
60+
deploy:
61+
environment:
62+
name: github-pages
63+
url: ${{ steps.deployment.outputs.page_url }}
64+
runs-on: ubuntu-latest
65+
needs: build
66+
steps:
67+
- name: Deploy to GitHub Pages
68+
id: deployment
69+
uses: actions/deploy-pages@v4

README.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</div>
88

99
<div align="center">
10-
<a href="book/src/chapter_1.md" target="_blank">电子书</a>
10+
<a href="https://sunrisepeak.github.io/mcpp-standard" target="_blank">在线电子书</a>
1111
|
1212
<a href="https://www.bilibili.com/video/BV182MtzPEiX" target="_blank">介绍</a>
1313
|
@@ -22,21 +22,19 @@
2222

2323
---
2424

25-
## 更新日志
25+
## 更新日志 - [ 2025/08/28 ]
2626

2727
> 持续更新中
2828
29-
**cpp11 - 10 - 委托构造函数**
29+
**C++11 - 11 - 继承造函数**
3030

31-
- **[video](https://www.bilibili.com/video/BV1zft3zSEER) / [anim-code](https://github.com/Sunrisepeak/mcpp-standard/blob/main/videos/cpp11/10-delegating-constructors.py)** - 2025/08/04
32-
- **[code-0](https://github.com/Sunrisepeak/mcpp-standard/blob/main/dslings/cpp11/10-delegating-constructors-0.cpp) / [code-1](https://github.com/Sunrisepeak/mcpp-standard/blob/main/dslings/cpp11/10-delegating-constructors-1.cpp)** - 2025/08/02
33-
- **[book](https://github.com/Sunrisepeak/mcpp-standard/blob/main/book/src/cpp11/10-delegating-constructors.md)** - 2025/08/01
31+
- **[book](https://sunrisepeak.github.io/mcpp-standard/cpp11/11-inherited-constructors.html)** - 2025/08/28
32+
- **video / code** - todo
3433

35-
**获取最新内容并进入自动检测**
34+
**获取最新内容**
3635

3736
```bash
3837
d2x update
39-
d2x checker delegating-constructors
4038
```
4139

4240
更多 -> [changelog](https://github.com/Sunrisepeak/mcpp-standard/blob/main/book/src/changelog.md)

book/src/changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
## 2025/08
44

5+
---
6+
**C++11 - 11 - 继承造函数**
7+
8+
- **[book](https://github.com/Sunrisepeak/mcpp-standard/blob/main/book/src/cpp11/11-inherited-constructors.md)** - 2025/08/28
9+
- **video / code** - todo
10+
511
---
612
**C++11 - 10 - 委托构造函数**
713

Lines changed: 156 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,165 @@
11
# 继承构造函数
22

3-
继承构造函数 - todo
3+
继承构造函数是C++11 引入的一个语法特性 - 解决了在类继承结构中 派生类重复定义基类构造函数 的繁琐问题
44

55
| Book | Video | Code | X |
66
| --- | --- | --- | --- |
7-
| [cppreference](https://en.cppreference.com/w/cpp/language/using_declaration.html#Inheriting_constructors) / [markdown](https://github.com/Sunrisepeak/mcpp-standard/blob/main/book/src/cpp11/10-delegating-and-inherited-constructors.md) | [视频解读]() | [练习代码]() | |
7+
| [cppreference](https://en.cppreference.com/w/cpp/language/using_declaration.html#Inheriting_constructors) / [markdown](https://github.com/Sunrisepeak/mcpp-standard/blob/main/book/src/cpp11/11-inherited-constructors.md) | [视频解读]() | [练习代码]() | |
88

99
**为什么引入?**
1010

11-
- ...
11+
- 减少重复代码, 避免手动转发
12+
- 提高代码的表达能力
1213

13-
## 一、基础用法和场景
14+
## 一、基础用法和场景
15+
16+
### 复用基类的构造函数
17+
18+
**继承构造函数**这个特性引入之前, 即使基类和派生的构造函数形式没有任何区别, 也需要重新定义, 然后通过手动把参数转发到基类, 这不仅造成了一定程度的代码重复, 而且也不够简洁。例如, 下面的`MyObject`就对每个`Base`中的构造函数做了重新实现
19+
20+
```cpp
21+
class ObjectBase {
22+
//...
23+
public:
24+
ObjectBase(int) {}
25+
ObjectBase(double) {}
26+
};
27+
28+
class MyObject : public ObjectBase {
29+
public:
30+
MyObject(int x) : ObjectBase(x) {}
31+
MyObject(double y) : ObjectBase(y) {}
32+
//...
33+
};
34+
```
35+
36+
而用这个特性, 通过`using ObjectBase::ObjectBase;`直接就可以继承基类中的构造函数, 不仅可以避免这个过程, 同代码也变的更简洁了
37+
38+
```cpp
39+
class MyObject : public ObjectBase {
40+
public:
41+
using ObjectBase::ObjectBase;
42+
//...
43+
};
44+
```
45+
46+
这里需要注意的是, **构造函数继承** 的编译期隐式代码生成, 不仅仅是对构造函数的"单纯"复制, 而且在派生类中还有类似"自动重命名的效果 `ObjectBase` -> `MyObject` "。即:
47+
48+
```cpp
49+
class MyObject : public ObjectBase {
50+
public:
51+
// 可能的生成代码
52+
MyObject(int x) : ObjectBase(x) {}
53+
MyObject(double y) : ObjectBase(y) {}
54+
};
55+
```
56+
57+
### 临时扩展功能用于测试
58+
59+
对一些类型做测试或调试时, 我们常常期望可以使用像`to_string()`之类的一些接口。如果不方便直接修改源代码, 这个时候 就可以使用 **继承构造函数** 的性质来追加一些方便调试的接口函数。例如下面有个`Student`类:
60+
61+
```cpp
62+
class Student {
63+
protected:
64+
//...
65+
double score;
66+
public:
67+
string id;
68+
string name;
69+
uint age;
70+
71+
Student(string id, string name);
72+
Student(string id, string name, uint age);
73+
Student(string id, ...);
74+
};
75+
```
76+
在对其测试时, 通过实现`StudentTest`并增加一些辅助测试的函数, 这样更方便测试代码的编写。
77+
78+
```cpp
79+
class StudentTest : public Student {
80+
public:
81+
using Student::Student;
82+
83+
std::string to_string() const {
84+
return "{ id: " + id + ", name: " + name
85+
+ ", age: " + std::to_string(age) + " }";
86+
}
87+
88+
void dump() const { /* 一些成绩细节 ... */ }
89+
void assert_valid() const {
90+
assert(score >= 0 && score <= 100);
91+
// ...
92+
}
93+
};
94+
```
95+
96+
其中需要注意的是, 在继承`Student`的同时, 也**继承了构造函数**。所以, 他们具有相同的内部布局、构造方式及接口, 从而实现了:
97+
98+
- 保证了, 使用上的一致性和间接测试的有效性
99+
- 不修改源码, 做了测试相关功能的扩展, 更方便代码的测试
100+
- 相对外部为其编写测试函数, 能访问到被保护的数据成员(一般建议以只读方式访问)
101+
102+
其实, 对于很多 "不改变类数据结构的前提下, 来扩展只读行为或工具函数的很多场景", **继承构造函数**都用发挥其作用
103+
104+
### 泛型装饰器和行为约束
105+
106+
**继承构造函数**不仅可以用于普通的继承中, 他还可以用于模板类型。例如, 下面定义的`NoCopy`中, 使用了`using T::T`对泛型T中的构造函数做继承。他的作用是在不改变目标对象的内存布局和使用接口下, 做一定的行为约束
107+
108+
```cpp
109+
template <typename T>
110+
class NoCopy : public T {
111+
public:
112+
using T::T;
113+
114+
NoCopy(const NoCopy&) = delete;
115+
NoCopy& operator=(const NoCopy&) = delete;
116+
};
117+
```
118+
119+
在一些模块或场景中, 我们期望再对象创想创建后, 不能再复制的方式创建其他对象时, 就可以在定义时使用这个`NoCopy`装饰器/包装器, 通过包装器中的`delete`显示告诉编译器删除了**拷贝构造**和**拷贝赋值**, 也意味着对象不在拥有**拷贝语义**。例如:
120+
121+
```cpp
122+
class Point {
123+
double mX, mX;
124+
public:
125+
Point() : mX { 0 }, mY { 0 } { }
126+
Point(double x, double y) : mX { x }, mY { y } { }
127+
128+
string to_string() const {
129+
return "{ " + std::to_string(mX)
130+
+ ", " + std::to_string(mY) + " }";
131+
}
132+
};
133+
134+
Point p1(1, 2);
135+
NoCopy<Point> p2(2, 3);
136+
137+
```
138+
139+
这个时候`p1``p2`在接口的使用上都是一样的, 但是`p2`相对`p1`就少了可拷贝的属性
140+
141+
```cpp
142+
p1.to_string(); // ok
143+
p2.to_string(); // ok
144+
145+
auto p3 = p1; // ok (拷贝构造)
146+
auto p4 = p2; // error (不能拷贝)
147+
```
148+
149+
## 二、注意事项
150+
151+
### 优先考虑继承还是组合
152+
153+
由于本章是介绍**继承构造函数**的特性和使用方式, 它是和**继承**性质绑定的。所以, 从实现上是倾向用继承的方式来实现的。 但是从于目标功能上考虑, 往往使用继承和组合都是可以实现的, 他们更偏向是手段而不是目的, 所以选择需要结合具体的应用场景。
154+
155+
例如, 对于一些测试环境, 或**仅功能函数扩展, 无数据结构变动**的场景下, 使用继承配合**继承构造函数**是比较方便的, 还可以避免大量的函数转发。但是, 对于一些 要对少量特定接口做"拦截"或较复杂的场景, 现在(2025)主流是更倾向用组合代替继承的
156+
157+
- 复杂场景或要加一个中间层做特殊处理 -> 一般组合优于继承
158+
- 简单功能扩展, 且需保留接口使用的一致 -> 一般继承优于组合
159+
160+
## 三、其他
161+
162+
- [交流讨论](https://forum.d2learn.org/category/20)
163+
- [mcpp-standard教程仓库](https://github.com/Sunrisepeak/mcpp-standard)
164+
- [教程视频列表](https://space.bilibili.com/65858958/lists/5208246)
165+
- [教程支持工具-xlings](https://github.com/d2learn/xlings)

0 commit comments

Comments
 (0)