当前位置: 首页 > news >正文

写C++十年,我现在怎么设计类和模块?(附真实项目结构)

博主介绍:程序喵大人

  • 35 - 资深C/C++/Rust/Android/iOS客户端开发
  • 10年大厂工作经验
  • 嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手
  • 《C++20高级编程》《C++23高级编程》等多本书籍著译者
  • 更多原创精品文章,首发gzh,见文末
  • 👇👇记得订阅专栏,以防走丢👇👇
    😉C++基础系列专栏
    😃C语言基础系列专栏
    🤣C++大佬养成攻略专栏
    🤓C++训练营
    👉🏻个人网站

最近爆肝了一个网站(希加加职业发展平台),可以对简历进行评估、并且能够根据你的简历内容进行面试押题预测,分享给大家。各位有需要的同学也可以去网站上实践体验一下,希望能帮助到你~

写 C++ 第一年,我觉得“写得能跑就行”; 第三年,我开始讲究“封装、继承、多态”; 第十年,我才明白:设计一个好的类和模块,远比把代码写对难得多。

今天我想和你聊聊:十年 C++ 开发之后,我在真实项目中,是如何设计类与模块的——不是学术讲解,是踩过坑、落过地、真在维护的经验总结。

1. 刚工作时的“错误设计”长啥样?

来看看我早期的写法:

class VideoDecoder {
public:bool open(const std::string& filePath);Frame decodeNextFrame();void close();
private:AVFormatContext* formatCtx;AVCodecContext* codecCtx;...
};

这段代码看起来挺顺,但项目一大,就暴露出不少问题:

  • 类太臃肿:既处理文件,又管理内存,还做解码逻辑,职责太多;
  • 强耦合:代码依赖 FFmpeg,写死了平台细节,换库得重构;
  • 不好测:你得准备视频文件才能测,没法写纯单元测试;
  • 不好扩展:你想支持网络流?得改原来的类;想加日志?又得动这个类……

这类“面条式类”,一开始写得爽,后面维护地狱。

2. 后来我怎么设计?

原则一:每个类只做“一件事”

我现在设计类,第一件事就是问自己:“这个类的职责到底是什么?”

比如“解码视频”听上去是一件事,其实包含了很多职责:

  • 打开资源(文件、本地、网络)
  • 初始化解码器(平台相关)
  • 解码逻辑(帧处理)
  • 日志记录、错误处理

所以我现在会拆成多个类,每个类只做一件事:

class VideoSource {
public:virtual bool open(const std::string& path) = 0;virtual std::vector<uint8_t> read() = 0;virtual void close() = 0;
};class VideoDecoder {
public:void setInput(std::shared_ptr<VideoSource> input);Frame decode();
};
  • VideoSource 负责“从某种来源读取视频数据”,可以有多个实现:
    • FileVideoSource(读取文件)
    • HttpVideoSource(读取网络)
  • VideoDecoder 负责“如何解码”,它不关心你从哪来的数据,也不关心你怎么管理资源。

这种设计的好处:

  • ✅ 测试方便:写个 MockVideoSource 就能测 VideoDecoder
  • ✅ 可扩展:以后支持新来源,不用动解码器代码;
  • ✅ 高内聚、低耦合:逻辑分明,维护轻松。

原则二:用接口 + 工厂解耦模块之间的依赖

在真实项目中,模块之间要解耦,我一般通过抽象接口 + 工厂来处理:

// 接口定义
class IEncoder {
public:virtual void init(const EncoderConfig& config) = 0;virtual void encode(Frame frame) = 0;virtual void close() = 0;
};// 不同实现
class H264Encoder :public IEncoder { ... };
class VP9Encoder :public IEncoder { ... };// 工厂
std::shared_ptr<IEncoder> CreateEncoder(const std::string& codec) {if (codec == "h264") return std::make_shared<H264Encoder>();if (codec == "vp9") return std::make_shared<VP9Encoder>();throw std::runtime_error("Unsupported codec");
}

这样调用方只依赖 IEncoder 接口,不关心具体实现:

auto encoder = CreateEncoder("h264");
encoder->init(cfg);
encoder->encode(frame);

易扩展 + 可测试 + 高可维护性,一石三鸟。

3. 我现在真实项目的模块结构是这样的:

以下是我们一个音视频处理系统的简化目录结构:

/src
├── common/             # 工具类、日志、配置、线程池等
├── interface/          # 对外暴露的 API 接口(HTTP/gRPC)
├── pipeline/           # 数据处理主流程控制模块
├── decoder/            # 解码模块
│   ├── ivideo_decoder.h
│   └── ffmpeg_decoder.cpp
├── encoder/            # 编码模块
├── io/                 # 文件/网络输入输出
├── tests/              # 所有模块的单元测试
└── main.cpp            # 启动程序

每个模块内部再用子模块细分,所有模块间只通过接口通信,没有环状依赖。我们用了 CMake 的 add_subdirectory 管理每个模块,所有模块可以独立编译测试。

总结我的类与模块设计习惯

原则说明
职责单一一个类只做一件事
接口优先抽象优先于实现
组合优于继承减少复杂继承链
依赖注入模块依赖通过参数传入
高内聚,低耦合模块边界清晰,可独立使用

写给每一个 C++ 同行的话:

很多人问:“为什么我总感觉项目写着写着就烂掉了?”

我的回答是:项目结构和类设计,从第一天就决定了它能活多久。

写清晰的类,不是炫技,是对未来自己的负责。 划清模块边界,不是繁琐,是为了团队后面几年的平稳推进。

类是局部的架构,模块是全局的工程,设计好它们,是每一个C++开发者最重要的“重构功课”。
码字不易,欢迎大家点赞,关注,评论,谢谢!

👉 C++训练营

一个专为校招、社招3年工作经验的同学打造的 1v1 项目实战训练营,量身定制学习计划、每日代码review,简历优化,面试辅导,已帮助多名学员获得大厂offer!

http://www.dtcms.com/a/366059.html

相关文章:

  • 66这才是真正懂C/C++的人,写代码时怎么区分函数指针和指针函数?
  • 技术方案之Mysql部署架构
  • 极空间打造 “超级中枢”,从书签笔记到聊天分享,一键全搞定!
  • 【单片机day02】
  • Swift 解法详解:LeetCode 370《区间加法》
  • C++ 5
  • 硬件基础与c51基础
  • 【Linux】分离线程
  • 如何下载免费的vmware workstation pro 17版本?
  • 小游戏公司接单难?这几点原因与破局思路值得看看
  • Pytorch笔记一之 cpu模型保存、加载与推理
  • AI隐私保护:当大模型遇上“隐身术”——差分隐私+同态加密,让模型“看不见原始数据”
  • LoRA微调分词器 应用模板(75)
  • test命令与参数
  • Python基础(⑧APScheduler任务调度框架)
  • 数据结构从青铜到王者第十九话---Map和Set(2)
  • git之分支
  • 如何创建交换空间
  • 【音视频】视频秒播优化实践
  • 无穿戴动捕如何深度结合AI数据分析,实现精准动作评估?
  • 代码随想录刷题Day48
  • Linux 字符设备驱动框架学习记录(三)
  • 数学建模-非线性规划(NLP)
  • STM32HAL 快速入门(十七):UART 硬件结构 —— 从寄存器到数据收发流程
  • DOM常见的操作有哪些?
  • Day34 UDP套接字编程 可靠文件传输与实时双向聊天系统
  • 信号调制与解调 matlab仿真
  • 异常处理机制与debug
  • 复写零(双指针)
  • 单片机day2