Skip to content

Commit 5fdd5fd

Browse files
committed
cpp11: 11: add anim code and update book for inherited-constructors
1 parent e5580be commit 5fdd5fd

File tree

2 files changed

+426
-9
lines changed

2 files changed

+426
-9
lines changed

book/src/cpp11/11-inherited-constructors.md

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,18 @@ public:
5454
};
5555
```
5656
57-
### 临时扩展功能用于测试
57+
### 类型的功能扩展
58+
59+
在很多特殊的场景下, 我们可能只想给某个类型追加额外的行为/方法, 而不改变其构造行为。这个时候就可使用继承构造
60+
61+
```cpp
62+
class ObjectXXX : public Object {
63+
public:
64+
using Object::Object;
65+
66+
void your_method() { /* ... */ }
67+
};
68+
```
5869

5970
对一些类型做测试或调试时, 我们常常期望可以使用像`to_string()`之类的一些接口。如果在不方便直接修改源代码的情况下, 就可以使用 **继承构造函数** 的性质创建一个"具有一样接口"的新类型, 并追加一些方便调试的接口函数, 从而在有更方便的调试函数下实现间接测试。例如下面有个`Student`类:
6071

@@ -73,10 +84,10 @@ public:
7384
Student(string id, ...);
7485
};
7586
```
76-
在对其测试时, 通过实现`StudentTest`并增加一些辅助测试的函数, 这样更方便测试代码的编写。
87+
通过实现`StudentDebug`并增加一些辅助函数, 这样更方便来获取调试信息
7788
7889
```cpp
79-
class StudentTest : public Student {
90+
class StudentDebug : public Student {
8091
public:
8192
using Student::Student;
8293
@@ -93,17 +104,60 @@ public:
93104
};
94105
```
95106

96-
其中需要注意的是, 在继承`Student`的同时, 也**继承了构造函数**。所以, 他们具有相同的内部布局、构造方式及接口, 从而实现了:
107+
同时, 在使用StudentDebug的时候, 不管是对象的创建还有原方法的使用都和Student保持了一致。所以对于这种 **只是增加行为, 而不改变原类型对象的构造形式的需求**, 使用继承构造能很大程度的简化代码
108+
109+
> 注: 一般这种方式可以保持同基类一样的 对象构造 + 行为/方法调用形式。但并不一定有一样的内存布局(例如新增虚方法), 并且类型判断上(RTTI)是不相等的
110+
111+
### 异常或错误类型标识和转发
97112

98-
- 保证了, 使用上的一致性和间接测试的有效性
99-
- 不修改源码, 做了测试相关功能的扩展, 更方便代码的测试
100-
- 相对外部为其编写测试函数, 能访问到被保护的数据成员(一般建议以只读方式访问)
113+
在错误和异常处理时, 我们可以只定义一个基础的错误类型
101114

102-
其实, 对于很多 "不改变类数据结构的前提下, 来扩展只读行为或工具函数的很多场景", **继承构造函数**都用发挥其作用
115+
```cpp
116+
class ErrorBase {
117+
public:
118+
ErrorBase() { }
119+
ErrorBase(const char *) { }
120+
ErrorBase(std::string) { }
121+
//...
122+
};
123+
```
124+
125+
在定义多个标识场景的错误类型时, 通过使用继承构造函数, 可以轻松的让他们保持和基础错误类型一样的构造形式。例如:
126+
127+
```cpp
128+
class ConfigError : public ErrorBase {
129+
public:
130+
using ErrorBase::ErrorBase;
131+
};
132+
133+
class RuntimeError : public ErrorBase {
134+
public:
135+
using ErrorBase::ErrorBase;
136+
};
137+
138+
class IoError : public ErrorBase {
139+
public:
140+
using ErrorBase::ErrorBase;
141+
};
142+
```
143+
144+
每个场景的错误, 对应一个错误类型, 不仅保持了错误对象构造的统一, 也非常适合配合C++的重载机制做错误类型的自动转发和处理。例如, 我们可以给每个错误类型实现对应的处理函数, 没有实现的类型将会使用基础类型对应的处理函数, 非常像很多编程语言中异常捕获和处理的设计。例如下面自定义的错误处理器:
145+
146+
```cpp
147+
148+
struct MyErrProcessor {
149+
static void process(ErrorBase err) { /* 基础处理 */ }
150+
static void process(ConfigError err) { /* 配置错误处理 */ }
151+
// ...
152+
};
153+
154+
MyErrProcessor::process(errObj); // 自动匹配对应的错误处理函数
155+
156+
```
103157
104158
### 泛型装饰器和行为约束
105159
106-
**继承构造函数**不仅可以用于普通的继承中, 他还可以用于模板类型。例如, 下面定义的`NoCopy`中, 使用了`using T::T`对泛型T中的构造函数做继承。他的作用是在不改变目标对象的内存布局和使用接口下, 做一定的行为约束
160+
**继承构造函数**不仅可以用于普通的继承中, 他还可以用于模板类型。例如, 下面定义的`NoCopy`中, 使用了`using T::T`对泛型T中的构造函数做继承。他的作用是在不改变目标对象的构造形式和使用接口下, 做一定的行为约束
107161
108162
```cpp
109163
template <typename T>

0 commit comments

Comments
 (0)