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

这篇文章比较了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 deriv

deriv 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 base

base 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 base

deriv 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 derived

Process finished with exit code 0


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


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

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

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值