构造函数未被调用的对象是否可以调用方法: 合法吗?

3
如果为一个对象(例如通过联合)设置了内存,但尚未调用构造函数,假设该方法不依赖于任何成员变量的值,调用对象的非静态方法是否合法?
我进行了一些研究,并发现了一些有关“变量成员”的信息,但我没有找到与此示例相关的信息。
class D {
 public:
  D() { printf("D constructor!\n"); }
  int a = 123;
  void print () const {
    printf("Pointer: %p\n", &a);
  };
};

class C {
 public:
  C() {};
  union {
    D memory;
  };
};

int main() {
  C c;
  c.memory.print();
} 

在这个例子中,我在不调用构造函数的情况下调用print()。意图是稍后调用构造函数,但即使在调用构造函数之前,我们也知道变量a将驻留在哪里。显然,在这一点上,a的值未初始化,但print()并不关心其值。
当使用gcc和clang编译c++11时,这似乎按预期工作。但我想知道我是否在这里引用了某些非法或未定义的行为。

1
嗯?C c;构造了变量c - user207421
2
@user207421,但它不构造c.memory,因为那是一个变体成员,而C的构造函数没有提供初始化程序... - Michael Kenzel
1个回答

5
我认为这是未定义行为。你的变量成员C::memory没有被初始化,因为C的构造函数没有提供初始化程序[class.base.init]/9.2。因此,在调用print()方法的时候,c.memory的生命周期还没有开始[basic.life]/1。根据[basic.life]/7.2

同样,在对象的生命周期开始之前,但在分配对象所需的存储空间后或者在对象的生命周期结束之后,并在对象占用的存储空间被重新使用或释放之前,可以以有限的方式使用任何引用到原始对象的glvalue。 [...] 如果程序有未定义行为:

  • [...]
  • 使用glvalue调用对象的非静态成员函数,或者
  • [...]

强调是我的

注意:我以上是参考当前的C++标准草案,然而,对于C++11来说,相关措辞基本相同,只是在C++11中,D具有非平凡的初始化很关键,否则你所做的可能在C++11中潜在地是可以接受的...


D 具有非平凡的初始化也在这里很重要。 - Ben Voigt
@BenVoigt 如果我错了,请纠正我,但由于memory是一个变体成员,它的生命周期不应该在没有执行显式初始化的情况下开始,它具有非平凡的初始化事实似乎对这种特殊情况没有影响! - Michael Kenzel
如果它具有微不足道的初始化,那么它的生命周期将在分配储存空间(适当大小和对齐等)的同时开始。 - Ben Voigt
1
啊,你链接中的措辞 [basic.life]/1 与 C++11 的措辞非常不同。适用的措辞是:“类型为 T 的对象的生命周期在以下情况下开始: 获得了适当对齐和大小的存储空间以容纳类型 T,并且 如果该对象具有非平凡初始化,则其初始化已完成。” - Ben Voigt
1
@BenVoigt 确实,这非常有趣。看起来在C++17中措辞已经改变为当前形式。我想知道这是否会破坏一些代码,因为新的措辞似乎是与以前的不同保守性的变化... - Michael Kenzel
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接