在比较C ++和Java多重继承时,我有些疑惑。
即使Java通过接口使用多个多级继承 - 但为什么它不像C ++那样使用虚拟基类之类的东西?是因为java接口的成员在内存中确保了一个副本(它们是公共静态final),并且这些方法只是声明而没有定义?
除了节省内存之外,C ++中是否有其他虚拟类用途?如果我在多重继承程序中忘记使用此功能,是否有任何警告?
这个有点哲学 - 但为什么C ++开发人员没有把它作为默认制作每个基类,虚拟?提供灵活性的需要是什么?
示例将不胜感激。谢谢!!
答案 0 :(得分:3)
1)Java接口没有属性。 c ++中虚拟基类的一个原因是为了防止重复的属性以及与之相关的所有困难。
2)在c ++中使用虚拟基类至少会有轻微的性能损失。此外,构造函数变得如此复杂,建议虚拟基类只有无参数构造函数。
3)正是因为c ++ philosphy:人们不应该对可能不需要的东西进行处罚。
答案 1 :(得分:3)
抱歉 - 不是Java程序员,而是细节问题。尽管如此,虚拟基础是对多重继承的改进,Java设计者总是在它过于复杂且可能容易出错的基础上进行辩护。
虚拟基础不只是用于保存内存 - 数据由从它们继承的不同对象共享,因此这些派生类型可以使用它以某种方式协调它们的行为。它们并不常用,但作为一个例子:对象标识符,您希望每个派生对象最多只有一个id,而不是计算所有子对象。另一个例子:确保乘法派生类型可以明确地映射/转换为指向基类的指针,使其易于在基类型上运行的函数中使用,或者存储在Base *的容器中。
由于C ++目前是标准化的,因此从两个类派生的类型通常可以期望它们独立运行,并且当在堆栈或堆上创建时,该类型的对象往往会这样做。如果一切都是虚拟的,突然间,独立性变得高度依赖于它们恰好被派生的类型 - 各种交互成为默认,并且派生本身变得不那么有用。所以,你的问题是为什么不制作默认的虚拟 - 好吧,因为这两种模式不那么直观,更危险,更容易出错。
答案 2 :(得分:2)
1.接口中的Java多重继承与C ++中的虚拟继承最相似。 更准确地说,要在c ++中实现类似java的继承模型,您需要使用c ++虚拟基类。
然而,c ++虚拟继承的一个缺点(除了小内存和性能损失)是static_cast<>的不可能性。从base到derived,所以需要使用rtti(dynamic_cast) (或者,如果是列表,则可以为子类提供“手工制作”虚拟强制转换功能 这样的儿童班是事先知道的)
2.如果你忘记了继承列表中的“虚拟”限定符,通常会导致编译错误 因为任何一个铸造起来的基础类都会变得暧昧。
3.哲学问题通常很难回答...... c ++是一种多范式(和多哲学)语言,并没有强加任何哲学决策。您可以在自己的项目中尽可能使用虚拟继承,并且(您是rioght)它有充分的理由。但是这样的最大值对于其他人来说可能是不可接受的,因此通用的c ++工具(标准和其他广泛使用的库)应该(如果可能的话)没有任何特定的哲学约定。答案 3 :(得分:1)
我正在开发一个开源项目,基本上是将大型C ++库翻译成Java。 C ++中原始生物的对象模型有时会非常复杂。我不得不说......这或多或少是Java设计师的座右铭......好吧......这是另一个主题。
关键是我写了一篇文章,展示如何绕过Java中的类型擦除。这篇文章很好地解释了它是如何完成的,最后你的源代码最终将如何最终与C ++相似。
http://www.jquantlib.org/index.php/Using_TypeTokens_to_retrieve_generic_parameters
我所做的研究的一个直接含义是,可以在你的应用程序中实现虚拟基类,我的意思是:不是用Java,不是用语言,而是用你的应用程序,通过一些技巧,或者很多技巧要更精确。
如果您确实对这种黑魔法感兴趣,下面的行可能对您有用。否则肯定不会。
确定。我们继续吧。
Java有几个难点: 1.类型擦除(在文章中解决) 2. javac不是为了理解虚拟基类是什么而设计的; 3.即使使用技巧,你也无法规避难度#2,因为这个难度出现在编译时。
如果您想使用虚拟基类,可以使用Scala,它基本上通过精确创建另一个编译器来解决难度#2,这完全理解了一些更复杂的对象模型,我会说。
如果你想探索我的文章并尝试用纯Java(而不是Scala)“循环”虚拟基类,你可以做一些像我在下面解释的那样:
假设你在C ++中有这样的东西:
template<Base>
public class Extended : Base { ... }
可以在Java中翻译成这样的东西:
public interface Virtual<T> { ... }
public class Extended<B> implements Virtual<B> { ... }
行。如下实例化Extended会发生什么?
Extended extended = new Extended<Base>() { /* required anonymous block here */ }
嗯..基本上你可以摆脱类型擦除,并且能够在你的类Extended中获取Base的类型信息。请参阅我的文章,了解黑魔法的全面解释。
行。一旦你在Extended中有Base类型,你就可以实例化Virtual的具体实现。
请注意,在编译时,javac可以为您验证类型,如下例所示:
public interface Virtual<Base> {
public List<Base> getList();
}
public class Extended<Base> implements Virtual<Base> {
@Override
public List<Base> getList() {
// TODO Auto-generated method stub
return null;
}
}
嗯......尽管已经付出了很多努力来实现它,但最终我们做得非常糟糕,像scalac这样出色的编译器比我们做得更好,特别是它在编译时就完成了它的工作。
我希望它有所帮助......如果不是你已经困惑了!