2015-04-25 111 views
1

我的目标是创建共享某些常用数据的类层次结构类的实例。我创建了(有联合)足够的内存,以便可以在分配的内存中创建最大的实例。现在我想创建/交换类的实例,并在那里的内存中使用“旧”数据。这是有效/合法的操作吗?类层次结构的内存布局

原始代码使用一些MTP东西来创建联合,目标是使用这个类层次作为状态机实现的核心。我只在这里显示包含问题的基本代码。

我看到,如果基类不包含虚拟方法,但派生的方法确实存在问题,那么这是一个问题。这是因为vtable指针在内存前面(在x86/linux上使用gcc)。

简单的问题:如果之前创建了基类的实例并且内存与该派生类的实例重用了内存,则派生类的实例是否可以从基类访问数据?

class Base 
{ 
    public: 
     int a; 
     Base()=default; 
     Base(int _a):a(_a){} 

     void Print() { cout << "Value: " << a << endl; } 
}; 

class Derived1: public Base 
{ 
    public: 
     int d; 

     Derived1(): d(0x11223344){} 
}; 

union U 
{ 
    U(){} 
    Base base; 
    Derived1 derived1; 
} u; 

int main() 
{ 
    memset(&u, 0, sizeof(u)); 

    new (&u) Base(12345678); 
    u.base.Print(); 

    new (&u) Derived1; 
    u.base.Print(); 
} 
+0

做OOP时这是完全错误的做法。你应该使用'Base *'指针,它可以指向任何派生类。 – Barmar

+0

问题不在于如何通过指针访问数据。问题在于层次结构中的布局是否安全以便在层次结构中使用公共数据是安全的。原始代码应该使用派生类中的数据。所提供的代码仅用于查看效果。 – Klaus

回答

1

不,这将无法工作,因为sandard说:

9.5/1:在工会,非静态数据成员的至多一个可以随时被激活也就是说,非静态数据成员中至多有一个值可以随时存储在联合中。

你试图做的是未定义的行为:

new (&u) Derived1; // RISKY !!! 

与放置新的您覆盖,这是u中对象之前,没有正确地破坏它。然后创建Derived1无论如何将创建自己的基地。如果以某种方式设法将旧值保存在内存中,它仍然是未定义的行为:它可以工作或不工作,具体取决于编译器的对象布局和实现。

0

这是未定义的行为 - 它可能似乎工作,但不是可移植的,不应该依赖。

联盟只能有一个活跃成员;构建Derived1使之前的操作无效。