我有一些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编程语言在创建新类实例期间没有为方法调度指定更改的规则。如果调用的方法在被初始化的对象的子类中被重写,则使用这些重写方法,甚至在新对象完全初始化之前。“
但我不知道背后的原因,感觉很奇怪。
有什么想法?
答案 0 :(得分:6)
这是极少数情况下,C ++试图保护你免受脚部攻击,而不是像Java那样。 (或者至少它有这样做的崇高意图。)
如果您尝试从B的构造函数中调用基类B的可覆盖(虚拟)方法M,那么您很可能会用任何语言开始自己。这是因为M很可能在派生类中被重写D,但是在B正在建造的那一刻,D尚未建成。因此,在调用D的构造函数之前调用D.M.这可能会造成灾难。
因此,Java只允许这种情况发生,使用风险自负。 (如果启用了足够的警告,编译器会告诉您危险的生活。)
C ++也没有禁止这一点,但是它稍微改变了它的行为以便包含损坏,可以这么说:当你从构造函数中调用虚方法时,它并不真正将它作为虚方法调用,(使用VMT查找,但它直接调用它作为非虚方法。
(无论是那个,还是来自B的构造函数,它只是使用B类的VMT而不是D的VMT。现在来想想它是有道理的。但我不确定,它已经很长了自从我上次困扰C ++的这种行为以来的时间。)