【问题标题】:C++ and Java : Use of virtual base classC++ 和 Java:虚拟基类的使用
【发布时间】:2011-04-02 11:38:36
【问题描述】:

在比较 C++ 和 Java 的多重继承时,我有些疑惑。

    1234563是不是因为 java 接口的成员被确保在内存中有一份副本(它们是 public static final),并且方法只是声明而不定义?
  1. 除了节省内存,C++ 中虚拟类还有其他用途吗?如果我忘记在我的多重继承程序中使用此功能,有什么注意事项吗?

  2. 这个有点哲学 - 但为什么 C++ 开发人员不将每个基类都设为虚拟?提供灵活性的需要是什么?

示例将不胜感激。谢谢!!

【问题讨论】:

  • C++ 中的“虚拟基类”是什么意思?具有至少一个纯虚函数的类(例如 virtual void booga() = 0; )?
  • 具有至少一个虚函数(不一定是纯虚函数)的基类是虚基类。具有至少一个纯虚函数的基类称为抽象基类。
  • @Ganesh:我们应该更精确。 C++ 的“虚拟基类”被明确定义为虚拟继承的基类。您的第一个术语最好是“多态基类”。

标签: java c++ virtual


【解决方案1】:

1) Java 接口没有属性。 c++ 中使用虚拟基类的一个原因是为了防止重复属性以及与之相关的所有困难。

2) 在 c++ 中使用虚拟基类至少会有轻微的性能损失。此外,构造函数变得如此复杂,建议虚拟基类只有无参数的构造函数。

3) 正是因为 c++ 的哲学:人们不应该要求对可能不需要的东西进行惩罚。

【讨论】:

    【解决方案2】:
    1. 抱歉 - 不是 Java 程序员,所以缺乏详细信息。尽管如此,虚拟基是多重继承的一种改进,Java 设计人员一直以它过于复杂且容易出错为由为省略它辩护。

    2. 虚拟基不只是为了节省内存——数据由从它们继承的不同对象共享,因此那些派生类型可以使用它以某种方式协调它们的行为。它们通常不是很有用,但作为一个例子:对象标识符,您希望每个派生最多的对象都有一个 id,而不是计算所有子对象。另一个例子:确保多重派生类型可以明确地映射/转换为指向基址的指针,使其易于在对基类型进行操作的函数中使用,或存储在 Base* 的容器中。

    3. 由于 C++ 当前已标准化,派生自两个类的类型通常可以期望它们独立运行,并且在堆栈或堆上创建该类型的对象时往往会这样做。如果一切都是虚拟的,那么这种独立性突然变得高度依赖于它们恰好从中派生的类型——各种交互成为默认设置,而派生本身变得不那么有用。所以,你的问题是为什么不将默认设置为虚拟 - 好吧,因为它是两种模式中不太直观、更危险和容易出错的一种。

    【讨论】:

      【解决方案3】:

      1.Java 接口中的多重继承行为最类似于 C++ 中的虚拟继承。 更准确地说,要在 c++ 中实现类 java 的继承模型,您需要使用 c++ 虚拟基类。

      但是,c++虚拟继承的缺点之一(除了内存小和性能损失外)是static_cast不可能从基类到派生类,所以需要使用rtti(dynamic_cast) (或者可以为子类提供“手工制作”的虚拟铸造功能,如果 这样的子类是事先知道的)

      2.如果在继承列表中忘记了“virtual”限定符,通常会导致编译器错误 因为任何从驱动到基类的转换都变得模棱两可

      3. 哲学问题通常很难回答... c++ 是一种多范式(和多哲学)语言,不会强加任何哲学决定。您可以在自己的项目中尽可能使用虚拟继承,并且(您是正确的)它有充分的理由。但是这样的最大值可能对其他人来说是不可接受的,因此通用 c++ 工具(标准库和其他广泛使用的库)应该(如果可能的话)不受任何特定的哲学约定。

      【讨论】:

        【解决方案4】:

        我正在开发一个开源项目,该项目基本上是将大型 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 */ }
        

        嗯..基本上你将能够摆脱类型擦除,并且能够在你的扩展类中获取 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 这样的优秀编译器比我们做得更好,特别是它在编译时完成了它的工作。

        希望对您有所帮助...如果您还没有感到困惑!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-05-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-30
          • 1970-01-01
          相关资源
          最近更新 更多