C++代码审查:检查什么才能让代码不“爆炸”

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么C++代码审查不是胆小者能做的

\n\n

C++是一种赋予程序员惊人力量的语言。但正如《蜘蛛侠》中的本叔叔所说:“能力越大,责任越大”。在Python中,如果你不小心混用了int和str,可能只会得到一个异常;但在C++中,同样的疏忽可能导致未定义行为(UB),它可能会在一个月后的凌晨3点,在客户服务器上爆发。

\n\n

C++代码审查不是Jira中的一个勾选框。它是你抵御段错误、内存泄漏和慢如蜗牛代码的盾牌。许多开发者(尤其是从Java或Python转来的)认为代码审查只是检查代码风格和变量命名。在C++中,情况要深入得多。你需要审视程序的灵魂:内存管理、RAII、常量正确性,以及那个智能指针是否会反过来伤到你。

\n\n

在本文中,我整理了一份用于C++代码检查的实用清单。在进行下一次代码审查时,请务必使用它。我们开始吧!

\n\n

1. 内存泄漏与资源管理(RAII)

\n\n

这是C++的核心。如果你看到new没有对应的delete——这是一个危险信号。但在现代C++(C++11及更高版本)中,我们根本不应该手动编写new。请使用智能指针:std::unique_ptr、std::shared_ptr。如果开发者使用原始指针(int*)来拥有资源——这是一个需要质疑的理由。

\n\n

糟糕代码示例(危险信号):

\n\n
void process() {\n    int* data = new int[1000];\n    // ... 一些代码 ...\n    if (error) {\n        return; // 内存泄漏!未调用 delete\n    }\n    delete[] data;\n}\n
\n\n

良好代码示例(RAII):

\n\n
void process() {\n    std::vector<int> data(1000);\n    // ... 代码 ...\n    if (error) {\n        return; // 没问题,vector 会自动清理内存\n    }\n}\n
\n\n

C++代码审查中要检查什么:

\n
    \n
  • 代码中是否有new/delete?如果有——为什么?为什么不用std::make_unique?
  • \n
  • 是否使用std::unique_ptr进行独占所有权?
  • \n
  • 是否在只需要引用(由于原子计数器,这很昂贵)的地方按值传递了std::shared_ptr?
  • \n
  • 是否存在异常安全方面的泄漏?
  • \n
\n\n

2. 未定义行为(Undefined Behavior)

\n\n

在C++中,编译器信任程序员。如果你写了无意义的代码,编译器不会责备你——它只会生成可能做任何事情的代码。UB是C++的祸害。以下是在审查中需要捕捉的内容:

\n\n
    \n
  • 数组越界:对于10个元素的数组使用arr[10]。
  • \n
  • 移动后使用变量:std::move不是魔法,移动后的对象处于有效但未定义的状态。
  • \n
  • 有符号整数溢出:INT_MAX + 1是UB!(与Java不同,在Java中这只是简单的回绕)。
  • \n
  • 悬空引用:返回对局部变量的引用。
  • \n
\n\n

UB示例(在代码审查中查找):

\n\n
int& getValue() {\n    int x = 42;\n    return x; // UB!x在函数退出后被销毁\n}\n\nvoid example() {\n    int& ref = getValue();\n    std::cout << ref; // 可能输出42,也可能输出垃圾或崩溃\n}\n
\n\n

给审查者的建议:如果看到涉及int的算术运算,尤其是在循环中或处理大小时——检查是否会发生溢出。最好使用size_t作为索引,并使用std::ssize()进行安全比较。

\n\n

3. 常量正确性:让一切成为常量

博客

书籍推荐