假设我有一个 5000 行长的深度嵌套函数,我想将 1000 行代码块提取到一个新函数中。
在 Java 和 C# 中,我可以让 ReSharper、IntelliJ 和 Visual C# 处理安全提取方法所需的分析,无论代码多么长和粗糙。我可以确信他们不会改变代码的行为,即使代码太复杂以至于我的小脑袋无法理解。
可用的 C++ 工具无法给我同样的信心。 CLion、ReSharper 和 Visual Assist 在提取方法时都会引入行为变化。
我有哪些选择?
最佳答案
一种选择是使用此配方,它基于 Tennent 对应原则。您可以将它应用于整个 block (用大括号包围)或 if
、while
或 for
语句(它们创建自己的范围).
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/