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

How can I safely extract a function from gnarly C++ code?

本文关键字:C++ 代码 函数 提取 何安全 安全      更新时间:2023-10-16

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

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

现有的C++工具无法给我同样的信心。CLion、ReSharper和Visual Assist在提取方法时都会引入行为更改。

我有什么选择?

一种选择是根据Tennent的对应原理使用此配方。您可以将其应用于整个块(用大括号包围)或ifwhilefor语句(它们创建自己的作用域)。

1.引入lambda并将其称为

用包围有问题的块

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

编译文件可能的错误:

  • 并非所有控制路径都返回一个值你很早就回来了。备份并消除提前返回/继续/中断或提取不同的内容。

  • 中断/继续语句只能在…中使用你可以休息/继续。备份并消除提前返回/继续/中断或提取不同的内容。

检查新lambda中是否有任何返回语句如果有任何返回,并且很明显所有代码路径都返回,那么在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参数列表-通过引用使参数常量和-从捕获列表中删除变量-将变量传入调用-编译。

即:

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语句并将其粘贴到当前函数上方
  • 删除= []
  • 编译