Skip to content

Commit 684a732

Browse files
committed
book: 10: delegating constructors
Signed-off-by: sunrisepeak <speakshen@163.com>
1 parent 7cfa6bc commit 684a732

File tree

6 files changed

+241
-1
lines changed

6 files changed

+241
-1
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@
2222

2323
---
2424

25+
## 更新日志
26+
27+
> 持续更新中
28+
29+
- **book:** [委托构造函数](https://github.com/Sunrisepeak/mcpp-standard/blob/main/book/src/cpp11/10-delegating-constructors.md) - 2025/08/01
30+
- **更多 -> [changelog](https://github.com/Sunrisepeak/mcpp-standard/blob/main/book/src/changelog.md)**
31+
32+
---
33+
2534
## 目标
2635

2736
- **1.** 任何人都可以在网上**免费获取**课程资源

book/src/SUMMARY.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,12 @@
99
- [类型自动推导 - auto和decltype](./cpp11/00-auto-and-decltype.md)
1010
- [...](./cpp11/01-default-and-delete.md)
1111
- [列表初始化](./cpp11/09-list-initialization.md)
12+
- [委托构造函数](./cpp11/10-delegating-constructors.md)
13+
- [继承构造函数](./cpp11/11-inherited-constructors.md)
1214

13-
# C++14核心语言特性
15+
# C++14核心语言特性
16+
17+
# 其他
18+
19+
- [更新日志](changelog.md)
20+
- [常见问题](fqa.md)

book/src/changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# mcpp-standard更新日志
2+
3+
## 2025/08
4+
5+
- **book:** [委托构造函数](https://github.com/Sunrisepeak/mcpp-standard/blob/main/book/src/cpp11/10-delegating-constructors.md) - 2025/08/01
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# 委托构造函数
2+
3+
委托构造是C++11中引入的语法糖, 通过简单的语法, 可以在不影响性能的情况下, 来避免过多重复代码的编写, 实现构造逻辑复用
4+
5+
| Book | Video | Code | X |
6+
| --- | --- | --- | --- |
7+
| [cppreference](https://en.cppreference.com/w/cpp/language/initializer_list.html#Delegating_constructor) / [markdown](https://github.com/Sunrisepeak/mcpp-standard/blob/main/book/src/cpp11/10-delegating-constructors.md) | [视频解读]() | [练习代码]() | |
8+
9+
**为什么引入?**
10+
11+
- 构造函数重载中, 避免重复代码的编写
12+
- 方便代码的维护
13+
14+
## 一、基础用法和场景
15+
16+
### 复用构造逻辑
17+
18+
当一个类需要编写重载的构造函数时, 很容易造成大量的重复代码, 例如:
19+
20+
```cpp
21+
class Account {
22+
string id;
23+
string name;
24+
string coin;
25+
public:
26+
27+
Account(string id_) {
28+
id = id_;
29+
name = "momo";
30+
coin = "0元";
31+
}
32+
33+
Account(string id_, string name_) {
34+
id = id_;
35+
name = name_;
36+
coin = "0元";
37+
}
38+
39+
Account(string id_, string name_, int coin_) {
40+
id = id_;
41+
name = name_;
42+
coin = std::to_string(coin_) + "元";
43+
}
44+
};
45+
```
46+
47+
这里3个构造函数中的初始化代码, 很明显是重复了(实际的初始化可能要更复杂)。 有了**委托构造**的支持后, 通过**在构造函数成员初始化列表**的位置以` : Account(xxx) `的形式来委托其他更加完整实现的构造函数进行构造, 这样就可以只保留一份代码
48+
49+
```cpp
50+
class Account {
51+
string id;
52+
string name;
53+
string coin;
54+
public:
55+
56+
Account(string id_) : Account(id_, "momo") { }
57+
58+
Account(string id_, string name_) : Account(id_, name_, 0) { }
59+
60+
Account(string id_, string name_, int coin_) {
61+
id = id_;
62+
name = name_;
63+
coin = std::to_string(coin_) + "元";
64+
}
65+
};
66+
```
67+
68+
上面的两个构造函数, 通过委托构造的方式, 最后都会转发到`Account(string id_, string name_, int coin_)`
69+
70+
### 为什么更方便维护?
71+
72+
可以假设, 如果上面货币的单位或名称需要修改时, 重复的代码实现不仅没有遵循复用原则, 而且修改构造逻辑时也要重复多次的修改, 提高了维护成本
73+
74+
而通过委托构造的方式, 把构造逻辑放到了一个地方, 这样修改和维护时也变的更加方便
75+
76+
例如, 我们需要把``改成`原石`时, 只要修改一次即可
77+
78+
```cpp
79+
class Account {
80+
// ...
81+
Account(string id_, string name_, int coin_) {
82+
//...
83+
//coin = std::to_string(coin_) + "元";
84+
coin = std::to_string(coin_) + "原石";
85+
}
86+
};
87+
```
88+
89+
### 和封装成一个init函数的区别
90+
91+
一些朋友可能会想到, 如果把构造逻辑写成一个`init`函数, 不就是也可以实现代码复用的效果吗? 为什么还要搞一个新的写法, 作为特性添加到标准中. 是不是有点多余并且让C++变的更加复杂了
92+
93+
```cpp
94+
class Account {
95+
// ...
96+
97+
init(string id_, string name_, int coin_) {
98+
id = id_;
99+
name = name_;
100+
coin = std::to_string(coin_) + "元";
101+
}
102+
103+
public:
104+
105+
Account(string id_) { init(id_, "momo", 0); }
106+
107+
Account(string id_, string name_) { init(id_, name_, 0); }
108+
109+
Account(string id_, string name_, int coin_) {
110+
init(id_, name_, coin_);
111+
}
112+
};
113+
```
114+
115+
实际, 从性能角度考虑。大多数时候, 单独封装一个`init`函数的性能是低于**委托构造**的。因为成员的构造, 一般会经历两个阶段:
116+
117+
- 第一步: 执行 默认初始化 或 成员初始化列表
118+
- 第二步: 运行构造函数体中的构造逻辑
119+
120+
```cpp
121+
class Account {
122+
// ...
123+
public:
124+
125+
Account(string id_, string name_, int coin_)
126+
/* : 1 - 成员初始化列表 */
127+
{
128+
// 2 - 执行构造函数的函数体
129+
init(id_, name_, coin_);
130+
}
131+
};
132+
```
133+
134+
这就导致使用init函数, 实际上成员被"初始化"了两次, 而**委托构造**可以通过**成员初始化列表**来避免这个问题
135+
136+
```cpp
137+
class Account {
138+
// ...
139+
public:
140+
141+
Account(string id_, string name_, int coin_)
142+
: id { id_ }, name { name_ }, coin { std::to_string(coin_) + "元" }
143+
{
144+
// ...
145+
}
146+
};
147+
```
148+
149+
## 二、注意事项
150+
151+
### 临时对象误会
152+
153+
在一些不使用委托构造的场景中, 一个构造函数体中调用另外一个构造函数, 他实际只是创建了一个临时对象
154+
155+
- 调用普通函数`init`: 初始化的是本对象的成员
156+
- 调用另外一个构造函数: 在本对象外, 创建了一个新的临时对象
157+
158+
```cpp
159+
class Account {
160+
// ...
161+
public:
162+
163+
Account(string id_, string name_) {
164+
Account(id_, name_, 0); // 创建的是临时对象
165+
// init(id_, name_, 0);
166+
// this->Account(id_, name_, 0); // error
167+
}
168+
169+
Account(string id_, string name_, int coin_) {
170+
id = id_;
171+
name = name_;
172+
coin = std::to_string(coin_) + "元";
173+
}
174+
};
175+
176+
```
177+
178+
### 不能重复初始化
179+
180+
当使用**委托构造**时, 就不能使用初始化列表去初始化其他成员, 这样的限制可以避免重复的初始化, 保证了数据成员只会被初始化一次
181+
182+
例如, 如果下面的语法被允许 `coin` 将会被初始化多次且可能会造成歧义
183+
184+
```cpp
185+
class Account {
186+
// ...
187+
public:
188+
189+
Account(string id_)
190+
: Account(id_, "momo"), coin { "0元" } // error
191+
{
192+
193+
}
194+
195+
};
196+
```
197+
198+
## 三、其他
199+
200+
- [交流讨论](https://forum.d2learn.org/category/20)
201+
- [mcpp-standard教程仓库](https://github.com/Sunrisepeak/mcpp-standard)
202+
- [教程视频列表](https://space.bilibili.com/65858958/lists/5208246)
203+
- [教程支持工具-xlings](https://github.com/d2learn/xlings)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# 继承构造函数
2+
3+
继承构造函数 - todo
4+
5+
| Book | Video | Code | X |
6+
| --- | --- | --- | --- |
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) | [视频解读]() | [练习代码]() | |
8+
9+
**为什么引入?**
10+
11+
- ...
12+
13+
## 一、基础用法和场景

book/src/fqa.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# 常见问题
2+
3+
更多问题和反馈 -> [教程论坛交流版块](https://forum.d2learn.org/category/20)

0 commit comments

Comments
 (0)