关于Java与c++隐藏、重写不同实现机制的探讨
一、文章来由
本人如今用c++很多其它。可是曾经Java也写过不少,Java和c++非常像,可是深入挖一些,Java跟c++的差别非常大,就拿刚刚发的另一篇博文虚函数与多态小览来说,里面就感觉有非常多不同了,至少“重写”在这两个语言里面的理解就不同了~~跟基友一番讨论。决定把这个问题彻底捋一捋。由于这个是探讨。所以有不同想法欢迎提出和评论。
二、关于“重写”Override的定义
“重写”多么常见的一个概念。
1、首先说明“重写”(Override)全然等于“覆盖”,可能由于翻译的问题,是个非常坑爹的问题
2、而且 Override 的定义在c++和Java中却不同~~
c++中这样定义“重写”:
Override 是指派生类函数覆盖基类函数:
(1)不同的范围(分别位于派生类与基类)。
(2)函数名字同样;
(3)參数同样。參数不同都不称之为重写。根本就是两个函数了;
(4)基类函数必须有virtual 关键字。
特别留意最后一点~~这是差别于隐藏的关键点
Java中这样定义“重写”:
(1)在子类中能够依据须要对从基类中继承来的方法进行重写,方法重写又称方法覆盖。
(2)重写的方法和被重写的方法必须具有同样方法名称、參数列表和返回类型。
(3)重写方法不能使用比被重写的方法更严格的訪问权限。如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
有没有发现什么,就是Java中根本没有virtual关键字,所以没有“重写”和“隐藏”的差别。Java中重写一定隐藏了父类的方法,要訪问就要用super关键字就能够了,所以Java里面仅仅有 override。
三、深入理解c++中的 Hidden 与 Override
看了定义就已经发现不同了,如今来自己看看c++中 Hidden 与 override 的实现机制。
谈到c++的 override 就一定要谈到 hidden。他们在c++中是要绝对区分开的。
3.1 Hidden
c++隐藏的定义(派生类的函数屏蔽了与其同名的基类函数):
(1)假设派生类的函数与基类的函数同名,可是參数不同。
此时。不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载混淆)
(2)假设派生类的函数与基类的函数同名,而且參数也同样。可是基类函数没有 virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)
这是上一篇博客也提到的类容。那么到底实现机制是什么?
在调用一个类的成员函数的时候。编译器会沿着类的继承链逐级的向上查找函数的定义。假设找到了那么就停止查找了。所以假设一个派生类和一个基类都有同一个同名(暂且不论參数是否同样)的函数。而编译器终于选择了在派生类中的函数,那么我们就说这个派生类的成员函数”隐藏”了基类的成员函数。也就是说它阻止了编译器继续向上查找函数的定义。。
。
。
回到隐藏的定义中,前面已经说了有virtual关键字而且分别位于派生类和基类的同名,同參数函数构成覆盖的关系,因此隐藏的关系仅仅有例如以下的可能:
1)必须分别位于派生类和基类中
2)必须同名
3)參数不同的时候本身已经不构成覆盖关系了,所以此时是否是virtual函数已经不重要了
当參数同样的时候就要看时候有virtual关键字了,有的话就是覆盖关系,没有的时候就是隐藏关系了
3.2 Override
覆盖指的是派生类的虚拟函数覆盖了基类的同名且參数同样的函数。
既然是和虚拟函数挂钩,说明了这个是一个多态支持的特性,所谓的覆盖指的是用基类对象的指针或者引用时訪问虚拟函数的时候会依据实际的类型决定所调用的函数,因此此时派生类的成员函数能够”覆盖”掉基类的成员函数。
注意: 唯有同名且參数同样还有带有virtual关键字而且分别在派生类和基类的函数才干构成虚拟函数,这个也是派生类的重要特征。
而且,由于是和多态挂钩的,所以仅仅有在使用类对象指针或者引用的时候才干使用上。
总之中的一个句话:覆盖函数都是虚函数。反之不然~~
四、代码实例
口说无凭,上代码~~
Java代码例如以下:
public class PolyTest
{
public static void main(String[] args)
{
//向上类型转换
Cat cat = new Cat();
Animal animal = cat;
animal.sing();
//向下类型转换
Animal a = new Cat();
Cat c = (Cat)a;
c.sing();
c.eat();
//编译错误
//用父类引用调用父类不存在的方法
//Animal a1 = new Cat();
//a1.eat();
//编译错误
//向下类型转换时仅仅能转向指向的对象类型
//Animal a2 = new Cat();
//Cat c2 = (Dog)a2;
}
}
class Animal
{
public void sing()
{
System.out.println("Animal is singing!");
}
}
class Dog extends Animal
{
public void sing()
{
System.out.println("Dog is singing!");
}
}
class Cat extends Animal
{
public void sing()
{
System.out.println("Cat is singing!");
}
public void eat()
{
System.out.println("Cat is eating!");
}
}
跑出来结果例如以下:
可见,Java即便第二组向下转换【父类强转子类】,输出任然是子类的东西,并不像c++的晚绑定。
c++代码例如以下:
#include<iostream>
using namespace std;
class Animal
{
public:
virtual void sing()
{
printf("Animal is singing!\n");
}
};
class Cat : public Animal
{
public:
void sing()
{
printf("Cat is singing!\n");
}
void eat()
{
printf("Cat is eating!\n");
}
};
int main(void)
{
Animal a;
Cat *cp = (Cat *)&a;
cp->sing();
cp->eat();
//编译错误
// Cat c;
// Animal a = c;
// a.eat();
return 0;
}
执行结果例如以下:
c++这里就良好体现了晚绑定的特性~~~即使强制转换了,指向的任然是基类对象的地址,所以通过虚函数列表还是能够找到基类的实现~
关于隐藏,另一个非常好的东西:
(1)子类和父类返回值參数同样,函数名同样,有virtual关键字。则由对象的类型决定调用哪个函数。
(2)子类和父类仅仅要函数名同样。没有virtual关键字,则子类的对象没有办法调用到父类的同名函数,父类的同名函数被隐藏了,也能够强制调用父类的同名函数class::funtion_name。
(3)子类和父类參数不同,函数名同样。有virtual关键字,则不存在多态性,子类的对象没有办法调用到父类的同名函数,父类的同名函数被隐藏了。也能够强制调用父类的同名函数class::funtion_name。
(4)子类和父类返回值不同。參数同样,函数名同样,有virtual关键字。则编译出错error C2555编译器不同意函数名參数同样返回值不同的函数重载。
五、小结
Java与c++在隐藏、重写的不同就介绍到这里。刚刚在线Markdown没有保存,就打开了上一篇的编辑。差点以为要冲掉这篇了。码了好多字啊。吓傻了。。
。看来以后要多导入到本地~~哈哈哈
—END—
參考文献
[1] http://bbs.chinaunix.net/thread-643467-1-1.html
[2] http://www.cnblogs.com/mengdd/archive/2012/12/25/2832288.html