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

时间:2018-08-23 22:23:50

标签: c++ c++03

更新:我不是在要求人们尝试这种方法,看看代码是否适合他们。我在问代码模式是否合法的C ++,无论它是否对您有用。

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

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

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;

在发生崩溃的情况下,在初始化父类引用时尚未构造子类对象;我怀疑编译器以某种方式使用子对象的vtable指针来初始化父类引用,尽管我还没有确定。 但是我认为编译器应该能够仅知道其引用对象的类型及其地址,就可以初始化一个引用,这两者应分别在编译时和链接时知道。如果是这样,那么初始化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

1 个答案:

答案 0 :(得分:2)

显示的代码是合法的。

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

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

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

  

在一个对象的生命周期开始之前,但是在分配了该对象将要占用的存储空间之后……,可以使用任何引用原始对象的glvalue,但是只能以有限的方式使用。有关正在构造或销毁的对象,请参见[class.cdtor]。否则,此类glvalue指分配的存储,并且使用不依赖于其值的glvalue属性是明确定义的。该程序在以下情况下具有未定义的行为:

     
      
  • glvalue用于访问对象,或者

  •   
  • glvalue用于调用对象的非静态成员函数,或者

  •   
  • glvalue绑定到对虚拟基类的引用,或者

  •   
  • glvalue用作dynamic_cast的操作数或typeid的操作数。

  •   

parentRefToChildInstance初始化期间,这四件事均未发生,特别是因为parent不是child的虚拟基类。因此,该代码属于定义明确的引用要求中提到的情况。