重构构造函数使用初始化列表

时间:2016-11-14 15:09:03

标签: c++ c++11 visual-studio-2012 automated-refactoring cppcheck

我正在开发一个非常大的代码库(超过3M loc)我们显然有很多类,但是大多数类都没有在它们的构造函数中使用初始化列表,而是在构造函数体中分配值(一些代码是在很久以前写的,所以这已成为事实上的标准)。也许这些都是由编译器优化的,但我不确定是否真的如此。

我正在尝试推广使用初始化列表,但是有一个需要更新的大型代码库,那么有没有任何工具可以自动执行此操作?将它指向一个类,找到所有m_var = 0;行并将它们移动到初始化列表(如果需要,可以创建它)。

除了将体内初始化转换为初始化列表之外,还有一种方法可以找出成员变量以正确的顺序初始化(即与类'头文件中定义的顺序相同?我希望CppCheck会选择这个,但它似乎并没有。

2 个答案:

答案 0 :(得分:2)

您好我是一名cppcheck开发人员。

Cppcheck还检查了不匹配的订单。但这是一个不确定的检查。

例如:

class Fred {
public:
    Fred() : y(0), x(0) {}
    int x;
    int y;
};

Cppcheck输出:

daniel@debian:~/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.

我们的简单检查只会在订单不匹配时发出警告。这就是为什么它没有结果。在上面的代码中,初始化顺序实际上并不重要 - 因为上面代码中的所有成员都是整数,所有初始值都是常量文字。

答案 1 :(得分:1)

与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);

此规则基本上对初始化程序进行排序。 [注意这是一个冒泡排序:但初始化程序列表往往不会很长,而且你只需要为每个构造函数运行一次。]在将所有初始化程序从构造函数中取出后,您将运行此规则身体使用前面显示的规则。