抽象函数和虚函数有什么区别?


1578

抽象函数和虚函数有什么区别?建议在哪种情况下使用虚拟或抽象?哪种方法最好?


271
必须覆盖抽象函数,而可以覆盖虚拟函数。
乔丹·帕默

15
虚函数可以在基类中具有默认的/ generic实现。
马丁

5
这里的关键词是抽象的 ; 他们不存在,是的功能是什么(方法签名)只是一个模糊的概念
科尔约翰逊

Answers:


2734

抽象函数不能具有功能。您基本上是在说,任何子类都必须提供自己的该方法的版本,但是它太笼统了,甚至无法尝试在父类中实现。

虚函数基本上是在说看,这里的功能对于子类可能足够好,也可能不够好。因此,如果足够好,请使用此方法;否则,请覆盖我并提供您自己的功能。


396
当然,如果您覆盖虚拟方法,则始终可以通过调用base.Foo(...)
Brann

196
谢谢。这比MSDN文档中的任何内容都更好,更容易解释。(阅读五分钟后,我很头疼:msdn.microsoft.com/en-us/library/aa645767(v=vs.71 ) .aspx
杰克

15
来自Java,我有点困惑为什么我们需要使其完全虚拟化,直到我读到这:stackoverflow.com/a/1062126/193634
Rosdi Kasim 2013年

4
@MeqDotNet这意味着,如果您喜欢我的实现,请使用我,如果自己写的不比我更好:)
Usman Younas

16
这应该在Microsoft参考库中,我花了10分钟阅读并且仍然感到困惑。
SamChen 2014年

302

抽象函数没有实现,只能在抽象类上声明。这将强制派生类提供实现。

虚函数提供了默认实现,它可以存在于抽象类或非抽象类上。

因此,例如:

public abstract class myBase
{
    //If you derive from this class you must implement this method. notice we have no method body here either
    public abstract void YouMustImplement();

    //If you derive from this class you can change the behavior but are not required to
    public virtual void YouCanOverride()
    { 
    }
}

public class MyBase
{
   //This will not compile because you cannot have an abstract method in a non-abstract class
    public abstract void YouMustImplement();
}

28
查看示例代码非常有用-有助于使答案中的各种解释更加清晰。
西蒙·图西

2
我回滚了以前版本的答案:这两个类只是示例,第一类将被编译,因为它被标记为抽象,第二类则不会。MyBase是否继承自其他类无关紧要。
德克2014年

2
您的MyBase类不必以某种方式实现抽象类吗?我不经常这样做,所以我可能会误会。我在您的示例中看不到。
jp2code

2
在上面的示例中,MyBase显示了您无法执行的操作。也就是说,您不能在非抽象类中使用抽象方法
JoshBerke 2014年

80
  1. 只有abstract班级可以有abstract成员。
  2. 一个非abstract类从继承abstract必须 overrideabstract成员。
  3. 一个abstract成员是隐式virtual
  4. 一个abstract成员不能提供任何实现(abstract被称为pure virtual在某些语言)。

第三对我来说没有意义。我认为您的意思是说“抽象类的成员是隐式虚拟的”(即,您可以为其提供功能而不必指定它是虚拟的)。
Hobo Spider

5
不,我的意思就是我写的东西。抽象类的成员可以是virtual或非virtual。一个abstract成员(即抽象属性,抽象方法)就像是一个虚拟的方法,即可以覆盖它,但它不与自身携带的默认实现。
Mehrdad Afshari 2014年

引用“抽象成员是“隐式”虚拟的。” 但是我看到某个地方,有人通过显式添加“虚拟”关键字来创建抽象成员。实际上,这是没有必要的,但在我读完您的答案之前,这使我产生了疑问。
bonCodigo 2014年

请包括关于第4点的支持参考。您的帖子不会带来以前的帖子没有的其他任何内容。
拉斐尔

这只是一堆陈述,没有任何解释。
逆向工程


37

抽象功能:

  1. 它只能在抽象类内部声明。
  2. 它仅包含方法声明,而不包含抽象类中的实现。
  3. 必须在派生类中重写它。

虚函数:

  1. 它可以在抽象类以及非抽象类中声明。
  2. 它包含方法的实现。
  3. 它可能会被覆盖。

29

抽象方法:当类包含抽象方法时,必须将该类声明为抽象方法。抽象方法没有实现,因此,从该抽象类派生的类必须为该抽象方法提供一个实现。

虚方法:类可以具有虚方法。虚拟方法有一个实现。当您从具有虚拟方法的类继承时,可以覆盖该虚拟方法并提供其他逻辑,或者将逻辑替换为自己的实现。

何时使用什么:在某些情况下,您知道某些类型应具有特定的方法,但是您不知道此方法应具有的实现。
在这种情况下,您可以创建一个包含带有此签名的方法的接口。但是,如果遇到这种情况,但您知道该接口的实现者还将有另一个通用方法(您已经可以为其提供实现),则可以创建一个抽象类。然后,该抽象类包含抽象方法(必须重写)和另一个包含“公共”逻辑的方法。

如果您有一个可以直接使用的类,但是您希望继承者能够更改某些行为,但不是必须的,则应使用虚拟方法。


29

说明:类推。希望它将对您有所帮助。

语境

我在建筑物的21楼工作。我对火灾抱有偏执。时不时地,在世界某个地方,大火烧毁了刮板。但幸运的是,在这里,我们有一份说明手册,介绍发生火灾时的处理方法:

火灾逃生()

  1. 不要收拾财物
  2. 步行逃生
  3. 走出大楼

这基本上是一个称为FireEscape()的虚拟方法

虚方法

该计划在99%的情况下都非常好。这是可行的基本计划。但是只有1%的可能性防火通道会被阻塞或损坏,在这种情况下,您将完全被拧紧,除非采取严厉措施,否则您将变成烤面包。使用虚方法,您可以做到这一点:您可以使用自己的计划版本覆盖基本的FireEscape()计划:

  1. 跑到窗口
  2. 跳出窗外
  3. 安全降落伞到底部

换句话说,虚拟方法提供了一个基本计划,如果需要,可以将其覆盖。如果程序员认为合适,子类可以覆盖父类的虚拟方法。

抽象方法

并非所有组织都进行了深入研究。一些组织不进行消防演习。他们没有整体的逃生政策。每个人都是他自己。管理层仅对现有这样的政策感兴趣。

换句话说,每个人被迫发展自己的FireEscape()方法。一个人会走出防火梯。另一个家伙会降落伞。另一个家伙将使用火箭推进技术从建筑物上飞走。另一个家伙会逃走。只要您有基本的FireEscape()计划,管理层就不会在意您如何逃脱-如果没有,您可以保证OHS会像一吨砖一样落在组织上。这就是抽象方法的含义。

两者又有什么区别?

抽象方法:子类被强制实现自己的FireEscape方法。使用虚拟方法,您有一个基本的计划在等着您,但是如果不够好,可以选择实施自己的计划。

现在不是那么难吗?


22

抽象方法是必须实现以构成具体类的方法。该声明在抽象类中(任何具有抽象方法的类都必须是抽象类),并且必须在具体类中实现。

虚方法是一种可以使用覆盖在派生类中覆盖,替换超类中的行为的方法。如果不覆盖,则会得到原始行为。如果这样做,您总是会得到新的行为。这与非虚拟方法相反,后者不能被覆盖但可以隐藏原始方法。这是使用new修饰符完成的。

请参见以下示例:

public class BaseClass
{
    public void SayHello()
    {
        Console.WriteLine("Hello");
    }


    public virtual void SayGoodbye()
    {
        Console.WriteLine("Goodbye");
    }

    public void HelloGoodbye()
    {
        this.SayHello();
        this.SayGoodbye();
    }
}


public class DerivedClass : BaseClass
{
    public new void SayHello()
    {
        Console.WriteLine("Hi There");
    }


    public override void SayGoodbye()
    {
        Console.WriteLine("See you later");
    }
}

当我实例化DerivedClass并调用SayHello或时SayGoodbye,我得到“嗨,那里”和“以后见”。如果我致电HelloGoodbye,我会收到“你好”和“稍后见”。这是因为它SayGoodbye是虚拟的,可以用派生类代替。SayHello只是隐藏的,所以当我从基类中调用它时,便得到了原始方法。

抽象方法是隐式虚拟的。它们定义了必须存在的行为,更像是界面。


9

抽象方法始终是虚拟的。他们无法实现。

那是主要区别。

基本上,如果您具有虚拟方法的实现,并且希望允许后代更改其行为,则可以使用虚拟方法。

使用抽象方法,您可以强制后代提供实现。


9

通过对以下类进行一些改进(通过其他答案),我使此过程变得更简单:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestOO
{
    class Program
    {
        static void Main(string[] args)
        {
            BaseClass _base = new BaseClass();
            Console.WriteLine("Calling virtual method directly");
            _base.SayHello();
            Console.WriteLine("Calling single method directly");
            _base.SayGoodbye();

            DerivedClass _derived = new DerivedClass();
            Console.WriteLine("Calling new method from derived class");
            _derived.SayHello();
            Console.WriteLine("Calling overrided method from derived class");
            _derived.SayGoodbye();

            DerivedClass2 _derived2 = new DerivedClass2();
            Console.WriteLine("Calling new method from derived2 class");
            _derived2.SayHello();
            Console.WriteLine("Calling overrided method from derived2 class");
            _derived2.SayGoodbye();
            Console.ReadLine();
        }
    }


    public class BaseClass
    {
        public void SayHello()
        {
            Console.WriteLine("Hello\n");
        }
        public virtual void SayGoodbye()
        {
            Console.WriteLine("Goodbye\n");
        }

        public void HelloGoodbye()
        {
            this.SayHello();
            this.SayGoodbye();
        }
    }


    public abstract class AbstractClass
    {
        public void SayHello()
        {
            Console.WriteLine("Hello\n");
        }


        //public virtual void SayGoodbye()
        //{
        //    Console.WriteLine("Goodbye\n");
        //}
        public abstract void SayGoodbye();
    }


    public class DerivedClass : BaseClass
    {
        public new void SayHello()
        {
            Console.WriteLine("Hi There");
        }

        public override void SayGoodbye()
        {
            Console.WriteLine("See you later");
        }
    }

    public class DerivedClass2 : AbstractClass
    {
        public new void SayHello()
        {
            Console.WriteLine("Hi There");
        }
        // We should use the override keyword with abstract types
        //public new void SayGoodbye()
        //{
        //    Console.WriteLine("See you later2");
        //}
        public override void SayGoodbye()
        {
            Console.WriteLine("See you later");
        }
    }
}

6

绑定是将名称映射到代码单元的过程。

后期绑定意味着我们使用了名称,但是推迟了映射。换句话说,我们首先创建/提及该名称,然后让一些后续过程处理代码到该名称的映射。

现在考虑:

  • 与人类相比,机器真的很擅长搜索和分类
  • 与机器相比,人类真的很擅长发明和创新

因此,简短的答案是:virtual是机器的后期绑定指令(运行时),而人的abstract是后期绑定指令(程序员)

换句话说,virtual是指:

“亲爱的运行时,通过尽力而为将适当的代码绑定到该名称:搜索

鉴于abstract

“亲爱的程序员,请通过做自己最擅长的工作:将适当的代码绑定到该名称上:发明

为了完整性,重载是指:

“亲爱的编译器,请执行您最擅长的工作:将适当的代码绑定到该名称:排序 ”。


3

如果希望继承者扩展他们想要的功能,则基本上可以使用虚拟方法。

当您希望继承者实现功能时,可以使用抽象方法(在这种情况下,他们别无选择)


3

虚方法

  • 虚拟意味着我们可以覆盖它。

  • 虚函数有一个实现。当我们继承该类时,我们可以覆盖虚函数并提供我们自己的逻辑。


  • 在子类中实现该函数时,我们可以更改Virtual函数的返回类型(可以说是
    Shadowing 的概念)。

抽象方法

  • 抽象意味着我们必须重写它。

  • 抽象函数没有实现,必须在抽象类中。

  • 它只能被声明。这将强制派生类提供其实现。

  • 抽象成员是隐式虚拟的。在某些语言中,抽象可以称为纯虚拟的。

    public abstract class BaseClass
    { 
        protected abstract void xAbstractMethod();
    
        public virtual void xVirtualMethod()
        {
            var x = 3 + 4;
        }
    } 
    

2

我在某些地方看到了抽象方法的定义如下。**

“必须在子类中实现抽象方法”

**我感觉就像。

如果子类也是abstract不必在子类中实现抽象方法。

1)抽象方法不能是私有方法。2)不能在同一抽象类中实现Abstract方法。

我要说的是..如果我们要实现一个抽象类,则必须重写基本抽象类中的抽象方法。因为..实现抽象方法是使用覆盖关键字。类似于Virtual方法。

不必在继承的类中实现虚拟方法。

                                 ----------CODE--------------

public abstract class BaseClass
{
    public int MyProperty { get; set; }
    protected abstract void MyAbstractMethod();

    public virtual void MyVirtualMethod()
    {
        var x = 3 + 4;
    }

}
public abstract class myClassA : BaseClass
{
    public int MyProperty { get; set; }
    //not necessary to implement an abstract method if the child class is also abstract.

    protected override void MyAbstractMethod()
    {
        throw new NotImplementedException();
    }
}
public class myClassB : BaseClass
{
    public int MyProperty { get; set; }
    //You must have to implement the abstract method since this class is not an abstract class.

    protected override void MyAbstractMethod()
    {
        throw new NotImplementedException();
    }
}

2

上面的大多数示例都使用代码-而且非常好。我无需多说,但以下是使用类比而不是代码/技术术语的简单说明。

简单说明-使用类比说明

抽象方法

想想乔治·W·布什。他对士兵说:“去伊拉克打仗”。就是这样。他所指定的只是必须打架。他没有具体说明如何发生。但是我的意思是,你不能只是出去战斗而已:这到底是什么意思?我要和B-52或我的杀人狂战斗吗?这些具体细节留给别人。这是一种抽象方法。

虚方法

大卫·彼得雷乌斯(David Petraeus)参军。他定义了战斗的含义:

  1. 寻找敌人
  2. 中和他。
  3. 之后喝啤酒

问题在于这是一种非常通用的方法。这是一个行之有效的好方法,但有时不够具体。对彼得雷乌斯来说,好事是他的命令有余地和范围-他允许其他人根据他们的特殊要求更改他对“战斗”的定义。

Private Job Bloggs阅读Petraeus的命令,并根据他的特殊要求获准实施自己的战斗版本:

  1. 寻找敌人。
  2. 射中他的头。
  3. 回家
  4. 喝啤酒

努里·马利基(Nouri al Maliki)也收到彼得雷乌斯(Petraeus)的同样命令。他也要战斗。但是他是政客,而不是步兵。显然,他不能四处射击他的政客敌人。由于彼得雷乌斯为他提供了一种虚拟方法,因此马利基可以根据自己的具体情况实施自己的战斗方法版本:

  1. 寻找敌人。
  2. 让他以一些BS大败诉被捕。
  3. 回家
  4. 喝啤酒

换句话说,虚拟方法提供样板指令-但这只是一般性指令,军队中的各个阶层的人可以根据自己的具体情况使这些指令更加具体。

两者的区别

  • 乔治·布什(George Bush)没有证明任何实施细节。这必须由其他人提供。这是一种抽象方法。

  • 另一方面 Petraeus 确实提供了实现细节,但他已允许他的下属使用他们自己的版本覆盖其命令,如果他们能提出更好的建议。

希望能有所帮助。


2

抽象函数(方法):

●抽象方法是使用关键字abstract声明的方法。

●它没有身体。

●应该由派生类实现。

●如果方法是抽象的,则该类应该抽象。

虚函数(方法):

●虚拟方法是用关键字virtual声明的方法,可以通过使用override关键字由派生类方法覆盖。

●是否覆盖它取决于派生类。


1

已经多次提供了答案,但是有关何时使用每种方法的问题是设计时的决定。我将尝试将常用方法定义捆绑到不同的接口中并将它们拉入适当的抽象级别的类中作为一种好习惯。当最好定义一个实现一组简洁接口的非抽象类时,将一组常见的抽象和虚拟方法定义转储到一个类中会使该类变得不可靠。与往常一样,这取决于最适合您的应用程序特定需求的内容。


1

抽象函数不能具有主体,并且必须由子类覆盖

虚函数将具有主体,并且可能会或可能不会被子类覆盖


1

从一般面向对象的角度来看:

关于抽象方法:实际上,当您将抽象方法放在父类中时,您对子类说的是:嘿,请注意您具有这样的方法签名。如果您想使用它,则应该自己实现!

关于虚函数:在父类中放置虚方法时,您对派生类说的是:嘿,这里有一项功能可以为您做一些事情。如果这对您有用,请使用它。如果没有,重写它并实现您的代码,即使您可以在代码中使用我的实现!

这是关于通用OO中这两个概念之间差异的一些哲学


1

抽象函数只是“签名”签名,没有实现。在接口中使用它来声明如何使用该类。它必须在派生类之一中实现。

虚函数(实际上是方法)也是您声明的函数,应在继承层次结构类之一中实现。

此类的继承实例也将在较低的层次结构类中继承实现(除非您实现了该实现)。


0

C#中没有调用虚拟类的内容。

对于功能

  1. 抽象函数仅具有签名,驱动器类应覆盖功能。
  2. 虚拟功能将保留驱动器类根据功能的要求可以覆盖或不覆盖的部分功能

您可以根据自己的要求决定。


0

抽象方法没有实现,它是在父类中声明的。子类可用于实现该方法。

虚方法应该在父类中具有一个实现,并且它方便子类选择是使用父类的该实现还是为子类中的该方法为其自身提供新的实现。


0

从C ++的背景来看,C#虚拟对应于C ++虚拟,而C#抽象方法对应于C ++纯虚函数


0

抽象函数或方法是类公开的公共“操作名称”,其目的与抽象类一起,主要是在对象设计中针对对象必须实现的结构提供了一种约束形式。

实际上,从其抽象类继承的类必须为此方法提供实现,通常,编译器在不这样做时会引发错误。

使用抽象类和方法非常重要,这可以避免在设计类时只关注实现细节,从而使类结构与实现过于相关,从而在相互协作的类之间建立依赖关系和耦合。

虚函数或方法只是为类的公共行为建模的方法,但是我们可以自由地在继承链中对其进行修改,因为我们认为子类可能需要对该行为进行某些特定的扩展。

它们都代表了面向对象范式中的多态性形式。

我们可以一起使用抽象方法和虚函数来支持良好的继承模型。

我们设计了解决方案主要对象的良好抽象结构,然后通过查找更易于进一步专业化的对象来创建基本实现,并将这些实现设为虚拟,最后,我们对基本实现进行了专业化,最终“覆盖”了继承的虚拟实现。


0

在这里,我正在编写一些示例代码,希望这可能是一个非常实际的示例,以便在非常基本的级别上查看接口,抽象类和普通类的行为。如果要使用它作为演示,也可以在github中找到该代码作为项目:https : //github.com/usavas/JavaAbstractAndInterfaceDemo

public interface ExampleInterface {

//    public void MethodBodyInInterfaceNotPossible(){
//    }
    void MethodInInterface();

}

public abstract class AbstractClass {
    public abstract void AbstractMethod();

    //    public abstract void AbstractMethodWithBodyNotPossible(){
    //
    //    };

    //Standard Method CAN be declared in AbstractClass
    public void StandardMethod(){
        System.out.println("Standard Method in AbstractClass (super) runs");
    }
}

public class ConcreteClass
    extends AbstractClass
    implements ExampleInterface{

    //Abstract Method HAS TO be IMPLEMENTED in child class. Implemented by ConcreteClass
    @Override
    public void AbstractMethod() {
        System.out.println("AbstractMethod overridden runs");
    }

    //Standard Method CAN be OVERRIDDEN.
    @Override
    public void StandardMethod() {
        super.StandardMethod();
        System.out.println("StandardMethod overridden in ConcreteClass runs");
    }

    public void ConcreteMethod(){
        System.out.println("Concrete method runs");
    }

    //A method in interface HAS TO be IMPLEMENTED in implementer class.
    @Override
    public void MethodInInterface() {
        System.out.println("MethodInInterface Implemented by ConcreteClass runs");

    //    Cannot declare abstract method in a concrete class
    //    public abstract void AbstractMethodDeclarationInConcreteClassNotPossible(){
    //
    //    }
    }
}

0

如果一个类从该抽象类派生,则将其强制重写该抽象成员。这与虚拟修饰符不同,后者指定可以有选择地覆盖成员。


-4

据我了解:

抽象方法:

只有抽象类可以容纳抽象方法。同样,派生类需要实现该方法,并且该类中未提供任何实现。

虚方法:

一个类可以声明它们,也可以提供它们的实现。派生类也需要实现该方法以覆盖它。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.