联盟作为基类 [英] Unions as Base Class

查看:139
本文介绍了联盟作为基类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

标准定义了Unions不能用作Base类,但是有什么具体的推理吗?据我所知,Unions可以有构造函数,析构函数,成员变量和方法来操作这些变量。简而言之,联盟可以封装可能通过成员函数访问的数据类型和状态。因此,在大多数常见的术语有资格作为一个类,如果它可以作为一个类,那么为什么它被限制作为一个基类?



编辑:虽然答案试图解释推理,我仍然不明白联盟作为Derived类如何比联盟只是一个类更糟糕。所以希望得到更具体的答案和推理,我会推这个一个赏金。没有进攻已经发布的答案,谢谢你们!

解决方案

Tony Park给出了一个非常接近真相的答案。 C ++委员会基本上不认为使工会成为C ++的强大部分是值得的,类似于数组的处理,我们不得不从C继承而不是真正想要的。



联合会有问题:如果我们允许非POD类型在联合会,他们如何构建?这当然可以做,但不一定安全,任何考虑都需要委员会资源。最后的结果将不太令人满意,因为在一个合理的语言中真正需要的是歧视的工会,而裸露的工会不可能被提升到与C兼容的歧视工会(我可以想象,无论如何) p>

要详细说明技术问题:因为你可以在一个结构中包装一个POD组件的联合而不会丢失任何东西,没有优点允许联合作为基础。使用POD-only union组件,显式构造函数只需分配一个组件,也不使用bitblit(memcpy)编译器生成的复制构造函数(或赋值),就没有问题。



然而,这样的工会不够用,不能保留它们,所以现有的C代码可以被认为是有效的C ++。这些仅限于POD的联合体在C ++中被破坏,因为它们不能保留它们在C中拥有的重要的不变性:任何数据类型都可以用作组件类型。



unions有用,我们必须允许可构造类型作为成员。这是非常重要的,因为在构造函数主体中分配一个组件是不可接受的,无论是union本身,还是任何封闭的struct:例如,不能为未初始化的字符串组件分配一个字符串。



接下来必须发明一些用mem初始化初始化union组件的规则,例如:

  union X {string a;字符串b; X(string q):a(q){}}; 

但现在的问题是:什么是规则?通常规则是你必须初始化类的每个成员和基类,如果你不明确这样做,默认的构造函数用于余数,如果一个没有显式初始化的类型没有默认的构造函数,它是一个错误[异常:复制构造函数,默认是成员复制构造函数]。



很明显,此规则不能用于联合:规则必须改为: union至少有一个非POD成员,则必须在构造函数中显式初始化一个成员。在这种情况下,不会生成默认构造函数,复制构造函数,赋值运算符或析构函数,如果这些成员中有任何实际使用,则必须显式提供。



所以现在的问题变成:你会怎么写,复制构造函数?当然,如果你设计联合的方式,也就是说,X-Windows事件联合是设计的:在每个组件中使用判别式标签,但是你必须使用placement运算符new来做它是很可能做到的。 ,并且您必须打破我上面写的规则,第一眼看上去是正确的!



默认构造函数怎么办?如果你没有其中一个,你不能声明一个未初始化的变量。



还有其他情况下,你可以从外部确定组件,在外部管理联合,但这不是一个复制构造函数。事实是,如果你有N个组件,你需要N个构造函数,而C ++有一个坏主意,构造函数使用类名,这使你相当短的名字,并迫使你使用幻影类型允许重载选择正确因为它的签名是固定的。



好的,有没有替代品?也许,是的,但他们不是那么容易梦想,更难说服100多人,这是值得考虑在一个三天的会议挤满了其他问题。



委员会没有实施上述规则是很可惜的:工会是强制性的对齐任意数据和组件的外部管理不是真的很难手动,并且微不足道和完全安全当代码由一个合适的算法,换句话说,如果您想使用C ++作为编译器目标语言,并仍然生成可读,可移植的代码,则规则是强制。这种具有可构造成员的联合具有许多用途,但最重要的是表示包含嵌套块的函数的堆栈帧:每个块在结构中具有本地数据,并且每个结构是联合组件,不需要任何构造器或者这样,编译器将使用placement new。联合提供对齐和大小,并且免费组件访问。
[没有其他合适的方式来获得正确的对齐!]



因此,你的问题的答案是:你问的是错误的问题。对于只有POD的union是没有优点的,它们肯定不能是派生类,因为它们不是POD。为了使它们有用,需要一些时间来理解为什么一个人应该遵循C ++中其他任何地方使用的原则:除非您尝试使用它们,否则缺少位不是错误。


The standard defines that Unions cannot be used as Base class, but is there any specific reasoning for this? As far as I understand Unions can have constructors, destructors, also member variables, and methods to operate on those varibales. In short a Union can encapsulate a datatype and state which might be accessed through member functions. Thus it in most common terms qualifies for being a class and if it can act as a class then why is it restricted from acting as a base class?

Edit: Though the answers try to explain the reasoning I still do not understand how Union as a Derived class is worst than when Union as just a class. So in hope of getting more concrete answer and reasoning I will push this one for a bounty. No offence to the already posted answers, Thanks for those!

解决方案

Tony Park gave an answer which is pretty close to the truth. The C++ committee basically didn't think it was worth the effort to make unions a strong part of C++, similarly to the treatment of arrays as legacy stuff we had to inherit from C but didn't really want.

Unions have problems: if we allow non-POD types in unions, how do they get constructed? It can certainly be done, but not necessarily safely, and any consideration would require committee resources. And the final result would be less than satisfactory, because what is really required in a sane language is discriminated unions, and bare C unions could never be elevated to discriminated unions in way compatible with C (that I can imagine, anyhow).

To elaborate on the technical issues: since you can wrap a POD-component only union in a struct without losing anything, there's no advantage allowing unions as bases. With POD-only union components, there's no problem with explicit constructors simply assigning one of the components, nor with using a bitblit (memcpy) for compiler generated copy constructor (or assignment).

Such unions, however, aren't useful enough to bother with except to retain them so existing C code can be considered valid C++. These POD-only unions are broken in C++ because they fail to retain a vital invariant they possess in C: any data type can be used as a component type.

To make unions useful, we must allow constructable types as members. This is significant because it is not acceptable to merely assign a component in a constructor body, either of the union itself, or any enclosing struct: you cannot, for example, assign a string to an uninitialised string component.

It follows one must invent some rules for initialising union component with mem-initialisers, for example:

union X { string a; string b; X(string q) : a(q) {} };

But now the question is: what is the rule? Normally the rule is you must initialise every member and base of a class, if you do not do so explicitly, the default constructor is used for the remainder, and if one type which is not explicitly initialised does not have a default constructor, it's an error [Exception: copy constructors, the default is the member copy constructor].

Clearly this rule can't work for unions: the rule has to be instead: if the union has at least one non-POD member, you must explicitly initialise exactly one member in a constructor. In this case, no default constructor, copy constructor, assignment operator, or destructor will be generated and if any of these members are actually used, they must be explicitly supplied.

So now the question becomes: how would you write, say, a copy constructor? It is, of course quite possible to do and get right if you design your union the way, say, X-Windows event unions are designed: with the discriminant tag in each component, but you will have to use placement operator new to do it, and you will have to break the rule I wrote above which appeared at first glance to be correct!

What about default constructor? If you don't have one of those, you can't declare an uninitialised variable.

There are other cases where you can determine the component externally and use placement new to manage a union externally, but that isn't a copy constructor. The fact is, if you have N components you'd need N constructors, and C++ has a broken idea that constructors use the class name, which leaves you rather short of names and forces you to use phantom types to allow overloading to choose the right constructor .. and you can't do that for the copy constructor since its signature is fixed.

Ok, so are there alternatives? Probably, yes, but they're not so easy to dream up, and harder to convince over 100 people that it's worthwhile to think about in a three day meeting crammed with other issues.

It is a pity the committee did not implement the rule above: unions are mandatory for aligning arbitrary data and external management of the components is not really that hard to do manually, and trivial and completely safe when the code is generated by a suitable algorithm, in other words, the rule is mandatory if you want to use C++ as a compiler target language and still generate readable, portable code. Such unions with constructable members have many uses but the most important one is to represent the stack frame of a function containing nested blocks: each block has local data in a struct, and each struct is a union component, there is no need for any constructors or such, the compiler will just use placement new. The union provides alignment and size, and cast free component access. [And there is no other conforming way to get the right alignment!]

Therefore the answer to your question is: you're asking the wrong question. There's no advantage to POD-only unions being bases, and they certainly can't be derived classes because then they wouldn't be PODs. To make them useful, some time is required to understand why one should follow the principle used everywhere else in C++: missing bits aren't an error unless you try to use them.

这篇关于联盟作为基类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆