编程范式
编程范式(Programming Paradigms)是指导我们如何组织代码、设计程序结构的一种“思维方式”或“风格体系”。不同范式之间的差异,往往体现在:
-
程序的组织结构(如模块、函数、对象)
-
状态的管理方式(如变量是否可变)
-
控制流的表达方式(如循环 vs 递归)
主流范式
1. 命令式编程
✨思想:
直接告诉计算机“怎么做”,一步一步地描述操作流程。
✅特点:
-
程序包含语句(statements)和可变变量(mutable variables)
-
强调状态的改变
-
控制流使用:顺序、选择(if)、循环(for/while)
计算 1 到 100 的和
#include <iostream>
int main() {
int sum = 0; // 声明一个整数变量 sum,用来保存累加结果
for (int i = 1; i <= 100; ++i) {// 使用 for 循环,从 1 加到 100
sum += i; // 每次循环将当前 i 加入到 sum
}
std::cout << "Sum = " << sum << std::endl; // 打印总和
return 0;
}
2. 结构化编程
✨思想:
命令式的子集,强调使用结构化控制流(非goto),提高程序可读性与可维护性。
✅特点:
-
使用子程序(函数)组织代码
-
禁用goto,使用 if/else、while、for 等结构
-
更强调代码块结构和嵌套层级
递归计算阶乘 #include <iostream> // 递归函数:用于计算 n 的阶乘 int factorial(int n) { if (n == 0) return 1; // 阶乘的边界条件:0! = 1 else return n * factorial(n - 1); // 递归定义:n! = n * (n - 1)! } int main() { int number = 5; int result = factorial(number); // 调用函数计算 5! std::cout << number << "! = " << result << std::endl; return 0; }
3. 面向对象编程
✨思想:
通过“对象和类”模拟现实世界,将数据和行为打包在一起,强调封装、继承、多态。
✅特点:
-
封装:隐藏内部实现,只暴露接口
-
继承:通过基类派生子类,重用逻辑
-
多态:通过接口/虚函数实现运行时动态行为
动物叫声模拟 #include <iostream> using namespace std; // 基类:Animal,提供公共接口 class Animal { public: virtual void speak() { // 虚函数:允许子类重写,实现多态 cout << "Some animal sound\n"; } }; // 派生类:Dog class Dog : public Animal { public: void speak() override { // 重写基类方法 cout << "Woof! Woof!\n"; } }; // 派生类:Cat class Cat : public Animal { public: void speak() override { cout << "Meow~\n"; } }; // 多态调用函数 void makeSound(Animal* a) { a->speak(); // 根据对象类型调用对应的 speak 方法 } int main() { Dog d; Cat c; makeSound(&d); // 输出:Woof! Woof! makeSound(&c); // 输出:Meow~ return 0; }
4. 函数式编程
✨思想:
告诉计算机“做什么”,而不是怎么做。函数是第一等公民,没有副作用,强调不可变性。
✅特点:
-
不可变变量(immutable)
-
无副作用的纯函数(pure function)
-
使用递归代替循环
-
常用高阶函数(如 map, reduce)
平方所有数组元素 #include <iotream> #include <vector> #include <ranges> // C++20 ranges int main() { std::vector<int> nums{1, 2, 3, 4, 5}; // 使用 ranges 的 transform 视图将每个元素平方(函数式写法) auto squares = nums | std::ranges::views::transform([](int x) { return x * x; // lambda 表达式,返回输入的平方 }); // 打印结果 for (int n : squares) { std::cout << n << " "; // 输出:1 4 9 16 25 } std::cout << std::endl; return 0; }
5. 声明式编程
✨思想:
不描述怎么做,只描述做什么,由底层系统去实现执行细节。
✅特点:
-
更接近人类逻辑描述
-
状态抽象、控制流抽象
-
SQL、正则表达式、HTML 都是声明式的典型例子
C++中使用 STL 算法(声明式风格) #include <iostream> #include <vector> #include <algorithm> int main() { std::vector<int> data{1, 2, 3, 4, 5}; // 使用 count_if 声明“我要统计偶数”,而不是手动写循环 int even_count = std::count_if(data.begin(), data.end(), [](int x) { return x % 2 == 0; // 条件:x 是偶数 }); std::cout << "Even numbers count: " << even_count << std::endl; // 输出:2 return 0; }
6. 事件驱动编程
✨思想:
程序执行依赖于事件(如用户输入、定时器、网络响应),事件监听和响应机制是核心。
✅特点:
-
常用于GUI、嵌入式、游戏开发
-
回调函数/事件循环为主要结构
7. 并发 / 并行编程
✨思想:
程序由多个“独立的执行流”组成,可能并行执行以提高效率。
✅特点:
-
多线程、多进程、协程
-
锁、互斥、信号量、线程池等同步机制
两个线程并行打印信息 #include <iostream> #include <thread> // 引入线程库 // 子线程函数 void printFromThread() { std::cout << "Hello from thread!\n"; } int main() { // 创建并启动线程,执行函数 printFromThread std::thread t(printFromThread); // 主线程继续执行 std::cout << "Hello from main!\n"; // 等待子线程执行完毕 t.join(); // 没有这句主线程可能提前结束 return 0; }
面向对象编程
在介绍面向对象之前,先回顾一下面向过程
面向过程编程
✅ 定义
面向过程是一种以过程(即函数)为核心的编程方式。它强调的是“先定义好步骤”,然后将数据传入函数处理。
程序 = 数据结构 + 算法
数据结构:如结构体、数组等
算法(过程):一组函数、子程序,用来处理数据结构
特征:
-
函数是代码的基本组织单位
-
数据是独立的,函数在处理时传入数据
-
逻辑上是从上往下执行的流程
#include <iostream> #include <string> // 数据结构 struct Student { std::string name; int score; }; // 操作函数 void printStudent(const Student& s) { std::cout << "Name: " << s.name << ", Score: " << s.score << std::endl; } void updateScore(Student& s, int newScore) { s.score = newScore; } int main() { Student s = {"Alice", 85}; // 创建一个学生对象 printStudent(s); // 打印信息 updateScore(s, 95); // 修改分数 printStudent(s); // 再次打印 return 0; }
面向对象编程
✅ 定义:
OOP 是一种以对象为核心的编程范式,对象将数据与操作数据的行为封装在一起。
对象 = 数据结构 + 算法
程序 = 一组对象 + 对象之间的交互(通信)
特征:
-
封装:数据和方法封装在对象中
-
继承:子类可以继承父类的属性和行为
-
多态:通过虚函数实现接口调用的多样性
-
数据和函数绑定在类内部
#include <iostream>
#include <string>
// 类定义
class Student {
private:
std::string name;
int score;
public:
// 构造函数
Student(std::string n, int s) : name(n), score(s) {}
// 成员函数:打印信息
void printInfo() const {
std::cout << "Name: " << name << ", Score: " << score << std::endl;
}
// 成员函数:更新分数
void setScore(int s) {
score = s;
}
};
int main() {
Student s("Bob", 90); // 创建对象,封装了数据与方法
s.printInfo(); // 对象内部方法访问数据
s.setScore(100); // 修改分数
s.printInfo(); // 再次打印
return 0;
}
对比维度 | 面向过程 | 面向对象 |
---|---|---|
设计核心 | 函数(操作)优先 | 对象(数据+行为)优先 |
数据与函数关系 | 分离,数据被函数操作 | 封装在类中,对象自己管理自己的数据 |
组织结构 | 函数 + 全局变量 | 类 + 对象 |
扩展性 | 修改已有函数 | 可继承、重写,扩展性好 |
复用性 | 以函数为单位复用 | 以类和对象为单位复用 |
适用场景 | 简单流程控制、嵌入式开发 | 复杂系统、图形界面、游戏、业务系统 |
角度 | 面向过程 | 面向对象 |
---|---|---|
比喻 | 操作流水线工厂:一批原料逐步加工 | 协作社会:每个对象各司其职相互通信 |
控制方式 | 程序主控一切,调度函数 | 对象主动响应调用,程序结构松耦合 |
管理数据 | 通过函数传参/访问全局变量 | 对象内封装数据,外部通过接口调用 |
面向对象编程的三大优势
优势 | 含义说明 |
---|---|
重用性 | 已编写好的类/模块可以被多个项目/类复用,避免重复开发。 |
灵活性 | 通过组合、继承、重写等方式对已有模块重新“拼装”,适应不同需求。 |
扩展性 | 对系统新增功能时,可以新增类/重写方法,而不是改动旧代码,符合开闭原则。 |
三大特性
一、封装(Encapsulation)
📌 概念:
封装是指将数据和对数据的操作方法组织到一个类中,并隐藏内部实现细节,外部通过接口访问数据。
✅ 优势:
-
限制外部访问,保证数据安全
-
提高模块化程度,使程序更易维护和扩展
#include <iostream>
#include <string>
// 封装:定义类 Student,把数据和操作封装在内部
class Student {
private:
std::string name; // 私有成员变量:外部无法直接访问
int score;
public:
// 构造函数(初始化对象)
Student(std::string n, int s) : name(n), score(s) {}
// 公共接口:获取名字
std::string getName() const {
return name; // 通过接口获取私有数据
}
// 公共接口:设置成绩(带范围检查)
void setScore(int s) {
if (s >= 0 && s <= 100)
score = s;
else
std::cout << "Invalid score!\n";
}
// 打印信息
void printInfo() const {
std::cout << "Name: " << name << ", Score: " << score << std::endl;
}
};
int main() {
Student stu("Alice", 90); // 创建对象
stu.printInfo(); // 访问公共方法
stu.setScore(110); // 设置非法成绩,被封装逻辑拒绝
stu.setScore(95); // 设置正确成绩
stu.printInfo();
return 0;
}
封装实现了“接口与实现分离”:我们不需要知道
score
是怎么存的、是否合法判断的,只要会用setScore()
即可。
👨👧👦 二、继承
📌 概念:
继承是指子类自动拥有父类的成员(变量和函数),并可以扩展或重写这些成员,从而实现代码的复用和功能拓展。
✅ 优势:
-
代码重用,提高开发效率
-
子类可以在不修改父类的前提下添加或更改功能(符合开闭原则)
#include <iostream>
using namespace std;
// 父类(基类):动物
class Animal {
public:
void breathe() {
cout << "Animal breathing...\n";
}
// 虚函数:让子类实现自己的说话方式
virtual void speak() {
cout << "Animal sound...\n";
}
};
// 子类:狗,继承自动获得 breathe 方法,并重写 speak
class Dog : public Animal {
public:
void speak() override {
cout << "Woof! Woof!\n";
}
void fetch() {
cout << "Dog fetching the ball!\n";
}
};
int main() {
Dog d;
d.breathe(); // 继承来的方法
d.speak(); // 重写的方法
d.fetch(); // 子类特有方法
return 0;
}
🔍继承实现了代码复用:
Dog
不需要重新实现breathe()
,可以直接用父类的。
🌀 三、多态
📌 概念:
多态允许不同的类对象对同一个方法调用作出不同响应。最常用于虚函数 + 基类指针/引用。
✅ 优势:
-
解耦调用者与实现者
-
可以对一组类的对象进行统一操作,提高可扩展性
#include <iostream> using namespace std; // 抽象基类:动物 class Animal { public: virtual void speak() { // 虚函数:声明接口 cout << "Animal sound...\n"; } }; // 子类:猫 class Cat : public Animal { public: void speak() override { cout << "Meow~\n"; } }; // 子类:狗 class Dog : public Animal { public: void speak() override { cout << "Woof!\n"; } }; // 接收基类指针的函数,表现出多态行为 void makeSound(Animal* a) { a->speak(); // 实际调用哪个 speak 取决于传入对象类型 } int main() { Cat c; Dog d; makeSound(&c); // 输出:Meow~ makeSound(&d); // 输出:Woof! return 0; }
泛型编程
泛型编程是一种编程范式,目标是编写与类型无关的算法和数据结构,以提高代码的通用性和重用性。
✅ 目的:
用统一的代码逻辑,处理多种不同的数据类型,而不必为每种类型编写重复的代码。
举个栗子🌰
以前我们写函数时,只能固定类型,比如:
int max(int a, int b) {
return a > b ? a : b;
}
但如果想比较两个 float
呢?还要写:
float max(float a, float b) { ... }
➡ 泛型编程就让我们写一个函数,可以比较任意类型:
template<typename T>
T max(T a, T b) {
return a > b ? a : b;
}
🌟 这就是“把类型抽象为参数”的过程,即“参数化类型”。
C++ 支持泛型的机制:模板
📌 模板的作用:
模板把类型或数值参数化,不再在代码中写死,而是等“使用时”再指定
两种模板:
模板类型 | 用途 | 语法示例 |
---|---|---|
函数模板 | 用于编写通用算法函数 | template<typename T> T func(...) |
类模板 | 用于编写通用数据结构 | template<typename T> class MyClass {} |
函数模板
#include <iostream>
using namespace std;
// 函数模板:用于比较任意类型的两个值,返回较大的一个
template<typename T> // T 是类型参数,占位
T myMax(T a, T b) {
return (a > b) ? a : b; // 使用传入类型的 > 运算符
}
int main() {
cout << myMax(3, 7) << endl; // int
cout << myMax(3.14, 2.72) << endl; // double
cout << myMax('a', 'z') << endl; // char(按ASCII比较)
return 0;
}
🔍 使用时,编译器会自动根据实参类型“实例化”为具体函数,例如
myMax<int>
、myMax<double>
等。
类模板
#include <iostream>
using namespace std;
// 类模板:定义一个通用的容器,只存储一个元素
template<typename T>
class Box {
private:
T value; // 成员变量:类型是模板参数 T
public:
void set(const T& v) {
value = v;
}
T get() const {
return value;
}
};
int main() {
Box<int> intBox; // 创建一个 int 类型的 Box
intBox.set(42);
cout << "intBox: " << intBox.get() << endl;
Box<string> strBox; // 创建一个 string 类型的 Box
strBox.set("Hello");
cout << "strBox: " << strBox.get() << endl;
return 0;
}
STL
STL 六大组件
组件名称 | 作用说明 | 实质 |
---|---|---|
容器 Containers | 存储数据的模板类(如数组、链表、集合等) | 模板类 |
迭代器 Iterators | 访问容器元素的“抽象指针” | 运算符重载类 |
算法 Algorithms | 对容器数据执行操作,如排序、查找、遍历 | 模板函数 |
配接器 Adapters | 修改容器/函数/迭代器行为的包装器 | 模板类 |
仿函数 Functors | 可像函数一样调用的对象,通常是函数对象或重载 () 的类 | 类对象 |
分配器 Allocators | 控制容器底层内存分配策略 | 模板类 |
① 容器
STL 容器是一些预定义的数据结构模板类,用于存储任意类型的数据,常见结构有:数组、链表、队列、集合、哈希表等。特殊的数据结构,实现了数组、 链表、队列等,实质是模板类
常用容器分类
序列式容器 | 描述 |
---|---|
vector<T> | 动态数组(常用) |
list<T> | 双向链表 |
deque<T> | 双端队列 |
关联式容器 | 描述 |
---|---|
set<T> | 集合(自动排序) |
map<K,V> | 键值对映射(自动排序) |
unordered_map | 哈希表映射(无序) |
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {1, 2, 3}; // 动态数组容器
v.push_back(4); // 插入元素
for (int n : v)
cout << n << " "; // 输出:1 2 3 4
return 0;
}
② 迭代器
迭代器是 STL 的“桥梁”,用于访问容器中的元素,一种复杂的指针,可以通过其读写容器中的对象,实质是运 算符重载
常见操作:
it++
/++it
:向后移动
*it
:访问元素值
begin()
/end()
:首尾迭代器
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {10, 20, 30};
vector<int>::iterator it;
for (it = v.begin(); it != v.end(); ++it) {
cout << *it << " "; // 输出元素值
}
return 0;
}
③ 算法
STL 提供了大量的通用算法(排序、查找、复制、替换、变换、合并等),使用 模板函数 实现,支持任意容器,只要提供迭代器。读写容器对象的逻辑算法:排 序、遍历、查找、等等,实质 是模板函数
常用算法函数:
类别 例子 排序 sort(begin, end)
查找 find(begin, end, value)
遍历 for_each(begin, end, func)
统计 count(begin, end, value)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> v = {5, 2, 9, 1};
sort(v.begin(), v.end()); // 升序排序
for (int n : v) cout << n << " "; // 输出:1 2 5 9
return 0;
}
④ 配接器
配接器是 包装器类,用于改造已有容器或函数接口,实现更特化的行为。用来修饰容器、仿函数、迭代器接口
容器适配器:
适配器 基础容器 行为特化 stack<T>
deque<T>
栈(先进后出) queue<T>
deque<T>
队列(先进先出) priority_queue<T>
vector<T>
优先队列(最大堆)
⑤ 仿函数
仿函数是一个重载了 ()
运算符的类对象,可像函数一样使用,常用作 算法参数。类似函数,通过重载()运算符来模拟函数行为的类.
作用:
代替函数指针传入算法
提供带状态的调用对象
与 STL 算法结合使用灵活高效
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 仿函数:用于排序时的自定义比较器(按降序)
struct Greater {
bool operator()(int a, int b) {
return a > b;
}
};
int main() {
vector<int> v = {3, 1, 4};
sort(v.begin(), v.end(), Greater()); // 使用仿函数
for (int n : v) cout << n << " "; // 输出:4 3 1
return 0;
}
⑥ 空间配置器
分配器是 STL 中管理内存分配策略的机制(容器的空间配置管理的模板类),默认使用 std::allocator<T>
,也可自定义实现内存池、对齐分配等策略。
分离数据结构与内存管理逻辑
提供统一的分配/释放接口
用于提高内存效率或控制行为(如嵌入式、游戏)
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v; // 默认使用 allocator<int>
v.push_back(1);
v.push_back(2);
cout << "Capacity: " << v.capacity() << endl;
return 0;
}