抽象方法和虚方法有什么区别?

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

641077 次浏览

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

因此:

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

抽象函数不能有功能。你基本上是说,任何子类都必须给出自己版本的这个方法,但是它太笼统了,甚至不能尝试在父类中实现。

一个虚拟函数,基本上是说看,这是对子类来说可能不够好的功能。所以如果它足够好,使用这个方法,如果不是,然后覆盖我,并提供你自己的功能。

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

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

例如:

public abstract class myBase{//If you derive from this class you must implement this method. notice we have no method body here eitherpublic abstract void YouMustImplement();
//If you derive from this class you can change the behavior but are not required topublic virtual void YouCanOverride(){}}
public class MyBase{//This will not compile because you cannot have an abstract method in a non-abstract classpublic abstract void YouMustImplement();}

抽象方法总是虚拟的。它们不能有实现。

这是主要的区别。

基本上,如果您拥有虚拟方法的“默认”实现并希望允许后代更改其行为,您将使用虚拟方法。

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

当您希望继承者根据需要扩展功能时,您基本上使用虚拟方法。

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

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

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

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

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

  1. 只有abstract类可以有abstract成员。
  2. abstract必须override继承其abstract成员的非abstract类。
  3. abstract成员隐式为virtual
  4. abstract成员不能提供任何实现(abstract在某些语言中称为pure virtual)。

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

虚方法是可以在派生类中使用覆盖的方法,取代超类中的行为。如果你不覆盖,你会得到原始行为。如果你这样做了,你总是得到新的行为。这与不能被覆盖但可以隐藏原始方法的非虚拟方法相反。这是使用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时,我得到“嗨,那里”和“稍后见”。如果我调用HelloGoodbye,我得到“你好”和“稍后见”。这是因为SayGoodbye是虚拟的,可以用派生类替换。SayHello只是隐藏的,所以当我从我的基类调用它时,我得到了我原来的方法。

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

我的理解是:

抽象方法:

只有抽象类可以保存抽象方法。派生类也需要实现方法,类中不提供实现。

虚拟方法:

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

我通过对以下类(来自其他答案)进行一些改进来简化这一点:

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

答案已经提供了很多次,但何时使用每个方法的问题是设计时的决定。我认为尝试将常见方法定义捆绑到不同的接口中,并将它们拉入适当抽象级别的类中是一种很好的做法。当最好定义一个实现了一组简洁接口的非抽象类时,将一组常见的抽象和虚方法定义倾倒到一个类中会使该类变得不确定。一如既往,这取决于什么最适合你的应用程序特定需求。

抽象功能:

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

虚拟功能:

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

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

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

**感觉就像

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

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

我想说…如果我们要实现一个抽象类,您必须重写基抽象类中的抽象方法。因为…实现抽象方法是使用覆盖关键字。类似于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();}}

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

虚函数将有一个主体,可能会也可能不会被子类覆盖

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

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

抽象方法

想想乔治·W·布什吧。他对他的士兵们说:“去伊拉克打仗吧。”就这样。他只具体说明了必须打仗。他没有具体说明会发生什么。但我的意思是,你不能只是出去“打仗”:那到底是什么意思?我是用B-52还是我的德林枪打仗?这些具体细节留给别人。这是一种抽象的方法。

虚拟方法

大卫·彼得雷乌斯是军队的高层。他定义了战斗的含义:

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

问题是,这是一个非常笼统的方法。这是一个有效的好方法,但有时不够具体。对彼得雷乌斯来说,好事是他的命令有回旋余地和范围——他允许其他人根据他们的特殊要求改变他对“战斗”的定义。

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

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

努里·马利基也从彼得雷乌斯那里得到同样的命令。他也要战斗。但他是一个政治家,而不是步兵。显然,他不能四处射击他的政治敌人的头部。因为彼得雷乌斯给了他一个虚拟的方法,那么马利基可以根据他的特殊情况实施他自己版本的战斗方法:

  1. 找到敌人。
  2. 以一些捏造的罪名逮捕他。
  3. 回家吧
  4. 喝啤酒。

换句话说,虚拟方法提供了样板指令-但这些是一般指令,可以由军队继承人根据他们的特殊情况更具体地制定。

两者之间的区别

  • 乔治布什没有证明任何实现细节。这必须由其他人提供。这是一个抽象方法。

  • 另一方面,彼得雷乌斯确实提供了实施细节,但他已经允许他的下属用自己的版本覆盖他的命令,如果他们能想出更好的东西。

希望有帮助。

抽象函数(方法):

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

它没有身体。

●它应该由派生类实现。

如果一个方法是抽象的,那么这个类就应该是抽象的。

虚函数(方法):

●虚拟方法是使用关键字虚拟声明的方法,并且可以通过使用over的关键字被派生类方法覆盖。

●是否重写它取决于派生类。

C#中没有什么叫做虚拟类。

对于函数

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

你可以根据你的要求来决定。

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

背景

我在一栋大楼的21层工作。我对火灾很偏执。时不时地,在世界的某个地方,一场大火正在烧毁一座摩天大楼。但幸运的是,我们这里有一本指南,说明发生火灾时该怎么做:

FireEscape()

  1. 不要收集物品
  2. 步行到消防通道
  3. 走出大楼

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

虚拟方法

这个计划在99%的情况下都很不错。这是一个有效的基本计划。但是有1%的可能性,消防通道被堵塞或损坏,在这种情况下,你完全完蛋了,除非你采取一些激烈的行动,否则你将成为烤面包。使用虚拟方法,你可以做到这一点:你可以用自己的计划版本覆盖基本的FireEscape()计划:

  1. 跑到窗口
  2. 从窗户跳出去
  3. 安全降落到底部

换句话说,如果程序员认为合适,子类可以覆盖父类的虚拟方法。

抽象方法

并非所有组织都经过良好的训练。一些组织没有进行消防演习。他们没有整体的逃生政策。每个人都为自己服务。管理层只对现有的这种政策感兴趣。

换句话说,每个人都要开发自己的FireEscape()方法。一个人会从消防通道走出来,另一个人会跳伞,另一个人会使用火箭推进技术飞离大楼,另一个人会从高空飞出去。管理层不在乎你是否能逃出,只要你有一个基本的FireEscape()计划——如果他们没有,你可以保证OHS会像吨砖头一样落在组织身上。这就是抽象方法的含义。

这两者又有什么区别呢?

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

这并不难,不是吗?

面向对象:

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

关于虚拟功能:当你在父类中放置一个虚拟方法时,你对派生类说:嘿,这里有一个为你做某事的功能。如果这对你有用,就使用它。如果没有,覆盖它并实现你的代码,甚至你可以在你的代码中使用我的实现!

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

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

后期装订表示我们使用该名称,但推迟映射。换句话说,我们首先创建/提及名称,并让一些后续进程处理代码到该名称的映射。

现在考虑:

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

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

virtual表示:

"亲爱的运行时,通过做你最擅长的事情将适当的代码绑定到此名称:搜索"

abstract表示:

“亲爱的程序员,请通过做你最擅长的事情将适当的代码绑定到此名称:发明

为了完整起见,超载表示:

“亲爱的编译器,通过做你最擅长的事情将适当的代码绑定到此名称:排序”。

虚拟方法

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

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

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

抽象方法

  • 抽象意味着我们必须覆盖它。

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

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

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

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

抽象方法没有实现,在父类中声明,子类负责实现抽象方法

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

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

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

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

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

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

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

使用抽象类和方法主要是为了避免在设计类时关注实现细节,类结构与实现过于相关,从而在它们之间协作的类之间创建依赖和耦合。

虚函数或虚方法只是一个模拟类的公共行为的方法,但我们可以在继承链中自由修改它,因为我们认为子类可能需要为该行为实现一些特定的扩展。

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

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

我们为我们的解决方案的主要对象设计一个很好的抽象结构,然后通过定位那些更容易进一步专业化的对象来创建基本实现,我们将这些实现作为虚拟化,最后我们专门化我们的基本实现,最终“覆盖”继承的虚拟实现。

这里我写了一些示例代码,希望这可能是一个相当有形的例子,可以在非常基本的层面上看到接口、抽象类和普通类的行为。如果你想将其用作演示,你也可以在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 AbstractClasspublic void StandardMethod(){System.out.println("Standard Method in AbstractClass (super) runs");}}
public class ConcreteClassextends AbstractClassimplements ExampleInterface{
//Abstract Method HAS TO be IMPLEMENTED in child class. Implemented by ConcreteClass@Overridepublic void AbstractMethod() {System.out.println("AbstractMethod overridden runs");}
//Standard Method CAN be OVERRIDDEN.@Overridepublic 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.@Overridepublic void MethodInInterface() {System.out.println("MethodInInterface Implemented by ConcreteClass runs");
//    Cannot declare abstract method in a concrete class//    public abstract void AbstractMethodDeclarationInConcreteClassNotPossible(){////    }}}

如果一个类从这个抽象类派生,那么它将被强制覆盖抽象成员。这与虚拟修饰符不同,后者指定可以选择性地覆盖该成员。

命题的传统三重分类

图。-命题的传统三重分类。

道义逻辑(义务和许可的研究)中,每个命题都是义务(“必须”运算符),可选(“可能和可能不”运算符)或不允许(“不得”运算符),并且没有命题属于这三个类别中的一个以上。

此外,允许(“可以”运算符)命题是强制性的或可选的命题,可省略的(“不得”运算符)命题是不允许的或可选的命题,非可选(“必须或不得”运算符)命题是强制性的或不允许的命题。

特别是,一个强制性的命题是允许的,一个不允许的命题是省略的。

将这些运算符应用于命题“方法被覆盖”会产生以下命题:

  • 抽象(纯)/具体方法:方法必须被覆盖/不能被覆盖;
  • 虚/实(最终)方法:该方法可以被覆盖/不能被覆盖。

特别是,抽象方法是虚拟的,真实方法是具体的。