原文链接:

OOP53-CPP. Write constructor member initializers in the canonical order


类构造函数的成员初始化允许成员被初始化为指定的值,和调用指定参数的基类构造函数。然而,初始化发生的顺序是固定的,并且不依赖所写的成员初始化列表的顺序。 C++ 标准, [class.base.init], 段 11 [ISO/IEC 14882-2014], 陈述如下:

In a non-delegating constructor, initialization proceeds in the following order:
— First, and only for the constructor of the most derived class, virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.
— Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
— Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
— Finally, the compound-statement of the constructor body is executed.
[Note: The declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. —end note]

所以,成员初始化的顺序与出现在成员初始化列表中的顺序是不相关的。成员被初始化的顺序,包括基类初始化,取决于类成员变量或者基类修饰符列表被声明的顺序。不遵循规范顺序编写成员初始化能导致 未定义行为,undefined behavior,比如读取未初始化内存。

始终按照规范顺序编写构造函数中成员的初始化:首先是在出现在类的 base-specific-list 的直接基类的顺序,然后是在类定义中被声明的非静态数据成员顺序。

不合规代码示例

在这个不合规代码代码示例中,C::C() 的成员初始化列表试图先初始化 someVal,然后初始化依赖于 someValdependsOnSomeVal 的值。因为成员变量的声明顺序于成员初始化顺序不匹配,试图读取 someVal 的值导致 不确定值,unspecified value 存储到 dependsOnSomeVal

1
2
3
4
5
6
7
class C {
int dependsOnSomeVal;
int someVal;

public:
C(int val) : someVal(val), dependsOnSomeVal(someVal + 1) {}
};

合规方案

该合规方案改变了类成员变量定义的顺序,以便于在构造函数成员初始化列表中的依赖项可以按照正确排序。

1
2
3
4
5
6
7
class C {
int someVal;
int dependsOnSomeVal;

public:
C(int val) : someVal(val), dependsOnSomeVal(someVal + 1) {}
};

依赖之前已被初始化的值对于初始化器来说是合理的。

不合规代码示例

在这个不合规的代码示例中,派生类, D,试图从基类,B2 获取的值来初始化基类,B1。然而,因为基于基类修饰符列表定义的顺序, B1B2 之前初始化,该行为的行为是 未定义的,undefined.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class B1 {
int val;

public:
B1(int val) : val(val) {}
};

class B2 {
int otherVal;

public:
B2(int otherVal) : otherVal(otherVal) {}
int get_other_val() const { return otherVal; }
};

class D : B1, B2 {
public:
D(int a) : B2(a), B1(get_other_val()) {}
};

合规方案

该合规方案使用从构造函数参数列表中获取的值来初始化两个基类,二不是依赖基类初始化的顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class B1 {
int val;

public:
B1(int val) : val(val) {}
};

class B2 {
int otherVal;

public:
B2(int otherVal) : otherVal(otherVal) {}
};

class D : B1, B2 {
public:
D(int a) : B1(a), B2(a) {}
};

例外

OOP53-CPP-EX0: 构造函数如果不使用成员初始化则不违反此规则。

Risk Assessment

Rule Severity Likelihood Remediation Cost Priority Level
OOP53-CPP Medium Unlikely Medium P4 L3

Automated Detection

Tool Version Checker Description
Tool Version Checker Description
Astrée 20.10 **initializer-list-order ** Fully checked
Axivion Bauhaus Suite 7.2.0 CertC++-OOP53
Clang 3.9 -Wreorder
CodeSonar 6.2p0 LANG.STRUCT.INIT.OOMI Out of Order Member Initializers
Helix QAC 2022.1 C++4053
Klocwork 2022.1 CERT.OOP.CTOR.INIT_ORDER
LDRA tool suite 9.7.1 206 S** ** Fully implemented
Parasoft C/C++test 2021.2 CERT_CPP-OOP53-a List members in an initialization list in the order in which they are declared
Polyspace Bug Finder R2021b CERT C++: OOP53-CPP Checks for members not initialized in canonical order (rule fully covered)
PRQA QA-C++ 4.4 **4053 **
RuleChecker 20.10 **initializer-list-order ** Fully checked
SonarQube C/C++ Plugin 4.10 S3229

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Bibliography

[ISO/IEC 14882-2014] Subclause 12.6.2, “Initializing Bases and Members”
[Lockheed Martin 2005] AV Rule 75, Members of the initialization list shall be listed in the order in which they are declared in the class

img img img