@@ -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 {
8091public:
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
109163template <typename T>
0 commit comments