对静态分配的子 class 对象进行静态分配的纯虚拟父 class 引用是否合法?

Is it legal to have statically-allocated pure-virtual-parent-class references to statically-allocated child class objects?

更新:我并不是要人们尝试这个并查看代码是否适合他们。我问的是代码模式是否合法的C++,不管它是否适合你。

我正在调查我认为是 Renesas RX CPU 的 IAR C++ 编译器中的错误。以下代码模式的示例有时可以在我的小型嵌入式系统上运行,有时它会在 parentRefToChildInstance 初始化期间崩溃并跳转到地址 0x00000000(或附近;我也看到过跳转到 0x00000038)。对于给定版本的源代码,编译之间的行为似乎是一致的,但如果代码以看似不相关的方式受到干扰,有时行为会发生变化。

对静态分配的子class对象的纯虚拟父class引用是合法的,还是因为静态分配对象的初始化顺序不能保证?

char aGlobalVar = 0;

struct parent
{
   virtual ~parent() {}
   virtual void method1() = 0;
};

struct child : public parent
{
   child(int someValue) : m_someData(someValue) {}
   virtual ~child() {}
   virtual void method1() { ++aGlobalVar; }
   int m_someData;
};

child childInstance(0x1234abcd);
parent &parentRefToChildInstance = childInstance;

在发生崩溃的情况下,子class对象在parent-class引用被初始化的时候还没有被构造;我怀疑编译器以某种方式使用子对象的 vtable 指针来初始化 parent-class 引用,尽管我还没有确定这一点。 但我认为编译器应该能够初始化一个只知道它引用的对象类型及其地址的引用,这两者应该分别在编译时和 link 时知道.如果那是真的,那么初始化 childInstanceparentRefToChildInstance 的顺序似乎无关紧要。

此外,如果重要的话,我们仍然限于 C++03。

这里有一个 main() 与上面的代码一起...

int main()
{
   printf("aGlobalVar = %u\n", aGlobalVar);
   childInstance.method1();
   printf("aGlobalVar = %u\n", aGlobalVar);
   parentRefToChildInstance.method1();
   printf("aGlobalVar = %u\n", aGlobalVar);
}

通常我希望它打印这个,而不是在静态对象初始化期间崩溃(甚至在 main() 开始之前):

aGlobalVar = 0
aGlobalVar = 1
aGlobalVar = 2

显示的代码是合法的。

的确,当定义在不同的翻译单元中时,在命名空间范围内或作为 static class 成员定义的对象和引用的初始化顺序是不可预测的,这通常会导致讨厌的问题。

但是初始化引用实际上并不需要初始化绑定对象,除非涉及虚拟继承。

C++17 [basic.life] 第 7 段说:

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated..., any glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. Otherwise, such a glvalue refers to allocated storage, and using the properties of the glvalue that do not depend on its value is well-defined. The program has undefined behavior if:

  • the glvalue is used to access the object, or

  • the glvalue is used to call a non-static member function of the object, or

  • the glvalue is bound to a reference to a virtual base class, or

  • the glvalue is used as the operand of a dynamic_cast or as the operand of typeid.

这四件事中的

None 发生在 parentRefToChildInstance 的初始化期间,特别是因为 parent 不是 child 的虚拟基础 class .所以代码属于定义明确的引用要求中提到的情况。