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

问题描述 投票:1474回答:26

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

oop programming-languages abstract virtual-functions
26个回答
2590
投票

抽象函数不具备功能。你基本上是在说,任何一个子类必须给出他们自己的这个方法的版本,但是它太普遍甚至不能尝试在父类中实现。

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


10
投票

我通过对以下类(从其他答案)进行一些改进使这更简单:

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
投票

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

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

**我觉得它就像。

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

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();
    }
}

3
投票

虚拟方法:

  • 虚拟意味着我们可以覆盖它。
  • 虚函数有一个实现。当我们继承类时,我们可以覆盖虚函数并提供我们自己的逻辑。
  • 我们可以在实现时更改虚函数的返回类型 子类中的函数(可以说是一个概念) 阴影)。

抽象方法

  • 摘要意味着我们必须覆盖它。
  • 抽象函数没有实现,必须在抽象类中。
  • 它只能被声明。这会强制派生类提供它的实现。
  • 抽象成员是隐式虚拟的。在某些语言中,抽象可以称为纯虚拟。 public abstract class BaseClass { protected abstract void xAbstractMethod(); public virtual void xVirtualMethod() { var x = 3 + 4; } }

2
投票

抽象函数不能有一个体,必须被子类覆盖

虚函数将有一个主体,可能会或可能不会被子类重写


2
投票

以上大多数示例都使用代码 - 而且它们非常好。我不需要添加他们所说的内容,但以下是使用类比而不是代码/技术术语的简单解释。

简单说明 - 使用类比的解释

抽象方法

想想乔治W布什。他对他的士兵说:“去伊拉克战斗”。就是这样。他所指出的只是战斗必须要做。他没有说明究竟会发生什么。但我的意思是,你不能只是出去“打架”:这究竟是什么意思?我和B-52或我的刽子手打架?这些具体细节留给其他人。这是一种抽象方法。

虚方法

大卫彼得雷乌斯在军队中处于领先地位。他已经确定了战斗意味着什么:

  1. 找到敌人
  2. 中立他。
  3. 之后喝啤酒

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

私人工作博客阅读彼得雷乌斯的命令,并根据他的特殊要求获准实施自己的战斗版本:

  1. 寻找敌人。
  2. 把他射中脑袋。
  3. 回家
  4. 喝啤酒。

Nouri al Maliki也从彼得雷乌斯收到同样的命令。他也要战斗。但他是政治家,而不是步兵。显然他无法绕过他的政治敌人。因为彼得雷乌斯给了他一个虚拟方法,所以马利基可以根据他的具体情况实施他自己的战斗方法版本:

  1. 寻找敌人。
  2. 让他被一些BS逮捕指控逮捕他。
  3. 回家
  4. 喝啤酒。

换句话说,虚拟方法提供了样板指令 - 但这些是一般性指令,根据其特定情况,军队层面下的人可以更具体地说明这些指令。

两者的区别

  • 乔治布什没有证明任何实施细节。这必须由其他人提供。这是一种抽象方法。
  • 另一方面,彼得雷乌斯确实提供了实施细节,但他已经允许他的下属用他们自己的版本覆盖他的订单,如果他们能够提出更好的东西。

希望有所帮助。


2
投票

抽象函数(方法):

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

●没有身体。

●它应该由派生类实现。

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

虚函数(方法):

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

●由派生类决定是否覆盖它。


1
投票

答案已多次提供,但关于何时使用每个答案的问题是设计时决定。我认为尝试将常用方法定义捆绑到不同的接口并将它们拉入适当抽象级别的类是一种很好的做法。将一组通用的抽象和虚拟方法定义转储到类中时,如果最好定义一个实现一组简洁接口的非抽象类,则会使该类不可复制。与往常一样,它取决于最适合您的应用程序的特定需求。


1
投票

从一般面向对象的角度看: 关于抽象方法:当你在父类中放置一个抽象方法实际上你正在对子类说:嘿注意你有这样的方法签名。如果你想使用它,你应该实现自己的! 关于虚函数:当您在父类中放置虚拟方法时,您要对派生类说:嘿,这里有一个功能可以为您做一些事情。如果这对您有用,请使用它。如果没有,请覆盖它并实现您的代码,即使您可以在代码中使用我的实现! 这是关于通用OO中这两个概念之间存在差异的一些哲学


276
投票

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

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

例如:

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();
}

1
投票

在C#中没有任何调用虚拟类。

对于功能

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

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


1
投票

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

虚方法应该在父类中有一个实现,它有助于子类选择是使用父类的实现还是为子类中的该方法创建一个新的实现。


1
投票

抽象函数只是一个签名,没有实现。它在接口中用于声明如何使用类。它必须在其中一个派生类中实现。

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

除非您在较低层次结构的类中实现它,否则此类的继承实例也会继承该实现。


0
投票

从C ++背景来看,C#virtual对应C ++虚拟,而C#abstract方法对应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(){
    //
    //    }
    }
}

-4
投票

据我所知:

抽象方法:

只有抽象类可以包含抽象方法。派生类也需要实现该方法,并且在类中没有提供实现。

虚拟方法:

一个类可以声明这些并且也提供相同的实现。派生类也需要实现该方法来覆盖它。


76
投票
  1. 只有abstract类可以有abstract成员。
  2. 继承自abstract类的非abstract类必须overrideabstract成员。
  3. 一个abstract成员隐含virtual
  4. abstract成员不能提供任何实现(abstract在某些语言中称为pure virtual)。

59
投票

您必须始终覆盖抽象函数。

从而:

  • 抽象函数 - 当继承者必须提供自己的实现时
  • 虚拟 - 何时由继承人决定

35
投票

摘要功能:

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

虚函数:

  1. 它可以在abstract和non abstract类中声明。
  2. 它包含方法实现。
  3. 它可能被覆盖了。

29
投票

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

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

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

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


26
投票

解释:用类比。希望它会对你有所帮助。

上下文

我在一栋建筑的21楼工作。我对火很偏执。在世界的某个地方,每时每刻都有一场大火烧毁着天空刮板。但幸运的是,我们在这里有一个说明手册,如果发生火灾该怎么办:

FireEscape()

  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并调用SayHelloSayGoodbye时,我得到“Hi There”和“See you later”。如果我打电话给HelloGoodbye,我会得到“你好”和“见到你”。这是因为SayGoodbye是虚拟的,可以用派生类替换。 SayHello只是隐藏的,所以当我从我的基类调用它时,我得到了我的原始方法。

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


10
投票

抽象方法总是虚拟的。他们无法实施。

这是主要的区别。

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

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

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.