C++:
覆盖,实现多态的基础,通过虚函数表来实现,下面这个例子便是覆盖 Override
1 #include
2
3 using namespacestd;4
5 classBase{6 public:7 Base(){8 cout << "Base::Base" <
11 virtual voidf(){12 cout << "Base::f" <
16 class Derived : publicBase{17 public:18 Derived(){19 cout << "Devried::Derived" <
22 voidf(){23 cout << "Derived::f" <
27 intmain(){28 Base *p = newDerived(); // Base::Base CRCL Derived::Derived29 p->f(); // Derived::f30 }
如果 Base类f()没有被virtual修饰,如下
1 #include
2
3 using namespacestd;4
5 classBase{6 public:7 Base(){8 cout << "Base::Base" <
11 voidf(){12 cout << "Base::f" <
16 class Derived : publicBase{17 public:18 Derived(){19 cout << "Devried::Derived" <
22 voidf(){23 cout << "Derived::f" <
27 intmain(){28 Base *p = newDerived();29 p->f(); // Base::f30 }
如果理解了陈大神博客上那些图,应该不会对这个结果产生意外,这是我画的一个草图,并不对应于真实的内存地址情况,只是为了方便说明概念,下面一排描述的是Derived:
解析:首先p是Base类型的指针,其对于p而言其能只能按照Base类型去运算偏移地址等来访问,所以其不可能访问到Derived中的函数。也就是说父类指针的作用域只有int a , Base::f Base::g这一部分。这样就能理解了。
隐藏,父类指针的访问域是父类部分,子类虽然拥有一份父类函数的拷贝,但如果子类中存在于父类同名的函数,则调用和变量类型一致的函数(即父类变量调用父类函数,子类变量调用子类函数,同时成对方的函数被隐藏)。例:
1 #include
2
3 using namespacestd;4
5 classBase{6
7 public:8 inta;9
10 Base(){11 cout << "Base::Base" <
14 void f(floata){15 cout << "Base::f" <
19 class Derived : publicBase{20 public:21 intd;22 Derived(){23 cout << "Devried::Derived" <
26 void f(inta){27 cout << "Derived::f" <
31 intmain(){32 Derived *p = newDerived();33 p->f(3.14f); // Derived::f34 }
这里虽然传入f的参数是float型,更适合调用Base::f,但根据同名优先调用子类的原则,实际调用为Derived::f。这就是父类同名方法被隐藏,如果指针类型为父类,则称子类方法被隐藏。
如果子类f定义为 void f() 这里对f传值就会编译不通过。
Java
覆盖,java的情况很简单,只要是子类含有和父类的同名方法,就是覆盖(无论子类的函数是否为abstract)。
public classTest {public static voidmain(String[] args){
Basebase = newDerived();base.f(); // Derived::f
}
}classBase {public voidf() {
System.out.println("Base::f");
}
}classDerived extends Base {public voidf() {
System.out.println("Derived::f");
}
}
隐藏,Java中也存在隐藏,不过这个隐藏和C++不太一样,子类对象引用可以调用父类的同名函数
1 public classTest {2 public static voidmain(String[] args){3 Derived derived = newDerived();4 derived.f(3); //Derived::f5 derived.f(3.14f); // Base::f6 }7 }8
9 classBase {10
11 public void f(floata) {12 System.out.println("Base::f");13 }14 }15
16 classDerived extends Base {17 public void f(inta) {18 System.out.println("Derived::f");19 }20 }
再讲覆盖,C++父类函数中如果调用了自己的虚成员函数,那么由于这个虚成员函数被覆盖了,所以其实相当于调用了子类的函数。例:
#include
using namespacestd;classBase{public:inta;
Base(){
cout<< "Base::Base" <
}voidf(){
g();
}virtual voidg(){
cout<< "Base::g" <
}
};class Derived : publicBase{public:intd;
Derived(){
cout<< "Devried::Derived" <
}voidg(){
cout<< "Derived::g" <
}
};intmain(){
Derived*p = newDerived();
p->f(); // Derived::g
}
Java中类似:
1 public classTest {2 public static voidmain(String[] args){3 Base base = newDerived();4 base.f(); //Derived::f5 }6 }7
8 classBase {9
10 public voidf() {11 g();12 }13
14 public voidg(){15 System.out.println("Base::g");16 }17 }18
19 classDerived extends Base {20 public voidg(){21 System.out.println("Derived::g");22 }23 }
由于想利用这种特性,有人会犯一个错误,在父类的构造函数中调用被覆盖的函数,由于子类在构建时先调用父类的构造函数,此时子类为构造所以不能调到子类的覆盖方法,由此
产生了奇怪的错误。