嵌入式系统中C++ 类的设计和实现分析
C++代码提供了足够的灵活性,因此对于大部分工程师来说都很难把握。
本文介绍了写好C++代码需要遵循的10个最佳实践,并在最后提供了一个工具可以帮助我们分析C++代码的健壮度。
原文:10 Best practices to design and implement a C++ class。

1. 尽可能尝试使用新的C++标准
到2022年,C++已经走过了40多个年头。新的C++标准实际上简化了许多令人沮丧的细节,提供了新的现代方法来改进C++代码,但让开发人员认识到这一点并不容易。
以内存管理为例,这可能是C++中受到最多批评的机制。多年来,对象分配都是由new关键字完成的,开发人员一定得记住在代码的某个地方调用delete。“现代C++”解决了这个问题,并促进了共享指针的使用。
2. 使用命名空间模块化代码
现代C++库广泛使用命名空间来模块化代码库,它们利用“Namespace-by-feature”方法,按功能划分命名空间来反映功能集,将单个特性(且仅与该特性)相关的所有内容放到单个命名空间中。从而使得命名空间具有高内聚性和高模块化,并且耦合最小,紧耦合的项目被放在了一起。
Boost是按特性分组的最佳示例,其包含数千个命名空间,每个命名空间用于对特定的特性进行分组。
3. 抽象
数据抽象是C++中面向对象编程最基本和最重要的特性之一。抽象意味着只显示基本信息而隐藏细节,数据抽象指的是仅向外部世界提供关于数据的基本信息,隐藏背景细节或实现。
尽管许多书籍、网络资源、会议演讲者和专家都推荐这种最佳实践,但在很多项目中,这条规则仍然被忽略了,许多类的细节并没有被隐藏。
4. 类越小越好
具有多行代码的类型应该被划分为一组较小的类型。
需要很大的耐心重构一个大的类,甚至可能需要从头重新创建所有东西。以下是一些重构建议:
-
BigClass中的逻辑必须被分成更小的类。这些较小的类最终可能成为嵌套在原始God Class中的私有类,God Class的实例对象由较小嵌套类的实例组成。
-
较小的类划分应该由God Class负责的多个职责驱动。要确定这些职责,通常需要查找与字段的子集强耦合的方法的子集。
-
如果BigClass包含的逻辑比状态多,一个好的选择是定义一个或几个不包含静态字段而只包含纯静态方法的静态类。纯静态方法是一种只根据输入参数计算结果的函数,它不读取或分配任何静态或实例字段。纯静态方法的主要优点是易于测试。
-
首先尝试维护BigClass的接口,并委托调用新提取的类。最后,BigClass应该是一个没有自己逻辑的纯接口,可以为了方便将其保留,也可以将其扔掉,并开始只使用新类。
-
单元测试可以提供帮助: 在提取方法之前为每个方法编写测试,以确保不会破坏功能。
5. 每个类尽量提供最少的方法
包含20个以上方法的类可能很难理解和维护。
一个类有许多方法可能是实现了太多责任的症状。
也许所面对的类控制了系统中太多的其他类,并且已经超出了应有的逻辑,成为了一个无所不能的类。
6. 加强低耦合
低耦合是理想状态,可以在应用中进行较少的更改实现程序的某个变更。从长远来看,可以减少修改、添加新特性的大量时间、精力和成本。
低耦合可以通过使用抽象类或泛型类和方法来实现。
7. 加强高内聚
单一责任原则规定一个类不应该有多于一个更改的理由,这样的类被称为内聚类。较高的LCOM值通常可以意味着类的内聚性较差。有几个LCOM指标,取值范围为[0-1]。LCOM HS (HS代表Henderson-Sellers)取值范围为[0-2]。LCOM HS值大于1时需要产生警惕。下面是计算LCOM指标:
LCOM = 1 — (sum(MF)/M*F)LCOM HS = (M — sum(MF)/F)(M-1)
其中……
-
M是类中方法的数量(包括静态方法和实例方法,它还包括构造函数、属性getter/setter、事件添加/删除方法)。
-
F是类中实例字段的数量。
-
MF是类访问特定实例字段的方法数量。
-
Sum(MF)是该类所有实例字段的MF之和。
这些公式背后的基本思想可以表述如下: 如果一个类的所有方法都使用它的所有实例字段,那么这个类就是完全内聚的,这意味着sum(MF)=M*F,然后LCOM = 0和LCOMHS = 0。
LCOMHS值大于1就需要警惕了。
8. 只注释代码不能表达的内容
鹦鹉学舌的代码注释没有为读者提供任何额外的东西。代码库中充斥着嘈杂的注释和不正确的注释,促使程序员忽略所有的注释,或者采取积极的措施隐藏它们。
9. 尽量不要用重复的代码
众所周知,重复代码的存在对软件开发和维护有负面影响。实际上,一个主要缺点是,当为了修复bug或添加新特性而更改重复代码的实例时,所有对应的代码必须同时更改。
产生重复代码最常见的原因是复制/粘贴操作,这种情况下,相似的源代码出现在两个或多个地方。许多文章、书籍和网站都警告不要采用这种做法,但有时实践这些建议并不容易,开发人员还是会选择简单的解决方案: 复制/粘贴大法。
使用适当的工具可以容易的从复制/粘贴操作中检测到重复代码,但是,在某些情况下,克隆代码很难被检测到。
10. 不变性有助于多线程编程
基本上,如果对象在创建之后状态不变,那么这个对象就是不可变(immutable)的。如果一个类的实例是不可变的,那么该类就是不可变的。
不可变对象极大简化了并发编程,这是支持使用它的重要理由。想想看,为什么编写适当的多线程程序是一项艰巨的任务?因为同步线程访问资源(对象或其他操作系统资源)是很困难的。为什么同步这些访问很困难?因为很难保证多个线程对多个对象进行的多次写访问和读访问之间不会出现竞争条件。如果不再有写访问会怎么样?换句话说,如果被线程访问的对象的状态没有改变会怎么样?就不再需要同步了!
关于不可变类的另一个好处是它们永远不会违反里氏替换原则(LSP, Liskov Subtitution Principle),以下是维基百科对LSP的定义:
Liskov的行为子类型的概念定义了可变对象可替换性的概念,也就是说,如果S是T的子类型,那么程序中T类型的对象可以被替换为S类型的对象,而不改变该程序的任何期望属性(例如,正确性)。
如果没有公共字段,没有可以更改其内部数据的方法,并且派生类方法无法更改其内部数据,那么引用对象类就是不可变的。因为值不可变,所以在所有情况下都可以引用相同的对象,不需要复制构造函数或赋值操作符。出于这个原因,建议将复制构造函数和赋值操作符设为私有,或者从boost::noncopyable继承,或者使用新的C++ 11特性“显式默认和删除特殊成员函数”[2]。
如何加强对这些最佳实践进行检查?
CppDepend[3]提供了名为CQLinq[4]的代码查询语言,可以像数据库一样查询代码库。开发人员、设计人员和架构师可以自定义查询,以便轻松找到容易出现bug的情况。
通过CQLinq,可以结合来自代码度量、依赖关系、API使用和其他模型的数据来定义非常高级的查询,以匹配容易出现bug的情况。
References:
[1] 10 Best practices to design and implement a C++ class: https://issamvb.medium.com/10-best-practices-to-design-and-implement-a-c-class-4326611827e1#:~:text=10%20Best%20practices%20to%20design%20and%20implement%20a,class%20as%20you%20can.%20...%20More%20items...%20
[2] Explicitly defaulted and deleted special member functions: http://en.wikipedia.org/wiki/C%2B%2B11#Explicitly_defaulted_and_deleted_special_member_functions
[3] CppDepend: http://www.cppdepend.com/
[4] CQLinq: https://www.cppdepend.com/cqlinq
相关文章:
嵌入式系统中C++ 类的设计和实现分析
C代码提供了足够的灵活性,因此对于大部分工程师来说都很难把握。 本文介绍了写好C代码需要遵循的10个最佳实践,并在最后提供了一个工具可以帮助我们分析C代码的健壮度。 原文:10 Best practices to design and implement a C class。 1. 尽…...
【torch高级】一种新型的概率学语言pyro(02/2)
前文链接:【torch高级】一种新型的概率学语言pyro(01/2) 七、Pyro 中的推理 7.1 背景:变分推理 引言中的每项计算(后验分布、边际似然和后验预测分布)都需要执行积分,而这通常是不可能的或计算…...
Git基本概念与使用
一、Git基本概念 git,是一种分布式版本控制软件,与CVS、Subversion这类的集中式版本控制工具不同,它采用了分布式版本库的作法,不需要服务器端软件,就可以运作版本控制,使得源代码的发布和交流极其方便。g…...
Kubernetes数据卷Volume和数据卷分类(emptyDir、nfs、hostPath、ConfigMap)详解
Kubernetes数据卷Volume和数据卷分类详解 数据卷概述 Kubernetes Volume(数据卷)主要解决了如下两方面问题: 数据持久性:通常情况下,容器运行起来之后,写入到其文件系统的文件暂时性的。当容器崩溃后&am…...
【MATLAB源码-第59期】基于matlab的QPSK,16QAM164QAM等调制方式误码率对比,调制解调函数均是手动实现未调用内置函数。
操作环境: MATLAB 2022a 1、算法描述 正交幅度调制(QAM,Quadrature Amplitude Modulation)是一种在两个正交载波上进行幅度调制的调制方式。这两个载波通常是相位差为90度(π/2)的正弦波,因此…...
经典目标检测神经网络 - RCNN、SSD、YOLO
文章目录 1. 目标检测算法分类2. 区域卷积神经网络2.1 R-CNN2.2 Fast R-CNN2.3 Faster R-CNN2.4 Mask R-CNN2.5 速度和精度比较 3. 单发多框检测(SSD)4. YOLO 1. 目标检测算法分类 目标检测算法主要分两类:One-Stage与Two-Stage。One-Stage与…...
mysql存在10亿条数据,如何高效随机返回N条纪录,sql如何写
1 低效方案 1.使用ORDER BY RAND(): SELECT * FROM your_table ORDER BY RAND() LIMIT 1; 这将随机排序表中的所有行,并且通过LIMIT 1仅返回第一行,从而返回一个随机记录。然而,对于大型表来说,ORDER BY RAND()可能会…...
c语言中啥时候用double啥时候用float?
c语言中啥时候用double啥时候用float? 一般来说,可以使用double来表示具有更高精度要求的浮点数,因为它可以存储更大范围的数值并且具有更高的精度。 最近很多小伙伴找我,说想要一些c语言资料,然后我根据自己从业十年…...
vscode 保存 “index.tsx“失败: 权限不足。选择 “以超级用户身份重试“ 以超级用户身份重试。
vscode 保存 "index.tsx"失败: 权限不足。选择 “以超级用户身份重试” 以超级用户身份重试。 操作:mac在文件夹中创建文件,sudo 创建umiJs项目 解决:修改文件夹权限 右键文件夹...
综合性练习
名片管理系统 综合性项目实现—详细请点这里 dict {} # 定义一个空字典,用于存储信息。 list [] # 定义一个列表,存储name值 list1 [] #存储age值 list2 [] #存储phone值 def people_tips(): #提示print("*****" * 10)print("…...
threejs(7)-精通粒子特效
一、初识Points与点材质 // 设置点材质 const pointsMaterial new THREE.PointsMaterial(); import * as THREE from "three"; // 导入轨道控制器 import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; // 导入动画库 import gsa…...
使用了百度OCR,记录一下
由于识别ocr有的频率不高,图片无保密性需求,也不想太大的库, 就决定还是用下api算了,试用了几家,决定用百度的ocr包,相对简单。 遇到的问题里面下列基本有提到:例如获取ID,KEY&…...
5.OsgEarth加载地形
愿你出走半生,归来仍是少年! 在三维场景中除了使用影像体现出地貌情况,还需要通过地形体现出地势起伏,还原一个相对真实的三维虚拟世界。 osgEarth可通过直接加载Dem数据进行场景内的地形构建。 1.数据准备 由于我也没有高程数据,…...
基于回溯搜索算法的无人机航迹规划-附代码
基于回溯搜索算法的无人机航迹规划 文章目录 基于回溯搜索算法的无人机航迹规划1.回溯搜索搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要:本文主要介绍利用回溯搜索算法来优化无人机航迹规划。 …...
微信小程序云开发笔记-初始化商城小程序
缘起:由于痴迷机器人,店都快倒闭了,没办法,拿出点精力给店里搞个小程序,要多卖货才能活下来搞机器人,在此记录一下搞小程序的过程,要不然搞完又忘了。腾讯的云开发,前端和后端都有了…...
vulnhub_DeRPnStiNK靶机渗透测试
VulnHub2018_DeRPnStiNK靶机 https://www.vulnhub.com/entry/derpnstink-1,221/ flag1(52E37291AEDF6A46D7D0BB8A6312F4F9F1AA4975C248C3F0E008CBA09D6E9166) flag2(a7d355b26bda6bf1196ccffead0b2cf2b81f0a9de5b4876b44407f1dc07e51e6) flag4(49dca65f362fee401292ed7ada96f9…...
网站如何判断请求是来自手机-移动端还是PC-电脑端?如何让网站能适应不同的客户端?
如果网站需要实现手机和PC双界面适应,可以有两种方式: 第一种是响应式界面,根据屏幕宽度来判定显示的格式。这种需要前端来做,手机/PC共用一套代码,有一定的局限性。 第二种是后端通过request请求头中的内容来分析客户…...
sass和 scss的区别?
Sass(Syntactically Awesome Style Sheets)和 SCSS(Sassy CSS)是两种流行的 CSS 预处理器,它们扩展了普通的 CSS 语法,提供了更多的功能和便利性。下面是 Sass 和 SCSS 的主要区别: 1ÿ…...
Vuex 动态模块状态管理器
模块化思想 我们之前的博文已经讲述了Vuex怎么使用命名空间实现模块化状态管理。详情可以看: Vuex命名空间及如何获取根模块、兄弟模块状态管理器_AI3D_WebEngineer的博客-CSDN博客https://blog.csdn.net/weixin_42274805/article/details/133269196?ops_request_…...
实现分片上传、断点续传、秒传 (JS+NodeJS)(TypeScript)
一、引入及效果 上传文件是一个很常见的操作,但是当文件很大时,上传花费的时间会非常长,上传的操作就会具有不确定性,如果不小心连接断开,那么文件就需要重新上传,导致浪费时间和网络资源。 所以࿰…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...
【Ftrace 专栏】Ftrace 参考博文
ftrace、perf、bcc、bpftrace、ply、simple_perf的使用Ftrace 基本用法Linux 利用 ftrace 分析内核调用如何利用ftrace精确跟踪特定进程调度信息使用 ftrace 进行追踪延迟Linux-培训笔记-ftracehttps://www.kernel.org/doc/html/v4.18/trace/events.htmlhttps://blog.csdn.net/…...
二叉树-144.二叉树的前序遍历-力扣(LeetCode)
一、题目解析 对于递归方法的前序遍历十分简单,但对于一位合格的程序猿而言,需要掌握将递归转化为非递归的能力,毕竟递归调用的时候会调用大量的栈帧,存在栈溢出风险。 二、算法原理 递归调用本质是系统建立栈帧,而非…...
【技巧】dify前端源代码修改第一弹-增加tab页
回到目录 【技巧】dify前端源代码修改第一弹-增加tab页 尝试修改dify的前端源代码,在知识库增加一个tab页"HELLO WORLD",完成后的效果如下 [gif01] 1. 前端代码进入调试模式 参考 【部署】win10的wsl环境下启动dify的web前端服务 启动调试…...
