c++ - 如何安全地从粗糙的 C++ 代码中提取函数?

标签 c++ refactoring legacy-code

假设我有一个 5000 行长的深度嵌套函数,我想将 1000 行代码块提取到一个新函数中。

在 Java 和 C# 中,我可以让 ReSharper、IntelliJ 和 Visual C# 处理安全提取方法所需的分析,无论代码多么长和粗糙。我可以确信他们不会改变代码的行为,即使代码太复杂以至于我的小脑袋无法理解。

可用的 C++ 工具无法给我同样的信心。 CLion、ReSharper 和 Visual Assist 在提取方法时都会引入行为变化。

我有哪些选择?

最佳答案

一种选择是使用此配方,它基于 Tennent 对应原则。您可以将它应用于整个 block (用大括号包围)或 ifwhilefor 语句(它们创建自己的范围).

1。 引入一个lambda并调用它

围绕有问题的 block :

[&]() {
    // original code
}();

编译文件。可能的错误:

  • 并非所有控制路径都返回一个值。您有一个提前返回。备份并消除 Early Return/Continue/Break 或提取不同的内容。

  • break/continue 语句只能在...中使用 您有一个 break/continue。备份并消除 Early Return/Continue/Break 或提取不同的内容。

检查新 lambda 是否有任何返回语句。如果有任何返回并且很明显所有代码路径都返回,则在 lambda 之后的下一行添加一个 return 语句。如果有任何返回并且所有代码路径都返回并不明显,则备份并消除 Early Return/Continue/Break 或尝试提取不同的内容。

2。在 lambda 上引入变量

[&]() {
    // ...
}();

变成:

auto Applesauce = [&]() {
    // ...
};
Applesauce();

编译以确保您没有输入错误。

3。设置返回类型

在 lambda 上设置返回类型(即使它是 void)。在 Visual Studio 中,auto 上的工具提示会告诉您类型。

即:

auto Applesauce = [&]() -> SOMETYPE {
    // ...
};

编译以确保返回类型正确。

4。明确捕获

[&]替换为[this](或自由函数中的[])并编译。

对于必须捕获的变量的每个错误: - 复制变量名 - 将其粘贴到捕获列表中,以 & 为前缀 - 重复直到绿色。

即:

auto Applesauce = [this, &foo]() -> void {
    cout << foo;
};

捕获列表的顺序会影响最终函数的参数顺序。如果您希望参数按特定顺序排列,现在是重新排列捕获列表的好时机。

5.将捕获转换为参数

对于每个捕获的局部变量(this 除外) - 转到变量的定义 - 复制变量声明(例如 Column* pCol) - 粘贴到 lambda 参数列表 - 使参数为常量和引用 - 从捕获列表中删除变量 - 将变量传递给调用 - 编译。

即:

Column* pCol = ...
auto Applesauce = [&pCol]() -> void { cout << pCol->name(); };
Applesauce();

成为

Column* pCol = ...
auto Applesauce = [](Column*& pCol) -> void { cout << pCol->name(); };
Applesauce(pCol);

6。将 lambda 转换为函数

如果 this 被捕获,使用 6A。 如果未捕获到 this,则使用 6B。

6A。将 this-bound lambda 转换为成员函数

  • 剪切 lambda 语句并将其粘贴到当前函数之外
  • 删除 = [this]
  • 复制签名行
  • 添加SomeClass::
  • 将签名粘贴到私有(private)部分的类声明中。
  • 编译

即:

auto SomeClass::Applesauce () const -> void {
    // ...
};

6B。将非 this Lambda 转换为自由函数

  • 剪切 lambda 语句并将其粘贴到当前函数之上。
  • 删除 = []
  • 编译

关于c++ - 如何安全地从粗糙的 C++ 代码中提取函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42180798/

相关文章:

C++:处理许多配置变量的设计建议

javascript - 在 Backbone.js View 中多次引用 div

refactoring - 重写继承的代码比修复它更好吗?

java - 从瘦客户端(Web 浏览器)流向已安装并正在运行的应用程序

c++ - 在 C++ 中使用 Link Seam 打破静态变量依赖

C++ LPCTSTR 到 char*

c# - 在网络中保持对象同步?

c++ - 成员变量在评估之前未正确更新

javascript - AngularJS 遗留集成和包含异步更改 id 的内容不会触发观察者

c++ - delete[] 在 C++ 中真的有效吗?