C++ 代码整洁之道

article/2025/6/13 19:47:14

NOTICE: 这篇文章的框架条目来自《C++代码整洁之道:C++17可持续软件开发模式实践》,作者: [德] 斯提芬·罗特。书籍原名"Clean C++: Sustainable Software Development Patterns and Best Practices with C++ 17"。

NOTICE:未经作者允许禁止转载!

文章目录

  • 1. 编码基本原则
    • 保持简单和直接原则(KISS)
    • 不需要原则(YAGNI)
    • 避免重复原则(DRY)
    • 信息隐藏原则
    • 高内聚原则
    • 松耦合原则
    • 小心优化原则
    • 最少惊讶原则
    • 童子军原则
  • 2. C++代码整洁的基本规范
    • 2.1 良好的命名
      • 名称应该自注释
      • 避免冗余的名称
      • 不要用匈牙利命名法
      • 避免晦涩的缩写
    • 2.2 注释
      • 不要为易懂的代码写注释
      • 不要用注释禁用代码
      • 特殊情况的注释
    • 2.3 函数
      • 只做一件事
      • 让函数尽可能小
      • 函数命名
      • 函数的参数和返回值
    • 2.4 替换C++中的C风格代码
      • 用string代替char*
      • 使用标准容器而不是C风格的数组
      • 使用C++的类型转换代替C的强制类型转换
      • 避免使用宏
    • 2.5 资源管理
      • 智能指针
      • 避免显示的使用new和delete
  • 3. 面向对象
    • 类的设计原则
      • 让类尽可能的小
      • 单一职责原则
      • 开闭原则
      • 里氏替换原则(LSP)
      • 接口隔离原则(ISP)
      • 无环依赖原则
      • 依赖倒置原则(DIP)
      • 不要和陌生人说话(迪米特法则)(LoD)
      • 避免类的静态成员
  • 4. 设计模式和习惯用法
    • 依赖注入模式
    • Adapter模式
    • Strategy模式
    • Command模式
    • Command处理器模式
    • Composite模式
    • Observer模式
    • Factory模式
    • Strategy模式
    • Facade模式
    • Money Class模式
    • 特例用法
  • 参考

1. 编码基本原则

保持简单和直接原则(KISS)

Keep It Simple and Stupid.尽可能地采取简短、明了和直观的设计或编码方案,以避免过度工程化和不必要的复杂性。

不需要原则(YAGNI)

You Aren’t Gonna Need It. 这个原则的核心观点是:不要浪费时间在你可能永远不需要的特性上。在确定真的有必要的时候再写代码,那时再重构也来得及。

避免重复原则(DRY)

Don’t Repeat Yourself。应该尽量避免重复代码和冗余数据的出现,而应该采用抽象化、封装和代码重用等方式来避免重复。
遵循DRY原则可以带来以下几个好处:

a. 提高代码的可维护性、可读性和可重用性:通过避免重复代码和数据,可以使代码更具有模块化和可重用性,从而降低开发和维护成本,并且使代码更易于理解和修改。
b. 减少潜在的错误:重复代码很容易导致错误和不一致性,因为如果需要更新某个行为或者修复一个问题,需改的地方就会增加,这样可能会导致不必要的错误。
c. 优化性能:使用代码重用技术可以避免不必要的计算,因为不需要执行重复的代码,可以提高程序的性能。

为了遵循DRY原则,开发人员应该注意以下几点:

a. 提取共同的行为:在编写代码时,应该寻找可以被多次使用的代码块,并将它们提取出来作为公共函数或类库。
b. 采用抽象化和封装:通过对代码进行抽象化和封装,可以降低代码耦合性,并且可以避免重复代码的出现。
c. 避免分散数据:在编写代码时,应该避免将同一数据保存在多个地方,尽可能地将所有相关的数据都保存在一个地方,这样可以避免数据的不一致性。

信息隐藏原则

一段代码调用了另外一段代码,那么调用者不应该知道被调用者的内部实现。

a. 封装实现细节:每个组件应该将自己的实现细节尽可能地封装起来,以减少对外暴露的内容。这样可以有效地控制系统的复杂度,并降低修改组件时的风险。
b. 限制接口暴露:每个组件应该仅向外部暴露必要的接口,并尽可能地减少对外暴露的内容。这不仅可以提高组件间的独立性,还可以使系统更加灵活、可重用和易于维护。

高内聚原则

高内聚原则是指一个模块或类应该尽可能地将自己的功能和数据集中在一起,形成一个独立的单元,并且各个单元之间应该相互独立。高内聚的目的是将一个大模块分解为更小、更容易理解和维护的部分。

松耦合原则

不同的模块或组件之间应该尽量减少相互依赖。松耦合原则包括以下几个关键点:

a.接口稳定:模块之间的通信应该通过稳定的接口进行,接口的设计应该简单、清晰,并且能够满足各种场景的需求。
b. 最小依赖:每个模块或组件应该尽可能地减少对其他模块或组件的依赖,只与必要的组件进行交互,避免与其他模块或组件之间建立过多的耦合关系。
c. 松散耦合:各个模块或组件之间应该尽可能松散地耦合,也就是说它们之间的关系是可插拔、可替换的。这样可以方便地对系统进行扩展或改进,同时也更容易理解和维护。

小心优化原则

小步优化。

最少惊讶原则

保持一致性:在设计软件时,应该尽可能地保持一致的设计风格和交互方式,确保用户可以更容易地理解和使用软件。
满足用户期望:设计的功能和操作应该满足用户的习惯和预期,不应该出现让用户感到惊讶或困惑的行为。
明确反馈:当用户执行操作时,软件应该给予明确的反馈,确保用户可以得知操作的结果和状态。

童子军原则

当发现代码中有需要改进的或者风格不好的代码,应该立刻修改,不管是谁写的代码。

2. C++代码整洁的基本规范

2.1 良好的命名

源代码文件名、命名空间、类、函数、模板、参数、变量、常量等等等等,都应该具有有意义且比较有表现力的名字。

名称应该自注释

  • bad examples
int num;
bool flag;
std::vector<Customer> list;
Product data;
  • good examples
unsigned int number_of_customer;
bool is_changed;
std::vector<Customer> customers;
Product ordered_product;

自注释的命名也不应该过长,如果变量的上下文很清楚,只需要要简短的描述性名称。

避免冗余的名称

  • bad example
class Customer
{
// somde code
private:std::string customer_name_;
};

在Customer类中已经有足够的信息能够知道name表示customer的名字了,不必要重复,重复的话违背了"Don’t Repeat Yourself "原则.

std::string string_customer_name;

不要在变量中包含变量类型。

不要用匈牙利命名法

不要在变量前面带类型前缀,在变量里面带上类型前缀,当你更改类型后还需要更改变量名,忘记改还会误导读者,匈牙利命名法在上个世纪没有强大的IDE时可能有用,但是现在提示变量类型的功能在IDE中已经非常普遍。
另外模板类怎么可能提前指定变量类型。

避免晦涩的缩写

Car ctw;	// bad
Car car_to_wash;	// goodPolygon ply1;	// bad
Polygon first_polygon;	// goodconst double GOE = 9.80665;	// bad
const double gravitation_acceleration_on_earth = 9.80665; // good

2.2 注释

不要为易懂的代码写注释

customer_index++;
Customer* customer = GetCustomerByIndex(customer_index);
CustomerAccount * account = customer->GetAccount();

像这样的代码完全不需要写注释,不要低估代码阅读者的智商。

不要用注释禁用代码

在代码里面不再需要的代码应该直接删除,有的想用注释的方式把一段代码注释掉,以防后面可能会用到,但是这种情况更应该做的的把代码放入代码版本管理工具中,如果有需要去版本工具中回溯。临时注释某些代码来debug是允许的,但是改完bug后应该删除。

特殊情况的注释

对于某些代码的特殊情况添加注释是很有用的。

2.3 函数

只做一件事

函数应该做一件事。做好这件事。只做这一件事

让函数尽可能小

函数短小的好处:

提高代码可维护性和可读性
提高代码的可复用性
方便调试和测试

如果以行数来计,一般不要超过15行,clean code 中说函数的第一要义是要短小,第二要义是要更短小,20行封顶最佳。

函数命名

函数的命名应该具有表达力和准确性,能够清楚地描述函数的功能和作用。使用动词或动词短语作为函数名,以描述函数执行的操作和结果。例如,getMax、setCount 等。

函数的参数和返回值

函数的参数应该尽可能少,最好没有参数,其次是一个参数,一般不要多于三个输入参数。需要返回的参数直接通过返回值返回。有些人可能会考虑在函数返回时会有临时对象的创建复制等过程,导致程序效率不足,但是现代C++体系发展了move语义,在返回的时候大多数情况下是直接move而不是创建临时对象再赋值,move语义的出现已经很大程度的改变了这种变成模式。

2.4 替换C++中的C风格代码

用string代替char*

string是C++中一个重要的组件,在用到字符串的时候首选string而不要使用c风格的char*。

使用标准容器而不是C风格的数组

C++提供很多标准容器,尽量使用标准容器而不要使用数组。最常用的例如使用vector替代c风格的数组。

使用C++的类型转换代替C的强制类型转换

C++ 中用static_cast(),来做强制类型转换。

float a = 1.0;
int b = (int)a; // c风格的强制类型转换,不推荐
int c = static_cast<int>(a); // C++ 风格的强制类型转换,推荐使用

避免使用宏

宏不利于调试:宏定义是在预处理阶段进行文本替换的,如果使用过多的宏定义,会使代码变得杂乱无章,不易于调试。
宏容易引起错误:宏定义中没有类型检查和作用域限制,这就容易导致一些错误的定义或使用。
宏不易于维护:宏定义的可读性较差,理解复杂的宏定义需要花费较大精力,也不易于修改和维护。

如果是定义常量,可以用 constexpr 来修饰。使用 constexpr 关键字可以优化代码性能,因为编译器可以在编译时将常量表达式计算出来,而无需运行时计算,从而避免了运行时开销。

#define PI 3.141592653 // 不推荐
constexpr long long PI = 3.141592653; //推荐

用一般函数替换宏函数。

2.5 资源管理

智能指针

智能指针是一种 RAII(资源获取即初始化)技术的应用,它自动管理对象的生命周期,解决了动态内存分配和回收的问题。C++11 提供了三种智能指针:std::unique_ptr、std::shared_ptr 和 std::weak_ptr(不推荐使用)。

std::unique_ptr:是一个独占式所有权的智能指针,保证同一时间只有一个指针可以访问其所指向的对象。当 unique_ptr 被销毁时,它所指向的对象也会被销毁。

std::shared_ptr:是一个共享所有权的智能指针,可被多个指针同时访问其所指向的对象,在最后一个 shared_ptr 被销毁时才会销毁所指向的对象。

避免显示的使用new和delete

make_unique 和 make_shared 是 C++11 引入的两个模板函数,用于创建智能指针的对象。它们都遵循 RAII 技术的原则,即对象的生命周期与智能指针的生命周期绑定在一起,从而有效避免了内存泄漏、重复释放等问题。
示例:

auto p = std::make_unique<int>(42);
auto p2 = std::make_shared<int>(42);

3. 面向对象

类的设计原则

让类尽可能的小

像函数一样小的类容易测试,容易理解,容易测试,容易复用。

单一职责原则

一个类应该只有一个单一的职责。

开闭原则

类应该对扩展开放,对修改关闭。

里氏替换原则(LSP)

子类对象应该能够替换掉程序中任何父类对象,并且保证不会产生任何负面影响。也就是说,子类继承父类时不能改变父类已有的行为,而是应该遵循父类的约束。

接口隔离原则(ISP)

“客户端不应该依赖于它不需要的接口”,也就是说,一个类不应该被强迫依赖它不使用的方法。简单来说,一个类对另一个类的依赖性,应该建立在最小化的接口上。

接口隔离原则可以减少系统的耦合度,提高系统的内聚性,从而增加系统的可维护性和可扩展性。

无环依赖原则

依赖倒置原则(DIP)

高层模块不应该依赖于低层模块的实现细节,而是应该依赖于抽象。同时,抽象不应该依赖于具体实现,而具体实现应该依赖于抽象。

不要和陌生人说话(迪米特法则)(LoD)

迪米特法则也被称为最少知道原则(LKP)。一个模块应该对外部模块产生最少依赖关系,即每个模块只应该关注与其密切相关的对象。迪米特法则的目的是为了减少系统中的耦合度,提高系统的可维护性、可扩展性和可测试性。

避免类的静态成员

4. 设计模式和习惯用法

依赖注入模式

Dependency Injection
将组件与其需要的服务分离,这样组件就不必知道这些服务的名称,也不必知道如何获取它们。
例如:日志记录器
在这里插入图片描述
常见的注入方式有构造器注入和setter注入。

Adapter模式

把一个类的接口转为期望的另一个接口,让接口不兼容的类可以适配

Strategy模式

定义一组算法,然后封装每个算法,使它们可以相互替换,策略模式允许算法独立于使用它的客户端而变化。
例如:在需要排序的地方定义许多排序算法,快排,堆排,冒泡,希尔排序等等的,在用的时候可以选择。或者在格式化输出的地方,用纯文本、xml、json格式输出文本。

Command模式

常用在client/server架构中

Command处理器模式

Composite模式

将对象组合成树结构来表示“部分-整体”的层次结构。

Observer模式

Factory模式

Strategy模式

Facade模式

Money Class模式

特例用法

参考

[1] https://www.clean-cpp.com/contact/
[2] 《Clean Code》Robert C. Martin
[3] 《C++代码整洁之道:C++17可持续软件开发模式实践》
[4] https://en.cppreference.com
[5] https://refactoringguru.cn/design-patterns/catalog
[6] https://google.github.io/styleguide/


http://chatgpt.dhexx.cn/article/1Df5mw1b.shtml

相关文章

SQL中的左连接与右连接,内连接有什么区别

例子&#xff0c;相信你一看就明白&#xff0c;不需要多说 A表(a1,b1,c1) B表(a2,b2) 左连接&#xff1a; select A.,B. from A left outer join B on(A.a1B.a2) 结果是: 右连接&#xff1a; select A.,B. from A right outer join B on(A.a1B.a2) 结果是: 内连接&#xff1a…

sql左连接、右连接结果总结

班级team 表 班级和教师连接表team_teacher – 左连接: – 对于左连接&#xff0c;查询结果中&#xff0c;left左边表格的记录会全部包含 – 再到left右边表格查&#xff0c;如果匹配结果是0或1&#xff0c;对结果总数没影响 – 如果匹配条数大于1&#xff0c;那么结果就增加…

内连接,左连接和右连接作用

在数据库查询中&#xff0c;经常会遇到多表连接来查询数据&#xff0c;下面来说一下他们的连接方法和作用 内连接&#xff1a;内连接是很常用的一个连接方法&#xff0c;用来查询两个表或者多个表之间相同的数据&#xff0c;例如&#xff1a; 查询的是他们之间相同的数据 内…

SQL中的连接(左、右、内连接)

目录 左连接&#xff08;LEFT JOIN或LEFT OUTER JOIN&#xff09;&#xff1a; 右连接&#xff08;RIGHT JOIN或RIGHT OUTER JOIN&#xff09;&#xff1a; 内连接&#xff08;INNER JOIN也叫等值连接&#xff09;&#xff1a; 全连接&#xff08;FULL JOIN&#xff09;&…

SQL内连接,左连接,右连接区别及联系

目录 数据库建表及其内容 内连接&#xff08;默认连接&#xff09; 左连接 右连接 总结&#xff1a; 数据库建表及其内容 stu表 班级表&#xff08;class&#xff09; 内连接&#xff08;默认连接&#xff09; SELECT stu.stuname , classname FROM class INNER JOIN stu…

SQL中左连接和右连接的理解误区

SQL中左连接和右连接的理解误区&#xff1a; 1.左右外连接中&#xff0c;怎么区分哪个表当左表&#xff0c;哪个表当右表&#xff1f; 2.什么时候用左连接&#xff0c;什么时候用右连接&#xff1f; 首先&#xff0c;重新捋一遍二者概念&#xff1a; 左连接的语法&#xff…

SQL语句什么是左连接、右连接、内连接?

1、左连接 以左表为基础&#xff0c;根据ON后给出的两表的条件将两表连接起来。结果会将左表所有的查询信息列出&#xff0c;而右表只列出ON后条件与左表满足的部分。 左表&#xff08;table1&#xff09;全部保留&#xff0c;右表&#xff08;table2&#xff09;关联不上用n…

sql的左连接,右连接,内连接

环境&#xff1a; SQL工具&#xff08;如Navicat&#xff0c;SQLyog&#xff09; MySQL驱动 全局总结&#xff1a; 左连接:关注左边&#xff0c;右边没有就为空。右连接:关注右边&#xff0c;左边没有就为空。内连接:返回交集 必备&#xff1a; 两张或多张表格 一&#x…

Linux系统之iostat命令的基本使用

Linux系统之iostat命令的基本使用 一、iostat命令介绍二、iostat命令帮助1.1 iostat的帮助信息1.2 iostat的选项解释 三、iostat命令的基本使用3.1 查看iostat工具版本3.2 直接使用iostat命令3.3 间隔5秒查看3次信息3.4 只查看磁盘状态3.5 以k或M为单位显示信息 四、查看磁盘I/…

Linux实时监控iostat命令详解

概述&#xff1a; iostat 主要用于输出CPU和磁盘I/O相关的统计信息。 格式&#xff1a; iostat [选项] [<时间间隔>] [<次数>] 如图 参数&#xff1a; -c 显示CPU使用情况 -d 显示磁盘使用情况 -N 显示磁盘阵列(LVM) 信息 -n 显示NFS 使用情况 -k 以 KB 为单位显…

服务器 iostat命令详解

iostat是I/O statistics&#xff08;输入/输出统计&#xff09;的缩写&#xff0c;iostat工具将对系统的磁盘操作活动进行监视。它的特点是汇报磁盘活动统计情况&#xff0c;同时也会汇报出CPU使用情况。iostat也有一个弱点&#xff0c;就是它不能对某个进程进行深入分析&#…

Linux中iostat命令参数详解

iostat主要用于监控系统设备的IO负载情况&#xff0c;iostat首次运行时显示自系统启动开始的各项统计信息&#xff0c;之后运行iostat将显示自上次运行该命令以后的统计信息。用户可以根据需求指定相应的次数和时间。 一、使用iostat 1命令&#xff1a; 各参数含义 Device:部…

转:linux iostat命令详解

转过来学习学习。 Linux系统中的 iostat是I/O statistics&#xff08;输入/输出统计&#xff09;的缩写&#xff0c;iostat工具将对系统的磁盘操作活动进行监视。它的特点是汇报磁盘活动统计情况&#xff0c;同时也会汇报出CPU使用情况。同vmstat一样&#xff0c;iostat也有一个…

iostat linux,centos安装iostat命令的方法详解

在centos系统中,可以使用yum命令安装iostat。 开始,想当然地运行命令: 复制代码 代码示例: #yum install iostat 出现如下图所示的提示: 经搜索后,知道使用yum安装iostat,需要安装的其实是sysstat。 例如: 复制代码 代码示例: #yum install sysstat 出现如下图所示的安装…

iostat 命令详解(二)

iostat 命令详解(二) iostat常用命令如下: 1,iostat 2,iostat -xk 1 1 3,iostat -dk 1 1 查看吞吐量 总结:

Linux中iostat含义,linux下iostat命令详解

iostat语法 用法&#xff1a;iostat [ 选项 ] [ [ ]] 常用选项说明&#xff1a; -c&#xff1a;只显示系统CPU统计信息&#xff0c;即单独输出avg-cpu结果&#xff0c;不包括device结果 -d&#xff1a;单独输出Device结果&#xff0c;不包括cpu结果 -k/-m&#xff1a;输出结果以…

iostat命令详解——linux性能分析

之前总结uptime和free命令&#xff0c;今天继续来总结一下iostat。给自己留个笔记&#xff0c;同时也希望对大家有用。 版本信息&#xff1a; sysstat version 9.0.4 (C) Sebastien Godard (sysstat <at> orange.fr) 基本使用&#xff1a; iostat [ -c ] [ …

Linux 性能分析之iostat命令详解

Linux 性能分析之iostat命令详解 iostat命令是IO性能分析的常用工具&#xff0c;其是input/output statistics的缩写。本文将着重于下面几个方面介绍iostat命令&#xff1a; iostat的安装iostat命令行选项说明iostat输出内容分析如何确定磁盘IO的瓶颈iostat实际案例 命令的安…

Linux命令---iostat命令详解

简介 iostat主要用于监控系统设备的IO负载情况&#xff0c;iostat首次运行时显示自系统启动开始的各项统计信息&#xff0c;之后运行iostat将显示自上次运行该命令以后的统计信息。用户可以通过指定统计的次数和时间来获得所需的统计信息。 语法 iostat [ -c ] [ -d ] [ -h ] […

iostat命令安装及详解

iostat linux系统出现了性能问题&#xff0c;一般我们可以通过top、iostat、free、vmstat等命令 来查看初步定位问题。其中iostat可以给我们提供丰富的IO状态数据。 iostat 由 Red Hat Enterprise Linux AS 发布。同时 iostat 也是 Sysstat 的一部分。所以我们安装要安装syss…