为什么JAVA中的覆盖与C ++有些不同?

时间:2015-12-01 11:34:51

标签: java c++

我有一些C ++的背景知识,也知道一些Java(显然远远不够)。

当我在Java或C ++中看到覆盖行为时,它似乎没有太大差别。以下是JAVA中的示例:

class Animal{

   public void move(){
      System.out.println("Animals can move");
   }
}

class Dog extends Animal{

   public void move(){
      System.out.println("Dogs can walk and run");
   }
}

public class TestDog{

   public static void main(String args[]){
      Animal a = new Animal(); // Animal reference and object
      Animal b = new Dog(); // Animal reference but Dog object

      a.move();// runs the method in Animal class
      b.move();//Runs the method in Dog class
   }
}

在Java中,您使用基类引用,在C ++中使用基类指针,并依赖于它指向的实例类型(基类对象实例或子类实例),您可以实现多态。 / p>

以上是基于您使用基类引用或指针调用实例方法,对吗?

现在我在Java中看到这个例子。

What is the order of the Constructors in this Java Code?

基本上它说如果基类函数被覆盖,那么在创建子类对象的过程中,甚至基类初始化部分也会受到影响。请参阅以下我从上面链接中复制的说明:

new Son()
=>  
  Son._init
   =>  first every constructor calls super()
      Father._init
         Object._init
         who()  => is overridden, so prints "son" !!!!!
         tell(name) => name is private, so cannot be overridden => "father"
   who()  => "son"
   tell(name)  => "son"

为什么会发生这种情况?我的意思是这符合多态性应该如何使用?在进行初始化的基类部分时,为什么要使用来自子类的覆盖函数?

在Java doc http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.5中,我只找到了这个:

“与C ++不同,Java编程语言在创建新类实例期间没有为方法调度指定更改的规则。如果调用的方法在被初始化的对象的子类中被重写,则使用这些重写方法,甚至在新对象完全初始化之前。“

但我不知道背后的原因,感觉很奇怪。

有什么想法?

1 个答案:

答案 0 :(得分:6)

这是极少数情况下,C ++试图保护你免受脚部攻击,而不是像Java那样。 (或者至少它有这样做的崇高意图。)

如果您尝试从B的构造函数中调用基类B的可覆盖(虚拟)方法M,那么您很可能会用任何语言开始自己。这是因为M很可能在派生类中被重写D,但是在B正在建造的那一刻,D尚未建成。因此,在调用D的构造函数之前调用D.M.这可能会造成灾难。

因此,Java只允许这种情况发生,使用风险自负。 (如果启用了足够的警告,编译器会告诉您危险的生活。)

C ++也没有禁止这一点,但是它稍微改变了它的行为以便包含损坏,可以这么说:当你从构造函数中调用虚方法时,它并不真正将它作为虚方法调用,(使用VMT查找,但它直接调用它作为非虚方法。

(无论是那个,还是来自B的构造函数,它只是使用B类的VMT而不是D的VMT。现在来想想它是有道理的。但我不确定,它已经很长了自从我上次困扰C ++的这种行为以来的时间。)