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

Refactor constructor to use initialization list

本文关键字:初始化 列表 构造函数 重构      更新时间:2023-10-16

我正在处理一个非常大的代码库(超过3M loc),我们显然有很多类,但它们中的大多数在构造函数中不使用初始化列表,而是在构造函数体中赋值(有些代码是很久以前编写的,所以这已经成为事实上的标准)。也许这些被编译器优化掉了,但我不确定真的是这样。

我正在尝试推广使用初始化列表,但是有一个大的代码库需要更新,所以有什么工具可以自动为我做这件事吗?把它指向一个类,找到所有的m_var = 0;行并将它们移动到初始化列表(如果需要的话创建它)。

以及转换体内初始化到初始化列表,是否有一种方法来找出成员变量初始化在正确的顺序(即相同的顺序,因为他们在类的头文件中定义?我希望CppCheck会捡起这个,但它似乎没有。

大家好,我是一个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.

我们的简单检查将在订单不匹配时发出警告。这就是为什么它是不确定的。在上面的代码中,初始化顺序实际上并不重要——因为上面代码中的所有成员都是整型,所有初始化式都是常量字面值。

对于OP代码库的规模,需要的是一个程序转换系统(PTS)。这是一种工具,它将目标语言源文件解析为编译器数据结构(通常是ast),允许您对ast应用转换,然后可以重新生成有效的源代码,包括修改后程序的原始注释。把pst看作是在大型中进行重构的工具。

一个好的PTS将允许您编写源到源的转换的形式:

when you see *this*, replace it by *that* if *condition*

其中thisthat用目标语言语法表示,其中this仅在源代码与显式语法匹配时才匹配。[这些不是字符串匹配;它们在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 } ";
这里解释了DMS软件再工程工具包的这些规则/模式的语法。DMS是我所知道的唯一一个可以处理c++的源码到源码的PTS;它甚至可以处理MSVS方言]。

我省略了一个可能必要的"if 条件"检查成员名是否确实是类的成员,假设您的构造函数没有滥用。

因为你的构造函数可能没有任何初始化列表,你需要一个辅助规则在必要时引入一个:

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提供了使用该信息访问符号表的方法]。

或者,您可以使用不同的重写规则简单地重新排序它们:

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

该规则本质上是对初始化式进行排序。[注意,这是冒泡排序:但初始化列表往往不会很长,而且无论如何你只会对每个构造函数运行一次。]

在使用前面所示的规则将所有初始化式从构造函数体中取出后运行此规则。