抽象函数和虚函数有什么区别?在哪些情况下建议使用虚拟或抽象?哪一个是最好的方法?
答案 0 :(得分:2549)
抽象函数不能具有功能。你基本上说,任何子类必须给出自己的这个方法版本,但是它太普遍甚至不能尝试在父类中实现。
一个虚函数,基本上就是说看,这里的功能对于子类来说可能是也可能不够好。因此,如果它足够好,请使用此方法,否则,请覆盖我,并提供您自己的功能。
答案 1 :(得分:270)
抽象函数没有实现,只能在抽象类上声明。这会强制派生类提供实现。虚函数提供默认实现,它可以存在于抽象类或非抽象类中。例如:
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();
}
答案 2 :(得分:76)
abstract
个类可以有abstract
个成员。abstract
类的非abstract
类必须 override
其abstract
成员。abstract
成员隐式virtual
。abstract
成员无法提供任何实施(abstract
在某些语言中称为pure virtual
。答案 3 :(得分:58)
您必须始终覆盖抽象函数。
因此:
答案 4 :(得分:35)
摘要功能:
虚拟功能:
答案 5 :(得分:29)
抽象方法: 当类包含抽象方法时,该类必须声明为抽象。 抽象方法没有实现,因此,从该抽象类派生的类必须为此抽象方法提供实现。
虚拟方法: 一个类可以有一个虚方法。虚方法有一个实现。 当您从具有虚方法的类继承时,可以覆盖虚方法并提供其他逻辑,或者用您自己的实现替换逻辑。
何时使用: 在某些情况下,您知道某些类型应该具有特定方法,但是,您不知道此方法应该具有哪种实现方式 在这种情况下,您可以创建一个包含具有此签名的方法的接口。 但是,如果您有这种情况,但是您知道该接口的实现者还将有另一个常用方法(您已经可以为其提供实现),则可以创建一个抽象类。 然后,这个抽象类包含抽象方法(必须覆盖),以及另一个包含“公共”逻辑的方法。
如果您有一个可以直接使用的类,但是您希望继承者能够更改某些行为,但应该使用虚方法,尽管它不是必需的。
答案 6 :(得分:25)
解释:与类比。希望它会对你有所帮助。
<强>上下文强>
我在一栋建筑的21楼工作。而且我对火灾感到妄想。在世界的某个地方,每时每刻都有一场大火烧毁着天空刮板。但幸运的是,我们在这里有一个说明手册,以防火灾时该怎么做:
<强> FireEscape()强>
这基本上是一个名为 FireEscape()
的虚拟方法虚拟方法
对于99%的情况,这个计划非常好。这是一个有效的基本计划。但是火灾逃生被阻挡或损坏的可能性有1%,在这种情况下,你完全被拧紧,除非你采取一些激烈的行动,否则你会变成烤面包。使用虚拟方法,您可以这样做:您可以使用自己的计划版本覆盖基本的FireEscape()计划:
换句话说,虚拟方法提供了一个基本计划,如果您需要,可以覆盖它。子类可以覆盖父类&#39;程序员认为合适的虚拟方法。
抽象方法
并非所有组织都经过精心培训。有些组织不进行消防演习。他们没有全面的逃生政策。每个人都是为了自己。管理层只对这样的政策感兴趣。
换句话说,每个人强制开发自己的FireEscape()方法。一个人会走出火灾逃生。另一个人会降落伞。另一个人将使用火箭推进技术飞离建筑物。另一个人会下降。只要你有一个基本的FireEscape()计划,管理层就不会关心你如何逃脱 - 如果他们不能保证,你可以保证OHS会像吨砖。这就是抽象方法的含义。
两者之间又有什么区别?
抽象方法:子类是 强制 来实现自己的FireEscape方法。使用虚拟方法,您有一个基本计划等着您,但如果它不够好,可以选择来实现您自己的。
现在那不是很难吗?
答案 7 :(得分: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
时,我会收到“Hi There”和“See you later”。如果我拨打HelloGoodbye
,我会收到“你好”和“以后见”。这是因为SayGoodbye
是虚拟的,可以用派生类替换。 SayHello
只是隐藏的,所以当我从我的基类调用它时,我得到了我原来的方法。
抽象方法是隐式虚拟的。它们定义必须存在的行为,更像界面。
答案 8 :(得分:10)
抽象方法总是虚拟的。他们无法实施。
这是主要区别。
基本上,如果您具有虚拟方法,并且希望允许后代更改其行为,则可以使用虚方法。
使用抽象方法,强制后代提供实现。
答案 9 :(得分: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");
}
}
}
答案 10 :(得分:6)
绑定是将名称映射到代码单元的过程。
后期绑定表示我们使用该名称,但推迟映射。换句话说,我们首先创建/提到名称,然后让一些后续进程处理代码到该名称的映射。
现在考虑:
所以,简短的回答是:virtual
是机器的后期绑定指令(运行时),而abstract
是人类(程序员)的后期绑定指令
换句话说,virtual
表示:
“亲爱的运行时,通过尽你所能将相应的代码绑定到此名称:搜索”
abstract
表示:
“亲爱的程序员,请按照您最擅长的方式将相应的代码绑定到此名称:发明”
为了完整起见,重载意味着:
“亲爱的编译器,通过执行您最擅长的操作,将相应的代码绑定到此名称:排序”。
答案 11 :(得分:3)
当你希望继承者扩展他们想要的功能时,你基本上使用虚拟方法。
当您希望继承者实现该功能时,您可以使用抽象方法(在这种情况下,他们别无选择)
答案 12 :(得分:3)
虚拟方法:
虚拟意味着我们可以覆盖它。
虚拟功能有一个实现。当我们继承我们的班级时 可以覆盖虚函数并提供我们自己的逻辑。
抽象方法
摘要意味着我们必须覆盖它。
抽象函数没有实现,必须在抽象类中。
只能声明。这会强制派生类提供它的实现。
抽象成员是隐式虚拟的。在某些语言中,摘要可以称为纯虚拟。
public abstract class BaseClass
{
protected abstract void xAbstractMethod();
public virtual void xVirtualMethod()
{
var x = 3 + 4;
}
}
答案 13 :(得分:3)
我在某些地方已经看到抽象方法的定义如下。 **
&#34;抽象方法必须在子类中实现&#34;
** 我觉得它就像。
没有必要在子类中实现抽象方法,如果子类也是抽象的 ..
1)抽象方法 cant 是私有方法。 2)抽象方法 cant 可以在同一个抽象类中实现。
我想说..如果我们要实现一个抽象类,你必须覆盖基本抽象类中的抽象方法。 因为..实现抽象方法是使用覆盖关键字。与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();
}
}
答案 14 :(得分:2)
上面的大多数示例都使用代码 - 而且它们非常好。我不需要添加他们所说的内容,但以下是使用类比而不是代码/技术术语的简单解释。
简单说明 - 使用类比解释
抽象方法
想想乔治W布什。他对他的士兵说:“去伊拉克战斗”。就是这样。他所指出的只是战斗必须要做。他没有详细说明 将如何发生。但我的意思是,你不能只是出去“打架”:这究竟是什么意思?我和B-52或我的刽子手打架?这些具体细节留给其他人。这是一种抽象的方法。
虚拟方法
大卫彼得雷乌斯在军队中处于领先地位。他已经确定了战斗意味着什么:
问题在于它是一种非常通用的方法。这是一种有效的方法,但有时不够具体。彼得雷乌斯的好处在于他的命令具有余地和范围 - 他允许其他人根据他们的特殊要求改变他对“战斗”的定义。
私人工作博客阅读彼得雷乌斯的命令,并根据他的特殊要求获准实施自己的战斗版本:
Nouri al Maliki也收到彼得雷乌斯的同样命令。他也要战斗。但他是政治家,而不是步兵。显然他无法绕过他的政治敌人。因为彼得雷乌斯给了他一个虚拟方法,所以马利基可以根据他的具体情况实施他自己的战斗方法版本:
换句话说,虚拟方法提供了样板指令 - 但这些是一般性指令,根据他们的具体情况,军队层面的人可以更具体地说明这些指令。
两者之间的差异
乔治布什没有证明任何实施细节。这必须由其他人提供。这是一种抽象方法。
另一方面,彼得雷乌斯 提供了实施细节,但他已经允许下属用他们自己的版本覆盖他的订单,如果他们能提出更好的东西。
答案 15 :(得分:2)
抽象函数(方法):
●抽象方法是使用关键字abstract。
声明的方法●没有身体。
●它应该由派生类实现。
●如果方法是抽象的,那么该类应该抽象。
虚拟功能(方法):
●虚方法是使用关键字virtual声明的方法,可以使用override关键字覆盖派生类方法。
●由派生类决定是否覆盖它。
答案 16 :(得分:2)
抽象函数不能有一个正文,必须被子类重写
虚拟功能将有一个正文,可能会也可能不会被子类重写
答案 17 :(得分:1)
C#中没有调用虚拟类。
对于功能
您可以根据自己的要求做出决定。
答案 18 :(得分:1)
从一般面向对象的观点来看:
关于抽象方法:当你在父类中放置一个抽象方法实际上你正在对子类说:嘿注意你有这样的方法签名。如果你想使用它,你应该实现自己的!
关于虚函数:当您将虚拟方法放在父类中时,您要对派生类说:嘿,这里有一个功能可以为您做些什么。如果这对您有用,请使用它。如果没有,请覆盖它并实现您的代码,即使您可以在代码中使用我的实现!
这是关于通用OO中这两个概念之间存在差异的一些哲学
答案 19 :(得分:1)
答案已多次提供,但关于何时使用每个答案的问题是设计时决定。我认为尝试将常用方法定义捆绑到不同的接口并将它们拉入适当抽象级别的类是一种很好的做法。将一组通用的抽象和虚拟方法定义转储到类中时,如果最好定义一个实现一组简洁接口的非抽象类,则会使该类不可复制。与往常一样,它取决于最适合您的应用程序的特定需求。
答案 20 :(得分:1)
抽象方法没有实现。它在父类中声明。子类可用于实现该方法。
虚方法应该在父类中有一个实现,它有助于子类选择是使用父类的实现还是为子类中的该方法创建一个新的实现。
答案 21 :(得分:1)
抽象函数只是“签名”,没有实现。 在接口中使用它来声明如何使用该类。 它必须在派生类之一中实现。
虚拟函数(实际上是方法),也是您声明的函数,应在继承层次结构类之一中实现。
此类的继承实例也将在较低层次结构的类中继承实现,除非您实现它。
答案 22 :(得分:0)
从C ++的背景来看,C#虚拟对应于C ++虚拟,而C#抽象方法对应于C ++纯虚函数
答案 23 :(得分:0)
抽象函数或方法是类公开的公共“操作名称”,其目的与抽象类一起,主要是在对象设计中提供了一种针对结构的约束形式,对象必须实现。
实际上,从其抽象类继承的类必须对此方法进行实现,通常,编译器在不这样做时会引发错误。
使用抽象类和方法非常重要,这可以避免在设计类时只关注实现细节,从而使类结构与实现过于相关,从而在相互协作的类之间建立依赖关系和耦合。
虚函数或方法只是一种模拟类的公共行为的方法,但是我们可以在继承链中自由修改它,因为我们认为子类可能具有需要对该行为实施一些特定的扩展。
它们都代表面向对象范式中的多态性形式。
我们可以将抽象方法和虚函数一起使用以支持良好的继承模型。
我们设计了解决方案主要对象的良好抽象结构,然后通过查找更易于进一步专业化的对象来创建基本实现,并将它们制作为虚拟机,最后我们对基本实现进行了专业化,最终“覆盖”了继承的虚拟实现
答案 24 :(得分: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(){
//
// }
}
}
答案 25 :(得分:0)
如果一个类从该抽象类派生,则将其强制重写该抽象成员。这不同于虚拟修饰符,后者指定可以选择覆盖该成员。
答案 26 :(得分:0)
方法可以根据义务/缺乏和权限/缺乏覆盖它们来划分:
所以根据定义,抽象方法只能是虚的,而实方法只能是具体的。
答案 27 :(得分:-3)
据我所知:
抽象方法:
只有抽象类可以包含抽象方法。派生类也需要实现该方法,并且在类中没有提供实现。
虚拟方法:
一个类可以声明这些并且也提供相同的实现。此外,派生类需要实现该方法来覆盖它。