工会活跃成员背后的理由

时间:2017-10-25 13:00:09

标签: c++ unions

C ++的工会比C的工会更具限制性,因为它们引入了“活跃成员”(最后一个被指定的成员)的概念作为唯一可以安全访问的成员。我看待它的方式,工会的这种行为是净负面的。有人可以解释一下这个限制所带来的好处吗?

1 个答案:

答案 0 :(得分:1)

简短回答

在C中,联合只是如何解释存储在给定位置的数据的问题。数据是被动的。

在C ++中,工会可以拥有不同类的成员。类对象不仅具有数据,还具有行为。当您依赖这种(可访问的)行为(并且甚至无法访问私有和受保护的成员)时,必须确保该对象从其构造到其破坏保持一致。活跃成员的概念完全出于此目的:确保对象生命周期是一致的。

更长的解释

想象一下以下的联盟:

union U {
    string s;
    int i;

    // due to string, I need to define constructor and destructor
    U (string s) : s(s) { cout << "s is active"<<endl;}
    U (int i) : i(i) { cout << "i is active"<<endl;}
    U() : s() { cout << "s is active by default" <<endl; }
    ~U() { cout << "delete... but what ?"<<endl; }
};

现在假设我初始化它:

U u("hello"); 

此时活跃成员为s。我现在可以毫无风险地使用这个活跃的成员:

u.s += ", world";  
cout << u.s <<endl;

在更改活动成员之前,我必须确保成员的生命周期结束(根据C ++标准的要求)。如果我忘记了这一点,例如使用其他成员:

u.i=0;  // ouch!!! this is not the active member : what happens to the string ?  

我有未定义的行为(在这里实际上,s现在已经损坏,并且不再可能恢复存储字符的内存)。你也可以想象相反的情况。假设活动成员是i,我现在想要使用字符串:

u.s="goodbye";  // thinks that s is an active valid string which is no longer the case 

这里,编译器确认我知道s是活动成员。但由于s不是正确初始化的字符串,执行复制操作符也会导致未定义的行为。

Demo of what you should not do

如何正确做到?

标准解释了它:

  

如果M有一个非平凡的析构函数,N有一个非平凡的构造函数   (例如,如果他们声明或继承虚函数),   你的活跃成员可以使用安全地从m切换到n   析构函数和放置new-expression如下:

u.m.~M();
new (&u.n) N;

所以在我们讨厌的例子中,以下内容可行:

u.s.~string(); // properly end the life of s
u.i=0;  // this is now the active member   
           // no need to end life of an int, as it has a trivial destructor 
new (&u.s) string("goodbye");  // placement new  
cout << u.s <<endl;    

Demo of how to (almost) do it right