如何从gnarly C ++代码中安全地提取函数?

时间:2017-02-11 20:21:08

标签: c++ refactoring legacy-code

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

在Java和C#中,我可以让ReSharper,IntelliJ和Visual C#处理安全提取方法所需的分析,无论代码有多长和多长。我可以确信他们不会改变代码的行为,即使这对我的小脑子来说太难理解了。

可用的C ++工具无法给予我相同的信心。 CLion,ReSharper和Visual Assist都会在提取方法时引入行为更改。

我有什么选择?

1 个答案:

答案 0 :(得分:6)

一种选择是根据Tennent的通信原则使用此配方。您可以将它应用于整个块(由大括号括起)或ifwhilefor语句(创建自己的范围)。

1。 介绍一个lambda并将其命名为

围绕块包围:

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

编译文件。可能的错误:

  • 并非所有控制路径都会返回值。您可以提早返回。备份并消除早期返回/继续/中断或提取不同的内容。

  • 中断/继续声明只能在...内使用您有休息/继续。备份并消除早期返回/继续/中断或提取不同的内容。

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

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参数列表中   - 使参数const和by-reference   - 从捕获列表中删除变量   - 将变量传递给呼叫   - 编译。

即:

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。将此绑定的lambda转换为成员函数

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

即:

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

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

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