C++ 类设计和实现的十大最佳实践

简介: 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 的情况。


例如,分析 clang 源代码后,可以检测到大类:


image.png

检测到有大量方法的类:

image.png


或者检测到内聚性较差的类:


image.png


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

目录
打赏
0
相关文章
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
【C++篇】深度解析类与对象(中)
在上一篇博客中,我们学习了C++类与对象的基础内容。这一次,我们将深入探讨C++类的关键特性,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载、以及取地址运算符的重载。这些内容是理解面向对象编程的关键,也帮助我们更好地掌握C++内存管理的细节和编码的高级技巧。
【C++篇】深度解析类与对象(上)
在C++中,类和对象是面向对象编程的基础组成部分。通过类,程序员可以对现实世界的实体进行模拟和抽象。类的基本概念包括成员变量、成员函数、访问控制等。本篇博客将介绍C++类与对象的基础知识,为后续学习打下良好的基础。
|
1月前
|
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
73 19
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
59 13
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
58 5
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
47 5
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
本任务要求设计一个矩形类、圆形类和图形基类,计算并输出相应图形面积。相关知识点包括纯虚函数和抽象类的使用。 **目录:** - 任务描述 - 相关知识 - 纯虚函数 - 特点 - 使用场景 - 作用 - 注意事项 - 相关概念对比 - 抽象类的使用 - 定义与概念 - 使用场景 - 编程要求 - 测试说明 - 通关代码 - 测试结果 **任务概述:** 1. **图形基类(Shape)**:包含纯虚函数 `void PrintArea()`。 2. **矩形类(Rectangle)**:继承 Shape 类,重写 `Print
52 4
【C++面向对象——类的多态性与虚函数】编写教学游戏:认识动物(头歌实践教学平台习题)【合集】
本项目旨在通过C++编程实现一个教学游戏,帮助小朋友认识动物。程序设计了一个动物园场景,包含Dog、Bird和Frog三种动物。每个动物都有move和shout行为,用于展示其特征。游戏随机挑选10个动物,前5个供学习,后5个用于测试。使用虚函数和多态实现不同动物的行为,确保代码灵活扩展。此外,通过typeid获取对象类型,并利用strstr辅助判断类型。相关头文件如<string>、<cstdlib>等确保程序正常运行。最终,根据小朋友的回答计算得分,提供互动学习体验。 - **任务描述**:编写教学游戏,随机挑选10个动物进行展示与测试。 - **类设计**:基类
41 3
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等