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