工会作为基础class
该标准定义联盟不能作为基类使用,但有没有具体的推理呢? 据我了解,联盟可以有构造函数,析构函数,成员variables和方法来操作这些varibales。 总之,一个联盟可以封装一个数据types和状态,可以通过成员函数来访问。 因此,在大多数情况下,它可以作为一个类来使用,如果它可以作为一个类,那么为什么它不能作为一个基类呢?
编辑:虽然答案试图解释的推理,我仍然不明白联盟作为一个派生类是最糟糕的时候,联盟只是一个类。 所以希望得到更具体的答案和推理,我会推这个奖金。 已经发布的答案没有冒犯,感谢那些!
Tony Park给出了一个非常接近事实的答案。 C ++委员会基本上不认为把工会变成C ++的一个重要组成部分是值得的,类似于将数组作为我们不得不从Cinheritance的传统工具,但实际上并不需要。
工会有问题:如果我们在工会中允许非PODtypes,他们是如何构build的? 这当然可以做到,但不一定是安全的,任何考虑都需要委员会的资源。 而最后的结果也不尽如人意,因为用一种理智的语言真正需要的就是被歧视的工会,而裸C的工会永远不会被提升到与C相兼容的被歧视的工会(无论如何我都能想象)。
为了详细说明技术问题:因为你可以在一个结构体中包装一个只包含POD组件的联合体而不会丢失任何东西,所以允许联合体作为基础没有任何优势。 使用仅POD联合组件,显式构造函数只需指定其中一个组件,也不需要使用bitblit(memcpy)来编译器生成的复制构造函数(或赋值)。
然而,这样的联合没有足够的用处,除非保留它们,所以现有的C代码可以被认为是有效的C ++。 这些仅限于POD的工会在C ++中被破坏,因为他们无法保留它们在C中拥有的重要不variables:任何数据types都可以用作组件types。
要使工会有用,我们必须允许可施工的types作为成员。 这很重要,因为仅仅在一个构造函数主体中分配一个组件,或者是联合本身,或者任何封闭的结构体都是不可接受的:例如,你不能将一个string分配给未初始化的string组件。
接下来,我们必须发明一些规则来初始化与mem-initialisers的联合组件,例如:
union X { string a; string b; X(string q) : a(q) {} };
但是现在的问题是:规则是什么? 通常的规则是,你必须初始化每个类的成员和基类,如果你没有明确地这样做,默认的构造函数被用于余数,如果一个没有明确初始化的types没有默认的构造函数,错误[例外:拷贝构造函数,默认是成员拷贝构造函数]。
显然,这个规则不能用于联合:规则必须是:如果联合至less有一个非POD成员,则必须明确地在一个构造函数中初始化一个成员。 在这种情况下,将不会生成默认构造函数,复制构造函数,赋值运算符或析构函数,并且如果实际使用这些成员中的任何一个,则必须显式提供它们。
所以现在的问题变成:你会怎样写一个拷贝构造函数? 当然,如果按照X-Windows事件工会的devise方式来devise工会,当然很有可能:做好每个组件的判别标记,但是必须使用新的工作人员才能做到这一点,你将不得不打破我上面写的规则,乍一看是正确的!
那么默认的构造函数呢? 如果您没有其中之一,则无法声明未初始化的variables。
在其他情况下,您可以从外部确定组件,并使用位置new来pipe理外部联合,但这不是复制构造函数。 事实是,如果你有N个组件,你需要N个构造函数,而C ++有一个错误的想法,那就是构造函数使用类名,这使得你缺less名字,并且迫使你使用幻像types来允许重载来select正确的构造函数..你不能这样做的复制构造函数,因为它的签名是固定的。
好的,有没有其他的select? 也许,是的,但是他们不是很容易想象,更难说服100多人,在三天的会议中挤满其他问题值得思考。
遗憾的是,委员会没有执行上述规则:工会对于任意数据的alignment是强制性的,组件的外部pipe理并不是真的很难手动完成,当代码由适当的algorithm生成时,换句话说,如果要将C ++用作编译器目标语言并且仍生成可读的可移植代码,则该规则是强制性的 。 具有可构造成员的这种联合具有许多用途,但最重要的是表示包含嵌套块的函数的堆栈框架:每个块在结构中具有本地数据,并且每个结构是联合组件,不需要任何构造器或者这样,编译器将只使用新的位置。 联盟提供了alignment和大小,并免费组件访问。 [而且没有其他合适的方法来正确alignment!]
所以你的问题的答案是:你问的是错误的问题。 基于POD的工会是没有好处的,他们当然不能派生类,因为那样他们就不会成为POD。 为了使它们有用,需要一些时间来理解为什么一个人应该遵循C ++中其他地方使用的原则:除非你尝试使用它们,否则丢失的位不是错误的。
联盟是一种types,可以作为其成员的任何一个成员使用,取决于哪个成员已经设置 – 只有该成员可以稍后阅读。
当你从一个types派生的派生typesinheritance基types – 派生types可以在任何基types可以使用。 如果你可以从一个工会派生出来,派生类可以在任何工会成员都可以使用的地方使用(不是隐含的,但明确地通过命名成员),但在这些成员中只有一个成员可以合法访问。 问题是成员已经设置的数据没有存储在联盟中。
为了避免这种微妙而危险的矛盾,实际上颠覆了一个来自工会的types体系是不允许的。
Bjarne Stroustrup在“Annotated C ++参考手册”中说'看起来没什么原因。
我想你在你对EJP答复的评论中得到了答案。
我认为工会只是包含在C ++中,以便与C向后兼容。我想工会在1970年似乎是一个好主意,在小内存空间的系统上。 到C ++出现时,我想工会已经看起来不那么有用了。
鉴于工会是非常危险的,而不是非常有用的,创造从工会inheritance的错误巨大的新机会可能创build可能只是不是一个好主意:-)
标题问为什么工会不能成为一个基类,但问题似乎是关于工会作为一个派生类。 那么,这是什么?
没有技术上的原因,为什么工会不能成为基地; 这是不允许的。 一个合理的解释是将联合想象成一个结构,其成员碰巧可能在内存中重叠,并将派生类视为从此(相当奇怪的)结构inheritance的类。 如果你需要这个function,你通常可以说服大多数编译器接受一个匿名联合作为一个结构的成员。 这里有一个例子,它适合用作基类。 (在这个联盟中有一个匿名的结构可以很好的衡量。)
struct V3 { union { struct { float x,y,z; }; float f[3]; }; };
工会作为一个派生类的基本原理可能更简单:结果不是一个工会。 工会必须是所有成员的联合,以及他们所有的基础。 这很公平,可能会打开一些有趣的模板的可能性,但是你会有一些限制(所有的基础和成员将不得不是POD – 并且你能够inheritance两次,因为派生types本质上是非-POD?),这种types的inheritance将与另一种types的语言运动不同(OK,并不是说这已经停止了C ++),而且它有点冗余 – 现有的联合function也可以。
Stroustrup在D&E书中这样说道:
和
void *
,程序员应该知道工会…本质上是危险的,应尽可能地避免,在实际需要时应该特别小心处理。
(这个elision并没有改变这个意思。)
所以我想这个决定是任意的,他只是没有理由改变联合function(它与C ++的C子集一样工作得很好),所以没有devise任何与新的C ++特性的集成。 风变了,就这样卡住了。
这是我对C ++ 03的猜测。
按照$ 9.5 / 1,在C ++ 03中,联盟不能有虚拟function。 有意义的推导的重点是能够覆盖派生类中的行为。 如果一个工会不能有虚拟职能,那就意味着从一个工会派生出来没有意义。
因此,规则。
您可以使用C ++ 11中的匿名联合functioninheritance联合的数据布局。
#include <cstddef> template <size_t N,typename T> struct VecData { union { struct { float x; float y; float z; }; float a[N]; }; }; template <size_t N, typename T> class Vec : public VecData<N,T> { //methods.. };
一般来说,它总是最好不直接与工会合作,而是把它们放在一个结构或类中。 那么你可以将你的inheritance从结构外层基础,并在需要时使用联合。