这篇文章比较了C++,C#和Java在面向对象中的继承和函数重写的不同。

介绍

我一直用三种不同的面向对象的语言,这三种语言都很明显的支持继承和迟绑定或者虚函数。我认为一篇好的文章当我在用Java,c#和C++中切换时他们在语法和内定行为的不同地方会提醒我。

背景

我不断的在切换用C++写霜冻视频游戏引擎和用C#写工具和网页服务器。我也用Java写我的安卓主要的项目(最新的一个是群集智能的蚂蚁巢穴模拟器-敬请期待)

同样我认为我要给自己做笔记,在继承、函数重写(虚函数)使用三种不同的语言直接的主要区别。

使用代码

可接入性

C++支持共有,保护,和私有继承。但是无论它做了什么,不要忘了任何的私有成员总是私有的,不能变为可接入的为任何基础或派生类调用。

Java简化了继承并且只允许共有继承。

C#在这个方面仿照了Java。

大家都普遍同意在面向对象语言中通过封装和其他功能可实现保护和私有继承,不需要这些其他复制的置换。

多继承

现在我们来将一点关于多继承。当建立了一个类的层级时,C++只用冒号继承,紧随其后的是逗号,用来分开其他基类列表。C++是唯一一个支持多继承的语言,如果你一不小心那将会出现像致命的死亡菱形(DDD)结构这种事情。总体来说,命的死亡菱形(DDD)是一个设计,这个设计是多个同一类是对象的一部分实例。设想一个类叫Dog,这个Dog继承了Animal和Pet,并且这个Animal和Pet都继承了一个叫Printable的类,狗这个实例现在有两个叫Printable的基类的实例。当你在Dog这个类里面改变了这个Printable类的属性后会发生什么呢?它是否会改变实例1或实例2的属性呢?等等等。为了避免继承里面这种复杂的问题(无论如何,我们通常喜欢类的组合胜过继承-组合展开是一个原因),C#和Java两者都不支持多继承。

接口

多年前,C++还是这个编程大陆的王者时,你可能会用抽象类(一个不能被实例化的类,而且最少包含了一个纯虚函数)并且要把所有的纯虚函数定义为一种格式也就是=0。这就意味着你需要在派生类中实现这些功能。这样就诞生了接口这个定义。在C#和Java中你能操作接口,从而给你类似与多继承的一种协定(或者你必须执行函数签名)

C#不仅支持接口(类似与C++里面的抽象类,类中包含了索引的纯虚函数)还支持带有一个或多个存虚函数的抽象类,并且这些函数是带有某种真实的功能。记住接口和抽象类里的函数必须是公用,这样就可以在调用类的时候对他们进行直接访问,在接口里面定义私有函数往往没有太多意义,谁又能调用它呢?抽象类中这个类是不能调用他自己的函数,这是没有意义的!所以,C#涵盖了抽象类、接口和具体类这三种。

让我们来看这些用三种编程语言,带有继承和接口声明类的代码。

C++

class derived : public base, base2 //C++ class definition with 2 public inherited classes


Java

class Derived extends Base implements printable // new derived class that derives from base, and implements the printable contract

C#

public interface printable //A contract interface
public abstract class baser //A class with one or more abstact functions
public class derived : baser, printable //A derived class from baser, it implements the printable interface


虚函数

面向对象的另一个方面是多态。这种方法是一种是在运行是构建的,在编译类型时我们只知道它的层次结构,举一个继承中衍生类的例子,我们有一个基类型的指针。使用迟绑定调用这个函数,这个函数是在运行中被判断而不是编译时。这三种语言都支持迟绑定这就是虚函数。


C++

有一个老旧的思想是默认把这个函数设置为非纯虚函数。正如待会我说的那样,如果你有一个基类和一个派生类,他们俩都有一个叫print()的函数,你实构建哪种类的实例都没有关系,你将在类上调用函数这取决你引用或者指针是什么类型,如果你不知道你指向的是什么。用虚函数,你一定要声明虚函数(这种层次结构在哪都是非常好的),这是告诉编译器创建一个虚函数表,当运行是你要查找函数的地址就会依赖这张表里面。一旦函数被标记成虚函数,实例将会判决到哪调用你的函数,不再依赖于引用和指针。

Java

Java一项都超级简单,所有的函数都是虚函数,不需要任何陈述去告诉运行引擎那些是虚函数,如果一个派生类和基类都声明了一个叫print的函数,都具有同样的签名,那么实例将依赖JRE的决定。超级简单!除非你不想要这个功能(也许安全是个令人担忧的问题比如你不想函数void AddMoneyToBankAccount()被重写),在这种情况下你要标记它为常函数,并且不要想着去重写它!艹!

C#

最后是C#,在C#这里所有函数默认都是非虚函数,不仅仅是这样,如果你想去重写一个函数,你得这个在基类上打上虚函数这个标签,并且重新时也要打上这个前缀标签。你可以做到的,然而,有两个相同签名的函数,在继承关系里面,并且他们并不是被重写的关系,你要给派生类的那个函数打上“new” 这个标签。你要明确的告诉编译器,我是故意这么做的。你想隐藏基类函数的功能,并且这不是虚函数。但是如果你调用函数用的是基类的指针,它将总是调用基类的函数,即使你指向一个派生类的指针,你所看的这样的功能是编译器静态链接时做的,运行时是动态的(尽管没有专门的C#编译器)

最后一些代码来说明所有的虚函数和函数重写!!

C++ Code

#include "stdafx.h"
#include <iostream>using namespace std;class base
{public:void hello(){cout << "Base func non-virtual" << endl;}virtual void hello2(){cout <<"Base func virtual"<<endl;}
};class derived : public base //C++ class definition with 2 inherited classes
{
public:void hello(){cout << "Derived func non-virtual" << endl;}virtual void hello2(){cout <<"Derived func virtual"<<endl;}};int main()
{cout << "calling function defined in both base and der that is NOT virtual, then calling a function that is virtual" << endl;{cout << "Instance is Derived - Reference is Derived" << endl;derived d;d.hello();d.hello2();}{cout << "Instance is Derived - Reference is Base" << endl;base *f = new derived ;f->hello();f->hello2();}{cout << "Instance is Base - Reference is Base" << endl;base t;t.hello();t.hello2();}{cout << "Instance is Derived - reinterpret cast to base - Reference is Derived" << endl;derived *g = new derived ;base *j = reinterpret_cast<base*>(g);j->hello();j->hello2();}getchar();return 0;
}

And the output:

calling function defined in both base and der that is NOT virtual,
then calling a function that is virtual
Instance is Derived - Reference is Derived
Derived func non-virtual
Derived func virtual
Instance is Derived - Reference is Base
Base func non-virtual
Derived func virtual
Instance is Base - Reference is Base
Base func non-virtual
Base func virtual
Instance is Derived - reinterpret cast to base - Reference is Derived
Base func non-virtual
Derived func virtual

And C#

using System;namespace ConsoleApplication2
{public class based{public void hello(){Console.WriteLine("hello on base instance - non virtual on base");Console.WriteLine("func is - non virtual on deriv");}public virtual void hello2(){Console.WriteLine("hello2 on base instance - virtual on base");Console.WriteLine("func is  - virtual on deriv");}public void hello3(){Console.WriteLine("hello3 on base instance - non virtual on base");Console.WriteLine("func is new and virtual on deriv");}public virtual void hello4(){Console.WriteLine("hello4 on base instance - virtual on base");Console.WriteLine("func is new and virtual on deriv");}public virtual void hello5(){Console.WriteLine("hello5 on base instance - virtual on base");Console.WriteLine("func is override on deriv");}public void hello6(){Console.WriteLine("hello6 on base instance - NOT virtual on base");Console.WriteLine("func is override on deriv");}}public class derived : based
{public void hello(){Console.WriteLine("hello on deriv - non virtual on deriv");Console.WriteLine("func is non virtual on base");}public virtual void hello2(){Console.WriteLine("hello2 on deriv - virtual on deriv");Console.WriteLine("func is virtual on base");}public new virtual void hello3(){Console.WriteLine("hello3 on deriv - new and virtual on deriv");Console.WriteLine("func is non virtual on base");}public new virtual void hello4(){Console.WriteLine("hello4 on deriv - new and virtual on deriv");Console.WriteLine("func is virtual on base");}public override void hello5(){Console.WriteLine("hello5 on deriv - override on deriv");Console.WriteLine("func is virtual on base");}}class Program{static void Main(string[] args){Console.WriteLine("base ref with base inst");based b = new based();b.hello();b.hello2();b.hello3();b.hello4();b.hello5();Console.WriteLine();Console.WriteLine("deriv ref with deriv inst");derived d = new derived();d.hello();d.hello2();d.hello3();d.hello4();d.hello5();Console.WriteLine();Console.WriteLine("base ref with deriv inst");based o = new derived();o.hello();o.hello2();o.hello3();o.hello4();o.hello5();Console.WriteLine();Console.WriteLine("deriv ref with deriv inst cast back to base ref");derived s = new derived();based ds = s;ds.hello();ds.hello2();ds.hello3();ds.hello4();ds.hello5();Console.WriteLine("The end--");Console.ReadLine();}}
}

And the output:

base ref with base inst
hello on base instance - non virtual on base
func is - non virtual on deriv
hello2 on base instance - virtual on base
func is  - virtual on deriv
hello3 on base instance - non virtual on base
func is new and virtual on deriv
hello4 on base instance - virtual on base
func is new and virtual on deriv
hello5 on base instance - virtual on base
func is override on derivderiv ref with deriv inst
hello on deriv - non virtual on deriv
func is non virtual on base
hello2 on deriv - virtual on deriv
func is virtual on base
hello3 on deriv - new and virtual on deriv
func is non virtual on base
hello4 on deriv - new and virtual on deriv
func is virtual on base
hello5 on deriv - override on deriv
func is virtual on basebase ref with deriv inst
hello on base instance - non virtual on base
func is - non virtual on deriv
hello2 on base instance - virtual on base
func is  - virtual on deriv
hello3 on base instance - non virtual on base
func is new and virtual on deriv
hello4 on base instance - virtual on base
func is new and virtual on deriv
hello5 on deriv - override on deriv
func is virtual on basederiv ref with deriv inst cast back to base ref
hello on base instance - non virtual on base
func is - non virtual on deriv
hello2 on base instance - virtual on base
func is  - virtual on deriv
hello3 on base instance - non virtual on base
func is new and virtual on deriv
hello4 on base instance - virtual on base
func is new and virtual on deriv
hello5 on deriv - override on deriv
func is virtual on base
The end--

And lastly Java

package com.roboticsfordreamers;class Base implements
{public void Hello(){System.out.println("Hello this the base");}
}class Derived extends Base
{public void Hello(){System.out.println("Hello this the derived");}
}public class Main {public static void main(String[] args) {Base b = new Base();Derived d = new Derived();Base b2 = new Derived();b.Hello();d.Hello();b2.Hello();}
}

And the output:

Hello this the base
Hello this the derived
Hello this the derivedProcess finished with exit code 0

我希望你能找到对你有用知识。


原文:https://www.codeproject.com/Tips/1164135/Overriding-Virtual-Functions-a-Comparison-Between

PS:这篇有点难,部分地方翻译不到位

让我们来比较C#,C++和Java之间重写虚函数的区别相关推荐

  1. Java 方法重写与重载的区别 示例 重载多数相加求和 重写toString()和equals()

    Java 方法重写与重载的区别 重载:在同一个类中,当方法名相同,形参列表不同的时候 多个方法构成了重载 重写:在不同的类中,子类对父类提供的方法不满意的时候,要对父类的方法进行重写. 名称\属性 E ...

  2. java 析构函数_C++虚函数

    码字不易,欢迎给个赞! C++虚函数是多态性实现的重要方式,当某个虚函数通过指针或者引用调用时,编译器产生的代码直到运行时才能确定到底调用哪个版本的函数.被调用的函数是与绑定到指针或者引用上的对象的动 ...

  3. java 能重写构成函数_java函数重载和函数重写

    java中,函数重载(overload)和 函数重写(override)是完全不同的两个概念,而这没有任何关系(除了名字有点像) 函数重载(overload): 1.针对同一个类中的一组函数来说的: ...

  4. Java方法重写与重载的区别

    Java方法重写(Override)与重载(Overload)的区别(超详细) 首页在我们要学习这个知识点之前,应该要先了解什么是多态? 在最初学习java的时候,人们都知道,java这种面向对象的语 ...

  5. JAVA中重写和实现的区别_Java中重载和重写的区别

    Java中重载和重写的区别 1.1重载是什么 Overloading   方法重载是让类以统一的方式处理不同类型数据的一种手段,多个同名函数同时存在,具有不同的参数个数/类型 1.2为什么用重载 重载 ...

  6. java虚成员函数_Java常见知识点汇总(④)——虚函数、抽象函数、抽象类、接口...

    一. Java虚函数 虚函数的存在是为了多态. 它虚就虚在所谓"推迟联编"或者"动态联编"上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的. ...

  7. C#、C++、JAVA中虚函数和抽象函数的概念对比

    这两天恶补了下C#的相关内容,虚函数的概念又挖掘了下,总结如下: 1.C++中函数被定义为虚函数的方法是在函数名前加virtual,虚函数可以有自己的具体内容,也可以不含有函数实现内容,称为纯虚函数. ...

  8. java中所有函数都是虚函数_关于Java:虚拟函数与纯虚函数之间的区别是什么?...

    本问题已经有最佳答案,请猛点这里访问. Possible Duplicate: C++ Virtual/Pure Virtual Explained 虚函数和纯虚函数有什么区别? CPP中的纯虚函数与 ...

  9. Java中的抽象函数与C++中的虚函数

    1:java中没有虚函数的概念,但是有抽 象函数的概念,用abstract关键字表示,java中抽象函数必须在抽象类中,而且抽象 函数不能有函数体,抽象类不能被实例化,只能由其子类实现抽象函数,如果某 ...

最新文章

  1. 信息化监理公司的所有问题归到底是人的使用和管理
  2. python基础语法 第0关print-python学习笔记1,新手小白也能看得懂
  3. linux 配置jupyter远程访问
  4. 一行代码引来的安全漏洞,就让我们丢失了整个服务器的控制权
  5. 用elemet-ui组件实现弹窗里的树形结构和拖拽功能
  6. Linux之dd命令使用
  7. 力扣-674 最长连续递增序列
  8. Flash遮罩层初识
  9. 寻找生态位置,中小险企破局生态建设——保险生态建设
  10. 网络性能衡量的指标有哪些?
  11. mongos魔兽世界模拟器
  12. 1317: PIPI的生日礼物
  13. 真无线降噪蓝牙耳机推荐,综合性能表现不错的降噪蓝牙耳机分享
  14. Aop 切入点表达式
  15. 反素数java_【Java自学】 反素数
  16. Vue2:网易云播放音乐并实现同步一次显示一行歌词
  17. 11篇推荐系统入门必读经典论文(附下载链接)
  18. loo-cv验证matlab,高光谱成像系统的基本原理,高光谱成像技术在红肉食用品质中的应用进展...
  19. 有分工、无合作的大企业病
  20. PS学习之如何绘制圆角图形

热门文章

  1. 肝了一个月,我做了个免费的面试刷题网
  2. 好的软件人员必看的书
  3. 身上的WZSZF01重担开始减轻
  4. 进程通信之飞鸽传书2007绿色版
  5. 为什么Python不支持 i ++ 语法
  6. 学C++你绝不能错过的干货!
  7. 发年终奖了,创了新高
  8. 一条来自 GitHub 重磅消息!
  9. 初中学历做开发,3年在北京买了房,超过了99%的程序员!
  10. 2017年公众号32篇热文回看