博客 阅读时间 4 分钟

探索 SQLite 代码库:提升您的 C++ 技能

分享本文
Exploring the SQLite Codebase: Improve Your C++ Skills

首次提交代码十六年后,SQLite 已成为世界上部署最广泛的数据库引擎。这样的开源项目是学习如何让代码易于理解和维护的绝佳资源。

让我们从下面这段代码片段开始,来了解 SQLite 代码库的一些事实:

sqlite

关于这个函数,有以下几点观察:

  • 函数声明为 static。
  • 函数返回错误码。
  • 函数只有少量参数。
  • 函数尽早退出。
  • 使用断言检查某些条件。
  • 未使用全局变量。
  • 变量名易于理解。
  • 方法很短。
  • 函数体中没有多余的注释。
  • 函数体缩进良好。

浏览 SQLite 源代码时,我们可以看到实现的一致性。同样的最佳实践规则应用于每一个函数。

以下是可以从 SQLite 代码库中学到的一些最佳实践:

使用结构体存储数据模型

在 C 编程中,函数使用变量来完成处理。这些变量可以是:

  • 静态变量。
  • 全局变量。
  • 局部变量
  • 结构体中的变量。

每个项目都有自己的数据模型,可能被许多源文件使用。使用全局变量是一种选择,但不是好的选择;将数据分组到结构体中通常更为可取。

让我们使用 CQLinq 和CppDepend搜索代码中定义的结构体:

sqlite22

许多结构体被用来定义数据模型。

让函数短小精悍

以下是Linux 编码风格网页上关于函数长度的建议:

Functions should be short and sweet, and do just one thing.  They should
fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24,
as we all know), and do one thing and do that well.

The maximum length of a function is inversely proportional to the
complexity and indentation level of that function.  So, if you have a
conceptually simple function that is just one long (but simple)
case-statement, where you have to do lots of small things for a lot of
different cases, it's OK to have a longer function.

让我们搜索代码行数少于 30 的函数。

sqlite5

超过 90% 的函数代码行数少于 30 行。

封装

封装是指隐藏实现内部的函数和数据。在 C 语言中,封装通过 static 关键字实现。这些实体被称为文件作用域的函数和变量。

让我们执行下面的 CQLinq 查询,搜索所有静态函数。

sqlite

我们可以看到,许多函数被声明为 static。

函数参数数量

参数个数 NbParameters > 8 的函数可能调用起来很痛苦,还可能降低性能。另一种方案是提供一个专门用于传递参数的结构体。

sqlite6

只有少数函数的参数超过 8 个。

局部变量数量

局部变量数 NbVariables 大于 8 的函数可能难以理解和维护。NbVariables 大于 15 的函数则极其复杂,应该拆分为更小的函数(除非它们是由工具自动生成的)。

sqlite7

只有少数函数的局部变量超过 15 个。

避免定义复杂的函数

有许多度量可以检测复杂函数;代码行数(NBLinesOfCode)、参数个数和局部变量个数是其中最基本的。

还有其他一些检测复杂函数的有趣度量:

  • 圈复杂度是一个流行的过程式软件度量,其结果等于一个过程中可能发生的决策数量。
  • 嵌套深度(Nesting Depth)是针对函数定义的度量,表示函数体中嵌套作用域的最大深度。
  • 最大嵌套循环(Max Nested Loop)是函数中循环嵌套的最大层数。

这些度量可容忍的最大值主要取决于团队的选择,因为没有标准值。

让我们搜索可能需要重构的函数:

sqlite8

可以称得上复杂的函数非常少。

保持 const 正确性

C 语言提供了const关键字,用于表示对象不可更改,以及函数不会修改其参数。在所有恰当的地方使用 const 被称为"const 正确性"。刚开始会很难,但使用 const 确实能让您的编码风格更加严谨。

让我们搜索至少拥有一个 const 参数的函数:

sqlite9

函数耦合

调用许多其他函数的函数可能难以理解和维护。建议您尽量减少函数的扇出耦合(efferent coupling)。

在 SQLite 中,扇出耦合很高的函数非常少:

sqlite10

尽可能让函数尽早退出

函数尽早退出——尤其是通过函数顶部的守卫子句——是推荐的做法,因为它们能简化函数后续部分的逻辑。

在 SQLite 源代码中,这一最佳实践几乎应用于所有函数。

结论

研究知名的开源项目始终是提升编程技能的好方法。您无需下载和构建项目——只需在 GitHub 上浏览代码即可。

分享本文