5

考虑以下示例。当bar被构造时,它为它的基类型 ( foo) 构造函数提供了尚未初始化的数据成员my_member.y的地址。my_member

struct foo {
    foo(int * p_x) : x(p_x) {}
    int * x;
};

struct member {
    member(int p_y) : y(p_y) {}
    int y;
};

struct bar : foo
{
    bar() : foo(&my_member.y), my_member(42) {}
    member my_member;
};

#include <iostream>

int main()
{
    bar my_bar;
    std::cout << *my_bar.x;
}

这定义好了吗?获取未初始化对象数据成员的地址是否合法?我发现了这个关于传递对未初始化对象的引用的问题,但它并不完全相同。在这种情况下,我在未初始化的对象上使用成员访问运算符。 .

确实,对象的数据成员的地址不应通过初始化来更改,但这并不一定会使地址得到很好的定义。此外,会员访问运营商的 ccpreference.com 页面有这样的说法:

即使没有必要,也会计算两个运算符的第一个操作数(例如,当第二个操作数命名为静态成员时)。

我理解这意味着在&my_member.y my_member将被评估的情况下,我认为这很好(int x; x;看起来很好),但我也找不到支持它的文档。

4

2 回答 2

7

首先让我们准确地提出问题。

您所做的不是使用未初始化的对象,而是使用不在其生命周期内的对象。my_member是在 之后构造foo的,因此 的生命周期my_member还没有开始于foo(&my_member.y)

来自[basic.life]

在对象的生命周期开始之前但在分配对象将占用的存储空间之后[...],可以使用任何引用原始对象的glvalue,但只能以有限的方式使用。[...] 这样的 glvalue 指的是分配的存储,并且使用不依赖于其值的 glvalue 的属性是明确定义的。如果出现以下情况,该程序具有未定义的行为:

  • 泛左值用于访问对象,或 [...]

这里访问它意味着读取或修改对象的值。

的评估my_member产生一个左值,没有什么需要转换为纯右值,因此它保持一个左值。同样,评估my_member.y也是一个左值。然后我们得出结论,没有对象的值被访问过,这是明确定义的。

于 2018-04-28T14:07:04.793 回答
2

是的,您可以传递&my_member.yfoo的构造函数,甚至可以复制指针 - 您可以使用x(p_x).

尽管在的构造函数中取消引用该指针的行为是未定义的。foo(但你不这样做。)

于 2018-04-25T18:44:50.437 回答