【问题标题】:Member initializer list. Order of arguments evaluation成员初始化器列表。参数评估顺序
【发布时间】:2019-01-05 09:37:12
【问题描述】:

带有成员初始化器列表的代码示例。

#include <memory>

struct Throwable
{
    Throwable()
    {
        throw "Exception!";
    }
};

struct A
{
    A() : t(Throwable()), i(new int()) {}

    Throwable t;
    std::unique_ptr<int> i;
};

如果可能有以下评估顺序,我会不会有内存泄漏?

  1. new int()
  2. Throwable()
  3. t()
  4. i()

标准中的顺序是什么?我们有一些规则。

https://en.cppreference.com/w/cpp/language/initializer_list

3) 然后,按照类定义中的声明顺序初始化非静态数据成员。

所以t 将在i 之前构造。

https://en.cppreference.com/w/cpp/language/eval_order

9) 内置逗号运算符, 的第一个(左)参数的每个值计算和副作用在第二个(右)参数的每个值计算和副作用之前排序。

但是成员初始化器列表由于之前的引用没有使用所有的逗号规则。而且它不是逗号运算符 (https://en.cppreference.com/w/cpp/language/operator_other#Built-in_comma_operator)。

10) 在列表初始化中,给定初始化器子句的每个值计算和副作用都排在与任何初始化器子句相关联的每个值计算和副作用之前,该初始化器子句在括号括起来的逗号分隔的初始化器列表中。

还有https://en.cppreference.com/w/cpp/language/list_initialization

列表初始化在以下情况下进行:

5) 如果使用了braced-init-list,则在构造函数的成员初始化列表中

我还有一个案例。

您能否提供定义成员初始化器列表中参数评估顺序的规则?

【问题讨论】:

  • 申报顺序?
  • std::make_unique 会避免任何“风险”。
  • '评估顺序'。我需要“sequenced-before”(en.cppreference.com/w/cpp/language/eval_order)方面的答案。
  • @JesperJuhl:因为标准是拜占庭式的,而且这是来自实际上设法主要理解其中定义的 C++ 对象模型的人。例如,我希望在定义了成员初始化器列表的 [class.base.init] 中找到这个问题的答案,但它不存在。它显然是在 [intro.execution] 的 10 章之前。为什么?因为它碰巧被一般性陈述而不是特定陈述所涵盖。标准中的信息通常足够分散,因此提出相关问题并非不合理。
  • @JesperJuhl standardese 不一定简单明了。我知道我花了很长时间才达到对阅读标准有信心的地步。即使多年后,有时我仍然很难确定我的解释是正确的。从他的问题中可以看出,他确实在努力寻找答案。虽然“RTFM”可能是对许多问题的有效回答,但我认为“在标准中查找”并不是对合法 C++ 问题的有用回答,尤其是明确询问标准内容的问题……跨度>

标签: c++ c++11 language-lawyer initializer-list member-initialization


【解决方案1】:

不,保证不会发生这种可能导致您泄漏的评估顺序。每个 mem-initializer 中的表达式是一个 full-expression ([intro.execution] §5.3)。 [intro.execution] §9 告诉我们

与完整表达式关联的每个值计算和副作用都在与要评估的下一个完整表达式关联的每个值计算和副作用之前排序。

因此,换句话说,用于初始化成员的每个表达式的计算在开始计算下一个成员的表达式之前完成。

【讨论】:

    【解决方案2】:

    如果可能出现以下情况,我会不会有内存泄漏 评价顺序?

    答案是否定的,

    因为非静态成员的初始化顺序被定义为与类定义中成员声明的顺序相同,与顺序无关 在成员初始化列表中, 那是因为销毁需要按照构造的相反顺序执行, 为了实现这一点,必须在破坏开始时遵循规则。 关于这一点,由于您在任何其他成员之前声明了类型为 throwable 的成员,它将首先构造并在堆分配发生之前抛出。 没有泄漏。

    但是如果你在声明 unique_pointer 之后声明了 throwable 类型的成员,那么在初始化过程中堆分配会先发生,然后 即,从 throwable 构造函数中抛出异常,将停止 A 构造,即使被捕获,异常也会在异常处理程序执行后重新抛出,但遵循任何构造的类成员也会被破坏的规则,unique_pointer 的资源管理功能将阻止泄漏,在这种情况下也是如此。

    我希望这个小小的观察会有所帮助。

    【讨论】:

      猜你喜欢
      • 2010-12-08
      • 2010-11-17
      • 1970-01-01
      • 2017-04-03
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多