重构构造函数使用初始化列表
我正在一个非常大的代码库(超过3M loc),我们显然有很多类,但他们大多数不在他们的构造函数中使用初始化列表,而是分配值构造函数体(一些代码在很久以前被写入了一个LONG,所以这已经成为事实上的标准)。也许这些被编译器优化掉了,但我不确定是否真的如此。重构构造函数使用初始化列表
我想推广初始化列表的使用,但有一个大的代码库需要提供最新的,所以有没有任何工具会自动为我做这个?将它指向一个类,找到所有m_var = 0;
行并将它们移动到初始化列表(如果需要,创建它)。
除了将体内初始化转换为初始化列表外,是否有一种方法可以找到成员变量是按照正确的顺序初始化的(即它们在类头文件中定义的顺序相同?本来希望CppCheck会挑这个,但它似乎没有没有。
你好,我是一个cppcheck开发商。
Cppcheck也有不匹配顺序检查,但它是一个不确定的检查。
例如:
class Fred {
public:
Fred() : y(0), x(0) {}
int x;
int y;
};
Cppcheck输出:
[email protected]:~/cppcheck$ ./cppcheck --enable=style --inconclusive 1.cpp
Checking 1.cpp ...
[1.cpp:3] -> [1.cpp:4]: (style, inconclusive) Member variable 'Fred::x' is in the wrong place in the initializer list.
我们简单的检查将只警告时的顺序不匹配。这就是为什么它没有结果。在上面的代码中,初始化顺序实际上并不重要 - 因为上面代码中的所有成员都是整数,所有初始化方法都是常量文字。
与OP的代码库大小一样,需要的是program transformation system (PTS)。这是一个将目标语言源文件解析为编译器数据结构(通常是AST)的工具,允许您将转换应用到AST,然后可以重新生成有效的源代码,包括修改程序的原始注释。把PST看作在大型中重构的工具。
一个很好的PTS将让你写源到源转换形式的:
when you see *this*, replace it by *that* if *condition*
其中这和是在目标语言的语法表达,其中这仅当源代码与显式语法匹配时才匹配。 [这些不是字符串匹配;他们在AST上工作,所以布局不会影响他们匹配的能力]。
你需要钥匙规则看起来是这样的:
rule move_to_initializer(constructor_name:IDENTIFIER,
arguments: argument_list,
initializer_list: initializer,
member_name:IDENTIFIER,
initializer_expression: expression,
statements: statement_list
): constructor -> constructor =
" \constructor_name(\arguments): \initializer_list
{ \member_name = \initializer_expression ;
\statements } "
-> " \constructor_name(\arguments): \initializer_list, \member_name(\initializer_expression)
{ \statements } ";
The syntax of these rules/patterns for our DMS Software Reengineering Toolkit is explained here。 DMS是唯一可以处理C++的源码到源码PTS;它甚至可以处理MSVS方言]。
我忽略了可能必要的“如果条件”检查成员名称确实是类的成员,假设您的构造函数不是滥用。
因为你的构造可能没有任何初始化列表,你需要一个帮手规则介绍一个在必要时:
rule move_to_initializer(constructor_name:IDENTIFIER,
arguments: argument_list,
member_name:IDENTIFIER,
initializer_expression: expression,
statements: statement_list
): constructor -> constructor =
" \constructor_name(\arguments)
{ \member_name = \initializer_expression ;
\statements } "
-> " \constructor_name(\arguments): \member_name(\initializer_expression)
{ \statements } ";
{ \member_name = \e ; } "
不变的是你需要额外的规则,以涵盖其他特殊情况,但它不应该超过一些。
关于对订单的初始化检查,则可以使用(DMS)图案触发这样的检查:
pattern check_initializer_order(constructor_name:IDENTIFIER,
initializer_list: initializer,
statements: statement_list
): constructor =
" \constructor_name(): \initializer_list,
{ \statements } "
if complain_if_not_ordered(constructor_name,initializer_list);
这需要一个辅助的元谓词检查顺序,如果它们是抱怨乱序。您需要constructor_name来使谓词能够查找相应的类并检查成员的顺序。 [DMS向means to access a symbol table提供这些信息]。
或者,你可能只是简单地重新排列它们使用不同的重写规则:
rule order_initializers(constructor_name:IDENTIFIER,
arguments: argument_list,
initializer_list_prefix: initializer,
initializer_list_suffix: initializer,
member1_name:IDENTIFIER,
initializer1_expression: expression,
member2_name:IDENTIFIER,
initializer2_expression:expression,
statements: statement_list
): constructor -> constructor =
" \constructor_name(\arguments):
\initializer_list_prefix,
\member1_name(\initializer1),
\member2_name(\initializer2),
\initialize_list_suffix
{ \statements } "
->
" \constructor_name(\arguments):
\initializer_list_prefix,
\member2_name(\initializer2),
\member1_name(\initializer1),
\initialize_list_suffix
{ \statements } "
if is_wrong_order(constructor_name,member1_name,member2_name);
这个规则基本上排序初始化。 [请注意,这是一个冒泡排序:但是初始化列表通常不会很长,并且您只需要为每个构造函数运行一次。]在将所有初始化器从构造函数体中提取出来之后,您将运行此规则前面显示的规则。
至于第二部分:GCC有'-Wreorder'。 – Biffen
@Biffen是不是Visual Studio 2012?这就是我正在使用的。 – fwgx
不是我所知道的,一个快速的谷歌没有给任何东西。我会留给你做更多的研究。 – Biffen