一、什么是“编程范式”(Programming Paradigm)
编程范式是程序员“如何看待程序”的一种基本思维方式。
- 它不是语法特性
- 不是某种具体语言
- 而是一种设计程序的世界观
这一概念最早由
Robert Floyd
在1979年图灵奖演讲中系统提出。
编程范式决定了我们:
- 程序由什么构成?
- 如何组织代码?
- 如何理解问题域(Domain)?
- 如何做抽象?
二、从 Turing Machine 到现代 C++:范式演进
可以把演进过程理解为:
从“机器如何执行”(How)
到“我要表达什么”(What)
再到“为什么这样设计”(Why)
三、机器/汇编语言(1950+)
思维方式:控制机器
程序 = 指令序列
开发者关注:
- 寄存器
- 内存地址
- 跳转
- CPU状态
本质模型:
图灵机(Turing Machine)
理论上:
程序 = 状态转移函数 程序 = 状态转移函数 程序=状态转移函数
δ ( q , s ) → ( q ′ , s ′ , d ) \delta(q, s) \rightarrow (q', s', d) δ(q,s)→(q′,s′,d)
表示: - 当前状态 q q q
- 当前符号 s s s
- 转移到 q ′ q' q′
- 写入 s ′ s' s′
- 向左或右移动 d d d
示例(类汇编风格 C++)
#include <iostream>
// 模拟“机器式”思维:手动控制状态
int main() {
int acc = 0; // 累加器
int i = 0; // 模拟寄存器
// 模拟跳转 + 状态控制
while (true) {
if (i >= 10)
break; // 类似跳转指令
acc += i; // 手动修改状态
i++;
}
std::cout << acc << std::endl;
}
特点:
- 关注状态
- 关注执行顺序
- 强依赖“如何做”(How)
四、过程化编程(1950+)
代表语言:C
思维方式:程序 = 函数集合
- 数据 + 操作分离
- 关注步骤
- Top-Down 设计
程序本质:
程序 = 一组函数 f 1 , f 2 , . . . , f n 程序 = 一组函数 f_1, f_2, ..., f_n 程序=一组函数f1,f2,...,fn
示例
#include <iostream>
// 过程:求和
int sum(int n) {
int result = 0; // 局部状态
for (int i = 0; i <= n; ++i) {
result += i; // 逐步计算
}
return result;
}
int main() {
std::cout << sum(10) << std::endl;
}
特点:
- 关注“怎么做”
- 强调步骤
- 可读性提升
五、面向对象编程(196X)
代表人物:
Alan Kay
思维方式:程序 = 对象的协作
核心思想:
- 封装(Encapsulation)
- 继承(Inheritance)
- 多态(Polymorphism)
程序建模方式:
系统 = 对象集合 + 对象之间的消息传递 系统 = 对象集合 + 对象之间的消息传递 系统=对象集合+对象之间的消息传递
从:
“怎么做”
变成:
“谁来做”
示例(C++ OOP)
#include <iostream>
// 抽象:数字计算器
class Calculator {
private:
int value; // 封装状态
public:
Calculator(int v) : value(v) {}
void add(int x) {
value += x; // 操作封装在对象内部
}
int get() const {
return value;
}
};
int main() {
Calculator calc(0);
calc.add(10);
std::cout << calc.get() << std::endl;
}
范式变化:
- 从“步骤”变成“角色”
- 更贴近现实世界建模
- 强调 Domain
六、多范式融合(198X)
C++ 的核心思想:
不是选择一种范式
而是融合多种范式
代表人物:
Bjarne Stroustrup
C++支持:
- 过程式
- 面向对象
- 泛型
- 函数式
- 元编程
七、函数式编程(2010+ 再次兴起)
函数式编程强调:
- 不可变数据(Immutable)
- 无副作用(Pure Function)
- 数学函数式思维
数学定义:
f ( x ) = y f(x) = y f(x)=y
没有状态变化。
示例(函数式风格 C++)
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<int> data{1,2,3,4,5};
// 累加:函数式表达
int result = std::accumulate(
data.begin(),
data.end(),
0,
[](int acc, int x) {
return acc + x; // 无副作用
}
);
std::cout << result << std::endl;
}
特点:
- 强调表达式
- 强调“是什么”
- 更接近数学
八、What / How / Why 的演进
| 阶段 | 关注点 | 思维层级 |
|---|---|---|
| 汇编 | How(怎么做) | 机器 |
| 过程 | How | 算法步骤 |
| 面向对象 | Who 做 | 领域建模 |
| 函数式 | What 是 | 数学映射 |
| 现代C++ | Why | 架构 + 抽象设计 |
九、编程范式与软件建模的关系
编程范式影响:
- 代码组织方式
- 抽象层次
- 架构风格
- 可维护性
- 可扩展性
例如: - OOP → DDD
- 函数式 → 数据流架构
- 泛型 → 高性能库设计
十、现代 C++ 的真正本质
现代 C++(C++20以后):
是一个多范式、零成本抽象语言
抽象成本理论:
抽象成本 ≈ 0 抽象成本 \approx 0 抽象成本≈0
(理想目标)
即:
- 高层抽象
- 机器级性能
这也是 C++ 在系统领域长期存在的原因。
十一、总结:范式演进本质
范式演进不是语法升级,而是:
抽象能力的升级
从:
- 控制机器
到: - 控制流程
到: - 控制对象
到: - 控制抽象
到: - 控制领域模型
最终:
控制“复杂性”
C++语言编程范式演进(深度解析版)
C++的发展不是“版本升级史”,而是:
抽象能力递进史 + 多范式融合史
可以用一个抽象公式表示:
C + + = 性能 + 抽象 + 多范式融合 C++ = 性能 + 抽象 + 多范式融合 C++=性能+抽象+多范式融合
或者更形式化一点:
L a n g u a g e P o w e r = A b s t r a c t i o n L e v e l × Z e r o C o s t G u a r a n t e e Language\ Power = Abstraction\ Level \times Zero\ Cost\ Guarantee Language Power=Abstraction Level×Zero Cost Guarantee
一、历史阶段与范式跃迁
① 1972:C
核心范式:过程式编程
特点:
- 手动内存管理
- 指针操作
- 函数驱动
- 面向机器
程序模型:
P r o g r a m = F u n c t i o n s + D a t a Program = { Functions } + { Data } Program=Functions+Data
关注:
How(怎么做)
② 1979:C with Classes
发明人:
Bjarne Stroustrup
引入:
- class
- 构造/析构
- 封装
开始支持:
面向对象范式(OOP)
程序模型转变为:
S y s t e m = O b j e c t s + M e s s a g e P a s s i n g System = Objects + Message Passing System=Objects+MessagePassing
③ 1990:模板 + 异常
这是第一次重大范式跃迁。
新增:
- Templates(泛型编程)
- Exception(错误控制流分离)
模板的数学意义
模板本质是“类型函数”:
F
:
T
y
p
e
→
C
o
d
e
F : Type \rightarrow Code
F:Type→Code
例如:
#include <iostream>
// 泛型函数模板
template<typename T>
T add(T a, T b) {
return a + b; // 与类型无关的抽象
}
int main() {
std::cout << add(1, 2) << std::endl; // int
std::cout << add(1.5, 2.5) << std::endl; // double
}
特点:
- 算法与类型解耦
- 编译期多态
④ 1994:STL —— 泛型编程革命
核心人物:
Alexander Stepanov
STL带来:
- Iterator抽象
- 算法与容器分离
- Traits技术
- 模板元编程
核心思想:
A l g o r i t h m ( I t e r a t o r ) Algorithm(Iterator) Algorithm(Iterator)
而不是:
A l g o r i t h m ( C o n t a i n e r ) Algorithm(Container) Algorithm(Container)
示例:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> v{1,2,3};
// 泛型算法与容器解耦
std::for_each(v.begin(), v.end(),
[](int x) {
std::cout << x << std::endl;
});
}
这一阶段标志着:
C++成为真正的“泛型编程语言”
⑤ 1998–2003:标准化阶段
- C++98
- C++03
目标: - 语言稳定
- 模板完善
- STL成熟
形成真正的:
多范式基础框架
二、现代C++阶段(2011之后)
2011是第二次重大跃迁。
⑥ C++11
新增能力:
- Lambda
- 右值引用
- 移动语义
- auto
- 可变参数模板
- constexpr
- 标准线程库
1⃣ 右值引用与移动语义
传统复制成本:
C
o
s
t
(
c
o
p
y
)
=
O
(
n
)
Cost(copy) = O(n)
Cost(copy)=O(n)
移动语义:
C
o
s
t
(
m
o
v
e
)
≈
O
(
1
)
Cost(move) \approx O(1)
Cost(move)≈O(1)
示例:
#include <vector>
// 返回局部对象
std::vector<int> create() {
std::vector<int> v{1,2,3};
return v; // 移动而不是拷贝
}
int main() {
auto data = create(); // 利用move语义
}
意义:
高抽象 + 零额外成本
2⃣ Lambda —— 函数式能力增强
#include <iostream>
int main() {
auto add = [](int a, int b) {
return a + b; // 无副作用表达式
};
std::cout << add(3,4) << std::endl;
}
程序逐渐表达为:
f
(
x
)
=
y
f(x) = y
f(x)=y
⑦ C++14
增强:
- 返回值自动推导
- 泛型Lambda
- 放宽constexpr
- 变量模板
示例(泛型Lambda):
auto add = [](auto a, auto b) {
return a + b; // 自动类型推导
};
泛型进一步“函数化”。
⑧ C++17
新增:
- 构造函数推导
- 折叠表达式
- constexpr if
- std::variant
- 并发内存模型增强
constexpr if 示例
#include <iostream>
#include <type_traits>
template<typename T>
void print(T value) {
if constexpr(std::is_integral_v<T>) {
std::cout << "int: " << value << std::endl;
} else {
std::cout << "other: " << value << std::endl;
}
}
这是:
编译期分支
⑨ C++20
重大升级:
- Concepts
- Ranges
- Coroutines
- Modules
Concepts —— 类型系统跃迁
旧模板:
T
∈
A
n
y
T
y
p
e
T \in AnyType
T∈AnyType
新模板:
T
∈
C
o
n
c
e
p
t
T \in Concept
T∈Concept
示例:
#include <concepts>
template<typename T>
concept Addable = requires(T a, T b) {
a + b;
};
template<Addable T>
T add(T a, T b) {
return a + b;
}
泛型进入:
类型约束时代
协程(Coroutines)
支持:
- 异步编程
- 生成器模式
- 状态机自动化
程序抽象为:
S t a t e M a c h i n e ⇒ C o r o u t i n e StateMachine \Rightarrow Coroutine StateMachine⇒Coroutine
三、C++范式演进的四大特点
① 逐步加入新的设计范式
| 阶段 | 新范式 |
|---|---|
| 1970s | 过程式 |
| 1980s | 面向对象 |
| 1990s | 泛型 |
| 2000s | 模板元编程 |
| 2011 | 函数式 + 并发 |
| 2020 | 类型系统化 |
② 持续优化完备性
例如:
- RAII
- 智能指针
- move语义
- constexpr
- Concepts
目标:
抽象成本 → 0 抽象成本 \rightarrow 0 抽象成本→0
③ 强调融合
C++不是“纯函数式”
也不是“纯OOP”
而是:
M
u
l
t
i
P
a
r
a
d
i
g
m
=
O
O
P
+
G
e
n
e
r
i
c
+
F
u
n
c
t
i
o
n
a
l
+
M
e
t
a
+
S
y
s
t
e
m
Multi\ Paradigm = OOP + Generic + Functional + Meta + System
Multi Paradigm=OOP+Generic+Functional+Meta+System
④ 社区驱动演进
- 工业实践
- 标准委员会
- Boost生态
- 开源贡献
范式的成熟来自:
实践 → 总结 → 标准化
四、本质总结
C++的演进可以理解为:
控制机器
→
控制流程
→
控制对象
→
控制类型
→
控制抽象
控制机器 → 控制流程 → 控制对象 → 控制类型 → 控制抽象
控制机器→控制流程→控制对象→控制类型→控制抽象
它最终解决的问题不是“写代码”,而是:
如何在保证性能的前提下控制复杂性
02 · C++编程范式剖析 —— 过程式设计(Procedural Design)
一、过程式设计的核心思想
著名计算机科学家
Niklaus Wirth
提出过一句非常经典的话:
程序 = 数据结构 + 算法
Program = Data Structures + Algorithms
可以形式化表示为:
P r o g r a m = D a t a + A l g o r i t h m Program = Data + Algorithm Program=Data+Algorithm
其中:
- Data:数据结构(struct、数组、内存布局)
- Algorithm:对数据的操作步骤(函数、流程)
二、从图灵机模型理解过程式设计
过程式编程非常贴近图灵机模型:
S
t
a
t
e
n
e
x
t
=
f
(
S
t
a
t
e
c
u
r
r
e
n
t
,
I
n
p
u
t
)
State_{next} = f(State_{current}, Input)
Statenext=f(Statecurrent,Input)
程序执行本质:
- 修改状态
- 顺序执行
- 显式控制流程
关注重点:
How —— “怎么做”
三、图示结构解析(你给的图)
图中表达的是:
左侧:
多个 Procedure(过程/函数)
右侧:
一个共享的数据区域
箭头表示:
- 过程可以读写数据
- 数据对多个过程开放
结构本质:
Procedure A → 数据
Procedure B → 数据
Procedure C → 数据
数据是中心,函数围绕数据操作。
四、C++中的过程式设计示例
示例1:典型过程式风格
#include <iostream>
// 全局数据(共享数据)
int balance = 0; // 所有函数都可以访问
// 算法1:存钱
void deposit(int amount) {
balance += amount; // 修改全局状态
}
// 算法2:取钱
void withdraw(int amount) {
balance -= amount; // 修改同一份数据
}
// 算法3:查询余额
void print_balance() {
std::cout << balance << std::endl;
}
int main() {
deposit(100);
withdraw(30);
print_balance(); // 输出70
}
特点:
- 数据与函数分离
- 数据全局共享
- 函数之间通过共享数据“间接通信”
五、过程式设计的优点
① 贴近机器模型
它几乎等价于:
M
e
m
o
r
y
+
I
n
s
t
r
u
c
t
i
o
n
s
Memory + Instructions
Memory+Instructions
优点:
- 性能可控
- 内存可控
- 易于做底层优化
- 适合系统编程
这也是: - 操作系统内核
- 嵌入式系统
- 驱动程序
大量使用C语言的原因。
② 可预测性强
执行路径清晰:
main → f1 → f2 → f3
流程可视化清楚。
六、过程式设计的缺点(核心问题)
① 全局数据导致高耦合
如果多个函数操作同一数据:
f1()
f2()
f3()
都依赖:
global_data
那么耦合度可以理解为:
C
o
u
p
l
i
n
g
∝
N
f
u
n
c
t
i
o
n
s
×
S
h
a
r
e
d
D
a
t
a
Coupling \propto N_{functions} \times SharedData
Coupling∝Nfunctions×SharedData
函数越多,共享数据越复杂,系统复杂度指数增长。
举例:问题演示
int counter = 0;
void increment() {
counter++;
}
void reset() {
counter = 0;
}
void print() {
std::cout << counter << std::endl;
}
问题:
- 任意函数都可以修改counter
- 很难追踪状态变化来源
- 容易出现隐藏Bug
② 可复用性差
函数往往依赖外部状态。
如果想复用increment:
必须保证:
存在一个全局counter
这降低了模块独立性。
③ 响应变化能力弱
如果数据结构变化:
int balance;
改为:
struct Account {
int balance;
int id;
};
所有相关函数都必须修改。
七、过程式 vs 面向对象(对比理解)
| 特征 | 过程式 | 面向对象 |
|---|---|---|
| 数据 | 共享 | 封装 |
| 组织方式 | 函数集合 | 对象集合 |
| 依赖关系 | 高耦合 | 低耦合 |
| 扩展能力 | 较弱 | 较强 |
| 贴近机器 | 非常贴近 | 稍抽象 |
八、什么时候应该使用过程式设计?
过程式并没有“过时”。
适合场景:
- 算法密集型代码
- 数值计算
- 内核模块
- 性能极端敏感场景
- 小型工具程序
例如:
排序算法:
void quick_sort(int arr[], int left, int right) {
if (left >= right)
return;
int i = left;
int j = right;
int pivot = arr[left];
while (i < j) {
while (i < j && arr[j] >= pivot)
j--;
while (i < j && arr[i] <= pivot)
i++;
std::swap(arr[i], arr[j]);
}
std::swap(arr[left], arr[i]);
quick_sort(arr, left, i - 1);
quick_sort(arr, i + 1, right);
}
这里用OOP反而会增加复杂度。
#include <iostream>
#include <vector>
#include <algorithm> // 用于 std::sort 做对比验证
#include <random> // 用于生成随机数
#include <ctime> // 用于设置随机种子
// 你提供的快速排序实现(原版)
void quick_sort(int arr[], int left, int right) {
if (left >= right)
return;
int i = left;
int j = right;
int pivot = arr[left];
while (i < j) {
while (i < j && arr[j] >= pivot)
j--;
while (i < j && arr[i] <= pivot)
i++;
std::swap(arr[i], arr[j]);
}
std::swap(arr[left], arr[i]);
quick_sort(arr, left, i - 1);
quick_sort(arr, i + 1, right);
}
// 打印数组的辅助函数
void print_array(const int arr[], int n, const std::string& title = "") {
if (!title.empty()) {
std::cout << title << ": ";
}
for (int i = 0; i < n; ++i) {
std::cout << arr[i];
if (i < n - 1) std::cout << ", ";
}
std::cout << "\n";
}
// 检查数组是否已排序(升序)
bool is_sorted(const int arr[], int n) {
for (int i = 1; i < n; ++i) {
if (arr[i - 1] > arr[i]) {
return false;
}
}
return true;
}
int main() {
std::srand(static_cast<unsigned>(std::time(nullptr)));
// 测试用例列表
std::vector<std::vector<int>> test_cases = {
{}, // 空数组
{5}, // 只有一个元素
{3, 1}, // 两个元素
{1, 2, 3, 4, 5}, // 已经有序
{5, 4, 3, 2, 1}, // 逆序
{4, 2, 4, 2, 4, 2}, // 有大量重复元素
{7, 7, 7, 7, 7}, // 全相同
{10, 80, 30, 90, 40, 50, 70}, // 经典例子
{64, 34, 25, 12, 22, 11, 90} // 另一个常见例子
};
// 生成一个较大的随机数组
std::vector<int> large_random(100);
std::generate(large_random.begin(), large_random.end(), []() {
return std::rand() % 1000;
});
test_cases.push_back(large_random);
int case_num = 1;
for (const auto& vec : test_cases) {
int n = vec.size();
std::cout << "\n=== 测试用例 " << case_num++ << " (大小: " << n << ") ===\n";
if (n == 0) {
std::cout << "空数组 → 已排序\n";
continue;
}
// 原始数组
std::cout << "原始: ";
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << vec[i] << (i < vec.size() - 1 ? ", " : "\n");
}
// 复制一份给 quick_sort 使用(因为会修改原数组)
std::vector<int> arr = vec;
quick_sort(arr.data(), 0, n - 1);
// 打印排序结果
std::cout << "排序后: ";
for (int x : arr) {
std::cout << x << " ";
}
std::cout << "\n";
// 验证是否正确排序
if (is_sorted(arr.data(), n)) {
std::cout << "[正确] 数组已升序排序 ✓\n";
} else {
std::cout << "[错误] 排序失败!\n";
}
// 额外验证:与 std::sort 结果对比
std::vector<int> std_sorted = vec;
std::sort(std_sorted.begin(), std_sorted.end());
bool match = (arr == std_sorted);
std::cout << (match ? "[通过] 与 std::sort 结果一致 ✓" : "[失败] 与 std::sort 不一致 ✗") << "\n";
}
std::cout << "\n所有测试用例执行完毕。\n";
return 0;
}
https://godbolt.org/z/sT5eehds5
九、从复杂度角度理解过程式的局限
假设:
- 有 n n n 个函数
- 有
m
m
m 个共享数据
那么潜在依赖关系规模为:
O ( n × m ) O(n \times m) O(n×m)
如果全部函数访问全部数据:
O ( n m ) O(nm) O(nm)
当系统变大时,复杂度爆炸。
十、过程式的历史意义
过程式是:
第一代“结构化编程思想”
解决了:
- goto滥用问题
- 流程混乱问题
- 无结构代码问题
它是后续范式的基础。
十一、本质总结
过程式设计的哲学是:
数据是核心
函数是操作
程序运行本质:
S t a t e → M o d i f i e d S t a t e State \rightarrow Modified\ State State→Modified State
优点:
- 高性能
- 可控
- 简洁
缺点: - 高耦合
- 扩展困难
- 复杂系统难维护
软件设计对“确定性”的追求(C++视角深度解析)
在系统级软件(操作系统、数据库、嵌入式、交易系统)中,确定性(Determinism) 是核心目标之一。
可以抽象成一句话:
S
a
m
e
I
n
p
u
t
⇒
S
a
m
e
B
e
h
a
v
i
o
r
Same\ Input \Rightarrow Same\ Behavior
Same Input⇒Same Behavior
进一步形式化:
P
r
o
g
r
a
m
(
I
n
p
u
t
,
E
n
v
i
r
o
n
m
e
n
t
)
=
O
u
t
p
u
t
+
R
e
s
o
u
r
c
e
U
s
a
g
e
Program(Input, Environment) = Output + ResourceUsage
Program(Input,Environment)=Output+ResourceUsage
如果在相同输入与环境下:
- 输出一致
- 内存使用一致
- 指令路径一致
- 二进制结构一致
那么系统就是“确定性的”。
一、确定性的两个维度
| 类型 | 关注点 |
|---|---|
| 静态确定性 | 编译期结构可控 |
| 动态确定性 | 运行期行为可控 |
二、静态确定性(Static Determinism)
静态确定性关注:
编译完成后的二进制是否可预测、可控制
1⃣ 代码符号与 ABI 的确定性控制
(1)Name Mangling 可追溯
C++支持函数重载,编译器会对符号“改名”(Name Mangling)。
例如:
int add(int a, int b);
double add(double a, double b);
底层会生成不同符号名。
如果想控制ABI稳定性,可以使用:
extern "C" int add(int a, int b);
这样:
- 禁止name mangling
- 保证符号稳定
- 便于跨语言调用
(2)Inline 的可控性
inline可能导致:
- 代码膨胀
- ABI变化
- 调试困难
可以通过: - 不在头文件中实现
- 使用编译选项控制
- 避免过度模板
保证:
B i n a r y S i z e = P r e d i c t a b l e Binary\ Size = Predictable Binary Size=Predictable
2⃣ 二进制大小的确定性控制
(1)模板显式实例化
模板默认是“按需实例化”:
template<typename T>
T square(T x) {
return x * x;
}
如果在多个文件使用不同类型:
会生成多个版本。
可以显式具现:
// 显式实例化
template int square<int>(int);
这样:
- 控制生成数量
- 避免隐式爆炸
(2)禁用 RTTI
RTTI(运行时类型识别)会:
- 增加typeinfo
- 增加vtable开销
- 增加异常依赖
编译选项:
-fno-rtti
可使:
B
i
n
a
r
y
S
i
z
e
↓
Binary\ Size \downarrow
Binary Size↓
(3)禁用异常
异常机制需要:
- 栈展开表
- 额外运行时支持
可使用:
-fno-exceptions
效果:
- 减少二进制
- 提升路径确定性
- 避免隐藏跳转
三、动态确定性(Runtime Determinism)
动态确定性关注:
程序运行时资源与行为是否可预测
四、内存使用的确定性控制
1⃣ placement new
允许在指定地址构造对象。
#include <new>
#include <iostream>
int main() {
alignas(int) char buffer[sizeof(int)];
// buffer对齐到int大小
int* p = new (buffer) int(42);
// 在buffer中构造int
std::cout << *p << std::endl;
// p->~int(); // 手动调用析构函数
}
https://godbolt.org/z/TzjYjrGnP
优点:
- 无堆分配
- 无malloc
- 内存来源完全可控
2⃣ alignof
获取类型对齐要求:
#include <iostream>
struct A {
double d;
int i;
};
int main() {
std::cout << alignof(A) << std::endl;
}
保证:
A
d
d
r
e
s
s
m
o
d
A
l
i
g
n
m
e
n
t
=
0
Address \bmod Alignment = 0
AddressmodAlignment=0
3⃣ std::align
手动调整指针满足对齐:
#include <memory>
#include <iostream>
int main() {
char buffer[64];
void* ptr = buffer;
std::size_t space = sizeof(buffer);
std::align(alignof(double), sizeof(double), ptr, space);
// ptr现在满足double对齐
}
4⃣ std::pmr::memory_resource
C++17引入多态内存资源。
可以控制:
- 内存池
- 栈内分配
- 固定大小分配
示例:
#include <memory_resource>
#include <vector>
int main() {
std::byte buffer[1024];
std::pmr::monotonic_buffer_resource pool(buffer, sizeof(buffer));
std::pmr::vector<int> vec(&pool);
// 使用指定内存资源
vec.push_back(1);
}
优点:
- 不走全局new
- 不走malloc
- 内存分配路径固定
五、运行时指令路径的确定性控制
1⃣ 禁用 RTTI / 异常
避免:
- 动态类型判断
- 栈展开
- 不可预测跳转
使程序更接近:
L i n e a r C o n t r o l F l o w Linear\ Control\ Flow Linear Control Flow
2⃣ 多线程内存模型
C++11引入正式内存模型。
核心问题:
R
e
o
r
d
e
r
i
n
g
Reordering
Reordering
CPU可能重排序。
C++提供:
- memory_order_relaxed
- memory_order_acquire
- memory_order_release
- memory_order_seq_cst
最强一致性:
S e q u e n t i a l C o n s i s t e n c y Sequential\ Consistency Sequential Consistency
示例:
#include <atomic>
std::atomic<int> flag{0};
void writer() {
flag.store(1, std::memory_order_release);
}
void reader() {
if (flag.load(std::memory_order_acquire)) {
// 保证看到完整写入
}
}
保证:
W
r
i
t
e
→
R
e
a
d
o
r
d
e
r
i
n
g
Write \rightarrow Read\ ordering
Write→Read ordering
六、为什么系统软件极度追求确定性?
在以下场景:
- 实时系统
- 金融交易
- 内核
- 嵌入式
- 游戏引擎
需要: - 可预测延迟
- 无隐藏开销
- 无异常路径
- 内存稳定
否则:
L a t e n c y J i t t e r ↑ Latency\ Jitter \uparrow Latency Jitter↑
七、静态 + 动态确定性的统一
可以抽象为:
D
e
t
e
r
m
i
n
i
s
m
=
C
o
m
p
i
l
e
T
i
m
e
C
o
n
t
r
o
l
+
R
u
n
t
i
m
e
C
o
n
t
r
o
l
Determinism = CompileTimeControl + RuntimeControl
Determinism=CompileTimeControl+RuntimeControl
C++之所以长期存在于系统领域,
正是因为它允许你:
- 控制ABI
- 控制符号
- 控制二进制
- 控制内存
- 控制指令路径
- 控制线程语义
八、本质总结
软件设计追求确定性,其实是在追求:
对复杂性的完全掌控
C++提供的能力是:
- 抽象
- 同时保持可控
- 同时保持性能
这也是它区别于很多高级语言的根本点。
过程式设计与分层模块化架构风格(深度解析)
在大型系统早期(尤其是C语言主导时期),
过程式设计 + 分层模块化架构 是主流工程方法。
它解决的是一个核心问题:
如何在多人协作下控制复杂度?
可以抽象为:
S y s t e m = ∑ i = 1 n L a y e r i System = \sum_{i=1}^{n} Layer_i System=i=1∑nLayeri
并满足:
D e p e n d e n c y ( L i ) → L i − 1 Dependency(L_i) \rightarrow L_{i-1} Dependency(Li)→Li−1
即:
层间单向依赖
一、分层架构(Layered Architecture)
1⃣ 核心思想
系统按照职责划分为多个层级,例如:
应用层
业务逻辑层
服务层
基础设施层
操作系统层
规则:
- 上层依赖下层
- 下层不依赖上层
- 禁止循环依赖
形式化表达:
∀ i , ; L i only depends on L i − 1 \forall i, ; L_i \text{ only depends on } L_{i-1} ∀i,;Li only depends on Li−1
这保证:
N o C i r c u l a r D e p e n d e n c y No\ Circular\ Dependency No Circular Dependency
2⃣ 示例结构
// =========================
// 底层:数据访问层
// =========================
int read_from_disk() {
return 42; // 模拟底层数据读取
}
// =========================
// 上层:业务逻辑层
// =========================
int process_data() {
int value = read_from_disk(); // 依赖下层
return value * 2;
}
特点:
- 上层依赖下层
- 依赖方向清晰
- 架构清晰
二、模块化设计(Modular Design)
模块化的核心目标:
将系统拆分为多个“功能单元”
可以表示为:
S y s t e m = M o d u l e 1 + M o d u l e 2 + ⋯ + M o d u l e n System = Module_1 + Module_2 + \cdots + Module_n System=Module1+Module2+⋯+Modulen
每个模块:
- 独立开发
- 通过接口通信
- 可单独测试
问题:同层模块间依赖难以控制
例如:
Module A
Module B
Module C
如果:
A → B
B → C
C → A
就形成:
C
i
r
c
u
l
a
r
D
e
p
e
n
d
e
n
c
y
Circular\ Dependency
Circular Dependency
复杂度迅速上升:
C
o
u
p
l
i
n
g
∝
n
2
Coupling \propto n^2
Coupling∝n2
三、模块内部的“过程式 + 全局变量”问题
在过程式时代,模块内部常见结构:
// module.cpp
// 全局变量(模块内部共享状态)
int counter = 0;
// 多个函数操作同一数据
void increment() {
counter++;
}
void reset() {
counter = 0;
}
int get() {
return counter;
}
问题:
- 状态隐式共享
- 行为分散
- 难以追踪状态变化来源
本质是:
S t a t e = G l o b a l State = Global State=Global
而函数:
F u n c t i o n ( S t a t e ) → M o d i f i e d S t a t e Function(State) \rightarrow Modified\ State Function(State)→Modified State
这导致: - 模块内部高耦合
- 难以并发扩展
- 难以局部重构
四、架构风格总结
过程式分层模块架构的设计步骤:
1⃣ 分层次(Layering)
明确系统结构。
2⃣ 划模块(Modularization)
按功能拆分:
- 网络模块
- 存储模块
- 计算模块
3⃣ 定接口(Interface)
接口定义:
// storage.h
int read_data();
void write_data(int value);
4⃣ 设计数据结构(数据建模)
例如:
struct User {
int id;
char name[32];
};
5⃣ 设计算法流程
用流程图或状态机表达:
I
n
p
u
t
→
P
r
o
c
e
s
s
→
O
u
t
p
u
t
Input \rightarrow Process \rightarrow Output
Input→Process→Output
五、这种架构的优点
① 支撑大规模并行开发
因为:
- 层次清晰
- 模块边界明确
- 接口固定
团队可以: - A组开发模块A
- B组开发模块B
这是一种:
静态规划式开发
② 易于裁剪
删除某层或某模块即可裁剪功能。
③ 性能可控
由于过程式贴近机器:
- 无隐藏对象开销
- 无虚函数
- 无异常栈展开
六、核心缺点
① 可复用性过粗
模块粒度太大。
例如:
整个存储模块
难以只复用其中一个算法。
② 响应变化能力弱
如果需求变化:
- 数据结构改变
- 可能影响整层
例如:
struct User {
int id;
};
改为:
struct User {
int id;
std::string email;
};
影响范围可能是:
I
m
p
a
c
t
≈
A
l
l
L
a
y
e
r
s
Impact \approx All\ Layers
Impact≈All Layers
③ 高耦合复杂度
如果:
- 每层多个模块
- 每模块多个全局变量
潜在依赖规模为:
O ( n × m ) O(n \times m) O(n×m)
系统变大时:
复杂度指数级上升。
七、为什么这种架构在历史上成功?
因为它:
- 适合C语言
- 适合操作系统
- 适合嵌入式
- 适合单机系统
典型代表: - Unix内核
- 早期数据库系统
- 嵌入式固件
八、本质分析:静态规划式开发
这种架构假设:
需求基本稳定
需求基本稳定
需求基本稳定
因此:
- 结构一次设计完成
- 层级固定
- 接口固定
但现代系统特点是:
C h a n g e R a t e ↑ Change\ Rate \uparrow Change Rate↑
变化频繁。
这就导致:
过程式分层架构的响应能力下降。
九、总结
过程式 + 分层模块化架构:
优点:
- 结构清晰
- 可并行开发
- 性能可控
- 工程实践成熟
缺点: - 模块内全局变量导致高耦合
- 变化传播范围大
- 粒度过粗
- 动态扩展能力弱
本质上:
S t r u c t u r e S t a b i l i t y ↑ ⇒ F l e x i b i l i t y ↓ Structure\ Stability \uparrow \Rightarrow Flexibility \downarrow Structure Stability↑⇒Flexibility↓
面向对象设计(Object-Oriented Design, OOD)
面向对象编程(OOP)的核心表达可以写成:
P
r
o
g
r
a
m
=
E
n
t
i
t
i
e
s
+
R
e
l
a
t
i
o
n
s
h
i
p
s
Program = Entities + Relationships
Program=Entities+Relationships
或者更精确一点:
S
y
s
t
e
m
=
O
b
j
e
c
t
s
+
R
e
l
a
t
i
o
n
s
h
i
p
s
System = { Objects } + { Relationships }
System=Objects+Relationships
与过程式:
P
r
o
g
r
a
m
=
D
a
t
a
+
A
l
g
o
r
i
t
h
m
Program = Data + Algorithm
Program=Data+Algorithm
不同,OOP强调:
谁拥有数据?
谁负责行为?
对象之间如何协作?
一、面向对象的三大核心特性
① 封装(Encapsulation)
定义:
将数据与操作数据的行为绑定在一起,并隐藏内部实现细节。
数学表达:
O b j e c t = S t a t e + B e h a v i o r Object = State + Behavior Object=State+Behavior
并且:
S t a t e ∉ E x t e r n a l D i r e c t A c c e s s State \notin External\ Direct\ Access State∈/External Direct Access
示例:封装
#include <iostream>
class BankAccount {
private:
int balance; // 私有数据,外部不可直接访问
public:
BankAccount(int initial) : balance(initial) {}
void deposit(int amount) {
balance += amount; // 行为与数据绑定
}
int get_balance() const {
return balance; // 只读访问
}
};
int main() {
BankAccount acc(100);
acc.deposit(50);
std::cout << acc.get_balance() << std::endl;
}
优点:
- 降低耦合
- 保证数据一致性
- 控制访问路径
② 继承(Inheritance)
继承表示:
子类“是一个”父类(is-a)
数学抽象:
S u b c l a s s ⊂ P a r e n t Subclass \subset Parent Subclass⊂Parent
或类型关系:
D e r i v e d ∈ B a s e Derived \in Base Derived∈Base
示例:继承
#include <iostream>
// 父类
class Shape {
public:
virtual double area() const = 0; // 纯虚函数(接口)
};
// 子类
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override {
return 3.14 * radius * radius;
}
};
特点:
- 代码复用
- 类型扩展
- 多态基础
③ 多态(Polymorphism)
多态核心:
相同接口,不同行为
数学表示:
f ( B a s e ∗ ) ⇒ D y n a m i c D i s p a t c h f(Base*) \Rightarrow Dynamic\ Dispatch f(Base∗)⇒Dynamic Dispatch
运行时决定调用哪个子类。
示例:多态
#include <iostream>
#include <memory>
class Shape {
public:
virtual double area() const = 0;
virtual ~Shape() = default;
};
class Rectangle : public Shape {
double w, h;
public:
Rectangle(double w_, double h_) : w(w_), h(h_) {}
double area() const override {
return w * h;
}
};
void print_area(const Shape& s) {
std::cout << s.area() << std::endl; // 多态调用
}
int main() {
Rectangle r(3, 4);
print_area(r); // 调用子类实现
}
本质:
I
n
t
e
r
f
a
c
e
≠
I
m
p
l
e
m
e
n
t
a
t
i
o
n
Interface \neq Implementation
Interface=Implementation
二、Mermaid图关系解析
你的类图表达了典型的OOP关系。
1⃣ 继承(Inheritance)
Class_Green_Left --|> Class_LightGreen
Class_Green_Right --|> Class_LightGreen
表示:
G
r
e
e
n
_
L
e
f
t
,
G
r
e
e
n
_
R
i
g
h
t
⊂
L
i
g
h
t
G
r
e
e
n
Green\_Left, Green\_Right \subset LightGreen
Green_Left,Green_Right⊂LightGreen
即:
- 子类继承父类
- 共享接口
- 可替换性(LSP原则)
2⃣ 聚合(Aggregation)
Class_Orange o-- Class_LightGreen
聚合表示:
“拥有”关系,但生命周期独立
数学表示:
A has-a B A \text{ has-a } B A has-a B
但:
D e s t r o y ( A ) ⇏ D e s t r o y ( B ) Destroy(A) \nRightarrow Destroy(B) Destroy(A)⇏Destroy(B)
示例:聚合
class Engine {
public:
void start() {}
};
class Car {
private:
Engine* engine; // 指针,不负责生命周期
public:
Car(Engine* e) : engine(e) {}
};
3⃣ 组合(Composition)
Class_Green_Left *-- Class_Grey
组合表示:
强拥有关系,生命周期绑定
数学表达:
D e s t r o y ( A ) ⇒ D e s t r o y ( B ) Destroy(A) \Rightarrow Destroy(B) Destroy(A)⇒Destroy(B)
示例:组合
class CPU {
};
class Computer {
private:
CPU cpu; // 成员对象,生命周期一致
};
三、面向对象的本质优势
① 降低耦合
过程式:
F
u
n
c
t
i
o
n
s
→
S
h
a
r
e
d
G
l
o
b
a
l
S
t
a
t
e
Functions \rightarrow Shared\ Global\ State
Functions→Shared Global State
OOP:
O
b
j
e
c
t
i
→
O
w
n
S
t
a
t
e
Object_i \rightarrow Own\ State
Objecti→Own State
状态被局部化。
② 提高扩展性
新增功能只需:
class NewShape : public Shape { ... };
无需修改已有代码。
符合:
开闭原则(Open/Closed Principle)
③ 更贴近现实建模
现实世界:
- 人
- 订单
- 账户
- 设备
天然就是:
E n t i t y + R e l a t i o n s h i p Entity + Relationship Entity+Relationship
四、OOP的代价
① 动态多态开销
虚函数调用:
I
n
d
i
r
e
c
t
C
a
l
l
Indirect\ Call
Indirect Call
需要:
- vtable
- 指针跳转
② 继承层次复杂化
如果:
D
e
p
t
h
↑
Depth \uparrow
Depth↑
则理解成本上升。
③ 过度设计风险
小问题却设计:
- 多层抽象
- 多接口
- 复杂继承树
会降低可读性。
五、与过程式对比总结
| 特征 | 过程式 | 面向对象 |
|---|---|---|
| 核心 | 数据 + 算法 | 实体 + 关系 |
| 状态管理 | 全局共享 | 封装 |
| 扩展方式 | 修改函数 | 扩展子类 |
| 依赖控制 | 较弱 | 强 |
| 建模能力 | 偏技术 | 偏领域 |
六、本质总结
面向对象的哲学是:
让对象对自己的状态负责
让系统由对象协作完成
可以抽象为:
C o m p l e x i t y C o n t r o l = E n c a p s u l a t i o n + A b s t r a c t i o n + P o l y m o r p h i s m Complexity\ Control = Encapsulation + Abstraction + Polymorphism Complexity Control=Encapsulation+Abstraction+Polymorphism
它解决的是:
复杂系统中“状态失控”的问题
1⃣ Class Diagram(类图)
描述类、属性、方法以及类之间的关系(继承 / 组合 / 聚合 / 依赖)。
说明:
--|>表示继承o--表示聚合*--表示组合
2⃣ Object Diagram(对象图)
描述某一时刻的实例关系(运行时结构)。
Mermaid 没有专门对象图语法,通常用 classDiagram 表达实例:
表示:
- 实例 dog1
- 实例 owner1
- owner1 拥有 dog1
3⃣ Component Diagram(组件图)
描述系统模块级结构。
说明:
- 表示模块依赖
- 常用于系统架构设计
4⃣ Deployment Diagram(部署图)
描述系统物理部署结构。
表示:
- 客户端访问 Web 服务器
- Web 调用应用服务器
- 应用访问数据库
5⃣ Package Diagram(包图)
描述包之间的依赖关系。
体现:
- 分层架构
- 单向依赖原则
6⃣ Composite Structure Diagram(复合结构图)
描述类内部结构组成。
说明:
*--表示组合关系- CPU 和 Memory 生命周期依附 Computer
7⃣ Profile Diagram(UML Profile)
用于扩展 UML 元模型(定义 stereotype)
Mermaid 不支持 profile 语法,可用注释模拟:
表示:
- 使用 stereotype 扩展语义
- 常用于框架建模(如 <>、<>)
总结对照表
| 图类型 | 关注点 | Mermaid 实现方式 |
|---|---|---|
| Class Diagram | 类型结构 | classDiagram |
| Object Diagram | 实例结构 | classDiagram模拟 |
| Component Diagram | 模块结构 | graph |
| Deployment Diagram | 物理部署 | graph |
| Package Diagram | 包依赖 | graph |
| Composite Structure | 类内部结构 | classDiagram |
| Profile Diagram | 元模型扩展 | stereotype模拟 |
面向对象建模(Object-Oriented Modeling)如何通过“静态视图 + 动态视图”完整描述一个系统。
可以理解为:
静态视图解决“系统是什么”
动态视图解决“系统如何运行”
下面做系统化、深入讲解。
一、面向对象建模的核心思想
面向对象建模的目标是:
系统
=
结构模型
+
行为模型
系统 = 结构模型 + 行为模型
系统=结构模型+行为模型
也可以理解为:
S
y
s
t
e
m
=
S
t
a
t
i
c
S
t
r
u
c
t
u
r
e
+
D
y
n
a
m
i
c
B
e
h
a
v
i
o
r
System = Static\ Structure + Dynamic\ Behavior
System=Static Structure+Dynamic Behavior
二、静态视图(Structural View)
1⃣ 本质
静态视图描述:
- 有哪些类
- 类之间什么关系
- 系统如何组织
- 代码如何分层部署
它回答的是:
“系统长什么样?”
2⃣ 常见结构类图(Structural Diagrams)
(1)Class Diagram —— 类图
最核心的结构图。
描述:
- 类
- 属性
- 方法
- 继承
- 组合
- 聚合
- 依赖
示例代码:
// 一个典型类图对应的代码
class Engine {
public:
void start() {}
};
class Car {
private:
Engine engine; // 组合关系(强拥有)
public:
void run() {
engine.start();
}
};
- Car 组合 Engine
- Engine 生命周期依附 Car
(2)Object Diagram —— 对象图
类图是“类型结构”
对象图是“运行时实例结构”
示例:
Car car1;
Car car2;
对象图描述:
- car1
- car2
- 它们各自拥有自己的 engine
(3)Component Diagram —— 组件图
强调模块级组织,而非类。
例如:
- Auth Module
- Payment Module
- Logging Module
适用于大型系统架构设计。
(4)Deployment Diagram —— 部署图
描述系统如何部署到物理环境:
- Server A
- Server B
- 数据库
- 网络
例如:
A p p S e r v e r → D a t a b a s e S e r v e r App\ Server \rightarrow Database\ Server App Server→Database Server
(5)Package Diagram —— 包图
用于描述代码包的依赖关系。
UI → Service → Repository
体现:
- 单向依赖
- 分层架构
(6)Composite Structure Diagram —— 复合结构图
强调类内部的组成关系。
例如:
class Computer {
CPU cpu;
Memory memory;
};
三、动态视图(Behavioral View)
1⃣ 本质
动态视图描述:
- 对象如何交互
- 状态如何变化
- 消息如何传递
- 系统如何运行
回答的是:
“系统是如何工作的?”
2⃣ 行为类图(Behavioral Diagrams)
(1)Use Case Diagram —— 用例图
从用户视角出发:
- 用户能做什么?
- 系统提供什么能力?
例如: - 用户登录
- 下订单
- 查询历史记录
它定义的是:
系统职责集合 = 功能 1 , 功能 2 , 功能 3 系统职责集合 = {功能1, 功能2, 功能3} 系统职责集合=功能1,功能2,功能3
(2)Sequence Diagram —— 时序图
描述对象间消息调用顺序。
例如:
User -> Controller -> Service -> Repository
调用链:
U
s
e
r
→
C
o
n
t
r
o
l
l
e
r
→
S
e
r
v
i
c
e
→
R
e
p
o
s
i
t
o
r
y
User \rightarrow Controller \rightarrow Service \rightarrow Repository
User→Controller→Service→Repository
代码对应:
controller.login();
内部:
void Controller::login() {
service.authenticate();
}
(3)Communication Diagram —— 通信图
与时序图类似,但强调:
- 对象间结构关系
- 消息编号
(4)State Machine Diagram —— 状态机图
描述对象生命周期。
例如:
订单状态:
C
r
e
a
t
e
d
→
P
a
i
d
→
S
h
i
p
p
e
d
→
C
o
m
p
l
e
t
e
d
Created \rightarrow Paid \rightarrow Shipped \rightarrow Completed
Created→Paid→Shipped→Completed
代码示例:
enum class OrderState {
Created,
Paid,
Shipped,
Completed
};
class Order {
OrderState state;
};
状态机的本质:
N
e
x
t
S
t
a
t
e
=
f
(
C
u
r
r
e
n
t
S
t
a
t
e
,
E
v
e
n
t
)
NextState = f(CurrentState, Event)
NextState=f(CurrentState,Event)
(5)Activity Diagram —— 活动图
类似流程图。
强调:
- 并行
- 分支
- 条件判断
例如:
验证账号 → 判断密码 → 成功 or 失败
(6)Interaction Overview Diagram —— 交互概览图
多个时序图的组合。
(7)Timing Diagram —— 时序变化图
强调时间维度上的状态变化。
常用于:
- 实时系统
- 嵌入式系统
1⃣ State Machine Diagram(状态机图)
✓ 描述订单状态流转
- [*] = 初始状态
- 事件触发状态变化
2⃣ Communication Diagram(通信图)
Mermaid 没有直接“通信图”,可以用 sequenceDiagram + 注释编号表示对象交互:
✓ 强调对象之间的消息交互,按顺序编号
3⃣ Use Case Diagram(用例图)
Mermaid 支持用 graph TD 模拟用例图:
✓ 描述系统提供的功能和参与者,不关心内部实现
4⃣ Activity Diagram(活动图)
✓ 强调流程控制、条件分支、活动顺序
5⃣ Sequence Diagram(顺序图)
✓ 描述对象消息顺序和交互逻辑
6⃣ Timing Diagram(时序图)
<?plantuml 1.2026.2beta3?>7⃣ Interaction Overview Diagram(交互概览图)
Mermaid 没有原生交互概览图,但可以用 graph TD +子图模拟:
✓ 强调 多个交互的整体流程,可以看作“活动图 +顺序图综合体”
四、静态 vs 动态的本质区别
| 维度 | 静态视图 | 动态视图 |
|---|---|---|
| 关注点 | 类型 | 实例 |
| 时间维度 | 无 | 有 |
| 描述内容 | 结构 | 行为 |
| 设计阶段 | 架构设计 | 业务流程设计 |
| 是否与代码强相关 | 强 | 强 |
五、面向对象建模的核心数学抽象
可以形式化理解为:
静态模型
M s = ( C , R ) M_s = (C, R) Ms=(C,R)
- C C C = 类集合
- R R R = 类之间的关系集合
动态模型
M d = ( O , E , S ) M_d = (O, E, S) Md=(O,E,S)
- O O O = 对象集合
- E E E = 事件集合
- S S S = 状态转移函数
系统完整模型
S y s t e m = M s + M d System = M_s + M_d System=Ms+Md
六、工程实践意义
1⃣ 为什么需要静态视图?
- 支撑大规模开发
- 明确依赖
- 便于裁剪
- 方便代码生成
2⃣ 为什么需要动态视图?
- 分析复杂业务流程
- 发现隐藏耦合
- 验证状态完整性
- 推导并发问题
七、面向对象建模的局限
优点:
- 结构清晰
- 适合复杂系统
- 强抽象能力
缺点: - 静态结构稳定但变更成本高
- 状态机爆炸问题
- 继承层级过深易失控

945

被折叠的 条评论
为什么被折叠?



