成员初始化器列表。参数评估顺序
Member initializer list. Order of arguments evaluation
带有成员初始值设定项列表的代码示例。
#include <memory>
struct Throwable
{
Throwable()
{
throw "Exception!";
}
};
struct A
{
A() : t(Throwable()), i(new int()) {}
Throwable t;
std::unique_ptr<int> i;
};
如果有下面的求值顺序,我会不会内存泄漏?
new int()
Throwable()
t()
i()
标准中的顺序是什么?我们有一些规则。
https://en.cppreference.com/w/cpp/language/initializer_list
3) Then, non-static data members are initialized in order of declaration in the class definition.
所以t
会在i
之前构造。
https://en.cppreference.com/w/cpp/language/eval_order
9) Every value computation and side effect of the first (left) argument of the built-in comma operator ,
is sequenced before every value computation and side effect of the second (right) argument.
但由于之前的引用,成员初始值设定项列表并未使用所有逗号规则。而且它不是逗号运算符 (https://en.cppreference.com/w/cpp/language/operator_other#Built-in_comma_operator).
10) In list-initialization, every value computation and side effect of a given initializer clause is sequenced before every value computation and side effect associated with any initializer clause that follows it in the brace-enclosed comma-separated list of initalizers.
和https://en.cppreference.com/w/cpp/language/list_initialization
List initialization is performed in the following situations:
5) in a member initializer list of a constructor if braced-init-list is used
我还有一个案例。
您能否提供定义成员初始值设定项列表中参数评估顺序的规则?
不,保证不会发生这种可能导致您泄漏的评估顺序。每个mem-initializer中的表达式是一个完整表达式([intro.execution] §5.3). And [intro.execution] §9告诉我们
Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.
换句话说,用于初始化成员的每个表达式的计算都在下一个成员的表达式计算开始之前完成。
Can I have got a memory leak if there could be the following
evaluation order?
答案是否定的,
因为非静态成员的初始化顺序定义为与 class 定义中成员声明的顺序相同,与顺序无关
在成员初始值设定项列表中,
那是因为销毁需要以与构建相反的顺序执行,
为了实现这一目标,必须在破坏开始时遵守规则。
关于这一点,因为您在任何其他成员之前声明了 throwable 类型的成员,它将首先构造并在堆分配发生之前抛出。
没有泄漏。
但是如果你在unique_pointer声明之后声明throwable类型的成员,在初始化过程中堆分配会先发生,然后再发生
从 throwable 构造函数中抛出异常,将停止 A 构造,即使被捕获,异常将在异常处理程序执行后重新抛出,但遵循任何构造的 class 成员也将被破坏的规则,资源管理功能的 unique_pointer 将防止泄漏,在这种情况下也是如此。
希望这个小小的观察对您有所帮助。
带有成员初始值设定项列表的代码示例。
#include <memory>
struct Throwable
{
Throwable()
{
throw "Exception!";
}
};
struct A
{
A() : t(Throwable()), i(new int()) {}
Throwable t;
std::unique_ptr<int> i;
};
如果有下面的求值顺序,我会不会内存泄漏?
new int()
Throwable()
t()
i()
标准中的顺序是什么?我们有一些规则。
https://en.cppreference.com/w/cpp/language/initializer_list
3) Then, non-static data members are initialized in order of declaration in the class definition.
所以t
会在i
之前构造。
https://en.cppreference.com/w/cpp/language/eval_order
9) Every value computation and side effect of the first (left) argument of the built-in comma operator
,
is sequenced before every value computation and side effect of the second (right) argument.
但由于之前的引用,成员初始值设定项列表并未使用所有逗号规则。而且它不是逗号运算符 (https://en.cppreference.com/w/cpp/language/operator_other#Built-in_comma_operator).
10) In list-initialization, every value computation and side effect of a given initializer clause is sequenced before every value computation and side effect associated with any initializer clause that follows it in the brace-enclosed comma-separated list of initalizers.
和https://en.cppreference.com/w/cpp/language/list_initialization
List initialization is performed in the following situations:
5) in a member initializer list of a constructor if braced-init-list is used
我还有一个案例。
您能否提供定义成员初始值设定项列表中参数评估顺序的规则?
不,保证不会发生这种可能导致您泄漏的评估顺序。每个mem-initializer中的表达式是一个完整表达式([intro.execution] §5.3). And [intro.execution] §9告诉我们
Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.
换句话说,用于初始化成员的每个表达式的计算都在下一个成员的表达式计算开始之前完成。
Can I have got a memory leak if there could be the following evaluation order?
答案是否定的,
因为非静态成员的初始化顺序定义为与 class 定义中成员声明的顺序相同,与顺序无关 在成员初始值设定项列表中, 那是因为销毁需要以与构建相反的顺序执行, 为了实现这一目标,必须在破坏开始时遵守规则。 关于这一点,因为您在任何其他成员之前声明了 throwable 类型的成员,它将首先构造并在堆分配发生之前抛出。 没有泄漏。
但是如果你在unique_pointer声明之后声明throwable类型的成员,在初始化过程中堆分配会先发生,然后再发生 从 throwable 构造函数中抛出异常,将停止 A 构造,即使被捕获,异常将在异常处理程序执行后重新抛出,但遵循任何构造的 class 成员也将被破坏的规则,资源管理功能的 unique_pointer 将防止泄漏,在这种情况下也是如此。
希望这个小小的观察对您有所帮助。